The Maven Release plugin allows you to easily craft releases of your own libraries, to share code between projects. When combined with Semantic Versioning you can communicate clearly to your library users which changes are minor, or potentially breaking. The plugin will trim off the -SNAPSHOT suffix of your artifact version, run through all the stages to create your build artifacts, and push those artifacts to a remote registry such as Artifactory. It will also push a Git tag to your code repository, as well as increment your artifact version to prepare for further development.

This blogpost will run you through the steps to authenticate with both GitLab and Artifactory when running a Maven Release from GitLab CI.

Allow to push commits and tags to GitLab

We will set up a SSH key pair, which the Maven Release plugin will use to push the commits and tag to GitLab.

Listing 1. Create a RSA keypair
ssh-keygen -m PEM -t rsa -P "" -f deploy_key

This creates a key pair named deploy_key.pub for the public key, and deploy_key for the private key, which does not require a password to use.

Next we will create a deploy key for the GitLab project that you want to release. This will allow read-write access to your repository using the SSH public key created above. Navigate to Settings > Repository > Deploy Keys, and paste the contents of deploy_key.pub in the corresponding Key field. Be sure to grant write permission to this key.

In addition to public and private key files, we will also need a known hosts file. To create one execute the following command.

Listing 2. Create a known hosts file
ssh-keyscan -H your.gitlab.host > known_hosts

We want to make both the private key file and the known hosts file available to our release job in GitLab CI. For that we create two CI / CD variables by navigating to Settings > CI / CD > Variables.

  1. Name the first one DEPLOY_PRIVATE_KEY and provide the contents of deploy_key.

  2. Name the second one KNOWN_HOSTS and provide the contents of known_hosts.

Be sure to select the File type, and mark both variables as Protected.

Add release step to GitLab CI

With the SSH credentials set up correcty, we move on to defining a release step in our GitLab CI pipeline. We will likely want to keep this a manual step at first, and only run this from the main or master branch.

We have to do some wrangling to convert the CI / CD file variables from the previous step into files the SSH client will know and look for. We install and configure Git, if not already present in the build image, and ensure we are not in a Git detached head state. Finally, we define a single script line that does both a release:prepare as well as release:perform.

Listing 3. gitlab-ci.yml
maven_release:
  image: openjdk:17
  stage: release
  only:
    - master
  when: manual
  before_script:
    - mkdir -p ~/.ssh/
    - cp $DEPLOY_PRIVATE_KEY ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
    - cp $KNOWN_HOSTS ~/.ssh/known_hosts
    - apt-get update && apt-get install -y git
    - git config --global user.email "noreply@your.gitlab.host"
    - git config --global user.name "GitLab CI"
    - git checkout -B "$CI_COMMIT_REF_NAME"
  script:
    - ./mvnw release:prepare release:perform -s $SETTINGS_XML

Record releases with GitLab

We can also track our releases in GitLab, using an additional CI step. This way users of GitLab can quickly navigate to and compare between release versions.

Listing 4. gitlab-ci.yml
gitlab_release:
  stage: deploy
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - echo "Running the release job."
  release:
    tag_name: $CI_COMMIT_TAG
    name: 'Release $CI_COMMIT_TAG'
    description: 'Release created using the release-cli.'

Reference your project repository in pom.xml

With SSH credentials and build step in place, next up we want to ensure our Maven Release connects to the correct repository. For that we need to define a SCM element in our pom.xml, with your specific details replaced in the sample below.

Listing 5. pom.xml > SCM
 <scm>
  <developerConnection>scm:git:git@gyour.gitlab.host:your-organization/your-library.git</developerConnection>
  <tag>HEAD</tag>
 </scm>

Allow to push artifacts to GitLab Package Repository for Maven

You can skip this step if you do not use GitLab Package Repository for Maven, although the steps might fit other artifactory repositories as well.

To push artifacts to GitLab Package Repository for Maven we need to authenticate with GitLab. By adding a distributionManagement section to our pom.xml file we can point Maven to GitLab.

Listing 6. pom.xml > distributionManagement
<distributionManagement>
  <repository>
    <id>gitlab-maven</id>
    <url>${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven</url>
  </repository>
  <snapshotRepository>
    <id>gitlab-maven</id>
    <url>${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/maven</url>
  </snapshotRepository>
</distributionManagement>

Authentication with GitLab can be handled a number of different ways; we chose to use a Job Token in the below example, and make settings.xml available as a File type CI/CD Variable, named SETTINGS_XML, referenced in the maven_release step above.

Listing 7. .m2/settings.xml
<settings xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd">
  <servers>
    <server>
      <id>gitlab-maven</id>
      <configuration>
        <httpHeaders>
          <property>
            <name>Job-Token</name>
            <value>${CI_JOB_TOKEN}</value>
          </property>
        </httpHeaders>
      </configuration>
    </server>
  </servers>
</settings>

Allow to push artifacts to Artifactory

You can skip this step if you do not use Artifactory. Additionally, Chris Graham pointed out that the Artifactory Maven Plugin might no longer be needed with more recent versions of Artifactory. Most likely you can adapt the GitLab Package Repository for Maven step to also fit Artifactory. The below instructions are kept as is for when they do prove necessary, but only use them when needed.

To upload artifacts to Artifactory, we needed the dedicated artifactory-maven-plugin. Configure it similar to the snippet provided below. Note how we use environment variables for the ARTIFACTORY_CONTEXT_URL, ARTIFACTORY_USERNAME and ARTIFACTORY_PASSWORD. These will once again have to be provided in GitLab CI for your project by navigating to Settings > CI / CD > Variables, now using the Variable type.

Listing 8. pom.xml > Build > Plugins
<plugin>
  <groupId>org.jfrog.buildinfo</groupId>
  <artifactId>artifactory-maven-plugin</artifactId>
  <version>3.2.0</version>
  <executions>
    <execution>
      <id>push-library</id>
      <goals>
        <goal>publish</goal>
      </goals>
      <configuration>
        <publisher>
        <contextUrl>${env.ARTIFACTORY_CONTEXT_URL}</contextUrl>
        <username>${env.ARTIFACTORY_USERNAME}</username>
        <password>${env.ARTIFACTORY_PASSWORD}</password>
        <repoKey>libs-release-local</repoKey>
        <snapshotRepoKey>libs-snapshot-local</snapshotRepoKey>
        </publisher>
      </configuration>
    </execution>
  </executions>
</plugin>

Conclusion

That’s it! With the above in place you’re now all set to create Maven Releases for your libraries on GitLab CI. Tested with GitLab 15.3 & Apache Maven 3.8.6.

shadow-left