This is a third post in a series, describing the problems my team has faced during implementation of Jenkins pipelines in Kubernetes.
- Jenkins Java Centric Pipelines in Kubernetes
- Building Maven Projects in Jenkins Docker workers
- Using Maven and Jenkins to perform modular Java builds (This article)
- Building Docker Images in Jenkins on Kubernetes
Modular Maven builds in stateless Jenkins workers
Maven is a pretty smart piece of software. Among its many capabilities it knows to build only the modified code since the last build (unless you run the “clean” directive.
When building in a stateless Jenkins workers for the reasons stated in the previous post, we lose these abilities, since every builds starts with an empty workspace.
To overcome these issues and save time on cumulative builds, we need to reliably detect changes and cache previous builds artifacts.
Detecting changes
To detect changes, we use Jenkins and Git. We can kindly ask Jenkins to provide us with the last successful build commit hash.
def lastSuccessfulBuild() {
def build = currentBuild.previousBuild
while (build != null) {
if(build.result == 'SUCCESS') {
return build.changeSets.get(0).getCommitId()
}
}
return ""
}
Using the two hashes (the last successful build and the current build), we use this command to get the list of changed files:git diff --name-only <last_successful_build> HEAD
Detecting Maven modules
We will find all of the Maven modules by searching the workspace for the pom.xml
files.findFiles(glob: '**/pom.xml')
Mapping changes to modules
Once we have a list of changed files, we map them to the closest pom.xml file, this way we detect modules that we need to rebuild.
For example, file webapps/appA/src/main/java/org/foo/Bar.java
will get mapped to webapps/appA/pom.xml
, but not to webapps/pom.xml
.
Running the build
Armed with a list of modules to build, we tell Maven to build only these modules.mvn install -also-make-dependents -P profile_name -pl webapps/appA,webapps/appB