We have a product that consists of many Maven projects that depend on each other. All of these Maven projects come together in a single project which delivers the end product.
The Maven projects share the same life cycle. In other words, they are not managed by separate teams of people with explicit <dependency>
changes to pick up newer versions of other projects. Rather, when someone changes something in one of the projects, then the result should go directly into the end product without additional changes.
We use Jenkins as our Continuous Integration tool.
The main wishes we have are as follows:
- No need to copy all the inter-project dependencies to Jenkins configuration: these should be in a single place, ideally the
pom.xml
files. - Avoid unnecessary builds: on an SCM change, only build the projects that are potentially affected.
- In case of diamond dependencies (C depends on both B1 and B2, which both depend on A), if the lowest (A) is changed, then the end product (C) should always use the version of A that was also used to build/test B1 and B2.
Question: What is the best approach to do this with Jenkins?
We are currently thinking to use a single job using the Jenkins Pipeline plugin, which analyzes the Maven dependencies and the SCM changes, decides what needs to be built and in which order, and then actually build the projects.
In order to achieve your requirements you could rely on many of the default features of a maven multi-module build and an additional configuration explained below.
From your question:
Using an aggregator/multi-module pom you can have a single entry point (a pom.xml file) for a maven build which would then build all of the defined modules:
That is, a
pom.xml
file like the following:Is a minimal example: define a list of modules (other maven projects) which will be built starting from this maven project (an empty project which only purpose is to build other maven projects). Note the
pom
packaging required to have modules, it's telling maven that this project only provides a pom (no further artifacts).You could hence have a root Maven aggregator project which would define the other maven projects as its modules and have a single Jenkins job building this entry point.
Additionally, from your question:
To meet this requirement, you could use the
incremental-build-plugin
:This plugin will verify whether any of the pom file, resources, sources, test sources, test resources would change in a module and if the case remove its output directory. As such, saving build time for a concerned module and in chain for the whole multi-module project (our own build, in this case).
To enable this mechanism, you can configure in the aggregator pom above, the following:
Above, we have simply added the
incremental-build-plugin
to the aggregator build and a property,skip.incremental
, which will skip the plugin execution for the first build, the empty aggregator one, while enabling it into the modules as following:Note: in the pom above of a sample module we are pointing at the aggregator pom file as a parent, hence using maven inheritance in combination with aggregation (a classic usage) and as such having a multi-module build plus a common build governance provided by the common parent for all the declared modules (in this case, the common build governance provides the additional
incremental-build-plugin
configuration). Moreover, each module re-configures theskip.incremental
property to not skip the aforementioned plugin. That's a trick: now the plugin will only be executed in modules, not in its root (which would not make sense and in this case would actually throw an error otherwise).Obviously, in the related Jenkins job, in its Source Code Management section we don't have to configure a fresh new check-out as part of each build, otherwise no changes would be detectable (and everytime it would start from zero, which is a good practice for release builds actually).
Moreover, your maven command should not invoke the
clean
lifecycle, which would also remove thetarget
at each build and as such make no change detectable either.Furthermore, from your question:
Maven will take care of this requirement as part of a multi-module build and its reactor mechanism:
By default, the reactor will also hence create a build order and always build a module before its dependent/consumer module. That also means that the last built module will actually be the module responsible of building the final artifact, the deliverable product depending on part or all of the other modules (for instance, a module responsible of delivering a
war
file will probably be the last one to build in a multi-module webapp project).Further related reading concerning Maven and skip actions when something is unchanged:
maven-jar-plugin
provides theforceCreation
which by default is already enabledmaven-compiler-plugin
provides theuseIncrementalCompilation
, although not properly working at the moment.maven-war-plugin
provides therecompressZippedFiles
option which could also be used to speed up builds and avoid re-doing something (to switch tofalse
in this case):Update
This requirement would also enforce the usage of an aggregator/multi-module project, however may also apply to different projects linked through dependencies indeed.
This point also enforces the usage of a multi-module project. In a multi-module project you may have different versions among modules, however the common practice and guideline is to share the same version, defined by the parent project (the aggregator) and cascaded across its modules. As such, its centralization and governance will avoid misalignments and mistakes.
Again, this would be handled automatically by a multi-module project. The same would happen with different projects each using SNAPSHOT versions, then no need to change the dependency version in its consumer project (the one responsible of building the final product). However, while SNAPSHOT versions are really helpful (and recommended) during development, they should definitely not be used when delivering the final product since build reproducibility would be in danger (that is, you may not be able to re-build the same version later on since it was relying on SNAPSHOT versions, hence not frozen versions). Hence, SNAPSHOT is not a silver bullet solution and should be used only during certain phases of the SDLC of the product, not as a finalized and frozen solution.
Update 2
Worth to also look at the new Maven Incremental Module Builder for Maven 3.3.1+ and Java 7.
More details on: