Optimizing Maven Builds with Gitlab Docker Runner
Maven is a great build tool. Although it's based on XML, it's still manageable XML. Easy to understand, easy to write.
It also is a quite simple build tool, the only thing we need, in terms of Java/Kotlin/any other JVM based language is a pom.xml
("Project Object Model"... makes sense in hindsight, but I could have never guessed it)
Maven and Gitlabs Docker Runners
Today we would like to promote the usage of Maven as a build tool with Gitlabs Docker Runner. And there is not much we can do wrong by using Maven and Gitlab Docker Runners, except two things.
Since Maven in contrast to other build dependency management solutions stores its general settings not in the before mentioned pom.xml
but rather uses a dedicated settings.xml
and a dedicated cache folder in the .m2
folder of the user directory, its performance in combination with Docker is open for improvements.
Mind you, nothing impossible there, but also not as straight forward as with other solutions like composer
and npm
So lets address these two pain points, shall we?
Caching Maven Dependencies with Gitlabs Docker Runner
Actually, it's quite simple, but takes a bit of digging through the World Wide Web to find the solution, which is as simple as an environment variable. In our case we found it best to utilize the MAVEN_OPTS
environment variable setting its value to -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
build_maven:
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
cache:
paths:
- ./.m2/repository/
image: maven:3-jdk-12-alpine
stage: build
script:
- mvn clean verify
only:
- master
- develop
What we can see from the example above is that we set the Maven local repository to the current build directory, which in turn now, as Gitlab is only able to cache stuff within the current project directory, enables Gitlab to cache our dependencies.
Of course, depending on the size of our project this could be a huge performance impact of our builds, or a really marginal one. Nevertheless, as our project grows, we definitely want to have those cody bits in there to save some time.
Using a settings.xml in our Build Project
Maybe, just maybe, we are one of the majorities that don't have their artifacts shipped to mvnrepository.com. That's fine, since most of the work a normal developer does is proprietary software, we probably have our own JFrog Artifactoy or Sonatype Nexus Repository setup within our company.
But that also means standard mvn
wouldn't know about it, that's why we generate a fancy settings.xml
out of those tools, put it into our .m2
folder on our development machine and be happy about it.
But as soon as CI/CD hits, we are faced with the question:
How can my Docker Runner use such a configuration?
And the answer is also, quite simple again.
mvn --settings $MAVEN_SETTINGS_XML clean deploy
Cheesy right? Gitlab offers the possibility to provide environment variables during build jobs, and we should utilize that possibility, create an CI/CD user for our repository, and copy the generated settings XML into such an environment variable.
Mind the type of that variable though: File.
Gitlab here is putting the content we put in there into a file on the file system referencing the path to it via the environment variable, which we are passing tomvn
.
So effectively we are passing a path to it, not an inline version of the settings.xml
This way and with the mvn
option --settings
we can pass those settings to Maven in order to be utilized by the build it's about to be doing for us.
Congratulations!
To be honest, it's not rocket science, and we can probably get away without optimizing our CI/CD pipeline in such a manner, but putting the time into making our CI/CD as efficient as possible will definitely help us in the long run!
Feel free to copy the combined example of both our above explained techniques.
build_maven:
variables:
MAVEN_OPTS: "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
cache:
paths:
- ./.m2/repository/
image: maven:3-jdk-12-alpine
stage: artifactory
script:
- mvn --settings $MAVEN_SETTINGS_XML clean deploy
only:
- master
- develop