Several questions here.
I'll start answering the Java build question, then the Runners one.
Java build
I'll start from the most basic Java build configuration, and progressively add features.
1. basic Java build
This configuration will hopefully run your Maven build (and only the build, explicitly excluding the unit tests):
stages:
- build
java-build:
# select the most appropriate image for your project
image: maven:3.8-openjdk-11
stage: build
script:
- mvn package -DskipTests=true
2. with artifacts, cache and recommended Maven options
This new version:
- declares Maven build output as GitLab artifacts (for later use in the downstream pipeline),
- takes benefit of GitLab's cache to cache local Maven repository (in
.m2/repository
),
- also enforces some recommended Maven options to use in CI/CD context.
stages:
- build
variables:
# This will suppress any download for dependencies and plugins or upload messages which would clutter the console log.
# `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
MAVEN_OPTS: >-
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=.m2/repository
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
# As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
# when running from the command line.
# `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
MAVEN_CLI_OPTS: >-
--batch-mode
--errors
--fail-at-end
--show-version
-DinstallAtEnd=true
-DdeployAtEnd=true
java-build:
# select the most appropriate image for your project
image: maven:3.8-openjdk-11
stage: build
# Cache downloaded dependencies and plugins between builds.
# The key here separates one cache per branch/tag ($CI_COMMIT_REF_SLUG)
cache:
key: "maven-$CI_COMMIT_REF_SLUG"
paths:
- .m2/repository
script:
- mvn $MAVEN_CLI_OPTS package -DskipTests=true
artifacts:
name: "Maven artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
paths:
- "**/target"
3. with unit tests
There are two options when integrating you unit tests in your CI/CD pipeline:
- run them in the same job as the build
- run them in a separate job
As a matter of pipeline execution speed and green-IT considerations, I definitely prefer option 1, but I admit people could prefer the second one.
Here is the new version of the .gitlab-ci.yml
file, also implementing GitLab unit tests integration:
stages:
- build
variables:
# This will suppress any download for dependencies and plugins or upload messages which would clutter the console log.
# `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work.
MAVEN_OPTS: >-
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=.m2/repository
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
# As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used
# when running from the command line.
# `installAtEnd` and `deployAtEnd` are only effective with recent version of the corresponding plugins.
MAVEN_CLI_OPTS: >-
--batch-mode
--errors
--fail-at-end
--show-version
-DinstallAtEnd=true
-DdeployAtEnd=true
java-build-and-test:
# select the most appropriate image for your project
image: maven:3.8-openjdk-11
stage: build
# Cache downloaded dependencies and plugins between builds.
# The key here separates one cache per branch/tag ($CI_COMMIT_REF_SLUG)
cache:
key: "maven-$CI_COMMIT_REF_SLUG"
paths:
- .m2/repository
script:
# the 'verify' goal is definitely the most appropriate here
- mvn $MAVEN_CLI_OPTS verify
artifacts:
name: "Maven artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
paths:
- "**/target"
reports:
# declare the JUnit reports (recursive pattern for multi-module projects)
junit:
- "**/target/*-reports/TEST-*.xml"
4. going further
From this step, the build job could still be enhanced further, for instance with code coverage computation and integration, but that requires a little more code.
Another way of implementing state-of-the-art pipeline with much less efforts is using GitLab CI/CD template. For example:
to be continuous is an open-source project that provides a collection of ready-to-use, configurable, extensible, composable templates.
About Runners
GitLab architecture is very versatile, with the notion of Runners.
Runners are the basic executors pool that make it possible to execute GitLab CI/CD jobs.
2 things of interest to know about runners
1. you can specialise your runners
With GitLab, you can register several kind of runners, made for special and complementary purpose.
In order to segregate them, GitLab supports the notion of Tags. When registering your runners, you shall associate them with functional tag names, that will help developers select the most appropriate one in their .gitlab-ci.yml
file.
For example, let's imagine you have 4 runners:
# |
Description |
Proposed Tags |
1 |
Linux-based general purpose runners for building code, running tests, ... with transparent access to the internet |
linux , general , internet you should also allow this one to run untagged jobs (makes it kind of default runner) |
2 |
Microsoft-based general purpose runners for building your .NET code |
windows , general , internet |
3 |
compute-optimized runners, made for - say - training super-secret neural networks with no access to the internet |
linux , compute , ml (for machine learning) |
4 |
runners located behind your DMZ in your on-premises datacenter, used to perform code/infrastructure deployments |
linux , deploy , datacenter |
With this setup, a monorepo project with both Java and .NET code may declare the following .gitlab-ci.yml
file:
stages:
- build
- test
- deploy
# this job declares no tag: will be executed by default runner (#1)
java-build:
image: maven:3.8-openjdk-11
stage: build
script:
- Java build code here
dotnet-build:
image: mcr.microsoft.com/dotnet/sdk:5.0
stage: build
tags:
- windows
- general
script:
- .NET build code here
# this job declares no tag: will be executed by default runner (#1)
java-test:
image: maven:3.8-openjdk-11
stage: test
script:
- Java test code here
dotnet-test:
image: mcr.microsoft.com/dotnet/sdk:5.0
stage: test
tags:
- windows
- general
script:
- .NET test code here
deploy:
stage: deploy
tags:
- deploy
- datacenter
script:
- deployment code here
2. runners have different scopes
Quoting the official documentation:
Runners are available based on who you want to have access:
- Shared runners are available to all groups and projects in a GitLab instance.
- Group runners are available to all projects and subgroups in a group.
- Specific runners are associated with specific projects. Typically, specific runners are used for one project at a time.