How to set project version in maven at build time?

2019-06-06 15:04发布

问题:

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?

回答1:

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 tho 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 grovy script. but perhabs 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)



回答2:

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.