mkko
mkko

Reputation: 4532

How to get Maven project version to the bash command line

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)

Upvotes: 302

Views: 243264

Answers (30)

Himeshgiri gosvami
Himeshgiri gosvami

Reputation: 2884

while using solution that has been given by rjrjrj , I am having error java.io.IOException: Stream closed execption and unable to use that variable in my script.

A more reliable way to retrieve Maven properties like ${project.version} is by using the help:evaluate plugin.

MVN_VERSION=$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout)
echo $MVN_VERSION

-q (quiet mode): This reduces unnecessary output, making the command cleaner and focused only on the result you care about. By using help:evaluate, you bypass the stream-related issues

Upvotes: 1

Ahmed Nabil
Ahmed Nabil

Reputation: 19046

Using the Build Helper Maven Plugin/ build-helper:parse-version, we can get/read:

  • the current project version
  • the major/minor/incremental versions
  • the nextMajor/nextMinor/nextIncremental versions:
projectVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=project.version  -q -DforceStdout)

majorVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.majorVersion  -q -DforceStdout)
minorVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.minorVersion  -q -DforceStdout)
incrementalVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.incrementalVersion  -q -DforceStdout)

nextMajorVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.nextMajorVersion  -q -DforceStdout)
nextMinorVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.nextMinorVersion  -q -DforceStdout)
nextIncrementalVersion=$(mvn build-helper:parse-version help:evaluate -Dexpression=parsedVersion.nextIncrementalVersion  -q -DforceStdout)

Upvotes: 1

lukeforehand
lukeforehand

Reputation: 790

Why not use the right tool for the job? Using xpath syntax is the best approach to retrieving the version number, since it is the intended method of accessing a XML data structure. The expression below is traversing the pom using the "local name" of the elements, in other words ignoring namespace declarations which may or may not be present in the xml.

xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml

Upvotes: 54

Max Barrass
Max Barrass

Reputation: 3242

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

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

Upvotes: 2

skyho
skyho

Reputation: 1903

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

Upvotes: -3

Pascal Thivent
Pascal Thivent

Reputation: 570595

The Maven Help Plugin is somehow already proposing something for this:

  • help:evaluate evaluates Maven expressions given by the user in an interactive mode.

Here is how you would invoke it on the command line to get the ${project.version}:

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

As noted in the comments by Seb T, to only print the version without the maven INFO logs, additionally use -q -DforceStdout:

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

Upvotes: 358

Eduardo Montoya
Eduardo Montoya

Reputation: 1571

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.

Upvotes: 145

freedev
freedev

Reputation: 30197

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

Upvotes: 12

Graeme McKerrell
Graeme McKerrell

Reputation: 99

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"

Upvotes: 3

vboerchers
vboerchers

Reputation: 897

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.)

Upvotes: 0

Manu Manjunath
Manu Manjunath

Reputation: 6421

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.

Upvotes: 58

robert.baboi
robert.baboi

Reputation: 364

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

Upvotes: 1

Yu Jiaao
Yu Jiaao

Reputation: 4714

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

Upvotes: 1

Florent Amaridon
Florent Amaridon

Reputation: 146

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.

Upvotes: 0

Michal Sipek
Michal Sipek

Reputation: 494

There is also one option without need Maven:

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

Upvotes: 16

Philip Ruff
Philip Ruff

Reputation: 21

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.

Upvotes: 0

MichaOnline
MichaOnline

Reputation: 41

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.

Upvotes: -1

rjrjr
rjrjr

Reputation: 4092

Tom's solution with the Exec Maven Plugin is much better, but still more complicated than it needs to be. For me it's as simple as:

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

Upvotes: 271

Vladimir I
Vladimir I

Reputation: 121

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.

Upvotes: 5

mturra
mturra

Reputation: 670

Should be easier since this bug is fixed in maven-help-plugin 3.0.0: MPH-99 Evaluate has no output in quiet mode.

Upvotes: 4

Jan Molak
Jan Molak

Reputation: 4536

I've recently developed the Release Candidate Maven plugin that solves this exact problem so that you don't have to resort to any hacky shell scripts and parsing the output of the maven-help-plugin.

For example, to print the version of your Maven project to a terminal, run:

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version

which gives output similar to maven-help-plugin:

[INFO] Detected version: '1.0.0-SNAPSHOT'
1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

However, you can also specify an arbitrary output format (so that the version could be picked up from the log by a CI server such as TeamCity):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="##teamcity[setParameter name='env.PROJECT_VERSION' value='{{ version }}']"

Which results in:

[INFO] Detected version: '1.0.0-SNAPSHOT'
##teamcity[setParameter name='env.PROJECT_VERSION' value='1.0.0-SNAPSHOT']
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

To save the output to a file (so that a CI server such as Jenkins could use it):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="PROJECT_VERSION={{ version }}" \
   -DoutputUri="file://\${project.basedir}/version.properties"

The resulting version.properties file will look as follows:

PROJECT_VERSION=1.0.0-SNAPSHOT

On top of all the above, Release Candidate also allows you to set the version of your project (which is something you'd probably do on your CI server) based on the API version you've defined in your POM.

If you'd like to see an example of Release Candidate being used as part of the Maven lifecycle, have a look at the pom.xml of my other open-source project - Build Monitor for Jenkins.

Upvotes: 6

Jose Alban
Jose Alban

Reputation: 7956

This will avoid the need for grepping off log entries from the output:

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

Upvotes: 36

Jakob O.
Jakob O.

Reputation: 344

A simple maven only solution

mvn -q -N org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${project.version}'

And for bonus points parsed part of a version

mvn -q -N org.codehaus.mojo:build-helper-maven-plugin:3.0.0:parse-version \
    org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}'

Upvotes: 6

Enrico M. Crisostomo
Enrico M. Crisostomo

Reputation: 1703

Either you have mvn give you the answer (as most answers suggest), or you extract the answer from the pom.xml. The only drawback of the second approach is that you can very easily extract the value of the <version/> tag, but it will be meaningful only if it's literal, that is, not a Maven property. I chose this approach anyway because:

  • mvn is way to verbose and I simply don't like filtering its output.
  • Starting mvn is very slow compared to reading the pom.xml.
  • I always use literal values in <version/>.

mvn-version is a zsh shell script that uses xmlstarlet to read the pom.xml and print the version of the project (if it exists) or the version of the parent project (if it exists):

$ mvn-version .
1.0.0-SNAPSHOT

The advantage is that it's way quicker than running mvn:

$ time mvn-version .
1.1.0-SNAPSHOT
mvn-version .  0.01s user 0.01s system 75% cpu 0.019 total

$ time mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
> -Dexpression=project.version
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate   4.17s user 0.21s system 240% cpu 1.823 total

The difference on my machine is greater than two orders of magnitude.

Upvotes: 1

Stepan Vavra
Stepan Vavra

Reputation: 4054

Just for the record, it's possible to configure Maven's Simple SLF4J logging directly in the command line to output only what we need by configuring:

  • org.slf4j.simpleLogger.defaultLogLevel=WARN and
  • org.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO

as documented at http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html

MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version

As a result, one can run simply tail -1 and get:

$ MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version | tail -1

1.0.0-SNAPSHOT

Note that this is a one-liner. MAVEN_OPTS are being rewritten only for this particular mvn execution.

Upvotes: 7

slow
slow

Reputation: 873

I kept running into side cases when using some of the other answers here, so here's yet another alternative.

version=$(printf 'VER\t${project.version}' | mvn help:evaluate | grep '^VER' | cut -f2)

Upvotes: 14

Ondřej Kmoch
Ondřej Kmoch

Reputation: 106

Exec plugin works without any output parsing because output can be redirected into file and injected back into the job environment via EnvInject plugin:

enter image description here

Upvotes: 1

George
George

Reputation: 6104

This worked for me, offline and without depending on mvn:

VERSION=$(grep --max-count=1 '<version>' <your_path>/pom.xml | awk -F '>' '{ print $2 }' | awk -F '<' '{ print $1 }')
echo $VERSION

Upvotes: 4

Tristik
Tristik

Reputation: 47

VERSION=$(head -50 pom.xml | awk -F'>' '/SNAPSHOT/ {print $2}' | awk -F'<' '{print $1}')

This is what I used to get the version number, thought there would have been a better maven way to do so

Upvotes: -2

pdr
pdr

Reputation: 381

python -c "import xml.etree.ElementTree as ET; \
  print(ET.parse(open('pom.xml')).getroot().find( \
  '{http://maven.apache.org/POM/4.0.0}version').text)"

As long as you have python 2.5 or greater, this should work. If you have a lower version than that, install python-lxml and change the import to lxml.etree. This method is quick and doesn't require downloading any extra plugins. It also works on malformed pom.xml files that don't validate with xmllint, like the ones I need to parse. Tested on Mac and Linux.

Upvotes: 21

Related Questions