5

I am trying to implement semantic versioning in our project. I tested maven semver plugin but that didn't help me so please don't ask me why. I finally ended up using maven groovy. It works like a charm, however, when I install or deploy the maven project the version in repository is the variable name.

This is despite the fact that all the artefacts and jar files are packaged with correct version.

So please look at my pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mytest.test</groupId>
    <artifactId>test-tag</artifactId>
    <version>${revision}</version> 


    <description>Test</description>

    <properties>
        <ChangeType>TO_BE_SET</ChangeType>
    </properties>


    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.gmaven</groupId>
                <artifactId>gmaven-plugin</artifactId>
                <version>1.5</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>execute</goal>
                        </goals>
                        <configuration>
                            <providerSelection>2.0</providerSelection>
                            <properties>
                                <script>git describe --abbrev=0 --tags</script>
                            </properties>
                            <source>
                                def tagIt = 'git tag -a vXXXX -m "Auto tagged"'
                                def changeType = project.properties.ChangeType
                                def command = project.properties.script
                                def process = command.execute()
                                process.waitFor()
                                def describe = process.in.text.trim()
                                println "Setting revision to: " + describe

                                if(!describe.startsWith("v")) {
                                    describe = "1.0.1"
                                } else {
                                    describe = describe.substring(1)
                                }

                                    project.properties.setProperty('revision', describe)


                            </source>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-testResources</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-jar</id>
                        <phase>package</phase>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-test</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

the version is ${revision} a variable name that is being set in groovy script. What groovy code does is getting the last tag from GIT and and then set it to the property 'revision'.

The final jar file has the correct version extracted but when installed into repository, the folder name and jar name are like:

m2\repository\com\mytest\test\test-tag\${revision}\test-tag-${revision}.jar

I tried to default 'revision' to a value using:

<properties>
  <revision>1.0.1</revision>
</properties>

but then groovy code setting the value has no effect.

I also tried different phase for the maven groovy plugin, no luck. Have I missed anything? Can anyone please help me on this?

I'd like to mention that as vatbub and StefanHeimberg mentioned I can use versions:set to set the version but this requires me to do an extra commit to GIT which I am trying to avoid, wondering if I can achieve this by writing a maven plugin instead?

xbmono
  • 2,084
  • 2
  • 30
  • 50
  • 1
    Did you try the versions maven plugin? It has a `set` goal which can modify the project version. ([more info](https://www.mojohaus.org/versions-maven-plugin/set-mojo.html)) However, afaik, maven parses the pom and changes which are made to the pom during the build are only taken into account in subsequent builds. In that case, you would need to invoke versions:set and then call maven a 2nd time to perform the actual build. – vatbub Apr 20 '18 at 09:05
  • @vatbub thanks for prompt reply. The groovy code determines the version so I need to execute the groovy-maven-plugin and then get the output and then run versions:set and then later mvn install. Not sure how I can do this! – xbmono Apr 20 '18 at 09:20
  • 2
    As I said, Maven is terribly bad at handling changes to the pom that occur during the build. I guess the best solution is to create a lightweight command line tool that just outputs the desired version to the console and pipe that output to `versions:set` – vatbub Apr 20 '18 at 09:27
  • But you probably won't get around calling maven twice – vatbub Apr 20 '18 at 09:28
  • 1
    I ended up using versions:set as you said. Actually I created a new plugin off versions:set and customised it in order to get latest tags and other information and then set version automatically – xbmono Apr 27 '18 at 08:47
  • 1
    Not something OP wants, but this is [possible with Gradle](https://stackoverflow.com/questions/32717251/how-to-set-project-version-by-passing-version-property-on-gradle-command-line). – Franklin Yu Mar 28 '19 at 20:42

3 Answers3

7

With Maven you can set the version at build time with

mvn versions:set -DnewVersion=${bamboo.inject.version}

as @vatbub already commented in your question.

In addition to this i wrote a Shell script that can be used in build pipeline to generate the version according to the maven project version and add the build number from the build server.

https://gist.github.com/StefanHeimberg/c19d7665e8df087845c036fe8b88c4f2

The Script reads the maven project version, add a the build number and writes a text file with all the new numbers that can be used.

The next step is to inject this text file in the Build Pipeline and call the versions plugin as stated above

pom.xml:

something like

<project>

    <groupId>ch.stefanheimberg.example</groupId>
    <artifactId>your-awesome-app</artifactId>
    <version>5.1.2-SNAPSHOT</version>

</project>

or

<project>

    <groupId>ch.stefanheimberg.example</groupId>
    <artifactId>your-awesome-app</artifactId>
    <version>5.1-SNAPSHOT</version>

</project>

Step 1:

./generate_version_txt.sh ${bamboo.buildNumber}

Step 2:

Inject generated version.txt in the build system that all the properties can be used in all tasks / plugins, etc...

In my case Bamboo CI ready the version.txt file and declares the content of the file as environment variables under the bamboo.inject. prefix.

For example ${bamboo.inject.long_version}

Step 3:

Update Maven Project version

mvn versions:set -DnewVersion=${bamboo.inject.version}

Step 4:

Run Maven Build

mvn clean verify

Step 5:

Run Docker build

for example use it also as docker tag version. etc...

docker build --build-arg version=${bamboo.inject.version} --tag your-awesome-app:${bamboo.inject.version} .

Example Dockerfile:

FROM jboss/wildlfy
ARG version
ADD target/your-awesome-app-${version}.war /opt/jboss/wildfly/standalone/deployments/

I know that can be a problem / not possible in your case with the groovy script. but perhaps it is an other view at your problem. and possibly also another solution for it.

(sorry for my english. but i hope it is understandable what i mean)

lepe
  • 24,677
  • 9
  • 99
  • 108
StefanHeimberg
  • 1,455
  • 13
  • 22
  • Thanks. The thing is I can do the same thing with groovy. I can write the version into a file and use it in versions:set. But the problem with this, you will have an extra commit in GIT repository for changing the Pom file and I am trying to avoid that, otherwise this is the same thing that maven release plugin does. By the way, it wasn’t me who voted down, someone else did. I’ll wait for more answers to see if anyone has come up with a better solution – xbmono Apr 22 '18 at 01:03
  • vote down ist not a problem... thats how the community works. but i would be happy to know WHY someone voted down. Some constructive Feedback would be appriciated. I know my answer was not THE answer to the Problem but possibly another way to solve this – StefanHeimberg Apr 22 '18 at 08:22
  • The `versions:set` works like a charm! Just what i needed! Thank you! – Max Cascone Mar 21 '22 at 16:43
1

I ran into a similar problem and ended up using the maven flatten plugin to ensure that all variables are removed from the POMs before being deployed. This remove all references to the string ${revision} and replace by the actual value at build time, without interfering with the original POMs.

eschnou
  • 401
  • 4
  • 8
1

You can use https://maven.apache.org/maven-ci-friendly.html

and simply set:

<project>
   <version>${revision}</version>

(revision is a maven reserved property)

The idea is that the version is no longer in the pom.xml. You have to set it as a maven arg :

mvn -Drevision=what.you.want …

The value for revision can be pickup up from anywhere : for example, if your build environment is gitlab-ci, you can use the CI_COMMIT_TAG environment variable to get the version from the git tag.

mvn -Drevision=$CI_COMMIT_TAG …

Thomas LIMIN
  • 387
  • 4
  • 9
  • While maybe not the most flexible solution, it is the more elegant one, without relying on a Maven Plugin. Thank you for pointing this one out! – h1dden.da3m0n Jun 15 '23 at 08:02