Una guida a CI/CD in GitLab per il principiante (quasi) assoluto
O come ottenere bellissimi badge per il tuo progetto in una serata di facile programmazione
Probabilmente, ogni sviluppatore che ha almeno un progetto per animali domestici ad un certo punto ha un prurito per i bellissimi badge con stati, copertura del codice, versioni dei pacchetti in nuget ... E questo prurito mi ha portato a scrivere questo articolo. In preparazione per scriverlo, ho ottenuto questa bellezza in uno dei miei progetti:
Questo articolo illustra la configurazione di base dell'integrazione e del recapito continui per un progetto di libreria di classi .Net Core in GitLab, la pubblicazione della documentazione nelle pagine GitLab e il push dei pacchetti compilati in un feed privato in Azure DevOps.
VS Code è stato utilizzato come ambiente di sviluppo con l'estensione Flusso di lavoro GitLab (per convalidare il file delle impostazioni direttamente dall'ambiente di sviluppo).
Breve introduzione
CD - è quando hai appena spinto e tutto è già caduto sul cliente?
Che cos'è CI / CD e perché ne hai bisogno: puoi facilmente cercarlo su Google. Trova la documentazione completa sulla configurazione delle pipeline in GitLab anche facile. Qui descriverò brevemente e, se possibile, senza difetti, il processo del sistema da una vista a volo d'uccello:
lo sviluppatore invia un commit al repository, crea una richiesta di unione attraverso il sito, o in qualche altro modo, avvia in modo esplicito o implicito la pipeline,
tutte le attività sono selezionate dalla configurazione, le cui condizioni consentono loro di essere avviate nel contesto dato,
i compiti sono organizzati secondo le loro fasi,
le fasi vengono eseguite a turno, ad es. parallelo a tutte le attività di questa fase sono state completate,
se la fase fallisce (ovvero, almeno una delle attività della fase fallisce), la pipeline si interrompe (quasi sempre),
se tutte le fasi vengono completate correttamente, la pipeline è considerata riuscita.
Quindi, abbiamo:
pipeline: una serie di attività organizzate in fasi in cui è possibile creare, testare, impacchettare il codice, distribuire una build finita a un servizio cloud, ecc.,
palcoscenico (palcoscenico) — unità organizzativa della pipeline, contiene 1+ attività,
compito (lavoro) è un'unità di lavoro nella pipeline. Consiste in uno script (obbligatorio), condizioni di avvio, impostazioni per gli artefatti di pubblicazione/memorizzazione nella cache e molto altro.
Di conseguenza, l'attività durante l'impostazione di CI / CD si riduce alla creazione di una serie di attività che implementano tutte le azioni necessarie per la creazione, il test e la pubblicazione di codice e artefatti.
Prima di iniziare: perché?
Perché GitLab?
Perché quando è diventato necessario creare repository privati per progetti per animali domestici, sono stati pagati su GitHub e io ero avido. I repository sono diventati gratuiti, ma finora questo non è un motivo sufficiente per me per passare a GitHub.
Perché non Azure DevOps Pipelines?
Perché lì l'impostazione è elementare: non è nemmeno richiesta la conoscenza della riga di comando. Integrazione con provider git esterni - in un paio di clic, importazione di chiavi SSH per inviare commit al repository - inoltre, la pipeline è facilmente configurabile anche non da un modello.
Posizione di partenza: cosa hai e cosa vuoi
Abbiamo:
deposito in GitLab.
Vogliamo:
assemblaggio e test automatici per ogni richiesta di unione,
compilazione di pacchetti per ogni richiesta di unione e invio al master, a condizione che ci sia una certa riga nel messaggio di commit,
invio di pacchetti compilati a un feed privato in Azure DevOps,
assemblaggio della documentazione e pubblicazione in GitLab Pages,
distintivi!11
I requisiti descritti ricadono organicamente sul seguente modello di pipeline:
Fase 1 - Assemblaggio
Raccogliamo il codice, pubblichiamo i file di output come artefatti
Fase 2 - test
Otteniamo artefatti dalla fase di costruzione, eseguiamo test, raccogliamo dati sulla copertura del codice
Fase 3 - Invia
Attività 1: crea il pacchetto nuget e invialo ad Azure DevOps
Attività 2: raccogliamo il sito da xmldoc nel codice sorgente e lo pubblichiamo in GitLab Pages
Quando fai clic sul pulsante Crea, il progetto verrà creato e verrai reindirizzato alla sua pagina. In questa pagina è possibile disabilitare le funzionalità non necessarie accedendo alle impostazioni del progetto (collegamento in basso nell'elenco a sinistra -> Panoramica -> blocco Azure DevOps Services)
Vai su Atrifacts, fai clic su Crea feed
Inserisci il nome della fonte
Scegli la visibilità
Deseleziona Includi pacchetti da origini pubbliche comuni, in modo che l'origine non si trasformi in un clone nuget dump
Fare clic su Connetti al feed, selezionare Visual Studio, copiare Source dal blocco Machine Setup
Vai alle impostazioni dell'account, seleziona Token di accesso personale
Crea un nuovo token di accesso
Nome - arbitrario
Organizzazione - corrente
Valido per un massimo di 1 anno
Ambito - Imballaggio/Lettura e scrittura
Copia il token creato - dopo la chiusura della finestra modale, il valore non sarà disponibile
Vai alle impostazioni del repository in GitLab, seleziona le impostazioni CI / CD
Espandi il blocco Variabili, aggiungine uno nuovo
Nome - qualsiasi senza spazi (sarà disponibile nella shell dei comandi)
Valore - token di accesso dal paragrafo 9
Selezionare Maschera variabile
Questo completa la preconfigurazione.
Preparazione del framework di configurazione
Per impostazione predefinita, la configurazione CI/CD in GitLab utilizza il file .gitlab-ci.yml dalla radice del repository. È possibile impostare un percorso arbitrario per questo file nelle impostazioni del repository, ma in questo caso non è necessario.
Come puoi vedere dall'estensione, il file contiene una configurazione nel formato YAML. La documentazione specifica quali chiavi possono essere contenute al livello superiore della configurazione ea ciascuno dei livelli nidificati.
Innanzitutto, aggiungiamo un collegamento all'immagine docker nel file di configurazione, in cui verranno eseguite le attività. Per questo troviamo Pagina delle immagini .Net Core su Docker Hub. In GitHub c'è una guida dettagliata su quale immagine scegliere per compiti diversi. Un'immagine con .Net Core 3.1 è adatta per noi da costruire, quindi sentiti libero di aggiungere la prima riga alla configurazione
image: mcr.microsoft.com/dotnet/core/sdk:3.1
Ora, quando la pipeline viene avviata dal repository di immagini Microsoft, verrà scaricata l'immagine specificata, in cui verranno eseguite tutte le attività della configurazione.
Il prossimo passo è aggiungere palcoscenico'S. Per impostazione predefinita, GitLab definisce 5 fasi:
.pre - eseguito fino a tutte le fasi,
.post - eseguito dopo tutte le fasi,
build - prima dopo .pre palcoscenico,
test - seconda fase,
deploy - la terza fase.
Nulla vieta però di dichiararli esplicitamente. L'ordine in cui sono elencati i passaggi influisce sull'ordine in cui vengono eseguiti. Per completezza, aggiungiamo alla configurazione:
stages:
- build
- test
- deploy
Per il debug, ha senso ottenere informazioni sull'ambiente in cui vengono eseguite le attività. Aggiungiamo un insieme globale di comandi che verranno eseguiti prima di ogni attività con before_script:
before_script:
- $PSVersionTable.PSVersion
- dotnet --version
- nuget help | select-string Version
Resta da aggiungere almeno un'attività in modo che quando i commit vengono inviati, la pipeline verrà avviata. Per ora, aggiungiamo un'attività vuota per dimostrare:
dummy job:
script:
- echo ok
Iniziamo la convalida, riceviamo un messaggio che va tutto bene, ci impegniamo, spingiamo, guardiamo i risultati sul sito ... E riceviamo un errore di script - bash: .PSVersion: command not found. wtf?
Tutto è logico: per impostazione predefinita, i corridori (responsabili dell'esecuzione degli script delle attività e forniti da GitLab) usano bash per eseguire i comandi. Puoi risolvere questo problema specificando esplicitamente nella descrizione dell'attività quali tag dovrebbe avere il corridore della pipeline in esecuzione:
dummy job on windows:
script:
- echo ok
tags:
- windows
Grande! La pipeline è ora in esecuzione.
Un lettore attento, dopo aver ripetuto i passaggi indicati, noterà che l'attività è stata completata nella fase test, anche se non abbiamo specificato la fase. Come puoi immaginare test è il passaggio predefinito.
Continuiamo a creare lo scheletro di configurazione aggiungendo tutte le attività sopra descritte:
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
Abbiamo una pipeline non particolarmente funzionale, ma comunque corretta.
Impostazione dei trigger
A causa del fatto che non sono specificati filtri trigger per nessuna delle attività, la pipeline lo farà completamente essere eseguito ogni volta che un commit viene inviato al repository. Poiché questo non è il comportamento desiderato in generale, imposteremo filtri di attivazione per le attività.
I filtri possono essere configurati in due formati: solo/tranne и norme. Brevemente, only/except consente di configurare i filtri per trigger (merge_request, ad esempio - imposta l'attività da eseguire ogni volta che viene creata una richiesta pull e ogni volta che i commit vengono inviati al ramo che è l'origine nella richiesta di unione) e i nomi dei rami (incluso l'uso di espressioni regolari); rules consente di personalizzare una serie di condizioni e, facoltativamente, modificare la condizione di esecuzione dell'attività in base al successo delle attività precedenti (when in GitLab CI/CD).
Ricordiamo una serie di requisiti - assemblaggio e test solo per richiesta di unione, creazione di pacchetti e invio ad Azure DevOps - per richiesta di unione e push al master, generazione di documentazione - per push al master.
Innanzitutto, impostiamo l'attività di compilazione del codice aggiungendo una regola che si attiva solo su richiesta di unione:
build job:
# snip
only:
- merge_request
Ora impostiamo l'attività di impacchettamento in modo che si attivi sulla richiesta di unione e aggiungiamo i commit al master:
A condizioni, puoi usare variabili elencate qui; regole rules incompatibile con le regole only/except.
Configurazione del salvataggio degli artefatti
Durante un compito build job avremo artefatti di costruzione che possono essere riutilizzati nelle attività successive. Per fare ciò, è necessario aggiungere i percorsi alla configurazione dell'attività, i file lungo i quali sarà necessario salvare e riutilizzare nelle attività successive, alla chiave artifacts:
I percorsi supportano i caratteri jolly, il che li rende sicuramente più facili da impostare.
Se un'attività crea artefatti, ogni attività successiva sarà in grado di accedervi: saranno posizionati lungo gli stessi percorsi relativi alla radice del repository che sono stati raccolti dall'attività originale. Gli artefatti sono disponibili anche per il download sul sito.
Ora che abbiamo il framework di configurazione pronto (e testato), possiamo procedere alla scrittura degli script per le attività.
Scriviamo sceneggiature
Forse, una volta, in una galassia molto, molto lontana, costruire progetti (compresi quelli su .net) dalla riga di comando era una seccatura. Ora puoi creare, testare e pubblicare il progetto in 3 team:
dotnet build
dotnet test
dotnet pack
Naturalmente, ci sono alcune sfumature a causa delle quali complicheremo un po 'i comandi.
Vogliamo una build di rilascio, non una build di debug, quindi aggiungiamo a ciascun comando -c Release
Durante il test, vogliamo raccogliere dati sulla copertura del codice, quindi dobbiamo includere un analizzatore di copertura nelle librerie di test:
Aggiungere il pacchetto a tutte le librerie di test coverlet.msbuild: dotnet add package coverlet.msbuild dalla cartella del progetto
Aggiungere al comando di esecuzione del test /p:CollectCoverage=true
Aggiungi una chiave alla configurazione dell'attività di test per ottenere i risultati della copertura (vedi sotto)
Quando impacchettate il codice nei pacchetti nuget, impostate la directory di output per i pacchetti: -o .
Raccolta dei dati sulla copertura del codice
Dopo aver eseguito i test, Coverlet stampa le statistiche della corsa sulla console:
GitLab ti consente di specificare un'espressione regolare per ottenere statistiche, che possono quindi essere ottenute sotto forma di badge. L'espressione regolare è specificata nelle impostazioni dell'attività con la chiave coverage; l'espressione deve contenere un gruppo di acquisizione, il cui valore verrà passato al badge:
test and cover job:
# snip
coverage: /|s*Totals*|s*(d+[,.]d+%)/
Qui otteniamo statistiche da una linea con copertura totale della linea.
Pubblica pacchetti e documentazione
Entrambe le azioni sono previste per l'ultima fase della pipeline: poiché l'assemblaggio e i test sono stati superati, possiamo condividere i nostri sviluppi con il mondo.
Innanzitutto, considera la pubblicazione nell'origine del pacchetto:
Se il progetto non ha un file di configurazione nuget (nuget.config), creane uno nuovo: dotnet new nugetconfig
Per quello: l'immagine potrebbe non avere accesso in scrittura alle configurazioni globali (utente e macchina). Per non rilevare errori, creiamo semplicemente una nuova configurazione locale e lavoriamo con essa.
Aggiungiamo una nuova sorgente del pacchetto alla configurazione locale: nuget sources add -name <name> -source <url> -username <organization> -password <gitlab variable> -configfile nuget.config -StorePasswordInClearText
name - nome della fonte locale, non critico
url - URL della fonte dalla fase "Preparazione degli account", pagina 6
organization -nome dell'organizzazione in Azure DevOps
gitlab variable - il nome della variabile con il token di accesso aggiunto a GitLab ("Preparazione degli account", p. 11). Naturalmente, nel formato $variableName
In caso di errori, può essere utile aggiungere -verbosity detailed
Invio del pacchetto alla fonte: nuget push -source <name> -skipduplicate -apikey <key> *.nupkg
Inviamo tutti i pacchetti dalla directory corrente, quindi *.nupkg.
name - dal passaggio precedente.
key - qualsiasi linea. In Azure DevOps, nella finestra Connetti al feed, l'esempio è sempre la riga az.
-skipduplicate - quando si tenta di inviare un pacchetto già esistente senza questa chiave, la fonte restituirà un errore 409 Conflict; con la chiave, l'invio verrà saltato.
Ora impostiamo la creazione della documentazione:
Innanzitutto, nel repository, nel ramo master, inizializziamo il progetto docfx. Per fare ciò, esegui il comando dalla radice docfx init e impostare in modo interattivo i parametri chiave per la documentazione dell'edificio. Descrizione dettagliata della configurazione minima del progetto qui.
Durante la configurazione, è importante specificare la directory di output ..public - GitLab per impostazione predefinita prende il contenuto della cartella pubblica nella radice del repository come origine per Pages. Perché il progetto si troverà in una cartella nidificata nel repository - aggiungi un output al livello superiore nel percorso.
Inviamo le modifiche a GitLab.
Aggiungere un'attività alla configurazione della pipeline pages (parola riservata per le attività di pubblicazione del sito in GitLab Pages):
script:
nuget install docfx.console -version 2.51.0 - installa docfx; la versione è specificata per garantire che i percorsi di installazione del pacchetto siano corretti.
.docfx.console.2.51.0toolsdocfx.exe .docfx_projectdocfx.json - raccolta della documentazione
Artefatti del nodo:
pages:
# snip
artifacts:
paths:
- public
Digressione lirica su docfx
In precedenza, durante l'impostazione di un progetto, specificavo il codice sorgente per la documentazione come file di soluzione. Lo svantaggio principale è che la documentazione viene creata anche per i progetti di test. Nel caso in cui ciò non sia necessario, è possibile impostare questo valore sul nodo metadata.src:
metadata.src.src: "../" - saliamo di un livello rispetto alla posizione docfx.json, Perché nei modelli, la ricerca nell'albero delle directory non funziona.
metadata.src.files: ["**/*.csproj"] - un modello globale, raccogliamo tutti i progetti C # da tutte le directory.
metadata.src.exclude: ["*.tests*/**"] - schema globale, escludi tutto dalle cartelle con .tests nel nome
subtotale
Una configurazione così semplice può essere creata in appena mezz'ora e un paio di tazze di caffè, il che ti consentirà di verificare che il codice sia compilato e che i test vengano superati, creare un nuovo pacchetto, aggiornare la documentazione e soddisfare l'occhio con bellissimi badge nel README del progetto ad ogni richiesta di unione e invio al master.
.gitlab-ci.yml finale
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
A proposito di distintivi
Grazie a loro, dopotutto, tutto è iniziato!
I badge con gli stati della pipeline e la copertura del codice sono disponibili in GitLab nelle impostazioni CI/CD nel blocco delle pipeline Gtntral:
Ho creato un badge con un link alla documentazione sulla piattaforma Scudi.io - tutto è abbastanza semplice lì, puoi creare il tuo badge e riceverlo utilizzando una richiesta.
![Пример с Shields.io](https://img.shields.io/badge/custom-badge-blue)
Azure DevOps Artifacts consente inoltre di creare badge per i pacchetti con la versione più recente. Per fare ciò, nel sorgente sul sito Azure DevOps, è necessario cliccare su Crea badge per il pacchetto selezionato e copiare il markup markdown:
Aggiunta di bellezza
Evidenziazione dei frammenti di configurazione comuni
Durante la scrittura della configurazione e la ricerca nella documentazione, mi sono imbattuto in un'interessante caratteristica di YAML: il riutilizzo dei frammenti.
Come puoi vedere dalle impostazioni dell'attività, richiedono tutte il tag windows al corridore e vengono attivati quando una richiesta di unione viene inviata al master/creata (tranne la documentazione). Aggiungiamo questo al frammento che riutilizzeremo:
E ora possiamo inserire il frammento dichiarato in precedenza nella descrizione del compito:
build job:
<<: *common_tags
<<: *common_only
I nomi dei frammenti devono iniziare con un punto, in modo da non essere interpretati come un compito.
Versione del pacchetto
Durante la creazione di un pacchetto, il compilatore controlla le opzioni della riga di comando e, in loro assenza, i file di progetto; quando trova un nodo Versione, assume il suo valore come versione del pacchetto in costruzione. Si scopre che per creare un pacchetto con una nuova versione, è necessario aggiornarlo nel file di progetto o passarlo come argomento della riga di comando.
Aggiungiamo un'altra lista dei desideri: lascia che i due numeri minori nella versione siano l'anno e la data di creazione del pacchetto e aggiungiamo le versioni preliminari. Naturalmente, puoi aggiungere questi dati al file di progetto e controllare prima di ogni invio, ma puoi anche farlo nella pipeline, raccogliendo la versione del pacchetto dal contesto e passandola attraverso l'argomento della riga di comando.
Accettiamo che se il messaggio di commit contiene una riga come release (v./ver./version) <version number> (rev./revision <revision>)?, quindi prenderemo la versione del pacchetto da questa riga, la integreremo con la data corrente e la passeremo come argomento al comando dotnet pack. In assenza di una linea, semplicemente non ritireremo il pacco.
Il seguente script risolve questo problema:
# регулярное выражение для поиска строки с версией
$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
Aggiunta di uno script a un'attività pack and deploy job e osservare l'assemblaggio dei pacchetti rigorosamente in presenza di una data stringa nel messaggio di commit.
In totale
Dopo aver trascorso circa mezz'ora o un'ora a scrivere la configurazione, eseguire il debug nel PowerShell locale e, possibilmente, un paio di lanci falliti, abbiamo ottenuto una configurazione semplice per automatizzare le attività di routine.
rilevare, creare, testare, distribuire e monitorare automaticamente le tue applicazioni
Ora i piani prevedono la configurazione di una pipeline per la distribuzione delle applicazioni in Azure, usando Pulumi e determinando automaticamente l'ambiente di destinazione, che verrà trattato nel prossimo articolo.