Deriving maven artifact version from git branch

时光总嘲笑我的痴心妄想 提交于 2019-11-28 15:17:48

Indeed, Maven can't change the version of it's own project in one run with other goals. On top of it, as far as I know, Maven doesn't support arbitrary properties in the <version> tag. Therefore, a separate execution is required to run a goal which will change the version of the POM. There are various plugins which can do it - for this case one might use the versions:set goal from versions plugin - http://mojo.codehaus.org/versions-maven-plugin/set-mojo.html

So, one might execute it as follows for example:

mvn versions:set -DgenerateBackupPoms=false -DnewVersion=$branch-SNAPSHOT

where the $branch variable has to contain current Git branch name; it can be extracted with git rev-parse, like this:

branch=$(git rev-parse --abbrev-ref HEAD)

But still, one needs to execute it somehow. You can do manually, but it is cumbersome. So, my guess is that indeed the most robust solution would be to approach this from Git side. That is - a Git hook. Here is the complete Git post-checkout hook which will do the job (same code as above with some filtering to run the hook only when the branch is checked out, not the individual files only):

#!/bin/bash

echo 'Will change the version in pom.xml files...'

# check if the checkout was to checkout a branch
if [ $3 != '1' ]
    then echo 'git checkout did not checkout a branch - quitting';exit
fi

# get current branch name
branch=$(git rev-parse --abbrev-ref HEAD)
version=$branch-SNAPSHOT

# run maven versions plugin to set new version
mvn versions:set -DgenerateBackupPoms=false -DnewVersion=$version

echo 'Changed version in pom.xml files to $version'

Put this content to the file PROJECTDIR\.git\hooks\post-checkout file. Note that the hook file should be executable to run it (chmod +x post-checkout).

Few notes about versions plugin - it is pretty flexible and supports many options and have few other goals which might be of help, depending on your project structure (do you use parent poms or not, do childs have their own versions or do they derive from parent, etc.). So, the hook above might be modified slightly to support you specific case by using other goals from versions plugin or by specifying additional parameters.

Pros:

  • Robust
  • No need to change anything in the pom.xml files themselves to make this work
  • This "functionality" can be turned off simply by deactivating the hook (remove or make not executable) - again, no changes are required in the pom.xml

Cons:

  • One can't enforce others to use a hook - it should be installed manually after the repo is cloned (or, you can provide a script to install the hook if supposed Git users are afraid of touching the stuff inside .git directory).

UPDATE

Hereafter is the more complicated version of the hook, which will not only set the version to the branch name, but will also preserve the suffix of the old version. For example, provided old version master-1.0-SNAPSHOT, switching to feature1 branch will change the version of the project to feature1-1.0-SNAPSHOT. This bash script suffers from few problems (requires branch names without dash symbol (-) in the name, and only takes the version of the root pom), but may give an idea of how the hook may be extended: given a mix of mvn and bash commands you can extract and update quite a lot of the information in the POM.

#!/bin/bash

echo 'Will change the version in pom.xml files...'

# check if the checkout was to checkout a branch
if [ $3 != '1' ]
    then echo 'git checkout did not checkout a branch - quitting';exit
fi

# get current branch name
branch=$(git rev-parse --abbrev-ref HEAD)

# get current version of the top level pom
current_version=$(mvn help:evaluate -Dexpression=project.version | grep -v '\[.*')

# extract version suffix
suffix=$(echo $current_version | cut -d \- -f 2)

# build new version
version=$branch-$suffix

# run maven versions plugin to set new version
mvn versions:set -DgenerateBackupPoms=false -DnewVersion=$version

echo 'Changed version in pom.xml files to $version'

Sorry to revive this question and to post very lately another solution but it is possible indeed to change maven version dynamically and to use some git features, a little bit like git describe would do.

The project that does this is jgitver-maven-plugin (disclaimer; I am the author), it uses jgitver a jgit based library in order to derive maven project version from git information .

It is very easy to use as a maven extension

...
  <build>
      <extensions>
          <extension>
              <groupId>fr.brouillard.oss</groupId>
              <artifactId>jgitver-maven-plugin</artifactId>
              <version>0.1.0</version>
          </extension>
      </extensions>
     ...
  </build>
...

The extension can also be used as a plugin extension, and then allows more configuration , for example in case you would not like to use SNAPSHOTS. See the project page for a description of full usage scenarios.

There is also a gradle plugin available that does more or less the same.


[edit 1]: answer to Thorbjørn Ravn Andersen comment

The plugin does not modify the original pom files or the build.gradle files.
For the maven plugin, modifications are done both in memory in the maven Object Model and written to a temporary file in temp directory. The calculation is based on the git metadata only (tags, commits, ...).
This non modification allows to not pollute the git history. WHen your are satisfied with a git commit, tag it git tag -a x.y.z and mvn deploy : that's all.
The version in your project files, is now useless and can be set to 0 for example.

As of today, and due to IDEA-155733 only recent EAP versions of IntelliJ work with the maven plugin. Eclipse & Netbeans have no problem.


Disclaimer: I am the author

My maven core extension will virtually set the version based on the current branch or tag. You can config custom version format patterns as you like.

https://github.com/qoomon/maven-branch-versioning-extension

Version Format Example

If it is sufficient to set the git tag and version information in the artifact file name, you can use maven-jgit-buildnumber-plugin:

<build>
  <finalName>${artifactId}-${git.buildnumber}</finalName>
  <plugins>
    <plugin>
      <groupId>ru.concerteza.buildnumber</groupId>
      <artifactId>maven-jgit-buildnumber-plugin</artifactId>
      <version>1.2.7</version>
      <executions>
        <execution>
          <id>git-buildnumber</id>
          <goals>
            <goal>extract-buildnumber</goal>
          </goals>
          <phase>prepare-package</phase>
        </execution>
      </executions>
    </plugin>
    <!-- more plugins -->
  </plugins>
</build>

Have you tried using this plugin?: https://github.com/ktoso/maven-git-commit-id-plugin. You can configure it to generate a properties file with all the relevant info about your repo state:

  • branch
  • describe
  • commitId
  • buildUserName
  • buildUserEmail
  • buildTime
  • commitUserName
  • commitUserEmail
  • commitMessageShort
  • commitMessageFull
  • commitTime

Have you checked the buildnumber-maven-plugin which gives you the opportunity to use the revision number of git. But you needed something different. Furthermore i would suggest to do a thing like:

   1.0.0-SNAPSHOT 
   1.0.0-SNAPSHOT 

beeing on master

on a branch you can simple change the version to

  1.0.0-BF-SNAPSHOT 

From maven-3.5.0 on there is support for ${revision}, ${sha1} and ${changelist} properties within the version tag. This feature may be sufficient for your purpose if, for example you want to incorporate the Git branchname into the version for a CI build job. See Maven CI Friendly Versions

Basically, in your pom.xml replace the fixed version by:

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

Set default values for revision and changelist in the project root dir by creating a file .mvn/maven.config containing:

-Drevision=1.2.3
-Dchangelist=-SNAPSHOT

Check this file into version control, update it when you bump your project revision.

In your CI system you can then override the changelist variable using a cleaned-up representation of the Git branch name, eg.

BRANCHNAME=$(git rev-parse --abbrev-ref HEAD | sed -E -e 's@[^0-9A-Za-z.-]+@-@g')
mvn clean install -Dchangelist="-${BRANCHNAME}"

(You may prefer git symbolic-ref --short HEAD for fetching the branchname, YMMV)

Your artifact built by the CI system for branch feature/branchname will then have a versioned-branch suffix like:

yourproject-1.2.3-feature-branchname.jar

whilst developers who do not use any overrides will still build it as:

yourproject-1.2.3-SNAPSHOT.jar
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!