How to get Maven project version to the bash command line

Viewed 179382

Previous I issued a question on how to change Maven project vesion from command line which lead me to a new issue.

Previously I was able to get the version number since the version was stored as a property that was easy to grep and parse from the command line (bash). Now that the pom.xml <version> element is used for this, it no longer is unique since all the dependencies and maybe some others too use this. I think there is no way to get the current version number with a bash script without external tools for parsing XML or some very context-aware sed command.

The most clean solution in my opinion would be for Maven to hand out this version information. I was thinking of writing a custom maven plugin for retrieving different properties but I thought I'd ask here first.

So, is there any easy way to get the value of ${project.version} to the command line?

Solution

I had to cd to the directory manually but that can be done easily. In my bash script I have:

version=`cd $project_loc && mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | sed -n -e '/^\[.*\]/ !{ /^[0-9]/ { p; q } }'`

Which gives me the current version that I can then advance. Grepping might be simpler but I thought I'd like as robust as possible, so I'm satisfied with the first line that starts with a number and try to handle this as a version number.

# Advances the last number of the given version string by one.
function advance_version () {
    local v=$1
    # Get the last number. First remove any suffixes (such as '-SNAPSHOT').
    local cleaned=`echo $v | sed -e 's/[^0-9][^0-9]*$//'`
    local last_num=`echo $cleaned | sed -e 's/[0-9]*\.//g'`
    local next_num=$(($last_num+1))
    # Finally replace the last number in version string with the new one.
    echo $v | sed -e "s/[0-9][0-9]*\([^0-9]*\)$/$next_num/"
}

And I use this by simply calling:

new_version=$(advance_version $version)
34 Answers

After doing some research I found the following:

  1. Maven has been blamed because integration with DevOps tools is not easy due to the fact that it does not follow some good practices regarding CLI tools, i.e:

http://www.faqs.org/docs/artu/ch01s06.html (not available anymore) "The Unix Way" (ii) Expect the output of every program to become the input of another, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input. What does it actually mean? Your output should be:

  • "grepable": (One "record" per line)
  • "cutable": (Delimited "fields")
  • exit codes: 0 for success, nonzero for failure.
  • messaging (stderr) vs. output (stdout)

(ref: Make awesome command line apps with ruby by Dave Copeland Jan 18, 2012 https://youtu.be/1ILEw6Qca3U?t=372)

  1. Honesty I think Dave Copeland was right when he said that maven does't play fairly with others. So I decided to give a look to maven's source code as well as to maven-help-plugin's source code as well. It seems that they have fixed a little bit the maven's -q switch (I was using version 3.5.3 at that time), so now if you pass it, you won't get all the annoying non-sense logging stuff that prevents maven from being used within automated scripts. So you should be able to use something like this:

    mvn help:evaluate -Dexpression=project.version -q
    

The problem is that this command prints nothing because by default the help plugin outputs through the logger which has been silenced by the -q switch. (latest available version of the plugin at that time was 3.1.0 released on June, 3rd 2018)

  1. Karl Heinz Marbaise (https://github.com/khmarbaise) fixed it by adding an optional parameter that allows you to call it in the following way:

    mvn help:evaluate -Dexpression=project.version -q -DforceStdout
    

The commit description is available at: (https://github.com/apache/maven-help-plugin/commit/316656983d780c04031bbadd97d4ab245c84d014)

Again, you should always verify the exit code of the command and redirect all stderr to /dev/null on unix.

This is the cleanest solution there is:

mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
-Dexpression=project.version -q -DforceStdout

Advantages:

  • This works fine on all operating systems and all shells.
  • No need for any external tools!
  • [important] This works even if project version is inherited from parent pom.xml

Note:

  • maven-help-plugin version 3.2.0 (and above) has forceStdout option. You may replace 3.2.0 in above command with a newer version from the list of available versions of mvn-help-plugin from artifactory, if available.
  • Option -q suppresses verbose messages ([INFO], [WARN] etc.)

Alternatively, you can add this entry in your pom.xml, under plugins section:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-help-plugin</artifactId>
    <version>3.2.0</version>
</plugin>

and then run above command compactly as follows:

mvn help:evaluate -Dexpression=project.groupId -q -DforceStdout

If you want to fetch groupId and artifactId as well, check this answer.

There is also one option without need Maven:

grep -oPm1 "(?<=<version>)[^<]+" "pom.xml"

This is by far the easiest bash cut and paste solution:

VERSION=$(mvn exec:exec -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive -q)
echo $VERSION

it echoes

1.4

I found right balance for me. After mvn package maven-archiver plugin creates target/maven-archiver/pom.properties with contents like this

version=0.0.1-SNAPSHOT
groupId=somegroup
artifactId=someArtifact

and I am using bash just to execute it

. ./target/maven-archiver/pom.properties

then

echo $version
0.0.1-SNAPSHOT

Of course this is not safe at all to execute this file, but execution can easily be converted into perl or bash script to read and set environment variable from that file.

Base on the question, I use this script below to automatic increase my version number in all maven parent/submodules:

#!/usr/bin/env bash  

# Advances the last number of the given version string by one.  
function advance\_version () {  
    local v=$1  
    \# Get the last number. First remove any suffixes (such as '-SNAPSHOT').  
  local cleaned=\`echo $v | sed \-e 's/\[^0-9\]\[^0-9\]\*$//'\`  
 local last\_num=\`echo $cleaned | sed \-e 's/\[0-9\]\*\\.//g'\`  
 local next\_num=$(($last\_num+1))  
  \# Finally replace the last number in version string with the new one.  
  echo $v | sed \-e "s/\[0-9\]\[0-9\]\*\\(\[^0-9\]\*\\)$/$next\_num/"  
}  

version=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)  

new\_version=$(advance\_version $version)  

mvn versions:set -DnewVersion=${new\_version} -DprocessAllModules -DgenerateBackupPoms=false

Add the following plugin to your pom.xml

    ...
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
            <execution>
                <id>generate-version-file</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>evaluate</goal>
                </goals>
                <configuration>
                    <expression>project.version</expression>
                    <output>${project.build.directory}/version.txt</output>
                </configuration>
            </execution>
        </executions>
    </plugin>
    ...

This will generate a target/version.txt file as a normal part of your build.

All you then need to do to get this to the shell is the following:

#!/bin/sh
value=`cat target/version.txt`
echo "$value"
#!/bin/bash
value=$(<target/version.txt)
echo "$value"

this is an edit from above

cat pom.xml | grep "" | head -n 1 | sed -e "s/version//g" | sed -e "s/\s*[<>/]*//g"

I tested it out on on the cmdline works well

grep "" pom.xml | head -n 1 | sed -e "s/version//g" | sed -e "s/\s*[<>/]*//g"

is another version of the same. I have a need to get the version number in Jenkins CI in k8s without mvn installed so this is most helpful

thanks all.

I need exactly this requirement during my Travis job but with multiple values. I start with this solution but when calling multiple time this is very slow (I need 5 expresions).

I wrote a simple maven plugin to extract pom.xml's values into .sh file.

https://github.com/famaridon/ci-tools-maven-plugin

mvn com.famaridon:ci-tools-maven-plugin:0.0.1-SNAPSHOT:environment -Dexpressions=project.artifactId,project.version,project.groupId,project.build.sourceEncoding

Will produce that:

#!/usr/bin/env bash
CI_TOOLS_PROJECT_ARTIFACTID='ci-tools-maven-plugin';
CI_TOOLS_PROJECT_VERSION='0.0.1-SNAPSHOT';
CI_TOOLS_PROJECT_GROUPID='com.famaridon';
CI_TOOLS_PROJECT_BUILD_SOURCEENCODING='UTF-8';

now you can simply source the file

source .target/ci-tools-env.sh

Have fun.

One alternative would be to parse with yq (https://github.com/kislyuk/yq) like so:

cat pom.xml | xq -r '.project.version'

Notice the executable is xq not yq

To get xq, install yq like so pip install yq

In docker containers with only busybox available I use awk.

version=$(
 awk '
  /<dependenc/{exit}
  /<parent>/{parent++};
  /<version>/{
    if (parent == 1) {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      parent_version = $0;
    } else {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      version = $0;
      exit
    }
  }
  /<\/parent>/{parent--};
  END {
    print (version == "") ? parent_version : version
  }' pom.xml
)

Notes:

  • If no artifact version is given parent version is printed instead.
  • Dependencies are required to come after artifact and parent version. (This could be overcome quite easily but I don't need it.)

Quickest way that gets you just the text without all the trimmings

mvn help:evaluate -q -DforceStdout -D"expression=project.version"

I am using a one-liner in my unix shell ...

cat pom.xml | grep "" | head -n 1 | sed -e "s/version//g" | sed -e "s/\s*[<>/]*//g"

You can hide this line in a shell script or as an alias.

VERSION=$(mvn -version | grep 'Apache Maven' | grep -o '[0-9].[0-9].[0-9]')
echo $VERSION

The easiest way to get mavens version is like so :

mvn -v | head -1 | cut -d ' ' -f 3 

prints : 3.8.5

Related