The topic for discussion is building Java applications in CI.

Let’s take a look at the Maven repository on Docker Hub. The latest version (3.8.1) appears everywhere, but images based on Alpine are only ibmjava-8, there are no 11 or current releases. The 11-slim image takes 230 MB (compressed size). In our projects, we use BellSoft Liberica (current lts / release) Alpine Musl as a base image for applications. It is known that some teams sometimes have problems with Alpine and Musl when solving specific tasks, but this never concerned us (only the limitation on the size of the space in the provided Container Registry concerned, and this was a plus for choosing Alpine). The Java 11 image takes up only 75 MB (compressed size) – only 30% of the 11-slim size. But it doesn’t have Maven.

When unpacked, the size difference increases even more.

All developed applications use Spring and Testcontainers are regularly encountered in them. Often this means that the docker daemon socket has already been forwarded to the container by the system administrators. If the image had the docker command-line utility, it would be possible to immediately build and push the image, and not transfer it through the GitLab artifacts to the next stage, wait for the docker: stable download, and thereby avoid various overheads. Building the image immediately after receiving the .jar would be useful for applications with a short build time, but if your unit tests take a lot of time and the Container Registry sometimes crashes, it’s better not to do this.

Gradually, you come to the conclusion that it would be most convenient to have your own image with all the necessary tools for building, such as Maven and/or Gradle, the docker command-line tool, plus all sorts of curl, jq, envsubst. In general, here it is:

The tricky package update came after the demands from the security guards on one of the projects, where scanning of images for vulnerabilities was built into the Container Registry. We noticed that the base image of Liberica JDK is built on the basis of Alpine 3.12 (at the time of this writing), and the current release is 3.14. Therefore, in this way we update the entire OS to the latest version. Before building, all docker files are run through hadolint, and you can see that here we are deliberately breaking several of its rules. We also act in the Dockerfile of the application itself, at the end of the article we will give it as a bonus.

Since almost all projects are closed, you have to drag this Dockerfile into the repository of each project and add a separate job to build it:

Further assembly of the application is already carried out using $ TOOLING_IMAGE as the image job.

In fact, another bold component has recently appeared in tooling looks. GitLab is able to display a lot in the Merge Request, for example, it can show a list of run tests and even display the log of each individual one. To do this, you just need to indicate in the job description where GitLab should take JUnit reports in XML format from. This is how it looks:

A test was added to the merge request and it broke

This feature costs almost nothing. It is much more interesting to add support for Code Coverage to your project in order to see directly in the diff which code is covered by tests and which is not:

Green stripes – lines covered with unit tests.

To do this, firstly, you need to use JaCoCo during assembly to get a report in its format. For this, the jacoco-maven-plugin has been added to the pom.xml:

Secondly, GitLab can show Code Coverage only in Cobertura format. From the links in the documentation, you can find Python scripts that convert the former to the latter. And yes, you need Python for that. In this case, we will trust the proposed instructions, and this is what we get. Add one more command to the tiling image to install everything you need:

Then we can put everything together in one job in GitLab.

Thus, we achieve our goals. Above they promised to give a version of their Dockerfile for the application. Here it is:

All of the above can turn out to be the wildest over-engineering.

In our experience, there is only one Gradle project that we build in GitLab CI, and that one is frozen, so you may have to finish something in the provided material to get a report on unit tests and code coverage.