ProHoster > blog > amministrazione > Circoli dell'inferno con GitHub Actions (creazione di una pipeline CI/CD per un progetto Java)
Circoli dell'inferno con GitHub Actions (creazione di una pipeline CI/CD per un progetto Java)
Spesso devo creare una pipeline per creare progetti in Java. A volte è open source, a volte no. Di recente ho deciso di provare a migrare alcuni dei miei repository da Travis-CI e TeamCity a GitHub Actions, e questo è ciò che ne è venuto fuori.
Cosa automatizzeremo
Per prima cosa abbiamo bisogno di un progetto che automatizzeremo, facciamo una piccola applicazione su Spring boot / Java 11 / Maven. Nell'ambito di questo articolo, la logica dell'applicazione non ci interessa affatto, l'infrastruttura attorno all'applicazione è importante per noi, quindi per noi sarà sufficiente una semplice API REST del controller.
Puoi visualizzare le fonti qui: github.com/antkorwin/github-actions tutte le fasi della creazione di una pipeline pipeline si riflettono nelle richieste pull di questo progetto.
Jira e la pianificazione
Vale la pena dire che di solito usiamo JIRA come tracker dei problemi, quindi creiamo una scheda separata per questo progetto e mettiamo lì i primi problemi:
Un po 'più tardi, torneremo su quali cose interessanti JIRA e GitHub possono dare insieme.
Automatizziamo l'assemblaggio del progetto
Il nostro progetto di test è compilato tramite Maven, quindi costruirlo è abbastanza semplice, tutto ciò di cui abbiamo bisogno è il pacchetto mvn clean.
Per fare questo usando Github Actions, dovremo creare un file nel repository con una descrizione del nostro flusso di lavoro, questo può essere fatto con un normale file yml, non posso dire che mi piaccia la "programmazione yml", ma cosa può Lo faccio - lo facciamo nel file .github / directory workflow/ build.yml in cui descriveremo le azioni durante la creazione del ramo principale:
on è una descrizione dell'evento su cui verrà lanciato il nostro script.
su: pull_request / push - dice che questo flusso di lavoro deve essere eseguito ogni volta che invii al master e crei richieste pull.
Di seguito la descrizione dei compitiposti di lavoro) e fasi di esecuzione (passi) per ogni attività.
gira su - qui possiamo scegliere il sistema operativo di destinazione, sorprendentemente puoi anche scegliere Mac OS, ma su repository privati questo è un piacere piuttosto costoso (rispetto a Linux).
usa consente di riutilizzare altre azioni, quindi ad esempio con l'azione actions/setup-java impostiamo l'ambiente per Java 11.
Per mezzo di con possiamo specificare i parametri con cui lanciare l'azione, infatti, questi sono gli argomenti che verranno passati all'azione.
Resta solo da avviare l'assemblaggio del progetto con Maven: run: mvn -B clean package bandiera -B dice che abbiamo bisogno di una modalità non interattiva in modo che Maven improvvisamente non voglia chiederci qualcosa
Grande! Ora, con ogni commit al master, viene avviata la compilazione del progetto.
Automatizzare il lancio dei test
Costruire va bene, ma in realtà il progetto può costruire in sicurezza, ma non funzionare. Pertanto, il passaggio successivo consiste nell'automatizzare l'esecuzione del test. Inoltre, è abbastanza comodo vedere il risultato del superamento dei test quando si esegue una revisione delle pubbliche relazioni: sai per certo che i test stanno superando e nessuno ha dimenticato di eseguire il proprio ramo prima di eseguire l'unione.
Eseguiamo test durante la creazione di una richiesta pull e la uniamo al master e allo stesso tempo aggiungiamo un rapporto sulla copertura del codice.
Per la copertura del test, utilizzo codecov insieme al plug-in jacoco. codecov ha la sua azione, ma ha bisogno di un token per funzionare con la nostra richiesta pull:
${{ secrets.CODECOV_TOKEN }} - incontreremo questa costruzione più di una volta, i segreti sono un meccanismo per archiviare i segreti nel github, possiamo scrivere lì password / token / host / url e altri dati che non dovrebbero essere inseriti nella base di codice del repository.
Puoi aggiungere una variabile ai segreti nelle impostazioni del repository su GitHub:
Puoi ottenere un gettone a codecov.io dopo l'autorizzazione tramite GitHub, per aggiungere un progetto pubblico è sufficiente seguire il link del form: Nome utente GitHub/[reponame]. Puoi anche aggiungere un repository privato, per questo devi fornire i diritti codecov all'applicazione in github.
Ora il bot codecov inserirà ciascuna delle nostre richieste pull e aggiungerà un programma di modifica della copertura:
Aggiungere un analizzatore statico
Nella maggior parte dei miei progetti open source, utilizzo sonar cloud per l'analisi del codice statico, è abbastanza facile collegarlo a travis-ci. Quindi è un passaggio logico durante la migrazione a GitHub Actions per fare lo stesso. Il mercato delle azioni è una cosa interessante, ma questa volta mi ha un po' deluso, perché per abitudine ho trovato l'azione giusta e l'ho aggiunta al flusso di lavoro. Ma si è scoperto che il sonar non supporta l'elaborazione di un'azione per analizzare i progetti su Maven o Gradle. Certo, questo è scritto nella documentazione, ma chi lo legge ?!
È impossibile tramite un'azione, quindi lo faremo tramite il plug-in mvn:
SONAR_TOKEN - disponibile a sonarcloud.io e devi registrarlo in segreti. GITHUB_TOKEN - questo è un token integrato che github genera, con l'aiuto di esso, sonarcloud[bot] sarà in grado di accedere a git per lasciarci messaggi nelle richieste pull.
Dsonar.projectKey — nome del progetto nel sonar, puoi vederlo nelle impostazioni del progetto.
Dsonar.organizzazione — nome dell'organizzazione da GitHub.
Facciamo una richiesta pull e aspettiamo che sonarcloud[bot] arrivi nei commenti:
Gestione di uscita
La build è configurata, i test vengono eseguiti e puoi fare un rilascio. Diamo un'occhiata a come GitHub Actions può semplificare notevolmente la gestione dei rilasci.
Al lavoro, ho progetti la cui base di codice è in bitbucket (proprio come in quella storia "Scrivo su bitbucket durante il giorno, mi impegno su GitHub di notte"). Sfortunatamente, bitbucket non dispone di strumenti di gestione delle versioni integrati. Questo è un problema, perché per ogni versione devi avviare manualmente una pagina in confluenza e buttare lì tutte le funzionalità incluse nella versione, lana i palazzi della mente, attività in jira, commit nel repository. Ci sono molte possibilità di commettere un errore, puoi dimenticare qualcosa o scrivere in ciò che è già stato rilasciato l'ultima volta, a volte non è chiaro a cosa dovrebbe essere attribuita una richiesta pull: è una funzionalità o una correzione di bug o test di modifica, o qualcosa di infrastrutturale.
In che modo le azioni di GitHub possono aiutarci? C'è un'ottima azione: release drafter, ti consente di impostare un modello di file delle note di rilascio per impostare categorie di richieste pull e raggrupparle automaticamente in un file di note di rilascio:
Un esempio di modello per personalizzare un report (.github/release-drafter.yml):
name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
- title: ' New Features'
labels:
- 'type:features'
# в эту категорию собираем все PR с меткой type:features
- title: ' Bugs Fixes'
labels:
- 'type:fix'
# аналогично для метки type:fix и т.д.
- title: ' Documentation'
labels:
- 'type:documentation'
- title: ' Configuration'
labels:
- 'type:config'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
template: |
## Changes
$CHANGES
Aggiungi uno script per generare una bozza di rilascio (.github/workflows/release-draft.yml):
Tutte le richieste pull d'ora in poi verranno raccolte automaticamente nelle note di rilascio: magia!
Qui potrebbe sorgere la domanda: cosa succede se gli sviluppatori dimenticano di taggare il PR? Quindi non è chiaro a quale categoria appartenga, e ancora una volta dovrai occupartene manualmente, con ogni PR separatamente. Per risolvere questo problema, possiamo utilizzare un'altra azione - label verifier - verifica la presenza di tag sulla richiesta pull. Se non c'è un singolo tag richiesto, il controllo fallirà e vedremo un messaggio al riguardo nella nostra richiesta pull.
Ora qualsiasi richiesta pull deve essere contrassegnata con uno dei tag: type:fix, type:features, type:documentation, type:tests, type:config.
Richieste pull con annotazione automatica
Poiché abbiamo toccato un argomento come il lavoro efficace con le richieste pull, vale la pena menzionare un'azione come labeler, inserisce i tag in PR in base ai file che sono stati modificati. Ad esempio, possiamo contrassegnare come [build] qualsiasi richiesta pull con modifiche nella directory .github/workflow.
Non sono riuscito a fare amicizia tra l'azione che mette automaticamente le etichette nelle pull request e l'azione che verifica la presenza delle etichette obbligatorie, la match-label non vuole vedere le etichette messe dal bot. Sembra più facile scrivere la tua azione che combini entrambi i passaggi. Ma anche in questo modulo è abbastanza comodo da usare, è necessario selezionare un'etichetta dall'elenco durante la creazione di una richiesta pull.
È ora di schierarsi
Ho provato diverse opzioni di distribuzione tramite GitHub Actions (tramite ssh, tramite scp e utilizzando docker-hub) e posso dire che molto probabilmente troverai un modo per caricare il binario sul server, non importa quanto sia perversa la tua pipeline È.
Mi è piaciuta l'opzione per mantenere tutta l'infrastruttura in un unico posto, quindi diamo un'occhiata a come distribuire i pacchetti GitHub (questo è un repository per contenuto binario, npm, jar, docker).
Script per creare un'immagine docker e pubblicarla nei pacchetti GitHub:
Innanzitutto, dobbiamo creare il file JAR della nostra applicazione, dopodiché calcoliamo il percorso del registro docker di GitHub e il nome della nostra immagine. Ci sono alcuni trucchi qui che non abbiamo ancora incontrato:
costruzione come: echo "::set-output name=NAME::VALUE" consente di impostare il valore di una variabile nel passo corrente, in modo che possa essere letta in tutti gli altri passi.
puoi ottenere il valore della variabile impostata nel passaggio precedente tramite l'identificatore di questo passaggio: ${{ steps.global_env.outputs.DOCKERHUB_IMAGE_NAME }}
La variabile standard GITHUB_REPOSITORY memorizza il nome del repository e il suo proprietario ("owner/repo-name"). Per rimuovere tutto tranne il nome del repository da questa riga, usa la sintassi bash: ${GITHUB_REPOSITORY#*/}
Per specificare la versione dell'immagine, utilizziamo le prime cifre dall'hash SHA del commit - GITHUB_SHA ha anche delle sfumature qui, se esegui tali build non solo durante l'unione nel master, ma anche sull'evento di creazione della richiesta pull, quindi SHA potrebbe non corrispondere all'hash che vediamo nella cronologia di git perché l'azione actions/checkout crea il proprio hash univoco per evitare deadlock di azione in PR.
Se tutto è andato a buon fine, aprendo la sezione dei pacchetti (https://github.com/antkorwin/github-actions/packages) nel repository, vedrai una nuova immagine docker:
Puoi anche vedere l'elenco delle versioni dell'immagine docker lì.
Resta solo da configurare il nostro server per funzionare con questo registro e riavviare il servizio. Su come farlo tramite systemd, forse, lo dirò un'altra volta.
Monitoraggio
Vediamo un modo semplice per controllare l'integrità della nostra applicazione utilizzando GitHub Actions. La nostra applicazione di avvio ha un attuatore, quindi non è nemmeno necessario scrivere un'API per verificarne lo stato, è già stato fatto tutto per i pigri. Devi solo estrarre l'host: SERVER-URL:PORT/actuator/health
Tutto ciò di cui abbiamo bisogno è scrivere un'attività per controllare il server tramite la corona, ma se all'improvviso non ci risponde, invieremo una notifica tramite telegrammi.
Innanzitutto, scopriamo come eseguire il flusso di lavoro tramite cron:
Controlliamo lo stato del server manualmente usando curl:
jobs:
ping:
runs-on: ubuntu-18.04
steps:
- name: curl actuator
id: ping
run: |
echo "::set-output name=status::$(curl ${{secrets.SERVER_HOST}}/api/actuator/health)"
- name: health check
run: |
if [[ ${{ steps.ping.outputs.status }} != *"UP"* ]]; then
echo "health check is failed"
exit 1
fi
echo "It's OK"
Per prima cosa salviamo in una variabile ciò che il server ha risposto alla richiesta, al passo successivo controlliamo che lo stato sia UP e, se non lo è, usciamo con un errore. Se hai bisogno di "riempire" l'azione con le mani, allora esci da 1 - l'arma giusta.
- name: send alert in telegram
if: ${{ failure() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
message: |
Health check of the:
${{secrets.SERVER_HOST}}/api/actuator/health
failed with the result:
${{ steps.ping.outputs.status }}
L'invio ai telegrammi viene eseguito solo se l'azione non è riuscita nel passaggio precedente. Per inviare un messaggio, usiamo appleboy/telegram-action, puoi leggere come ottenere il token del bot e l'id della chat nella documentazione: github.com/appleboy/telegram-action
Non dimenticare di scrivere nei segreti su github: l'URL per il server e i token per il bot di Telegram.
Traccia bonus - JIRA per i pigri
Ho promesso che saremmo tornati a JIRA e siamo tornati. Centinaia di volte ho assistito a una situazione durante gli stand-up in cui gli sviluppatori hanno creato una funzionalità, unito un ramo, ma si sono dimenticati di trascinare l'attività su JIRA. Ovviamente, se tutto questo fosse fatto in un unico posto, sarebbe più semplice, ma in realtà scriviamo codice nell'IDE, uniamo i rami in bitbucket o GitHub, quindi trasciniamo le attività su Jira, per questo dobbiamo aprire nuove finestre , a volte accedi di nuovo e così via. Quando ricordi perfettamente cosa devi fare dopo, non ha senso aprire di nuovo il tabellone. Di conseguenza, al mattino in piedi, devi passare il tempo ad aggiornare la bacheca delle attività.
GitHub ci aiuterà anche in questa routine, per cominciare, possiamo trascinare automaticamente le attività nella colonna code_review quando carichiamo una richiesta pull. Tutto quello che devi fare è attenersi alla convenzione di denominazione dei rami:
[имя проекта]-[номер таска]-название
ad esempio, se la chiave del progetto "GitHub Actions" è GA, allora GA-8-jira-bot può essere un ramo per l'implementazione dell'attività GA-8.
L'integrazione di JIRA funziona attraverso le azioni di Atlassian, non sono perfette, devo dire che alcune di esse non hanno funzionato affatto per me. Ma discuteremo solo di quelli che funzionano sicuramente e vengono utilizzati attivamente.
Per prima cosa devi passare attraverso l'autorizzazione in JIRA usando l'azione: atlassian/gajira-login
- name: Find Issue
id: find_issue
shell: bash
run: |
echo "::set-output name=ISSUE_ID::$(echo ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}')"
echo brach name: $GITHUB_HEAD_REF
echo extracted issue: ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}'
- name: Check Issue
shell: bash
run: |
if [[ "${{steps.find_issue.outputs.ISSUE_ID}}" == "" ]]; then
echo "Please name your branch according to the JIRA issue: [project_key]-[task_number]-branch_name"
exit 1
fi
echo succcessfully found JIRA issue: ${{steps.find_issue.outputs.ISSUE_ID}}
Se cerchi nel marketplace di GitHub, puoi trovare un'azione per questa attività, ma ho dovuto scrivere la stessa cosa tramite grep con il nome del ramo, perché questa azione di Atlassian non voleva in alcun modo funzionare sul mio progetto, per capire cosa c'era che non andava lì - più a lungo che fare lo stesso a mano.
Resta solo da spostare l'attività nella colonna "Revisione del codice" durante la creazione di una richiesta pull:
C'è un'azione speciale su GitHub per questo, tutto ciò di cui ha bisogno è l'ID del problema ottenuto nel passaggio precedente e l'autorizzazione in JIRA che abbiamo fatto sopra.
Allo stesso modo, puoi trascinare le attività durante l'unione al master e altri eventi dal flusso di lavoro GitHub. In generale, tutto dipende dalla tua immaginazione e dal desiderio di automatizzare i processi di routine.
risultati
Se guardi il classico diagramma DEVOPS, abbiamo coperto tutte le fasi, ad eccezione di operare, penso che se ci provi, puoi trovare qualche azione nel mercato per l'integrazione con il sistema di help-desk, quindi supponiamo che la pipeline si è rivelato solido e si possono trarre conclusioni basate sul suo utilizzo.
pro:
Marketplace con azioni già pronte per tutte le occasioni, è molto bello. Nella maggior parte di essi, puoi anche guardare i codici sorgente per capire come risolvere un problema simile o inviare una richiesta di funzionalità all'autore direttamente nel repository github.
La scelta della piattaforma di destinazione per la compilazione: Linux, mac os, windows è una caratteristica piuttosto interessante.
Github Packages è un'ottima cosa, è conveniente mantenere l'intera infrastruttura in un unico posto, non devi navigare attraverso finestre diverse, tutto è nel raggio di uno o due clic del mouse ed è perfettamente integrato con GitHub Actions. Anche il supporto del registro Docker nella versione gratuita è un buon vantaggio.
GitHub nasconde i segreti nei registri di compilazione, quindi utilizzarlo per archiviare password e token non è così spaventoso. Per tutto il tempo degli esperimenti, non sono mai riuscito a vedere il segreto nella sua forma pura nella console.
Gratuito per progetti Open Source
contro:
YML, beh, non mi piace. Quando lavoro con un tale flusso, il mio messaggio di commit più frequente è "fix yml format", quindi ti dimentichi di inserire una scheda da qualche parte o scrivi sulla riga sbagliata. In generale, sedersi davanti a uno schermo con un goniometro e un righello non è l'esperienza più piacevole.
DEBUG, non è sempre conveniente eseguire il debug dei commit del flusso, avviare le ricostruzioni e l'output sulla console, ma è più come "stai ridacchiando", sei abituato a lavorare con IDEA convenienti quando puoi eseguire il debug di tutto ciò che desideri.
Puoi scrivere la tua azione su qualsiasi cosa se la avvolgi nella finestra mobile, ma solo javascript è supportato nativamente, ovviamente questa è una questione di gusti, ma preferirei qualcos'altro invece di js.
La prossima settimana mi esibirò con rapporto alla conferenza Heisenbug 2020 Piter. Ti dirò non solo come evitare errori durante la preparazione dei dati di test, ma condividerò anche i miei segreti per lavorare con i set di dati nelle applicazioni Java!