3

I have a number of Maven projects being built my Jenkins server. These projects have dependencies on each other, e.g.

service-base -> java-base -> pom-base

In other words, the Maven project service-base depends on the Maven project java-base. Naturally, my POM files look like this:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>my.com</groupId>
  <artifactId>service-base</artifactId>
  <dependencies>
    <dependency>
        <groupId>my.com</groupId>
        <artifactId>java-base</artifactId>
        <version>1.0.0</version>
    </dependency>
  </dependencies>
</project>

The issue is that none of my Maven projects have "releases" per-se, since I'm using continuous integration to release my changes. Currently, I allow artifact overwriting in my Maven repo and keep all of my versions at 1.0.0. This is because I release my packages many times a day and changing the versions in all the POM files each time I submit a new package version.

Ideally, what I would like is for Jenkins to generate a new version, e.g. 1.0.{BUILD_NUMBER} and then for it update the dependencies all the way up the dependency tree.

Question: Is this possible? Or does anyone have any other solutions to versioning?

A_Di-Matteo
  • 26,902
  • 7
  • 94
  • 128
Max
  • 15,157
  • 17
  • 82
  • 127
  • 1
    Is this a multi-module project? In this case, versioning is just handled at the parent / root level and `maven-release-plugin` updates the version in each POM automatically. – Tunaki Jan 18 '16 at 19:53
  • No, it is many independent Maven projects. – Max Jan 18 '16 at 19:55
  • 1
    Then why are they following the same release cycle? – Tunaki Jan 18 '16 at 19:55
  • Because they are continuously integrated. When I submit code to the `java-base` package, my CI server builds it. Once `java-base` builds successfully, then `server-base` must be rebuilt to use the new changes. Jenkins takes care of building up the dependency tree automatically, but it doesn't handle the versioning. – Max Jan 18 '16 at 19:59
  • But then the generated 1.0.{BUILD_NUMBER} would also be the new version of the project/artefact or would you just have it for Jenkins while keeping the released artefact at 1.0 (even more complex)? – A_Di-Matteo Jan 19 '16 at 09:44

1 Answers1

4

Here is how I achieved the same, using Maven profiles, Maven classifiers and Jenkins parametrized builds.

You can define a jenkins profile (or whatever name you prefer) in the pom of the concerned projects. This profile will not be active by default, so your local builds will keep on working as usual. However, this profile will be activated on the Jenkins builds (via the -Pjenkins option on the Maven execution).

How this profile look like in the project at the top of the hierarchy:

<profiles>
    <profile>
        <id>jenkins</id>
        <properties>
            <groupId>${project.groupId}</groupId>
            <artifactId>${project.artifactId}</artifactId>
            <version>${project.version}</version>
            <packaging>${project.packaging}</packaging>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.5</version>
                    <executions>
                        <execution>
                            <id>generate-default-version</id>
                            <phase>package</phase>
                            <goals>
                                <goal>jar</goal>
                            </goals>
                            <configuration>
                                <classifier>${BUILD_NUMBER}</classifier>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.4</version>
                    <executions>
                        <execution>
                            <id>install-default-version</id>
                            <phase>install</phase>
                            <goals>
                                <goal>install-file</goal>
                            </goals>
                            <configuration>
                                <file>${project.build.directory}/${project.build.finalName}-${BUILD_NUMBER}.${project.packaging}</file>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

What is the profile doing?

  • We are using the Maven Jar Plugin to generate on the package phase yet another artefact for the same project, so the project will create the normal jar plus another jar having as classifier the BUILD_NUMBER (i.e. myproject-1.0.jar and myproject-1.0-4567.jar)
  • We are also using the Maven Install Plugin to install the additional artefact (the myproject-1.0-4567.jar) into the local Maven cache (so it will be visible to other dependent projects)
  • We need to define some properties for the Install Plugin, otherwise the install-file will not work

Hence, when on your Jenkins build you will execute the following:

mvn clean install -Pjenkins -DBUILD_NUMBER=${BUILD_NUMBER}

Jenkins will actually pass its BUILD_NUMBER to Maven, which will use it as defined in the jenkins profile and create (and install) an additional artefact for us using it as classifier.

Fine, now we have a dynamically created artefact using the Jenkins build number and available for other projects/builds.

But how other projects can use it?
We define in the dependent projects another profile (or again called jenkins for coherency) and re-define the dependency we now need at runtime:

<profiles>
    <profile>
        <id>jenkins</id>
        <dependencies>
            <dependency>
                <groupId>com.sample</groupId>
                <artifactId>test</artifactId>
                <version>1.1.0</version>
                <classifier>${BUILD_NUMBER}</classifier>
            </dependency>
        </dependencies>
    </profile>
</profiles>

Note: we are actually overriding as part of the profile a dependency and saying we want that specific classifier for it. Which classifier? The BUILD_NUMBER classifier, which will be available in the local Maven cache of the Jenkins server because installed by the previous build.

But how can the dependent build know which build number and as such which classifier to use, dynamically?
Using Jenkins parametrized builds and the Jenkins Parametrized Trigger plugin.

So, to summarize:

  • Provider project defines the profile to create additional classifier
  • Consumer project defines the profile to use as dependency a specific classifier
  • If a project is Provider for others and Consumer of others, it can then merge the two approaches above in the same profile
  • The first Jenkins build activates this specific profile and pass to Maven its build number
  • The downstream Jenkins builds are triggered by the first, which is passing them its build number via the Parametrized Plugin
  • Each downstream build would then resolve the classifier specified by the parameter and, if required, also create yet another classifier for its own build (according to its profile)

Using this approach, you local builds will keep on working as usual and no classifier would be used, while Jenkins builds would use an additional classifier used across them.

Community
  • 1
  • 1
A_Di-Matteo
  • 26,902
  • 7
  • 94
  • 128