This article is aimed at Java developers who need to quickly publish their products in Sonatype and/or Maven Central repositories using GitLab. In this article, I will discuss the setup of gitlab-runner, gitlab-ci, and maven-plugin to achieve this goal.
Prerequisites:
- Secure storage of mvn and GPG keys.
- Secure execution of public CI tasks.
- Uploading artifacts (release/snapshot) to public repositories.
- Automatic verification of release versions for publishing to Maven Central.
- General solution for uploading artifacts to the repository for multiple projects.
- Simplicity and ease of use.
Content
General information
- A detailed description of the mechanism for publishing artifacts to Maven Central via the Sonatype OSS Repository Hosting Service has already been provided in by the user , so I will reference this article in the necessary places.
- First, register at and create a ticket to open a repository (for more details, see the section ). After opening the repository, the login/password pair from JIRA (thus, the Sonatype account) will be used to upload artifacts to Sonatype Nexus.
- Then the process of generating a GPG key is described rather dryly. For more details, refer to the section
- If you are using the Linux console to generate a GPG key (gnupg/gnupg2), you need to install to generate entropy. Otherwise, key generation may take a very long time.
- Storage services for public GPG keys
Setting up the deploy project in GitLab
- First, you need to create and configure a project where the pipeline for deploying artifacts will be stored. I named my project simply and straightforwardly —
- After creating the repository, it is necessary to restrict access to modify the repository.
Go to the project -> Settings -> Repository -> Protected Branches. Remove all rules and add a single rule with Wildcard * allowing push and merge only for users with the Maintainers role. This rule will apply to all users in both this project and the group to which this project belongs.
- If there are several maintainers, the best solution would be to restrict access to the project altogether.
Go to the project -> Settings -> General -> Visibility, project features, permissions and set Project visibility to Private.
I have my project publicly accessible since I use my own GitLab Runner and only I have access to modify the repository. Besides, it's not in my interest to expose private information in public pipeline logs. - Tightening the rules for modifying the repository
Go to the project -> Settings -> Repository -> Push Rules and enable the Committer restriction flag and Check whether author is a GitLab user. I also recommend configuring , and enable the Reject unsigned commits flag. - Next, configure a trigger to start tasks
Go to the project -> Settings -> CI / CD -> Pipeline triggers and create a new trigger-token
This token can be added to the common configuration of variables for the project group.
Go to the group -> Settings -> CI / CD -> Variables and add the variableDEPLOY_TOKENwith the trigger-token value.
GitLab Runner
This section describes the configuration for deploying tasks with the use of a specific (Specific) and a shared (Shared) runner.
Specific Runner
I use my own runners, as this is more convenient, faster, and cheaper.
For the runner, I recommend a Linux VDS with 1 CPU, 2 GB RAM, and 20 GB HDD. The cost is about ~3000₽ per year.
My runner
For the runner, I chose a VDS with 4 CPU, 4 GB RAM, and 50 GB SSD. It cost around ~11000₽ and I have never regretted it.
I have a total of 7 machines. 5 on Aruba and 2 on Ihor.
So, we have the runner. Now we will set it up.
Log into the machine via SSH and install java, git, maven, gnupg2.
Install GitLab runner
- Create a new group
runnersudo groupadd runner - Create a directory for the maven cache and assign group permissions
runner
This step can be skipped if you do not plan to run multiple runners on the same machine.mkdir -p /usr/cache/.m2/repository chown -R :runner /usr/cache chmod -R 770 /usr/cache - Create a user
gitlab-deployerand add it to the grouprunneruseradd -m -d /home/gitlab-deployer gitlab-deployer usermod -a -G runner gitlab-deployer - Add the following line to the file
/etc/ssh/sshd_config.AllowUsers root@* gitlab-deployer@127.0.0.1 - Restarting
sshd.systemctl restart sshd - Setting password for user
gitlab-deployer(can be simple as there is a restriction for localhost)passwd gitlab-deployer - Installing GitLab Runner (Linux x86-64)
sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64 sudo chmod +x /usr/local/bin/gitlab-runner ln -s /usr/local/bin/gitlab-runner /etc/alternatives/gitlab-runner ln -s /etc/alternatives/gitlab-runner /usr/bin/gitlab-runner - Go to gitlab.com -> deploy-project -> Settings -> CI/CD -> Runners -> Specific Runners and copy the registration token
Screenshot
- Registering runner
gitlab-runner register --config /etc/gitlab-runner/gitlab-deployer-config.toml
Process
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!- Checking that the runner is registered. Go to gitlab.com -> deploy-project -> Settings -> CI/CD -> Runners -> Specific Runners -> Runners activated for this project
Screenshot
- Add separate service
/etc/systemd/system/gitlab-deployer.service[Unit] Description=GitLab Deploy Runner After=syslog.target network.target ConditionFileIsExecutable=/usr/local/bin/gitlab-runner [Service] StartLimitInterval=5 StartLimitBurst=10 ExecStart=/usr/local/bin/gitlab-runner "run" "--working-directory" "/home/gitlab-deployer" "--config" "/etc/gitlab-runner/gitlab-deployer-config.toml" "--service" "gitlab-deployer" "--syslog" "--user" "gitlab-deployer" Restart=always RestartSec=120 [Install] WantedBy=multi-user.target - Starting the service.
systemctl enable gitlab-deployer.service systemctl start gitlab-deployer.service systemctl status gitlab-deployer.service - Checking that the runner is started.
Example
Generating GPG keys
- From this same machine, log in via ssh as user
gitlab-deployer(this is important for generating the GPG key)ssh gitlab-deployer@127.0.0.1 - Generating the key by answering the questions. I used my own name and email.
Make sure to specify a password for the key. This key will be used to sign the artifacts.gpg --gen-key - Checking
gpg --list-keys -a /home/gitlab-deployer/.gnupg/pubring.gpg ---------------------------------------- pub 4096R/00000000 2019-04-19 uid Petruha Petrov sub 4096R/11111111 2019-04-19 - Uploading our public key to the key server
gpg --keyserver keys.gnupg.net --send-key 00000000 gpg: sending key 00000000 to hkp server keys.gnupg.net
Setting up Maven
- Logging in as user
gitlab-deployersu gitlab-deployer - Creating a maven directory repository and linking with the cache (be careful)
This step can be skipped if you do not plan to run multiple runners on one machine.mkdir -p ~/.m2/repository ln -s /usr/cache/.m2/repository /home/gitlab-deployer/.m2/repository - Creating master key
mvn --encrypt-master-password password {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=} - Creating file ~/.m2/settings-security.xml
{hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=} - Encrypting password for the Sonatype account
mvn --encrypt-password SONATYPE_PASSWORD {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J} - Creating file ~/.m2/settings.xml
env true GPG_SECRET_KEY_PASSPHRASE sonatype SONATYPE_USERNAME {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
where,
GPG_SECRET_KEY_PASSPHRASE — password for the GPG key
SONATYPE_USERNAME — login for the Sonatype account
This completes the runner setup, and we can move on to the section
Shared Runner
Generating GPG keys
- First, you need to create a GPG key. To do this, install gnupg.
yum install -y gnupg - Generate the key by answering the questions. I used my own name and email. Be sure to specify a password for the key.
gpg --gen-key - Display information about the key
gpg --list-keys -a pub rsa3072 2019-04-24 [SC] [expires: 2021-04-23] 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 uid [ultimate] tttemp sub rsa3072 2019-04-24 [E] [expires: none] - Uploading our public key to the key server
gpg --keyserver keys.gnupg.net --send-key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 gpg: sending key 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 to hkp server keys.gnupg.net - Get the private key
gpg --export-secret-keys --armor 2D0D1706366FC4AEF79669E24D09C55BBA3FD728 -----BEGIN PGP PRIVATE KEY BLOCK----- lQWGBFzAqp8BDADN41CPwJ/gQwiKEbyA902DKw/WSB1AvZQvV/ZFV77xGeG4K7k5 ... =2Wd2 -----END PGP PRIVATE KEY BLOCK----- - Go to project settings -> Settings -> CI / CD -> Variables and save the private key in the variable
GPG_SECRET_KEY
Setting up Maven
- Creating master key
mvn --encrypt-master-password password {hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=} - Go to project settings -> Settings -> CI / CD -> Variables and save in the variable
SETTINGS_SECURITY_XMLthe following lines:{hnkle5BJ9HUHUMP+CXfGBl8dScfFci/mpsur/73tR2I=} - Encrypting password for the Sonatype account
mvn --encrypt-password SONATYPE_PASSWORD {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J} - Go to project settings -> Settings -> CI / CD -> Variables and save in the variable
SETTINGS_XMLthe following lines:env true GPG_SECRET_KEY_PASSPHRASE sonatype sonatype_username {98Wv5+u+Tn0HX2z5G/kR4R8Z0WBgcDBgi7d12S/un+SCU7uxzaZGGmJ8Cu9pAZ2J}
where,
GPG_SECRET_KEY_PASSPHRASE — password for the GPG key
SONATYPE_USERNAME — login for the Sonatype account
Deploying Docker image
- Create a simple Dockerfile for running deployment tasks with the required version of Java. Below is an example for 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/ - Building a container for your project
docker build -t registry.gitlab.com/group/deploy . - Authenticating and uploading the container to the registry.
docker login -u USER -p PASSWORD registry.gitlab.com docker push registry.gitlab.com/group/deploy
GitLab CI
Deploy project
Adding the .gitlab-ci.yml file to the root of the deploy project
The script contains two mutually exclusive deployment tasks: Specific Runner or Shared Runner, respectively.
.gitlab-ci.yml
stages:
- deploy
Specific Runner:
extends: .java_deploy_template
# The task will run on your shell runner
tags:
- deploy
Shared Runner:
extends: .java_deploy_template
# The task will run on a public docker runner
tags:
- docker
# Image from the GitLab Runner section -> Shared Runner -> Docker
image: registry.gitlab.com/group/deploy-project:latest
before_script:
# Importing GPG key
- printf "${GPG_SECRET_KEY}" | gpg --batch --import
# Saving maven configuration
- printf "${SETTINGS_SECURITY_XML}" > ~/.m2/settings-security.xml
- printf "${SETTINGS_XML}" > ~/.m2/settings.xml
.java_deploy_template:
stage: deploy
# The task will trigger if the DEPLOY variable is set to java
only:
variables:
- $DEPLOY == "java"
variables:
# Disable cloning the current project
GIT_STRATEGY: none
script:
# Allow storing the password in plain text
- git config --global credential.helper store
# Save temporary credentials for gitlab-ci-token user
# The token works for all public projects on gitlab.com and for group projects
- echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/.git-credentials
# Completely clean the current directory
- rm -rf .* *
# Clone the project that will be deployed to Sonatype Nexus
- git clone ${DEPLOY_CI_REPOSITORY_URL} .
# Switch to the desired commit
- git checkout ${DEPLOY_CI_COMMIT_SHA} -f
# If any pom.xml file contains the autoReleaseAfterClose parameter, abort the build.
# Otherwise, there's a risk of uploading raw artifacts to maven central
- >
for pom in $(find . -name pom.xml); do
if [[ $(grep -q autoReleaseAfterClose "$pom" && echo $?) == 0 ]]; then
echo "File $pom contains prohibited setting: ";
exit 1;
fi;
done
# If the DEPLOY_CI_COMMIT_TAG parameter is empty, force set the SNAPSHOT version
- >
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
# Running the task to build and deploy artifacts
- mvn clean deploy -DskipTests=trueJava project
In Java projects that are intended to be uploaded to public repositories, it is necessary to add two steps for uploading Release and Snapshot versions.
.gitlab-ci.yml
stages:
- build
- test
- verify
- deploy
Release:
extends: .trigger_deploy
# Run the job only on tags.
only:
- tags
Snapshot:
extends: .trigger_deploy
# Manually trigger the publish task for the SNAPSHOT version
when: manual
# Do not run the job if a tag is set.
except:
- tags
.trigger_deploy:
stage: deploy
variables:
# Disable cloning of the current project
GIT_STRATEGY: none
# URL for the deploy job trigger
URL: "https://gitlab.com/api/v4/projects//trigger/pipeline"
# Variables for the deploy job
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:
# Do not use cURL, as with the flags --fail --show-error
# it does not output the response body if HTTP code 400 or higher
- wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}In this solution, I went a bit further and decided to use a single CI template for Java projects.
More details
I created a separate project where I placed the CI template for Java projects .
common.yml
stages:
- build
- test
- verify
- deploy
variables:
SONAR_ARGS: "
-Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA}
-Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME}
"
.build_java_project:
stage: build
tags:
- touchbit-shell
variables:
SKIP_TEST: "false"
script:
- mvn clean
- mvn package -DskipTests=${SKIP_TEST}
artifacts:
when: always
expire_in: 30 day
paths:
- "*\/target\/reports"
.build_sphinx_doc:
stage: build
tags:
- touchbit-shell
variables:
DOCKERFILE: .indirect\/docs\/Dockerfile
script:
- docker build --no-cache -t ${CI_PROJECT_NAME}\/doc -f ${DOCKERFILE} .
.junit_module_test_run:
stage: test
tags:
- touchbit-shell
variables:
MODULE: ""
script:
- cd ${MODULE}
- mvn test
artifacts:
when: always
expire_in: 30 day
paths:
- "*\/target\/reports"
.junit_test_run:
stage: test
tags:
- touchbit-shell
script:
- mvn test
artifacts:
when: always
expire_in: 30 day
paths:
- "*\/target\/reports"
.sonar_review:
stage: verify
tags:
- touchbit-shell
dependencies: []
script:
- >
if [ "$CI_BUILD_REF_NAME" == "master" ]; then
mvn compile sonar:sonar -Dsonar.login=$SONAR_LOGIN $SONAR_ARGS
else
mvn compile sonar:sonar -Dsonar.login=$SONAR_LOGIN $SONAR_ARGS -Dsonar.analysis.mode=preview
fi
.trigger_deploy:
stage: deploy
tags:
- touchbit-shell
variables:
URL: "https:\/\/gitlab.com\/api\/v4\/projects\/10345765\/trigger\/pipeline"
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:
- wget --content-on-error -qO- ${URL} --post-data ${POST_DATA}
.trigger_release_deploy:
extends: .trigger_deploy
only:
- tags
.trigger_snapshot_deploy:
extends: .trigger_deploy
when: manual
except:
- tags
As a result, the .gitlab-ci.yml files in the Java projects are quite compact and succinct.
.gitlab-ci.yml
include: https:\/\/gitlab.com\/TouchBIT\/gitlab-ci\/raw\/master\/common.yml
Shields4J:
extends: .build_java_project
Sphinx doc:
extends: .build_sphinx_doc
variables:
DOCKERFILE: .docs\/Dockerfile
Sonar review:
extends: .sonar_review
dependencies:
- Shields4J
Release:
extends: .trigger_release_deploy
Snapshot:
extends: .trigger_snapshot_deployConfiguration of pom.xml
This topic is described in great detail. downward API support (simultaneously with this in , therefore I will describe some nuances of using plugins. I will also explain how easily and effortlessly one can use nexus-staging-maven-plugin, if you do not want to or cannot use org.sonatype.oss:oss-parent as the parent for your project.
maven-install-plugin
Installs modules in the local repository.
Very useful for local verification of solutions in other projects, as well as checksums.
org.apache.maven.plugins
maven-install-plugin
install-project
install
target/${project.artifactId}-${project.version}.jar
target/${project.artifactId}-${project.version}-sources.jar
dependency-reduced-pom.xml
true
truemaven-javadoc-plugin
Generating javadoc for the project.
org.apache.maven.plugins
maven-javadoc-plugin
jar
prepare-package
true
true
falseIf you have a module that does not contain Java (for example, only resources)
Or you simply do not want to generate javadoc, then consider this option maven-jar-plugin
org.apache.maven.plugins
maven-jar-plugin
empty-javadoc-jar
generate-resources
jar
javadoc
${basedir}/javadocmaven-gpg-plugin
org.apache.maven.plugins
maven-gpg-plugin
sign-artifacts
deploy
signnexus-staging-maven-plugin
Configuration:
org.sonatype.plugins
nexus-staging-maven-plugin
org.sonatype.plugins
nexus-staging-maven-plugin
true
sonatype
https://oss.sonatype.org/
true
org.apache.maven.plugins
maven-deploy-plugin
true
sonatype
Nexus Snapshot Repository
https://oss.sonatype.org/content/repositories/snapshots/
sonatype
Nexus Release Repository
https://oss.sonatype.org/service/local/staging/deploy/maven2/If you have a multi-module project and do not need to upload a specific module to the repository, you need to add nexus-staging-maven-plugin with the flag skipNexusStagingDeployMojo
org.sonatype.plugins
nexus-staging-maven-plugin
trueAfter uploading, snapshot/release versions are available in
SonatypeNexus
https://oss.sonatype.org/content/groups/staging/
Other advantages
- A very rich list of goals for working with the Nexus repository (
mvn help:describe -Dplugin=org.sonatype.plugins:nexus-staging-maven-plugin). - Automatic release validation for uploading to Maven Central
Result
Publishing SNAPSHOT version
When building the project, there is an option to manually trigger the task to upload the SNAPSHOT version to Nexus
When this task is executed, a corresponding task in the deploy project is triggered ().
Trimmed log
Running with gitlab-runner 11.10.0 (3001a600)
on Deploy runner JSKWyxUw
Using Shell executor...
Running on ih1174328.vds.myihor.ru...
Skipping Git repository setup
Skipping Git checkout
Skipping Git submodules setup
$ rm -rf .* *
$ git config --global credential.helper store
$ echo "https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com" >> ~/\.git-credentials
$ git clone ${DEPLOY_CI_REPOSITORY_URL} .
Cloning into 'shields4j'...
$ git checkout ${DEPLOY_CI_COMMIT_SHA}
Note: checking out '850f86aa317194395c5387790da1350e437125a7'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at 850f86a... skip deploy test-core
$ for pom in $(find . -name pom.xml); do # collapsed multi-line command
$ if [[ "${DEPLOY_CI_COMMIT_TAG}" != "" ]]; then # collapsed multi-line command
[INFO] Scanning for projects...
[INFO] Inspecting build with total of 4 modules...
[INFO] Installing Nexus Staging features:
[INFO] ... total of 4 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Shields4J [pom]
[INFO] test-core [jar]
[INFO] Shields4J client [jar]
[INFO] TestNG listener [jar]
[INFO]
[INFO] -----------------------------
[INFO] Building Shields4J 1.0.0 [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- versions-maven-plugin:2.5:set (default-cli) @ shields4j-parent ---
[INFO] Searching for local aggregator root...
[INFO] Local aggregation root: /home/gitlab-deployer/JSKWyxUw/0/TouchBIT/deploy/shields4j
[INFO] Processing change of org.touchbit.shields4j:shields4j-parent:1.0.0 -> 1.0.0-SNAPSHOT
[INFO] Processing org.touchbit.shields4j:shields4j-parent
[INFO] Updating project org.touchbit.shields4j:shields4j-parent
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]
[INFO] Processing org.touchbit.shields4j:client
[INFO] Updating parent org.touchbit.shields4j:shields4j-parent
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] Updating dependency org.touchbit.shields4j:test-core
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]
[INFO] Processing org.touchbit.shields4j:test-core
[INFO] Updating parent org.touchbit.shields4j:shields4j-parent
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]
[INFO] Processing org.touchbit.shields4j:testng
[INFO] Updating parent org.touchbit.shields4j:shields4j-parent
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] Updating dependency org.touchbit.shields4j:client
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO] Updating dependency org.touchbit.shields4j:test-core
[INFO] from version 1.0.0 to 1.0.0-SNAPSHOT
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Shields4J 1.0.0 .................................... SUCCESS [ 0.992 s]
[INFO] test-core .......................................... SKIPPED
[INFO] Shields4J client ................................... SKIPPED
[INFO] TestNG listener 1.0.0 .............................. SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.483 s
[INFO] Finished at: 2019-04-21T02:40:42+03:00
[INFO] ------------------------------------------------------------------------
$ mvn clean deploy -DskipTests=${SKIP_TESTS}
[INFO] Scanning for projects...
[INFO] Inspecting build with total of 4 modules...
[INFO] Installing Nexus Staging features:
[INFO] ... total of 4 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] Shields4J [pom]
[INFO] test-core [jar]
[INFO] Shields4J client [jar]
[INFO] TestNG listener [jar]
[INFO]
[INFO] -----------------------------
[INFO] Building Shields4J 1.0.0-SNAPSHOT [1/4]
[INFO] --------------------------------[ pom ]---------------------------------
...
DELETED
...
[INFO] * Bulk deploy of locally gathered snapshot artifacts finished.
[INFO] Remote deploy finished with success.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Shields4J 1.0.0-SNAPSHOT ........................... SUCCESS [ 2.375 s]
[INFO] test-core .......................................... SUCCESS [ 3.929 s]
[INFO] Shields4J client ................................... SUCCESS [ 3.815 s]
[INFO] TestNG listener 1.0.0-SNAPSHOT ..................... SUCCESS [ 36.134 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 47.629 s
[INFO] Finished at: 2019-04-21T02:41:32+03:00
[INFO] ------------------------------------------------------------------------As a result, the version has been uploaded to nexus .
All snapshot versions can be deleted from the repository on the website under your account.
Publishing release version
When a tag is set, the corresponding task in the deploy project is automatically triggered to upload the release version to nexus ().
The best part is that the close release in nexus is triggered automatically.
[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] ------------------------------------------------------------------------And if something goes wrong, the task will definitely fail
[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 http://keys.gnupg.net:11371/. 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] ------------------------------------------------------------------------As a result, we are left with only one choice. Either delete this version or publish it.
After the release, the artifacts will appear in
off-topic
It was a surprise to me that Maven indexes other public repositories.
I had to add a robots.txt since it indexed my old repository.
Conclusion
What we have
- A separate deploy project where multiple CI tasks for uploading artifacts to public repositories for various programming languages can be implemented.
- The deploy project is isolated from external interference and can be modified only by users with the Owner and Maintainer roles.
- A separate Specific Runner with a 'hot' cache for running only deploy tasks.
- Publication of snapshot/release versions in a public repository.
- Automated check of the release version's readiness for publication in Maven Central.
- Protection against the automatic publication of 'raw' versions in Maven Central.
- Building and publishing snapshot versions 'at the click'.
- A single repository for obtaining snapshot/release versions.
- A general pipeline for building/testing/publishing a Java project.
Setting up GitLab CI isn't as complicated as it seems at first glance. You just need to configure CI 'turnkey' a couple of times, and you'll no longer be a novice in this field. Moreover, GitLab documentation is quite extensive. Don't be afraid to take the first step. The path emerges under the feet of those who walk (I can't remember who said that 🙂 ).
I would appreciate your feedback.
In the next article, I'll explain how to configure GitLab CI for concurrent task execution with integration tests (launching the tested services using docker-compose), if you only have one shell runner.
Source: habr.com
