Настройка GitLab CI для загрузкі java праекта ў maven central
Дадзены артыкул разлічаны на java распрацоўнікаў, у якіх паўстала запатрабаванне хутка публікаваць свае прадукты ў рэпазітарах sonatype і/ці maven central з выкарыстаннем GitLab. У дадзеным артыкуле я раскажу пра наладу gitlab-runner, gitlab-ci і maven-plugin для рашэння дадзенай задачы.
перадумовы:
Бяспечнае захоўванне mvn і GPG ключоў.
Бяспечнае выкананне публічных CI задач.
Загрузка артэфактаў (release / snapshot) у публічныя рэпазітары.
Аўтаматычная праверка release-версій для публікацыі ў maven central.
Агульнае рашэнне па загрузцы артэфактаў у рэпазітар для некалькіх праектаў.
Падрабязнае апісанне механізму публікацыі артэфактаў у Maven Central праз Sonatype OSS Repository Hosting Service ужо апісана ў дадзеным артыкуле карыстальнікам гуголплекс, таму ў патрэбных месцах буду спасылацца на дадзены артыкул.
Папярэдне рэгіструемся ў Sonatype JIRA і заводзім тыкет на адкрыццё рэпазітара (больш падрабязна чытаць раздзел Ствараем тыкет на Sonatype JIRA). Пасля адкрыцця рэпазітара пара лагін/пароль ад JIRA (далей уліковы запіс Sonatype) будзе выкарыстоўвацца для загрузкі артэфактаў у Sonatype nexus.
Калі вы выкарыстоўваеце Linux кансоль для генерацыі GPG ключа (gnupg/gnupg2), тое неабходна ўсталяваць RNG-інструменты для генерацыі энтрапіі. У адваротным выпадку генерацыя ключа можа праходзіць вельмі доўга.
У першую чаргу неабходна стварыць і наладзіць праект, у якім будзе захоўвацца pipeline, для дэплою артэфактаў. Свой праект я назваў проста і немудрагеліста разгортванне
Пасля стварэння рэпазітара, неабходна абмежаваць доступ на змену рэпазітара.
Пераходзім у праект -> Settings -> Repository -> Protected Branches. Выдаляем усе правілы і дадаем адзінае правіла з Wildcard* з правам на push і merge толькі для карыстачоў з роляй Maintainers. Дадзенае правіла будзе працаваць для ўсіх карыстальнікаў як дадзенага праекту, так і групы ў якую дадзены праект уваходзіць.
Калі мэйнтэйнераў некалькі, то лепшым рашэннем будзе абмежаваць доступ да праекту ў прынцыпе.
Пераходзім у праект -> Settings -> General -> Visibility, project features, permissions і выстаўляем Project visibility у значэнне прыватны.
У мяне праект у публічным доступе, бо я выкарыстоўваю ўласны GitLab Runner і доступ на змену рэпазітара ёсць толькі ў мяне. Ды і ўласна не ў маіх інтарэсах свяціць прыватную інфармацыю ў публічных pipeline-логах.
Узмацненне жорсткасці правіл на змену рэпазітара
Пераходзім у праект -> Settings -> Repository -> Push Rules і ўсталёўваны сцягі Committer restriction, Check whether author is a GitLab user. Таксама рэкамендую наладзіць подпіс комітаў, і ўсталяваць сцяг Reject unsigned commits.
Далей патрабуецца наладзіць трыгер для запуску задач
Пераходзім у праект -> Settings -> CI / CD -> Pipeline triggers і ствараем новы trigger-token
Дадзены токен можна адразу дадаць у агульную канфігурацыю зменных для групы праектаў.
Пераходзім у групу -> Settings -> CI / CD -> Variables і дадаем зменную DEPLOY_TOKEN з trigger-token у значэнні.
У дадзеным раздзеле апісана канфігурацыя для запуску задач на deploy з выкарыстаннем уласнага (Specific) і публічнага (Shared) раннера.
Specific Runner
Я выкарыстоўваю ўласныя раннеры, бо ў першую чаргу гэта зручна, хутка, танна.
Для раннера рэкамендую лінуксавую VDS з 1 CPU, 2 GB RAM, 20 GB HDD. Кошт пытання ~3000₽ у год.
Мой раннер
Для раннера я ўзяў VDS 4 CPU, 4 GB RAM, 50 GB SSD. Абышлася ~11000₽ і ні разу не пашкадаваў.
У мяне ў агульнай складанасці 7 машынак. 5 на aruba і 2 на ihor.
Такім чынам, у нас ёсць раннер. Цяпер мы яго будзем настройваць.
Заходзім на машынку па SSH і ўсталёўваны java, git, maven, gnupg2.
Ствараем дырэкторыю для maven кэша і наважваем правы групы runner
Гэты пункт можна прапусціць, калі вы не плануеце запускаць некалькі раннераў на адной машыне.
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!
Правяраем, што раннер зарэгістраваны. Пераходзім на сайт gitlab.com -> deploy-project -> Settings -> CI/CD -> Runners -> Specific Runners -> Runners activated for this project
Скрын
Дадаем асобны сэрвіс /etc/systemd/system/gitlab-deployer.service
Генераваны ключ адказваючы на пытанні. Я выкарыстоўваў уласныя імя і пошту.
Абавязкова паказваем пароль для ключа. Гэтым ключом будуць падпісвацца артэфакты.
gpg --gen-key
правяраем
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
Загружаем наш публічны ключ на сервер ключоў
gpg --keyserver keys.gnupg.net --send-key 00000000
gpg: sending key 00000000 to hkp server keys.gnupg.net
Ствараем дырэторыю maven сховішча і лінцуем з кэшам (не памыліцеся)
Гэты пункт можна прапусціць, калі вы не плануеце запуску некалькі раннераў на адной машыне.
Дадаем у корань deploy-праекта файл .gitlab-ci.yml
У скрыпце прадстаўлена дзве ўзаемавыключальныя задачы на дэплой. Specific Runner ці Shared Runner адпаведна.
.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
Калі ў вас шматмодульны праект, і вам няма неабходнасці загружаць пэўны модуль у рэпазітар, то ў pom.xml дадзенага модуля неабходна дадаць nexus-staging-maven-plugin са сцягам skipNexusStagingDeployMojo
<repositories>
<repository>
<id>SonatypeNexus</id>
<url>https://oss.sonatype.org/content/groups/staging/</url>
<!-- Не надо указывать флаги snapshot/release для репозитория -->
</repository>
</repositories>
Яшчэ плюсы
Вельмі багаты спіс мэт для працы з nexus рэпазітаром (mvn help:describe -Dplugin=org.sonatype.plugins:nexus-staging-maven-plugin).
Аўтаматычная праверка рэлізу на магчымасць загрузкі ў maven central
Пры ўсталёўцы тэга, аўтаматычна трыгерыцца адпаведная задача ў праекце deploy на загрузку рэлізнай версіі ў nexus (прыклад).
Самае прыемнае, што аўтаматычна спрацоўвае close release у 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] ------------------------------------------------------------------------
І калі нешта пайшло не так, то задача абавязкова заваліцца.
[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] ------------------------------------------------------------------------
У выніку нам застаецца адзіны выбар. Або выдаліць дадзеную версію ці апублікаваць.
Пасля рэлізу, праз некаторы час артэфакты апынуцца ў
афтоп
Для мяне было адкрыццём, што maven індэксуе іншыя публічныя рэпазітары.
Прыйшлося падкінуць robots.txt, бо ён праіндэксаваў мой стары рэпазітар.
Асобны deploy-праект у якім можна рэалізаваць некалькі CI задач на загрузку артэфактаў у публічныя рэпазітары для розных моў распрацоўкі.
Deploy-праект ізаляваны ад старонняга ўмяшання і можа быць зменены толькі карыстальнікамі з роляй Owner і Maintainer.
Асобны Specific Runner з "гарачым" кэшам для запуску толькі deploy задач.
Публікацыя snapshot/release версій у публічным рэпазітары.
Аўтаматычная праверка release версіі на гатоўнасць да публікацыі ў maven central.
Абарона ад аўтаматычнай публікацыі "волкіх" версій у maven central.
Зборка і публікацыя snapshot версій "па кліку".
Адзіны рэпазітар для атрымання snapshot/release версій.
Агульны пайплайн на зборку/тэставанне/публікацыю java праекту.
Настройка GitLab CI не такая складаная тэма як здаецца на першы погляд. Дастаткова пару разоў наладзіць CI "пад ключ" і вось, ты ўжо далёка не дылетант у гэтай справе. Тым больш GitLab дакументацыя вельмі залішняя. Не бойцеся рабіць першы крок. Дарога ўзнікае пад крокамі ідучага (не памятаю хто сказаў 🙂).
Буду рады фідбэку.
У наступным артыкуле раскажу пра тое, як наладзіць GitLab CI для канкурэнтнага запуску задач з інтэграцыйнымі тэстамі (з запускам тэстоўваных сэрвісаў пры дапамозе docker-compose), калі ў вас ёсць толькі адзін shell раннер.