En guide til CI/CD i GitLab for den (nesten) absolutte nybegynneren
Eller hvordan du får vakre merker for prosjektet ditt på en kveld med enkel koding
Sannsynligvis har alle utviklere som har minst ett kjæledyrprosjekt på et tidspunkt en kløe på vakre merker med statuser, kodedekning, pakkeversjoner i nuget ... Og denne kløen førte til at jeg skrev denne artikkelen. Som forberedelse til å skrive den, fikk jeg denne skjønnheten i et av prosjektene mine:
Denne artikkelen vil lede deg gjennom det grunnleggende oppsettet av kontinuerlig integrasjon og levering for et .Net Core-klassebibliotekprosjekt i GitLab, publisere dokumentasjon til GitLab-sider og skyve innebygde pakker til en privat feed i Azure DevOps.
VS-kode ble brukt som utviklingsmiljø med utvidelsen GitLab arbeidsflyt (for å validere innstillingsfilen direkte fra utviklingsmiljøet).
Kort introduksjon
CD - er det når du bare presset, og alt har allerede falt på klienten?
Hva er CI / CD og hvorfor du trenger det - du kan enkelt google det. Finn fullstendig dokumentasjon om konfigurering av rørledninger i GitLab også lett. Her vil jeg kort og om mulig uten feil beskrive prosessen med systemet fra et fugleperspektiv:
utvikleren sender en forpliktelse til depotet, oppretter en sammenslåingsforespørsel gjennom nettstedet, eller på annen måte, eksplisitt eller implisitt starter rørledningen,
alle oppgaver velges fra konfigurasjonen, hvis betingelser gjør at de kan startes i den gitte konteksten,
oppgavene er organisert etter stadier,
trinn utføres etter tur - dvs. parallell alle oppgavene på dette stadiet er fullført,
hvis etappen mislykkes (dvs. minst én av oppgavene til etappen mislykkes), stopper rørledningen (nesten alltid),
hvis alle stadier er fullført, anses rørledningen som vellykket.
Dermed har vi:
pipeline - et sett med oppgaver organisert i stadier der du kan bygge, teste, pakke kode, distribuere en ferdig build til en skytjeneste, etc.,
scene (scene) — rørledningsorganisasjonsenhet, inneholder 1+ oppgave,
oppgave (jobb) er en arbeidsenhet i rørledningen. Den består av et skript (obligatorisk), lanseringsbetingelser, innstillinger for publisering/bufring av artefakter og mye mer.
Følgelig kommer oppgaven når du setter opp CI / CD ned til å lage et sett med oppgaver som implementerer alle nødvendige handlinger for å bygge, teste og publisere kode og artefakter.
Før du starter: hvorfor?
Hvorfor Gitlab?
For da det ble nødvendig å lage private depoter for kjæledyrprosjekter, ble de betalt på GitHub, og jeg var grådig. Lagrene har blitt gratis, men så langt er ikke dette nok grunn for meg til å flytte til GitHub.
Hvorfor ikke Azure DevOps Pipelines?
Fordi der er innstillingen elementær - kunnskap om kommandolinjen er ikke engang nødvendig. Integrasjon med eksterne git-leverandører - med et par klikk, import av SSH-nøkler for å sende commits til depotet - også, pipelinen konfigureres enkelt selv ikke fra en mal.
Utgangsposisjon: hva du har og hva du vil ha
Vi har:
repository i GitLab.
Vi vil:
automatisk montering og testing for hver sammenslåingsforespørsel,
bygge pakker for hver sammenslåingsforespørsel og skyve til masteren, forutsatt at det er en bestemt linje i commit-meldingen,
sende innebygde pakker til en privat feed i Azure DevOps,
sammenstilling av dokumentasjon og publisering i GitLab-sider,
merker!11
De beskrevne kravene faller organisk på følgende rørledningsmodell:
Trinn 1 - montering
Vi samler inn koden, publiserer utdatafilene som artefakter
Trinn 2 - testing
Vi henter artefakter fra byggestadiet, kjører tester, samler inn kodedekningsdata
Trinn 3 - Send inn
Oppgave 1 – bygg nuget-pakken og send den til Azure DevOps
Oppgave 2 - vi samler nettstedet fra xmldoc i kildekoden og publiserer det i GitLab Pages
Når du klikker på Opprett-knappen, opprettes prosjektet og du blir omdirigert til siden. På denne siden kan du deaktivere unødvendige funksjoner ved å gå til prosjektinnstillingene (nedre lenke i listen til venstre -> Oversikt -> Azure DevOps Services-blokk)
Gå til Atrifacter, klikk på Opprett feed
Skriv inn navnet på kilden
Velg synlighet
Fjern merket Inkluder pakker fra vanlige offentlige kilder, slik at kilden ikke blir til en dump nuget-klon
Klikk på Koble til feed, velg Visual Studio, kopier kilde fra maskinoppsettblokken
Gå til kontoinnstillinger, velg Personlig tilgangstoken
Opprett et nytt tilgangstoken
Navn - vilkårlig
Organisasjon - aktuell
Gyldig i maks 1 år
Omfang - Pakking/Les & Skriv
Kopier det opprettede tokenet - etter at det modale vinduet er lukket, vil verdien være utilgjengelig
Gå til depotinnstillingene i GitLab, velg CI / CD-innstillingene
Utvid Variables-blokken, legg til en ny
Navn - alle uten mellomrom (vil være tilgjengelig i kommandoskallet)
Verdi - tilgangstoken fra avsnitt 9
Velg Mask-variabel
Dette fullfører forhåndskonfigurasjonen.
Forbereder konfigurasjonsrammeverket
Som standard bruker CI/CD-konfigurasjon i GitLab filen .gitlab-ci.yml fra roten til depotet. Du kan angi en vilkårlig bane til denne filen i depotinnstillingene, men i dette tilfellet er det ikke nødvendig.
Som du kan se av utvidelsen, inneholder filen en konfigurasjon i formatet YAML. Dokumentasjonen beskriver hvilke nøkler som kan inneholde på toppnivået i konfigurasjonen, og på hvert av de nestede nivåene.
La oss først legge til en lenke til docker-bildet i konfigurasjonsfilen, der oppgavene skal utføres. For dette finner vi .Net Core-bildeside på Docker Hub. I GitHub det er en detaljert veiledning om hvilket bilde du skal velge for ulike oppgaver. Et bilde med .Net Core 3.1 er egnet for oss å bygge, så legg gjerne til den første linjen i konfigurasjonen
image: mcr.microsoft.com/dotnet/core/sdk:3.1
Nå, når rørledningen er lansert fra Microsofts bildelager, vil det angitte bildet bli lastet ned, der alle oppgavene fra konfigurasjonen vil bli utført.
Neste trinn er å legge til scene's. Som standard definerer GitLab 5 stadier:
.pre - fremført opp til alle stadier,
.post - fremført etter alle stadier,
build - først etter .pre scene,
test - andre fase,
deploy - den tredje fasen.
Ingenting hindrer deg imidlertid i å deklarere dem eksplisitt. Rekkefølgen som trinnene er oppført i, påvirker rekkefølgen de utføres i. For fullstendighetens skyld, la oss legge til konfigurasjonen:
stages:
- build
- test
- deploy
For feilsøking er det fornuftig å få informasjon om miljøet der oppgavene utføres. La oss legge til et globalt sett med kommandoer som vil bli utført før hver oppgave med before_script:
before_script:
- $PSVersionTable.PSVersion
- dotnet --version
- nuget help | select-string Version
Det gjenstår å legge til minst én oppgave slik at rørledningen starter når forpliktelsene er sendt. For nå, la oss legge til en tom oppgave for å demonstrere:
dummy job:
script:
- echo ok
Vi starter validering, vi får en melding om at alt er i orden, vi forplikter oss, vi presser, vi ser på resultatene på nettstedet ... Og vi får en skriptfeil - bash: .PSVersion: command not found. wtf?
Alt er logisk - som standard bruker løpere (ansvarlig for å utføre oppgaveskript og levert av GitLab) bash å utføre kommandoer. Du kan fikse dette ved å spesifisere eksplisitt i oppgavebeskrivelsen hvilke tagger den utførende pipeline-løperen skal ha:
dummy job on windows:
script:
- echo ok
tags:
- windows
Flott! Rørledningen er nå i gang.
En oppmerksom leser, etter å ha gjentatt de angitte trinnene, vil legge merke til at oppgaven ble fullført på scenen test, selv om vi ikke spesifiserte scenen. Som du kanskje gjetter test er standardtrinn.
La oss fortsette å lage konfigurasjonsskjelettet ved å legge til alle oppgavene beskrevet ovenfor:
build job:
script:
- echo "building..."
tags:
- windows
stage: build
test and cover job:
script:
- echo "running tests and coverage analysis..."
tags:
- windows
stage: test
pack and deploy job:
script:
- echo "packing and pushing to nuget..."
tags:
- windows
stage: deploy
pages:
script:
- echo "creating docs..."
tags:
- windows
stage: deploy
Vi fikk en ikke spesielt funksjonell, men likevel riktig rørledning.
Sette opp triggere
På grunn av at det ikke er spesifisert triggerfiltre for noen av oppgavene, vil rørledningen fullt utføres hver gang en commit skyves til depotet. Siden dette ikke er ønsket oppførsel generelt sett, vil vi sette opp triggerfiltre for oppgaver.
Filtre kan konfigureres i to formater: bare/unntatt и regler. Kort, only/except lar deg konfigurere filtre etter utløsere (merge_request, for eksempel - setter oppgaven som skal utføres hver gang en pull-forespørsel opprettes og hver gang commits sendes til grenen som er kilden i sammenslåingsforespørselen) og grennavn (inkludert bruk av regulære uttrykk); rules lar deg tilpasse et sett med betingelser og eventuelt endre betingelsen for oppgaveutførelse avhengig av suksessen til tidligere oppgaver (when i GitLab CI/CD).
La oss huske et sett med krav – montering og testing kun for sammenslåingsforespørsel, pakking og sending til Azure DevOps – for sammenslåingsforespørsel og push til master, dokumentasjonsgenerering – for push til master.
Først, la oss sette opp kodebyggingsoppgaven ved å legge til en regel som bare utløses ved sammenslåingsforespørsel:
build job:
# snip
only:
- merge_request
La oss nå sette opp pakkeoppgaven til å sende på sammenslåingsforespørselen og legge til forpliktelser til masteren:
Under forhold kan du bruke variabler oppført her; regler rules uforenlig med reglene only/except.
Konfigurere Artefaktlagring
Under en oppgave build job vi vil ha byggeartefakter som kan gjenbrukes i påfølgende oppgaver. For å gjøre dette må du legge til banene til oppgavekonfigurasjonen, filene som du må lagre og gjenbruke i følgende oppgaver, til nøkkelen artifacts:
Baner støtter jokertegn, noe som definitivt gjør dem enklere å sette.
Hvis en oppgave lager artefakter, vil hver påfølgende oppgave kunne få tilgang til dem - de vil være plassert langs de samme banene i forhold til depotroten som ble samlet inn fra den opprinnelige oppgaven. Artefakter er også tilgjengelig for nedlasting på nettstedet.
Nå som vi har konfigurasjonsrammeverket klart (og testet), kan vi fortsette å faktisk skrive skript for oppgaver.
Vi skriver manus
Kanskje, en gang i tiden, i en galakse langt, langt unna, var det vanskelig å bygge prosjekter (inkludert de på .net) fra kommandolinjen. Nå kan du bygge, teste og publisere prosjektet i 3 team:
dotnet build
dotnet test
dotnet pack
Naturligvis er det noen nyanser som gjør at vi kompliserer kommandoene noe.
Vi vil ha et utgivelsesbygg, ikke et feilsøkingsbygg, så vi legger til hver kommando -c Release
Når vi tester, ønsker vi å samle kodedekningsdata, så vi må inkludere en dekningsanalysator i testbibliotekene:
Legg pakken til alle testbibliotekene coverlet.msbuild: dotnet add package coverlet.msbuild fra prosjektmappe
Legg til i testkjøringskommandoen /p:CollectCoverage=true
Legg til en nøkkel til testoppgavekonfigurasjonen for å få dekningsresultater (se nedenfor)
Når du pakker koden inn i nuget-pakker, setter du utdatakatalogen for pakkene: -o .
Samler inn kodedekningsdata
Etter å ha kjørt testene, skriver Coverlet ut kjørestatistikk til konsollen:
GitLab lar deg spesifisere et regulært uttrykk for å få statistikk, som deretter kan fås i form av et merke. Det regulære uttrykket er spesifisert i oppgaveinnstillingene med nøkkelen coverage; uttrykket må inneholde en fangstgruppe, hvis verdi vil bli sendt til merket:
test and cover job:
# snip
coverage: /|s*Totals*|s*(d+[,.]d+%)/
Her får vi statistikk fra en linje med total linjedekning.
Publiser pakker og dokumentasjon
Begge handlingene er planlagt for siste fase av rørledningen - siden monteringen og testene har bestått, kan vi dele utviklingen vår med verden.
Vurder først å publisere til pakkekilden:
Hvis prosjektet ikke har en nuget-konfigurasjonsfil (nuget.config), lag en ny: dotnet new nugetconfig
For hva: bildet kan ikke ha skrivetilgang til globale (bruker og maskin) konfigurasjoner. For ikke å fange opp feil, oppretter vi ganske enkelt en ny lokal konfigurasjon og jobber med den.
La oss legge til en ny pakkekilde til den lokale konfigurasjonen: nuget sources add -name <name> -source <url> -username <organization> -password <gitlab variable> -configfile nuget.config -StorePasswordInClearText
name - lokalt kildenavn, ikke kritisk
url - URL til kilden fra stadiet "Forberede kontoer", s. 6
organization - organisasjonsnavn i Azure DevOps
gitlab variable - navnet på variabelen med tilgangstoken lagt til GitLab ("Forberede kontoer", s. 11). Naturligvis i formatet $variableName
Vi sender alle pakker fra gjeldende katalog, så *.nupkg.
name - fra trinnet ovenfor.
key - hvilken som helst linje. I Azure DevOps, i Koble til feed-vinduet, er eksemplet alltid linjen az.
-skipduplicate - når du prøver å sende en allerede eksisterende pakke uten denne nøkkelen, vil kilden returnere en feil 409 Conflict; med nøkkelen vil sendingen hoppes over.
La oss nå sette opp opprettelsen av dokumentasjon:
Først, i depotet, i mastergrenen, initialiserer vi docfx-prosjektet. For å gjøre dette, kjør kommandoen fra roten docfx init og interaktivt angi nøkkelparametere for bygningsdokumentasjon. Detaljert beskrivelse av minimum prosjektoppsett her.
Ved konfigurering er det viktig å spesifisere utdatakatalogen ..public - GitLab tar som standard innholdet i den offentlige mappen i roten av depotet som en kilde for Pages. Fordi prosjektet vil være plassert i en mappe nestet i depotet - legg til en utgang til nivået opp i banen.
La oss presse endringene til GitLab.
Legg til en oppgave i rørledningskonfigurasjonen pages (reservert ord for nettstedspubliseringsoppgaver i GitLab-sider):
Manus:
nuget install docfx.console -version 2.51.0 - installer docfx; versjonen er spesifisert for å sikre at pakkeinstallasjonsbanene er riktige.
.docfx.console.2.51.0toolsdocfx.exe .docfx_projectdocfx.json - innhenting av dokumentasjon
Node-artefakter:
pages:
# snip
artifacts:
paths:
- public
Lyrisk digresjon om docfx
Tidligere, når jeg satte opp et prosjekt, spesifiserte jeg kodekilden for dokumentasjonen som en løsningsfil. Den største ulempen er at det også lages dokumentasjon for testprosjekter. I tilfelle dette ikke er nødvendig, kan du sette en slik verdi til noden metadata.src:
metadata.src.src: "../" – vi går ett nivå opp i forhold til beliggenheten docfx.json, fordi i mønstre fungerer det ikke å søke opp katalogtreet.
metadata.src.files: ["**/*.csproj"] - et globalt mønster, vi samler alle C #-prosjekter fra alle kataloger.
metadata.src.exclude: ["*.tests*/**"] - globalt mønster, ekskluder alt fra mapper med .tests I tittelen
Delsum
En slik enkel konfigurasjon kan lages på bare en halv time og et par kopper kaffe, som lar deg sjekke at koden er bygget og testene passerer, bygge en ny pakke, oppdatere dokumentasjonen og glede øyet med vakre merker i README for prosjektet med hver sammenslåingsforespørsel og sending til masteren.
Endelig .gitlab-ci.yml
image: mcr.microsoft.com/dotnet/core/sdk:3.1
before_script:
- $PSVersionTable.PSVersion
- dotnet --version
- nuget help | select-string Version
stages:
- build
- test
- deploy
build job:
stage: build
script:
- dotnet build -c Release
tags:
- windows
only:
- merge_requests
- master
artifacts:
paths:
- your/path/to/binaries
test and cover job:
stage: test
tags:
- windows
script:
- dotnet test -c Release /p:CollectCoverage=true
coverage: /|s*Totals*|s*(d+[,.]d+%)/
only:
- merge_requests
- master
pack and deploy job:
stage: deploy
tags:
- windows
script:
- dotnet pack -c Release -o .
- dotnet new nugetconfig
- nuget sources add -name feedName -source https://pkgs.dev.azure.com/your-organization/_packaging/your-feed/nuget/v3/index.json -username your-organization -password $nugetFeedToken -configfile nuget.config -StorePasswordInClearText
- nuget push -source feedName -skipduplicate -apikey az *.nupkg
only:
- master
pages:
tags:
- windows
stage: deploy
script:
- nuget install docfx.console -version 2.51.0
- $env:path = "$env:path;$($(get-location).Path)"
- .docfx.console.2.51.0toolsdocfx.exe .docfxdocfx.json
artifacts:
paths:
- public
only:
- master
Apropos merker
På grunn av dem ble tross alt alt startet!
Merker med rørledningsstatuser og kodedekning er tilgjengelig i GitLab i CI/CD-innstillingene i Gtntral-rørledningsblokken:
Jeg laget et merke med en lenke til dokumentasjonen på plattformen shields.io - alt er ganske enkelt der, du kan lage ditt eget merke og motta det ved å bruke en forespørsel.
![Пример с Shields.io](https://img.shields.io/badge/custom-badge-blue)
Azure DevOps Artifacts lar deg også lage merker for pakker med den nyeste versjonen. For å gjøre dette, i kilden på Azure DevOps-nettstedet, må du klikke på Opprett merke for den valgte pakken og kopiere markeringen:
Legger til skjønnhet
Utheving av vanlige konfigurasjonsfragmenter
Mens jeg skrev konfigurasjonen og søkte gjennom dokumentasjonen, kom jeg over en interessant funksjon ved YAML – gjenbruk av fragmenter.
Som du kan se fra oppgaveinnstillingene, krever de alle taggen windows hos løperen, og utløses når en sammenslåingsforespørsel sendes til master/opprettes (unntatt dokumentasjon). La oss legge dette til fragmentet som vi skal gjenbruke:
Og nå kan vi sette inn fragmentet som er erklært tidligere i oppgavebeskrivelsen:
build job:
<<: *common_tags
<<: *common_only
Fragmentnavn må begynne med en prikk, for ikke å bli tolket som en oppgave.
Pakkeversjon
Når du oppretter en pakke, sjekker kompilatoren kommandolinjebryterne, og i deres fravær prosjektfilene; når den finner en versjonsnode, tar den verdien som versjonen av pakken som bygges. Det viser seg at for å bygge en pakke med en ny versjon, må du enten oppdatere den i prosjektfilen eller sende den som et kommandolinjeargument.
La oss legge til en ønskeliste til - la de to mindre tallene i versjonen være år og byggedato for pakken, og legg til forhåndsversjoner. Selvfølgelig kan du legge til disse dataene i prosjektfilen og sjekke før hver innsending - men du kan også gjøre det i pipelinen, samle pakkeversjonen fra konteksten og sende den gjennom kommandolinjeargumentet.
La oss bli enige om at hvis commit-meldingen inneholder en linje som release (v./ver./version) <version number> (rev./revision <revision>)?, så tar vi versjonen av pakken fra denne linjen, supplerer den med gjeldende dato og sender den som et argument til kommandoen dotnet pack. I mangel av linje vil vi rett og slett ikke hente pakken.
Følgende skript løser dette problemet:
# регулярное выражение для поиска строки с версией
$rx = "releases+(v.?|ver.?|version)s*(?<maj>d+)(?<min>.d+)?(?<rel>.d+)?s*((rev.?|revision)?s+(?<rev>[a-zA-Z0-9-_]+))?"
# ищем строку в сообщении коммита, передаваемом в одной из предопределяемых GitLab'ом переменных
$found = $env:CI_COMMIT_MESSAGE -match $rx
# совпадений нет - выходим
if (!$found) { Write-Output "no release info found, aborting"; exit }
# извлекаем мажорную и минорную версии
$maj = $matches['maj']
$min = $matches['min']
# если строка содержит номер релиза - используем его, иначе - текущий год
if ($matches.ContainsKey('rel')) { $rel = $matches['rel'] } else { $rel = ".$(get-date -format "yyyy")" }
# в качестве номера сборки - текущие месяц и день
$bld = $(get-date -format "MMdd")
# если есть данные по пререлизной версии - включаем их в версию
if ($matches.ContainsKey('rev')) { $rev = "-$($matches['rev'])" } else { $rev = '' }
# собираем единую строку версии
$version = "$maj$min$rel.$bld$rev"
# собираем пакеты
dotnet pack -c Release -o . /p:Version=$version
Legge til et skript til en oppgave pack and deploy job og observer sammenstillingen av pakker strengt i nærvær av en gitt streng i commit-meldingen.
Totalt
Etter å ha brukt omtrent en halvtime eller en time på å skrive konfigurasjonen, feilsøke i det lokale powershell og muligens et par mislykkede lanseringer, fikk vi en enkel konfigurasjon for å automatisere rutineoppgaver.
Selvfølgelig er GitLab CI / CD mye mer omfattende og mangefasettert enn det kan virke etter å ha lest denne veiledningen - det er ikke sant i det hele tatt. Der til og med Auto DevOps ertillater
automatisk oppdage, bygge, teste, distribuere og overvåke applikasjonene dine
Nå er planene å konfigurere en pipeline for distribusjon av applikasjoner til Azure, ved å bruke Pulumi og automatisk bestemme målmiljøet, som vil bli dekket i neste artikkel.