Skip to content
Snippets Groups Projects
README.md 18.2 KiB
Newer Older
Pierre Smeyers's avatar
Pierre Smeyers committed
# GitLab CI template for Maven

This project implements a generic GitLab CI template for [Maven](https://maven.apache.org/).

It provides several features, usable in different modes (by configuration).

## Usage

In order to include this template in your project, add the following to your `gitlab-ci.yml`:

```yaml
include:
Pierre Smeyers's avatar
Pierre Smeyers committed
  - project: 'to-be-continuous/maven'
    ref: '2.3.0'
Pierre Smeyers's avatar
Pierre Smeyers committed
    file: '/templates/gitlab-ci-maven.yml'
```

## Global configuration

The Maven template uses some global configuration used throughout all jobs.

| Name                  | description                            | default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `MAVEN_IMAGE`         | The Docker image used to run Maven <br/>:warning: **set the version required by your project** | `maven:latest` |
| `MAVEN_PROJECT_DIR`   | Maven projet root directory | `.` |
Pierre Smeyers's avatar
Pierre Smeyers committed
| `MAVEN_CFG_DIR`       | The Maven configuration directory      | `.m2`             |
| `MAVEN_OPTS`          | [Global Maven options](http://maven.apache.org/configure.html#maven_opts-environment-variable) | `-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=${MAVEN_CFG_DIR}/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true` |
| `MAVEN_CLI_OPTS`      | Additional [Maven options](https://maven.apache.org/ref/3-LATEST/maven-embedder/cli.html) used on the command line | `--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 for 2 purposes:

* in case a Maven settings file (`settings.xml`) is found, the template automatically uses it (using the `-s` option on command line),
* the cache policy declares the `${MAVEN_CFG_DIR}/repository` directory as cached (not to download Maven dependencies over and over again).
Pierre Smeyers's avatar
Pierre Smeyers committed

If you have a good reason to do differently, you'll have to override the `MAVEN_CLI_OPTS` variable as well as the [`cache`](https://docs.gitlab.com/ee/ci/yaml/README.html#cache) policy.

## 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:

| Name                  | description                              | default value     |
| --------------------- | ---------------------------------------- | ----------------- |
| `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](https://www.eclemma.org/jacoco/trunk/doc/maven.html)
to compute code coverage during unit tests execution.

In addition it [makes the necessary](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#test-coverage-parsing)
to integrate code coverage stats into your GitLab project: [report badge](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#test-coverage-report-badge)
and viewable 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](https://www.eclemma.org/jacoco/trunk/doc/maven.html) in your `pom.xml`, but be aware of the
following:

* do not declare JaCoCo executions for `prepare-agent` and `report` goals otherwise then would be ran 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 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:

* [Maven Surefire Plugin](https://maven.apache.org/surefire/maven-surefire-plugin)
* [`surefire:test` parameters](https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html)

### SonarQube analysis job

This job is **disabled by default** and performs a SonarQube analysis of your code.

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

| Name                     | description                            | default value     |
| ------------------------ | -------------------------------------- | ----------------- |
| `SONAR_URL`              | SonarQube server url            | _none_ (disabled) |
| :lock: `SONAR_AUTH_TOKEN`| SonarQube authentication [token](https://docs.sonarqube.org/latest/user-guide/user-token/) (depends on your authentication method) | _none_ |
| :lock: `SONAR_LOGIN`     | SonarQube login (depends on your authentication method)                | _none_ |
| :lock: `SONAR_PASSWORD`  | SonarQube password (depends on your authentication method)             | _none_ |
| `SONAR_BASE_ARGS`        | SonarQube [analysis arguments](https://docs.sonarqube.org/latest/analysis/analysis-parameters/) | `sonar:sonar -Dsonar.host.url=${SONAR_URL} -Dsonar.links.homepage=${CI_PROJECT_URL} -Dsonar.links.ci=${CI_PROJECT_URL}/-/pipelines -Dsonar.links.issue=${CI_PROJECT_URL}/-/issues` |
Pierre Smeyers's avatar
Pierre Smeyers committed
| :lock: `SONAR_GITLAB_TOKEN` | GitLab [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with `api` scope. When set, activates the [Sonar GitLab plugin](https://github.com/gabrie-allaigre/sonar-gitlab-plugin/#plugins-properties) integration. | _none_ |
| `SONAR_BRANCH_ANALYSIS_DISABLED` | Set to `true` to disable automatic [Pull Request Analysis](https://docs.sonarqube.org/latest/analysis/pull-request/) and [Branch Analysis](https://docs.sonarqube.org/latest/branches/overview/)  | _none_ (enabled) |
Pierre Smeyers's avatar
Pierre Smeyers committed
| `SONAR_GITLAB_ARGS`      | Extra arguments to use with [Sonar GitLab plugin](https://github.com/gabrie-allaigre/sonar-gitlab-plugin/#plugins-properties) | `-Dsonar.gitlab.url=${CI_SERVER_URL} -Dsonar.gitlab.user_token=${SONAR_GITLAB_TOKEN} -Dsonar.gitlab.project_id=${CI_PROJECT_ID} -Dsonar.gitlab.commit_sha=${CI_COMMIT_SHA} -Dsonar.gitlab.ref_name=${CI_COMMIT_REF_NAME}` |
| `SONAR_AUTO_ON_DEV_DISABLED` | When set to `true`, SonarQube analysis becomes **manual** on development branches (automatic otherwise) | _none_ |
| `SONAR_QUALITY_GATE_ENABLED` | Set to `true` to enables check of SonarQube [Quality Gate](https://docs.sonarqube.org/latest/user-guide/quality-gates/) | _none_ (disabled) |
#### Automatic Branch Analysis & Pull Request Analysis
By default, this template tries to auto-detect and use [Pull Request Analysis](https://docs.sonarqube.org/latest/analysis/pull-request/) or [Branch Analysis](https://docs.sonarqube.org/latest/branches/overview/) (depending on the context).
Those is a great SonarQube features but it assumes one of the following conditions:
Pierre Smeyers's avatar
Pierre Smeyers committed

* you are using a [Developer Edition](https://www.sonarqube.org/developer-edition/) version,
* or you are using Community Edition with an opensource plugin emulating those features, such as [sonarqube-community-branch-plugin](https://github.com/mc1arke/sonarqube-community-branch-plugin).
If you're not in one of those cases, then you shall disable this feature by setting `SONAR_BRANCH_ANALYSIS_DISABLED`.

If you leave the feature enabled, if `SONAR_AUTH_TOKEN` is provided, the template will try to autodetect (using GitLab APIs) an opened merge request matching the current branch:

* If one is found, a SonarQube [Pull Request Analysis](https://docs.sonarqube.org/latest/analysis/pull-request/) will be made.
* Otherwise, a simple [Branch Analysis](https://docs.sonarqube.org/latest/branches/overview/) is performed on the current branch.
Pierre Smeyers's avatar
Pierre Smeyers committed

#### About Sonar GitLab plugin

The [Sonar GitLab plugin](https://github.com/gabrie-allaigre/sonar-gitlab-plugin) uses the GitLab APIs to inline comments
into your commits directly in GitLab for each new anomaly.

As explained above, this template automatically enables the Sonar GitLab plugin if `SONAR_GITLAB_TOKEN` is set.
It will then simply append the `SONAR_GITLAB_ARGS` (overridable) to the SonarQube analysis arguments.

Comments added to GitLab will appear as owned by the user associated to the GitLab [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html).

### `mvn-dependency-check` job

This job enables a manual [Dependency-Check](https://jeremylong.github.io/DependencyCheck/dependency-check-maven/configuration.html)
analysis.

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

| Name                  | description                            | default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `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.

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
Pierre Smeyers's avatar
Pierre Smeyers committed
* Add `-Dsonar.dependencyCheck.htmlReportPath` and `-Dsonar.dependencyCheck.jsonReportPath` with the paths of the generated html and json reports to SonarQube arguments.

More info:

* [Maven Dependency-Check Plugin](https://jeremylong.github.io/DependencyCheck/dependency-check-maven/configuration.html)

### `mvn-forbid-snapshot-dependencies` job

This job checks your project has release-only dependencies (no _snapshot_), using the [Maven Enforcer](https://maven.apache.org/enforcer/enforcer-rules/requireReleaseDeps.html) plugin.

Failure is allowed in feature branches.

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

| Name                  | description                            | default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `MVN_FORBID_SNAPSHOT_DEPENDENCIES_DISABLED` | Set to `true` to disable this job | _none_ |
Pierre Smeyers's avatar
Pierre Smeyers committed
### `mvn-snapshot` &amp; `mvn-release` jobs

Those jobs are **disabled by default** and perform respectively:

* a [Maven deploy](https://maven.apache.org/plugins/maven-deploy-plugin/) of your Java packages (jar, war or else),
* a [Maven release](http://maven.apache.org/maven-release/maven-release-plugin/index.html) of your current branch.

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

| Name                                | description                                                  | default value     |
| ----------------------------------- | ------------------------------------------------------------ | ----------------- |
| `MAVEN_DEPLOY_ENABLED`              | Set to `true` to enable a publish jobs                            | _none_ (disabled) |
| `MAVEN_DEPLOY_FROM_UNPROTECTED_DISABLED`       | Set to `true` to limit snapshot publication to protected branches | _none_ (disabled) |
| `MAVEN_DEPLOY_ARGS`                 | Maven arguments for the Snapshot job                         | `deploy -Dmaven.test.skip=true` |
| `MAVEN_RELEASE_ARGS`                | Maven arguments for the Release job                          | `release:prepare release:perform -Darguments=-Dmaven.test.skip=true` |
Pierre Smeyers's avatar
Pierre Smeyers committed
| `MAVEN_RELEASE_SCM_COMMENT_PREFIX`  | Maven release plugin [scmCommentPrefix](https://maven.apache.org/maven-release/maven-release-plugin/prepare-mojo.html#scmCommentPrefix) parameter  | `[ci skip][maven-release-plugin]` |
Pierre Smeyers's avatar
Pierre Smeyers committed
| `MVN_SEMREL_RELEASE_DISABLED`  | Set to `true` to disable [semantic-release integration](#semantic-release-integration)   | _none_ (disabled) |
Pierre Smeyers's avatar
Pierre Smeyers committed

More info:

* [Maven Deploy Plugin](https://maven.apache.org/plugins/maven-deploy-plugin/)
* [Maven Release Plugin](http://maven.apache.org/maven-release/maven-release-plugin/index.html)

#### `semantic-release` integration

Pierre Smeyers's avatar
Pierre Smeyers committed
If you activate the [`semantic-release-info` job from the `semantic-release` template](https://gitlab.com/to-be-continuous/semantic-release/#semantic-release-info-job), the `mvn-release` job will rely on the generated next version info.

* the release will only be performed if a next semantic release is present
* the version is passed to the maven release plugin as release version argument adding `-DreleaseVersion=${SEMREL_INFO_NEXT_VERSION}` to the `MAVEN_RELEASE_ARGS` value

:warning: Both maven release plugin and semantic-release use a dedicated tag format that need to be set accordingly.
By default maven release plugin uses `${artifactId}-${version}` and semantic-release uses `s${version}`
Pierre Smeyers's avatar
Pierre Smeyers committed
For exemple you can modify the semantic-release tag format with the `SEMREL_TAG_FORMAT` variable (see [semantic-release template variables](https://gitlab.com/to-be-continuous/semantic-release/#variables)).

```yml
variables:
  SEMREL_TAG_FORMAT: "myArtifactId-$${version}"
```

Or you can [override the maven release plugin tag format](http://maven.apache.org/maven-release/maven-release-plugin/examples/prepare-release.html#Overriding_the_default_tag_name_format).

Note: You can disable the `semantic-release` job (as it's the `mvn-release` job that will perform the release and so we only need the `semantic-release-info` job) with the `SEMREL_RELEASE_DISABLED` variable.

```yml
variables:
  SEMREL_RELEASE_DISABLED: "true"
```

Pierre Smeyers's avatar
Pierre Smeyers committed
Finally, the semantic-release integration can be disabled with the `MVN_SEMREL_RELEASE_DISABLED` variable.
Pierre Smeyers's avatar
Pierre Smeyers committed
#### Maven repository authentication

Your Maven repository may require authentication credentials to publish artifacts.

You shall handle them in the following way:

1. define all required credentials as :lock: [project variables](https://docs.gitlab.com/ee/ci/variables/#create-a-custom-variable-in-the-ui),
2. make sure your `pom.xml` (or ancestor) [declares your `<repository>` and `<snapshotRepository>` with server **id**s in a `<distributionManagement>` section](https://maven.apache.org/pom.html#repository),
3. in your `${MAVEN_CFG_DIR}/settings.xml` file, [define the repository servers credentials in the `<servers>` section](https://maven.apache.org/settings.html#Servers)
  using the `${env.VARIABLE}` pattern (will be automatically evaluated and replaced by Maven).

**Example 1** (using the [GitLab Maven Repository](https://docs.gitlab.com/ee/user/packages/maven_repository/)):

`pom.xml`:

```xml
<!-- ... -->
<distributionManagement>
  <snapshotRepository>
      <id>gitlab-maven</id>
      <url>${env.CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/packages/maven</url>
  </snapshotRepository>
  <repository>
      <id>gitlab-maven</id>
      <url>${env.CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/packages/maven</url>
  </repository>
</distributionManagement>
<!-- ... -->
```

`${MAVEN_CFG_DIR}/settings.xml`:

```xml
<settings>
  <servers>
    <!-- required when using GitLab's package registry to deploy -->
    <!-- see: https://docs.gitlab.com/ee/user/packages/maven_repository/index.html#use-the-gitlab-endpoint-for-maven-packages -->
Pierre Smeyers's avatar
Pierre Smeyers committed
    <server>
        <id>gitlab-maven</id>
        <configuration>
            <httpHeaders>
                <property>
                    <name>Job-Token</name>
                    <value>${env.CI_JOB_TOKEN}</value>
                </property>
            </httpHeaders>
        </configuration>
    </server>
  </servers>
</settings>
```

**Example 2** (using an Artifactory repository with same credentials for snapshot &amp; release):

`pom.xml`:

```xml
<!--... -->
<distributionManagement>
  <snapshotRepository>
    <id>artifactory</id>
    <url>https://artifactory.acme.host/artifactory/maven-snapshot-repo</url>
  </snapshotRepository>
  <repository>
    <id>artifactory</id>
    <url>https://artifactory.acme.host/artifactory/maven-release-repo</url>
  </repository>
</distributionManagement>
<!--...-->
```

`${MAVEN_CFG_DIR}/settings.xml`:

```xml
<settings>
  <servers>
    <server>
      <id>artifactory</id>
      <username>${env.ARTIFACTORY_USER}</username>
      <password>${env.ARTIFACTORY_PASSWORD}</password>
    </server>
  </servers>
  <mirrors>
    <mirror>
      <id>artifactory.mirror</id>
      <mirrorOf>central</mirrorOf>
      <name>Artifactory Maven 2 central repository mirror</name>
      <url>https://artifactory.acme.host/artifactory/maven-virtual-repo/</url>
    </mirror>
  </mirrors>
</settings>
```

#### SCM authentication

A Maven release involves some Git push operations.

You can either use a ssh key or an authenticated and authorized Git user.

##### Using a SSH key

We recommend you to use a [project deploy key](https://docs.gitlab.com/ee/user/project/deploy_keys/#project-deploy-keys) with write access to your project.

The key should not have a passphrase (see [how to generate a new SSH key pair](https://docs.gitlab.com/ce/ssh/README.html#generating-a-new-ssh-key-pair)).

Specify :lock: `$GIT_PRIVATE_KEY` as protected project variable with the private part of the deploy key.

```PEM
-----BEGIN OPENSSH PRIVATE KEY-----
blablabla
-----END OPENSSH PRIVATE KEY-----
```

The template handle both classic variable and file variable.

:warning: The scm connections in your pom.xml should use the ssh protocol

```xml
  <scm>
    <connection>scm:git:git@gitlab-host/path/to/my/project.git</connection>
    <developerConnection>scm:git:git@gitlab-host/path/to/my/project.git</developerConnection>
    ...
  </scm>
```

##### Using Git user authentication

Simply specify :lock: `$GIT_USERNAME` and :lock: `$GIT_PASSWORD` as protected project variables : they will be dynamically 
evaluated and appended to the Maven release arguments.

Note that the password should be an access token with `read_repository` and `write_repository` scopes.

:warning: The scm connections in your pom.xml should use the https protocol

```xml
  <scm>
    <connection>scm:git:https://gitlab-host/path/to/my/project.git</connection>
    <developerConnection>scm:git:https://gitlab-host/path/to/my/project.git</developerConnection>
    ...
  </scm>
```