Skip to content
Snippets Groups Projects
README.md 32.84 KiB

GitLab CI template for Maven

This project implements a GitLab CI/CD template to build, test and analyse your Maven-based projects.

Usage

This template can be used both as a CI/CD component or using the legacy include:project syntax.

Use as a CI/CD component

Add the following to your gitlab-ci.yml:

include:
  # 1: include the component
  - component: gitlab.com/to-be-continuous/maven/gitlab-ci-maven@3.11.0
    # 2: set/override component inputs
    inputs:
      # ⚠ this is only an example
      image: registry.hub.docker.com/library/maven:3.8-openjdk-18
      deploy-enabled: true

Use as a CI/CD template (legacy)

Add the following to your gitlab-ci.yml:

include:
  # 1: include the template
  - project: 'to-be-continuous/maven'
    ref: '3.11.0'
    file: '/templates/gitlab-ci-maven.yml'

variables:
  # 2: set/override template variables
  # ⚠ this is only an example
  MAVEN_IMAGE: registry.hub.docker.com/library/maven:3.8-openjdk-18
  MAVEN_DEPLOY_ENABLED: "true"

Global configuration

The Maven template uses some global configuration throughout all jobs.

Input / Variable Description Default value
image / MAVEN_IMAGE The Docker image used to run Maven
⚠️ set the version required by your project
registry.hub.docker.com/library/maven:latest
project-dir / MAVEN_PROJECT_DIR Maven projet root directory .
cfg-dir / MAVEN_CFG_DIR The Maven configuration directory .m2
settings-file / MAVEN_SETTINGS_FILE The Maven settings.xml file path ${MAVEN_CFG_DIR}/settings.xml
opts / MAVEN_OPTS Global Maven options -Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=${MAVEN_CFG_DIR}/repository -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true
cli-opts / MAVEN_CLI_OPTS Additional Maven options used on the command line --no-transfer-progress --batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true

About $MAVEN_CFG_DIR

This variable is used to define the Maven configuration directory. It is used to declare the cache policy and marked the ${MAVEN_CFG_DIR}/repository directory as cached (not to download Maven dependencies over and over again).

If you have a good reason to do differently, you'll have to override the MAVEN_CLI_OPTS variable as well as the cache policy.

About $MAVEN_SETTINGS_FILE

If a file is found at the $MAVEN_SETTINGS_FILE location, the template automatically uses it as a settings.xml (using the --settings option on command line).

Note that with this design you are free to either:

  1. inline the settings.xml file into your repository source (⚠️ make sure not to inline secrets but use the ${env.MY_PASSWORD} replacement pattern instead and define the MY_PASSWORD variable as secret project variable),
  2. or define the settings.xml content as a file type project variable.

Jobs

mvn-build job

The Maven template features a job mvn-build that performs build and tests at once. This stage is performed in a single job for optimization purpose (it saves time) and also for test jobs dependency reasons (some test jobs such as SONAR analysis have a dependency on test results).

It uses the following variable:

Input / Variable Description Default value
build-args / MAVEN_BUILD_ARGS Maven arguments for the build & test job org.jacoco:jacoco-maven-plugin:prepare-agent verify org.jacoco:jacoco-maven-plugin:report

About Code Coverage

With its default arguments, the GitLab CI template for Maven forces the use of JaCoCo Maven Plugin to compute code coverage during unit tests execution.

In addition it makes the necessary to integrate code coverage stats into your GitLab project (report badge and viewable coverage in merge requests).

If yo want to fix the JaCoCo plugin version or tweak the default configuration, you may have to configure the JaCoCo Maven Plugin in your pom.xml, but be aware of the following:

  • do not declare JaCoCo executions for prepare-agent and report goals as each would run twice during unit tests (not necessarily with the expected configuration). If you really need to do so anyway, you'll have to override the $MAVEN_BUILD_ARGS variable to remove the explicit invocation to JaCoCo goals.
  • make sure the report goal computes a CSV report, that is used by the Maven template to compute the global coverage stat.

More info:

mvn-sonar job — SonarQube analysis

This job, disabled by default, is bound to the test stage and performs a SonarQube analysis of your code. This job uses the following variables:

Input / Variable Description Default value
sonar-host-url / SONAR_HOST_URL SonarQube server url none (disabled)
🔒 SONAR_TOKEN SonarQube authentication token (depends on your authentication method) none
🔒 SONAR_LOGIN SonarQube login (depends on your authentication method) none
🔒 SONAR_PASSWORD SonarQube password (depends on your authentication method) none
sonar-base-args / SONAR_BASE_ARGS SonarQube analysis arguments sonar:sonar -Dsonar.links.homepage=${CI_PROJECT_URL} -Dsonar.links.ci=${CI_PROJECT_URL}/-/pipelines -Dsonar.links.issue=${CI_PROJECT_URL}/-/issues
sonar-quality-gate-enabled / SONAR_QUALITY_GATE_ENABLED Set to true to enable SonarQube Quality Gate verification.
Uses sonar.qualitygate.wait parameter (see doc).
none (disabled)

Recommended minimal configuration

  1. set the SONAR_HOST_URL value either in your .gitlab-ci.yml file or as a project or group variable (⚠️ setting it as a group variable will enable the SonarQube analysis for all the children projects),
  2. define your SonarQube credentials (🔒 SONAR_TOKEN or SONAR_LOGIN & 🔒 SONAR_PASSWORD) as project or group variables,
  3. configure the project SonarQube settings in the pom.xml file (⚠️ the SonarScanner for Maven completely ignores the sonar-project.properties file):
    <properties>
      <!-- the SonarQube project key -->
      <sonar.projectKey>write-key-here</sonar.projectKey>
      <!-- additional SonarQube settings can go here -->
      ...
    </properties>
    More info about SonarQube settings

⚠️ if using SonarCloud (a cloud-based SonarQube-as-a-Service), you'll have to define the additional sonar.organization property (see mandatory-parameters).

ℹ️ As SonarCloud determined the organization and projectKey properties from the project's GitLab context when importing the project, you can reuse the predefined GitLab variables as follows in your pom.xml:

<properties>
  <!-- SonarCloud settings -->
  <sonar.organization>${env.CI_PROJECT_ROOT_NAMESPACE}</sonar.organization>
  <sonar.projectKey>${env.CI_PROJECT_ROOT_NAMESPACE}_${env.CI_PROJECT_NAME}</sonar.projectKey>
</properties>

Automatic Branch Analysis & Merge Request Analysis

This template relies on SonarScanner's GitLab integration, which is able to auto-detect whether to launch Branch Analysis or Merge Request Analysis from GitLab's environment variables.

⚠️ This feature also depends on your SonarQube server version and license. If using Community Edition, you'll have to install the sonarqube-community-branch-plugin to enable automatic Branch & Merge Request analysis (only works from SonarQube version 8).

⚠️ Merge Request Analysis only works if you're running Merge Request pipeline strategy (default).

Disable the job

ℹ️ See Usage for more information about disabling any job that MAY not be required in a project or group.

Without disabling the job, you can still exclude a particular project by defining a property <sonar.skip>true</sonar.skip> in the pom.xml of the project or module you want to exclude.

mvn-dependency-check job

This job enables a manual Dependency-Check analysis.

It is bound to the test stage, and uses the following variables:

Input / Variable Description Default value
dependency-check-disabled / MAVEN_DEPENDENCY_CHECK_DISABLED Set to true to disable this job none
dependency-check-args / MAVEN_DEPENDENCY_CHECK_ARGS Maven arguments for Dependency Check job org.owasp:dependency-check-maven:check -DretireJsAnalyzerEnabled=false -DassemblyAnalyzerEnabled=false

A Dependency Check is a quite long operation and therefore the job is configured to be ran manually by default.

However, if you want to enable an automatic Dependency-Check scan, you will have to override the rules keyword for the mvn-dependency-check job.

Dependency-Check fetches its vulnerbility database from the NVD API which has rate limiting. Using an NVD API key, the rate limit is higher which reduces the execution time of Dependency-Check.

In order to configure an NVD API key you need to:

  • Set the NVD_API_KEY variable with your NVD API key. ⚠️ This is a sensitive value, so we recommend you add it as a masked Gitlab variable
  • Add -DnvdApiKey=$NVD_API_KEY to MAVEN_DEPENDENCY_CHECK_ARGS

In case your Gitlab runners cannot contact the NVD API (e.g. if they are not allowed to connect to the Internet), you can maintain a local data feed cache with the vulnz tool.

In order to configure Dependency-Check to fetch the vulnerabilities from your cache you need to:

  • Run the vulnz tool in order to fetch the vulnerabilities and store them as data feeds (the data feeds will be stored as JSON files with the NVD Vulnerability Data API version 2.0 schema)
  • Make these data feeds accessible via a URL reachable by your Gitlab runners
  • Add -DnvdDatafeedUrl=https://URL-OF-LOCAL-CACHE to MAVEN_DEPENDENCY_CHECK_ARGS

Furthermore, if you want to upload Dependency-Check reports to SonarQube, you have to:

  • Move mvn-dependency-check to the build stage
  • Add -Dformats=html,json,xml to MAVEN_DEPENDENCY_CHECK_ARGS to output reports
    • HTML report to read the report on SonarQube UI
    • JSON report to create SonarQube issues from the report
    • XML report to import into DefectDojo security dashboard
  • Add -Dsonar.dependencyCheck.htmlReportPath and -Dsonar.dependencyCheck.jsonReportPath with the paths of the generated html and json reports to SonarQube arguments.

More info:

mvn-no-snapshot-deps job

This job checks if the project has release-only dependencies, i.e., no _*-SNAPSHOT_ versions, using the Maven Enforcer plugin.

Failure is allowed in feature branches.

It is bound to the test stage, and uses the following variables:

Input / Variable Description Default value
mvn-forbid-snapshot-dependencies-disabled / MVN_FORBID_SNAPSHOT_DEPENDENCIES_DISABLED Set to true to disable this job none

mvn-sbom job

This job generates a SBOM file listing all dependencies using cyclonedx-maven-plugin.

It is bound to the test stage, and uses the following variables:

Input / Variable Description Default value
sbom-disabled / MAVEN_SBOM_DISABLED Set to true to disable this job none
sbom-gen-args / MAVEN_SBOM_GEN_ARGS Maven command used for SBOM analysis org.cyclonedx:cyclonedx-maven-plugin:makeAggregateBom

mvn-release & mvn-deploy-* jobs

These jobs are disabled by default and - when enabled - respectively perform the following:

  1. a Maven release:prepare of your current branch
    • only triggers the first part of the release (version changes in pom.xml, Git commits and version tag)
    • provides a default integration with semantic-release (see below)
    • the second part of the release (package and publish to a Maven registry) is performed by the mvn-deploy job below
  2. a Maven deploy of your Java packages (jar, war, etc.) to any Maven-compliant registry
    • mvn-deploy-release publishes the stable version on the tag pipeline triggered by a mvn-release,
    • mvn-deploy-snapshot publishes the snapshot version on other branches.

They are bound to the publish stage, and use the following variables:

Input / Variable Description Default value
deploy-enabled / MAVEN_DEPLOY_ENABLED Set to true to enable release and publish jobs none (disabled)
deploy-from-unprotected-disabled / MAVEN_DEPLOY_FROM_UNPROTECTED_DISABLED Set to true to limit snapshot publication to protected branches none (disabled)
deploy-snapshot-with-slug-enabled / MAVEN_DEPLOY_SNAPSHOT_WITH_SLUG_ENABLED Set to true to inject the Git branch slug in SNAPSHOT versions none (disabled)
deploy-args / MAVEN_DEPLOY_ARGS Maven arguments for the mvn-deploy job deploy -Dmaven.test.skip=true
release-args / MAVEN_RELEASE_ARGS Maven arguments for the mvn-release job release:prepare -DtagNameFormat=@{project.version} -Darguments=-Dmaven.test.skip=true
release-version / MAVEN_RELEASE_VERSION Explicit version to use when triggering a release none (uses the current snapshot version from pom.xml)
release-scm-comment-prefix / MAVEN_RELEASE_SCM_COMMENT_PREFIX Maven release plugin scmCommentPrefix parameter chore(maven-release):
release-scm-release-comment / MAVEN_RELEASE_SCM_RELEASE_COMMENT Maven release plugin scmReleaseCommitComment parameter (since Maven 3.0.0-M1) none (Maven default)
release-scm-dev-comment / MAVEN_RELEASE_SCM_DEV_COMMENT Maven release plugin scmDevelopmentCommitComment parameter (since Maven 3.0.0-M1) none (Maven default)
mvn-semrel-release-disabled / MVN_SEMREL_RELEASE_DISABLED Set to true to disable semantic-release integration none (disabled)

More info:

Inject the Git branch slug in SNAPSHOT versions

In order to segregate your SNAPSHOT versions per branch, the Maven template allows you to add the Git branch slug into the pom version. Thus, any other project willing to use your SNAPSHOT packages will be able to do so, explicitly setting its dependency on the desired version (branch). Only the production branch (main or master by default) will be preserved from this behavior.

This feature can be enabled setting MAVEN_DEPLOY_SNAPSHOT_WITH_SLUG_ENABLED to true.

Example: for a project with <version>1.2.1-SNAPSHOT</version> in its pom.xml:

Branch Altered version
feature/support-facebook-login 1.2.1-feature-support-facebook-login-SNAPSHOT
fix/timedate-serialization 1.2.1-fix-timedate-serialization-SNAPSHOT
develop (integration branch) 1.2.1-develop-SNAPSHOT
main (production branch) 1.2.1-SNAPSHOT

semantic-release integration

If you activate the semantic-release-info job from the semantic-release template, the mvn-release job will rely on the generated next version info.

  • the release will only be performed if a semantic release is present
  • the version is passed to the Maven Release plugin as the release version argument adding -DreleaseVersion=${SEMREL_INFO_NEXT_VERSION} to the MAVEN_RELEASE_ARGS value

⚠️ Both the Maven Release plugin and semantic-release template use a dedicated tag format that need to be set accordingly. By default, the Maven Release plugin uses ${artifactId}-${version} and semantic-release uses ${version} For exemple you can modify the semantic-release tag format with the SEMREL_TAG_FORMAT variable (see semantic-release template variables).

ℹ️ semantic-release determines the next release version from the existing tags in the Git repository. As the default semantic-release tag format (${version}) is not the Maven default, if leaving defaults, semantic-release will always determine the next version to release as 1.0.0, trying to keep overwriting the same version. In addition to the functional problem, this might result in a release failure as soon as trying to release version 1.0.0 for the second time (as Maven repos configured as "release" repos will not permit overwriting). Simply set SEMREL_TAG_FORMAT as shown below to have the semantic-release tag format match the maven release plugin one.

variables:
  # double dollar to prevent evaluation (escape char)
  SEMREL_TAG_FORMAT: "myArtifactId-$${version}"

Or you can override the maven release plugin tag format.

Note: It is the mvn-release job that will perform the release and so you only need the semantic-release-info job. Set the SEMREL_RELEASE_DISABLED variable as shown below.

variables:
  SEMREL_RELEASE_DISABLED: "true"

Finally, the semantic-release integration can be disabled with the MVN_SEMREL_RELEASE_DISABLED variable.

Maven repository authentication

Your Maven repository may require authentication credentials to publish artifacts.

You may handle them in the following ways:

  1. define all required credentials as 🔒 project variables,
  2. make sure your pom.xml (or ancestor) declares your <repository> and <snapshotRepository> with server ids in a <distributionManagement> section,
  3. in your ${MAVEN_CFG_DIR}/settings.xml file, define the repository servers credentials in the <servers> section using the ${env.VARIABLE} pattern—will be automatically evaluated and replaced by Maven.

Example 1 — using the GitLab Maven Repository

pom.xml: