Configurazione di GitLab CI per caricare un progetto Java su Maven Central
Questo articolo è destinato agli sviluppatori Java che necessitano di pubblicare rapidamente i propri prodotti su sonatype e/o repository centrali Maven utilizzando GitLab. In questo articolo parlerò della configurazione di gitlab-runner, gitlab-ci e maven-plugin per risolvere questo problema.
Prerequisiti:
Archiviazione sicura delle chiavi mvn e GPG.
Esecuzione sicura delle attività CI pubbliche.
Caricamento di artefatti (rilascio/istantanea) nei repository pubblici.
Controllo automatico delle versioni di rilascio per la pubblicazione in Maven Central.
Una soluzione generale per caricare artefatti in un repository per più progetti.
Una descrizione dettagliata del meccanismo per la pubblicazione degli artefatti su Maven Central tramite il servizio di hosting del repository OSS Sonatype è già descritta in questo articolo per utente Googolplex, quindi farò riferimento a questo articolo nei posti giusti.
Preregistrati su Sonatipo JIRA e avviare un ticket per aprire il repository (per maggiori dettagli leggere la sezione Crea un biglietto Sonatype JIRA). Dopo aver aperto il repository, la coppia login/password JIRA (di seguito denominata account Sonatype) verrà utilizzata per caricare gli artefatti sul nexus Sonatype.
Se stai utilizzando la console Linux per generare una chiave GPG (gnupg/gnupg2), devi installare rng-strumenti per generare entropia. Altrimenti, la generazione delle chiavi può richiedere molto tempo.
Configurazione di un progetto di distribuzione in GitLab
Prima di tutto, è necessario creare e configurare un progetto in cui verrà archiviata la pipeline per la distribuzione degli artefatti. Ho chiamato il mio progetto in modo semplice e senza complicazioni: schierare
Dopo aver creato il repository, è necessario limitare l'accesso per modificare il repository.
Vai al progetto -> Impostazioni -> Repository -> Rami protetti. Eliminiamo tutte le regole e aggiungiamo una singola regola con Wildcard* con diritto di push e merge solo per gli utenti con ruolo Manutentori. Questa regola funzionerà per tutti gli utenti sia di questo progetto che del gruppo a cui appartiene questo progetto.
Se ci sono più manutentori, la soluzione migliore sarebbe in linea di principio limitare l’accesso al progetto.
Vai al progetto -> Impostazioni -> Generale -> Visibilità, funzionalità del progetto, autorizzazioni e imposta Visibilità del progetto su Privata.
Ho un progetto ad accesso pubblico, poiché utilizzo il mio GitLab Runner e solo io ho accesso per modificare il repository. Beh, in realtà non è nel mio interesse mostrare informazioni private nei registri della pipeline pubblica.
Inasprimento delle regole per la modifica del repository
Vai al progetto -> Impostazioni -> Repository -> Regole push e imposta i flag Restrizione committente, Controlla se l'autore è un utente GitLab. Consiglio anche l'impostazione impegnarsi a firmaree imposta il flag Rifiuta commit non firmati.
Successivamente, è necessario configurare un trigger per eseguire le attività
Vai a progetto -> Impostazioni -> CI/CD -> Trigger pipeline e crea un nuovo token trigger
Questo token può essere immediatamente aggiunto alla configurazione generale delle variabili per un gruppo di progetti.
Vai al gruppo -> Impostazioni -> CI/CD -> Variabili e aggiungi una variabile DEPLOY_TOKEN con trigger-token nel valore.
Questa sezione descrive la configurazione per l'esecuzione delle attività durante la distribuzione utilizzando il runner nativo (Specifico) e pubblico (Condiviso).
Corridore specifico
Utilizzo i miei runner, perché prima di tutto è comodo, veloce, economico.
Per i corridori consiglio Linux VDS con 1 CPU, 2 GB RAM, 20 GB HDD. Prezzo di emissione ~ 3000₽ all'anno.
Il mio corridore
Per il corridore ho preso CPU VDS 4, 4 GB RAM, SSD 50 GB. È costato ~ 11000₽ e non me ne sono mai pentito.
Ho un totale di 7 macchine. 5 su Aruba e 2 su ihor.
Quindi, abbiamo un corridore. Adesso lo sistemeremo.
Andiamo alla macchina tramite SSH e installiamo java, git, maven, gnupg2.
Crea una directory per la cache Maven e assegna i diritti al gruppo runner
Puoi saltare questo passaggio se non prevedi di correre più corridori sulla stessa macchina.
Runtime platform arch=amd64 os=linux pid=17594 revision=3001a600 version=11.10.0
Running in system-mode.
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.com/
Please enter the gitlab-ci token for this runner:
REGISTRATION_TOKEN
Please enter the gitlab-ci description for this runner:
[ih1174328.vds.myihor.ru]: Deploy Runner
Please enter the gitlab-ci tags for this runner (comma separated):
deploy
Registering runner... succeeded runner=ZvKdjJhx
Please enter the executor: docker-ssh, parallels, virtualbox, docker-ssh+machine, kubernetes, docker, ssh, docker+machine, shell:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Verificare che il corridore sia iscritto. Vai su gitlab.com -> deploy-project -> Impostazioni -> CI/CD -> Runner -> Runner specifici -> Runner attivati per questo progetto
schermo
Aggiunta separato servizio /etc/systemd/system/gitlab-deployer.service
Generiamo una chiave rispondendo alle domande. Ho usato il mio nome e la mia email.
Assicurati di specificare la password per la chiave. Gli artefatti verranno firmati con questa chiave.
gpg --gen-key
controllare la
gpg --list-keys -a
/home/gitlab-deployer/.gnupg/pubring.gpg
----------------------------------------
pub 4096R/00000000 2019-04-19
uid Petruha Petrov <[email protected]>
sub 4096R/11111111 2019-04-19
Caricando la nostra chiave pubblica sul keyserver
gpg --keyserver keys.gnupg.net --send-key 00000000
gpg: sending key 00000000 to hkp server keys.gnupg.net
Crea una directory esperti deposito e collegarsi alla cache (non commettere errori)
Questo passaggio può essere saltato se non prevedi di correre più corridori sulla stessa macchina.
Creiamo un Dockerfile abbastanza semplice per eseguire attività al momento della distribuzione con la versione desiderata di Java. Di seguito è riportato un esempio per alpine.
FROM java:8u111-jdk-alpine
RUN apk add gnupg maven git --update-cache
--repository http://dl-4.alpinelinux.org/alpine/edge/community/ --allow-untrusted &&
mkdir ~/.m2/
Aggiungi il file .gitlab-ci.yml alla radice del progetto di distribuzione
Lo script presenta due attività di distribuzione che si escludono a vicenda. Rispettivamente Runner specifico o Runner condiviso.
.gitlab-ci.yml
stages:
- deploy
Specific Runner:
extends: .java_deploy_template
# Задача будет выполняться на вашем shell-раннере
tags:
- deploy
Shared Runner:
extends: .java_deploy_template
# Задача будет выполняться на публичном docker-раннере
tags:
- docker
# Образ из раздела GitLab Runner -> Shared Runner -> Docker
image: registry.gitlab.com/group/deploy-project:latest
before_script:
# Импортируем GPG ключ
- printf "${GPG_SECRET_KEY}" | gpg --batch --import
# Сохраняем maven конфигурацию
- printf "${SETTINGS_SECURITY_XML}" > ~/.m2/settings-security.xml
- printf "${SETTINGS_XML}" > ~/.m2/settings.xml
.java_deploy_template:
stage: deploy
# Задача сработает по триггеру, если передана переменная DEPLOY со значением java
only:
variables:
- $DEPLOY == "java"
variables:
# отключаем клонирование текущего проекта
GIT_STRATEGY: none
script:
# Предоставляем возможность хранения пароля в незашифрованном виде
- git config --global credential.helper store
# Сохраняем временные креды пользователя gitlab-ci-token
# Токен работает для всех публичных проектов gitlab.com и для проектов группы
- echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/.git-credentials
# Полностью чистим текущую директорию
- rm -rf .* *
# Клонируем проект который, будем деплоить в Sonatype Nexus
- git clone ${DEPLOY_CI_REPOSITORY_URL} .
# Переключаемся на нужный коммит
- git checkout ${DEPLOY_CI_COMMIT_SHA} -f
# Если хоть один pom.xml содержит параметр autoReleaseAfterClose валим сборку.
# В противном случае есть риск залить сырые артефакты в maven central
- >
for pom in $(find . -name pom.xml); do
if [[ $(grep -q autoReleaseAfterClose "$pom" && echo $?) == 0 ]]; then
echo "File $pom contains prohibited setting: <autoReleaseAfterClose>";
exit 1;
fi;
done
# Если параметр DEPLOY_CI_COMMIT_TAG пустой, то принудительно ставим SNAPSHOT-версию
- >
if [[ "${DEPLOY_CI_COMMIT_TAG}" != "" ]]; then
mvn versions:set -DnewVersion=${DEPLOY_CI_COMMIT_TAG}
else
VERSION=$(mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec)
if [[ "${VERSION}" == *-SNAPSHOT ]]; then
mvn versions:set -DnewVersion=${VERSION}
else
mvn versions:set -DnewVersion=${VERSION}-SNAPSHOT
fi
fi
# Запускаем задачу на сборку и деплой артефактов
- mvn clean deploy -DskipTests=true
Nei progetti Java che dovrebbero essere caricati su repository pubblici, è necessario aggiungere 2 passaggi per scaricare le versioni Release e Snapshot.
.gitlab-ci.yml
stages:
- build
- test
- verify
- deploy
<...>
Release:
extends: .trigger_deploy
# Запускать задачу только пo тегу.
only:
- tags
Snapshot:
extends: .trigger_deploy
# Запускаем задачу на публикацию SNAPSHOT версии вручную
when: manual
# Не запускать задачу, если проставлен тег.
except:
- tags
.trigger_deploy:
stage: deploy
variables:
# Отключаем клонирование текущего проекта
GIT_STRATEGY: none
# Ссылка на триггер deploy-задачи
URL: "https://gitlab.com/api/v4/projects/<deploy project ID>/trigger/pipeline"
# Переменные deploy-задачи
POST_DATA: "
token=${DEPLOY_TOKEN}&
ref=master&
variables[DEPLOY]=${DEPLOY}&
variables[DEPLOY_CI_REPOSITORY_URL]=${CI_REPOSITORY_URL}&
variables[DEPLOY_CI_PROJECT_NAME]=${CI_PROJECT_NAME}&
variables[DEPLOY_CI_COMMIT_SHA]=${CI_COMMIT_SHA}&
variables[DEPLOY_CI_COMMIT_TAG]=${CI_COMMIT_TAG}
"
script:
# Не использую cURL, так как с флагами --fail --show-error
# он не выводит тело ответа, если HTTP код 400 и более
- wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}
In questa soluzione sono andato un po' oltre e ho deciso di utilizzare un modello CI per i progetti Java.
Maggiori dettagli
Ho creato un progetto separato gitlab-ci in cui ha inserito il modello CI per i progetti Java comune.yml.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
<!-- Генерация javadoc должна быть после фазы генерации ресурсов -->
<phase>prepare-package</phase>
<configuration>
<!-- Очень помогает в публичных проектах -->
<failOnError>true</failOnError>
<failOnWarnings>true</failOnWarnings>
<!-- Убирает ошибку поиска документации в target директории -->
<detectOfflineLinks>false</detectOfflineLinks>
</configuration>
</execution>
</executions>
</plugin>
Se hai un modulo che non contiene Java (ad esempio solo risorse)
Oppure non vuoi generare Javadoc in linea di principio, quindi per aiutare maven-jar-plugin
Se hai un progetto multi-modulo e non hai bisogno di caricare un modulo specifico nel repository, devi aggiungere al pom.xml di questo modulo nexus-staging-maven-plugin con bandiera skipNexusStagingDeployMojo
Dopo il caricamento delle versioni snapshot/release sono disponibili in repository di staging
<repositories>
<repository>
<id>SonatypeNexus</id>
<url>https://oss.sonatype.org/content/groups/staging/</url>
<!-- Не надо указывать флаги snapshot/release для репозитория -->
</repository>
</repositories>
Più vantaggi
Un elenco molto ricco di obiettivi per lavorare con il repository Nexus (mvn help:describe -Dplugin=org.sonatype.plugins:nexus-staging-maven-plugin).
Controllo automatico del rilascio per la scaricabilità in Maven Central
Quando il tag è impostato, l'attività corrispondente nel progetto di distribuzione viene attivata automaticamente per caricare la versione di rilascio su nexus (esempio).
La parte migliore è che il rilascio ravvicinato si attiva automaticamente in Nexus.
[INFO] Performing remote staging...
[INFO]
[INFO] * Remote staging into staging profile ID "9043b43f77dcc9"
[INFO] * Created staging repository with ID "orgtouchbit-1037".
[INFO] * Staging repository at https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1037
[INFO] * Uploading locally staged artifacts to profile org.touchbit
[INFO] * Upload of locally staged artifacts finished.
[INFO] * Closing staging repository with ID "orgtouchbit-1037".
Waiting for operation to complete...
.........
[INFO] Remote staged 1 repositories, finished with success.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Shields4J 1.0.0 .................................... SUCCESS [ 9.603 s]
[INFO] test-core .......................................... SUCCESS [ 3.419 s]
[INFO] Shields4J client ................................... SUCCESS [ 9.793 s]
[INFO] TestNG listener 1.0.0 .............................. SUCCESS [01:23 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:47 min
[INFO] Finished at: 2019-04-21T04:05:46+03:00
[INFO] ------------------------------------------------------------------------
E se qualcosa è andato storto, l'attività fallirà
[INFO] Performing remote staging...
[INFO]
[INFO] * Remote staging into staging profile ID "9043b43f77dcc9"
[INFO] * Created staging repository with ID "orgtouchbit-1038".
[INFO] * Staging repository at https://oss.sonatype.org:443/service/local/staging/deployByRepositoryId/orgtouchbit-1038
[INFO] * Uploading locally staged artifacts to profile org.touchbit
[INFO] * Upload of locally staged artifacts finished.
[INFO] * Closing staging repository with ID "orgtouchbit-1038".
Waiting for operation to complete...
.......
[ERROR] Rule failure while trying to close staging repository with ID "orgtouchbit-1039".
[ERROR]
[ERROR] Nexus Staging Rules Failure Report
[ERROR] ==================================
[ERROR]
[ERROR] Repository "orgtouchbit-1039" failures
[ERROR] Rule "signature-staging" failures
[ERROR] * No public key: Key with id: (1f42b618d1cbe1b5) was not able to be located on <a href=http://keys.gnupg.net:11371/>http://keys.gnupg.net:11371/</a>. Upload your public key and try the operation again.
...
[ERROR] Cleaning up local stage directory after a Rule failure during close of staging repositories: [orgtouchbit-1039]
[ERROR] * Deleting context 9043b43f77dcc9.properties
[ERROR] Cleaning up remote stage repositories after a Rule failure during close of staging repositories: [orgtouchbit-1039]
[ERROR] * Dropping failed staging repository with ID "orgtouchbit-1039" (Rule failure during close of staging repositories: [orgtouchbit-1039]).
[ERROR] Remote staging finished with a failure: Staging rules failure!
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Shields4J 1.0.0 .................................... SUCCESS [ 4.073 s]
[INFO] test-core .......................................... SUCCESS [ 2.788 s]
[INFO] Shields4J client ................................... SUCCESS [ 3.962 s]
[INFO] TestNG listener 1.0.0 .............................. FAILURE [01:07 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
Di conseguenza, ci resta solo una scelta. Oppure elimina questa versione o pubblica.
Dopo il rilascio, dopo un po' di tempo, gli artefatti saranno disponibili
offtop
È stata una rivelazione per me che Maven indicizzi altri repository pubblici.
Ho dovuto aggiungere robots.txt perché indicizzava il mio vecchio repository.
Un progetto di distribuzione separato in cui è possibile implementare diverse attività CI per caricare gli artefatti nei repository pubblici per vari linguaggi di sviluppo.
Il progetto di distribuzione è isolato da interferenze esterne e può essere modificato solo dagli utenti con i ruoli di Proprietario e Manutentore.
Un Runner specifico separato con una cache "attiva" per eseguire solo le attività di distribuzione.
Pubblicazione delle versioni snapshot/release in un repository pubblico.
Controllo automatico della versione di rilascio per essere pronta per la pubblicazione in Maven Central.
Protezione contro la pubblicazione automatica delle versioni "grezze" in Maven Central.
Crea e pubblica versioni di snapshot “al clic”.
Repository unico per ottenere versioni snapshot/release.
Pipeline generale per creare/testare/pubblicare un progetto Java.
La configurazione di GitLab CI non è un argomento così complicato come sembra a prima vista. È sufficiente impostare la CI chiavi in \uXNUMXb\uXNUMXbmano un paio di volte e ora sei tutt'altro che un dilettante in questa materia. Inoltre, la documentazione di GitLab è molto ridondante. Non aver paura di fare il primo passo. La strada appare sotto i passi di chi cammina (non ricordo chi lo ha detto :)
Sarò felice di ricevere un feedback.
Nel prossimo articolo, ti mostrerò come configurare GitLab CI per eseguire attività di test di integrazione in modo competitivo (eseguendo servizi di test con docker-compose) se disponi di un solo shell runner.