41

I've created a pipeline and using the embedded groovy pipeline script definition and can't seem to get the version ID of the project from the POM. I tried this which works in a groovy console but on in the Jenkins build pipeline script:

def project = new XmlSlurper().parse(new File("pom.xml"))
def pomv = project.version.toString()

According to the documentation Jenkins has a $POM_VERSION but the value doesn't have anything in it when I assign it to a variable and echo it out.

def pomv = "$POM_VERSION"

OR

def pomv = '$POM_VERSION"
Krzysztof Krasoń
  • 26,515
  • 16
  • 89
  • 115
user301693
  • 2,377
  • 7
  • 22
  • 24
  • Possible duplicate of [Getting Project Version from Maven POM in Jenkins](https://stackoverflow.com/questions/9893503/getting-project-version-from-maven-pom-in-jenkins) – Raedwald Feb 25 '19 at 13:46
  • @user301693 can you accept my answer? This post has been here for a long time and still there are no answers accepted. – pitchblack408 May 18 '22 at 19:46

10 Answers10

87

Use readMavenPom like this:

pom = readMavenPom file: 'pom.xml'
pom.version

See Model reference for properties (like the above version).

For this to work, one has to install Pipeline Utility Steps plugin

Krzysztof Krasoń
  • 26,515
  • 16
  • 89
  • 115
  • 1
    This works... Need to install the Pipeline Utility plugin though for it to work – user301693 Jun 06 '16 at 16:12
  • 3
    This function unfortunately doesn't work with maven feature like revision ( https://maven.apache.org/maven-ci-friendly.html ) and it's going to be deprated as well as write as writeMavenPom ( https://github.com/jenkinsci/pipeline-utility-steps-plugin/pull/47/commits/eeebaa891a006c083ce10f883b7c1264533bb692 ) – Vincenzo Cerbone Dec 27 '18 at 10:02
  • 1
    Even though it is marked deprecated, I prefer it to calling `mvn help:evaluate` for this because it is faster and does not require my `pom.xml` to be valid (because it references an internal parent POM which is unknown to Maven Central - and at the place where I read the version I do not have access to the necessary `settings.xml`). – msa Mar 03 '20 at 19:49
30

In Jenkins 2.138.3 there are two different types of pipelines.

Declarative and Scripted pipelines.

"Declarative pipelines is a new extension of the pipeline DSL (it is basically a pipeline script with only one step, a pipeline step with arguments (called directives), these directives should follow a specific syntax. The point of this new format is that it is more strict and therefore should be easier for those new to pipelines, allow for graphical editing and much more. scripted pipelines is the fallback for advanced requirements."

jenkins pipeline: agent vs node?

Here is an example of a Declarative Pipeline:

pipeline {

    agent any


    environment {
    //Use Pipeline Utility Steps plugin to read information from pom.xml into env variables
    IMAGE = readMavenPom().getArtifactId()
    VERSION = readMavenPom().getVersion()
    }


    stages {

        stage('Test') {
            steps {
                echo "${VERSION}"
            }

        }

    }

}

Example of Scripted Pipeline

node('master') {

  stage('Test') {
    IMAGE = readMavenPom().getArtifactId()
    VERSION = readMavenPom().getVersion()
    echo "IMAGE: ${IMAGE}"
    echo "VERSION: ${VERSION}"
  }

}

Here are some good links:

Declarative https://github.com/jenkinsci/pipeline-examples/blob/master/declarative-examples/jenkinsfile-examples/mavenDocker.groovy

Scripted https://bulldogjob.com/articles/726-exploring-jenkins-pipelines-a-simple-delivery-flow

pitchblack408
  • 2,913
  • 4
  • 36
  • 54
10

You could always shell out and use maven to query the version, e.g. like so:

echo sh(returnStdout: true, 
        script: 'mvn org.apache.maven.plugins:maven-help-plugin:3.1.0:evaluate ' +
                 '-Dexpression=project.version -q -DforceStdout ' +
                 '--batch-mode -U -e -Dsurefire.useFile=false'
        ).trim()
# Note: Use "./mvnw" with Maven wrapper

Where

  • -Dexpression=project.version -q -DforceStdout are mandatory (see here for details), and
  • --batch-mode -U -e -Dsurefire.useFile=false are recommended for CI usage (see here for details).

This is also implemented in the shared library ces-build-lib. Here it is as convenient as

echo mvn.version

The Getting Started with Pipeline page showed yet another option. It has some disadvantages, though (see bellow and comments):

def version() {
    def matcher = readFile('pom.xml') =~ '<version>(.+?)</version>'
    matcher ? matcher[0][1] : null
}

This always matches the first <version> tag to be found in the pom.xml. This should be the version of the maven module or its parent in most cases. It could be the version of the parent, though.

schnatterer
  • 7,525
  • 7
  • 61
  • 80
  • If you depend on a parent POM, then use matcher[1][1], because matcher[0][1] matches the parent POM's version. – Kevin Seidler Sep 17 '17 at 12:16
  • This answer doesn't work in any of my POMs, because there is always more than one tag. – Jolta Oct 26 '17 at 14:38
  • @Jolta thanks for the hint. I edited the post. Does this work for you now? – schnatterer Oct 26 '17 at 16:57
  • I don't think there is any guarantee whatsoever that "the first tag to be found in the pom.xml" is the artifact version. You'd be taking a large chance on the assumption that people are putting XML tags in a certain order, which is not at all required by the format. As you stated yourself, it's quite likely to be the parent artifact version instead, which might give completely wrong results. Parsing the XML seems like the only safe way to edit a POM. – Jolta Oct 30 '17 at 08:48
  • 1
    Your first solution (calling sh(mvn... and capture the console out) worked out of the box for me. Thanks for the interesting mvn command line. Maybe you could elaborate a little bit why the used CLI options are needed. It's not obvious for me what "-U -e -Dsurefire.useFile=false" really does. – Heri Jan 28 '21 at 13:38
  • @Heri Done. Please add anything that you might find useful – schnatterer Jan 28 '21 at 14:37
  • Thanks. I think the further link explains everything. – Heri Jan 28 '21 at 16:51
3

You can try readMavenPom function that is available.

More info is here: https://jenkins.io/doc/pipeline/steps/pipeline-utility-steps/#readmavenpom-read-a-maven-project-file

  • 1
    Note that you need to install the Pipeline Utility Steps plugin to get this step. – Jesse Glick Jun 06 '16 at 12:18
  • Pipeline Utility Steps plugin says it needs Pipeline: Groovy plugin 1.11 which is almost 2 years old and way behind the current 2.30 (or so). This makes me nervous. – Lee Meador Apr 25 '17 at 20:16
2

I usually use the map to do this.

 def pomFile = readFile(pomName)
 def pom = new XmlParser().parseText(pomFile)
 def gavMap = [:]
 gavMap['groupId'] =  pom['groupId'].text().trim()
 gavMap['artifactId'] =  pom['artifactId'].text().trim()
 gavMap['version'] =  pom['version'].text().trim()
  • dont know why this was downvoted. this will allow you to get the first version and avoid parent version – red888 Nov 08 '19 at 16:42
  • In '21 this needs to be in a `script { }` block, and also requires the following approvals in /scriptApproval which introduces security problems; `method groovy.util.NodeList text method groovy.util.XmlParser parseText java.lang.String new groovy.util.XmlParser `, Nevertheless works nicely. – RookieGuy Sep 01 '21 at 18:16
1

I've just enhanced @haridsv solution without using grep:

#!/usr/bin/env
def call(String mvnBin = 'mvn',String pom_file = 'pom.xml') {

return sh(
        script: mvnBin+''' -N -f '''+pom_file+''' org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -q -DforceStdout''',
        returnStdout: true).trim()
}

You'd better not use readMavenPom since it's going to be deprecated ( see PR https://github.com/jenkinsci/pipeline-utility-steps-plugin/pull/47/commits/eeebaa891a006c083ce10f883b7c1264533bb692 ). You can copy and paste this in a file such as evaluateMavenPomVersion.groovy :-)

Vincenzo Cerbone
  • 404
  • 5
  • 12
1

jenkins pipeline add this stage. more see

    stage('info')
    {
        steps
        {
            script
            {
                def version = sh script: 'mvn help:evaluate -Dexpression=project.version -q -DforceStdout', returnStdout: true
                def artifactId = sh script: 'mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout', returnStdout: true
                
            }

        }
    }
yunussen
  • 21
  • 3
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 11 '22 at 10:29
0

Seems like readMavenPom is the most straight-forward answer, but since it required installation of an additional pipeline plugin, here is another that uses the native maven approach instead of loading xml (based on this answer)

def mvn_project_version(pom_file) {
    return sh(
        script: """mvn -N -f $pom_file org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version |
                   grep -Ev '(^\\s*\\[|Download\\w+:)'""",
        returnStdout: true).trim()
}

Because of the use of grep command this may not work on some platforms that are not posix compatible, but you can always process the output in Groovy instead of piping through grep.

haridsv
  • 9,065
  • 4
  • 62
  • 65
0

Maven is the source of truth, use it directly.

For example, to get the value of property foo.bar from pom.xml in Jenkinsfile:

def bar = sh(script: 'mvn help:evaluate -Dexpression=foo.bar -q -DforceStdout', returnStdout: true)

Also, docs of Pipeline Utility Steps suggest avoiding using readMavenPom.

Avoid using this step and writeMavenPom. It is better to use the sh step to run mvn goals.

-1

I'm just starting with Jenkisfile and I had the same problem as you. Since XmlSlurper and XmlParser are forbidden by default configuration i have crated below function to extract maven version:

String readProjectVersion(String pom) {
    //Take value of the <project> tag
    def matcher = pom.trim() =~ /(?s)<project[^>]*>(.*)<\/project>/
    def pomWithoutProject = matcher[0][1].trim()

    //remove every tag except <version>, since only project version is not encapsulated in the other tag e.g. dependency, parent, plugin
    def nonVersionMatcher = pomWithoutProject =~ /(?s)\s*(?!<version>)<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>(.*?)<\/\1>\s*/
    def versionTag = nonVersionMatcher.replaceAll("").trim()
    //Take value of the <version> tag
    def versionTagMatcher = versionTag =~ /<version>(.*)<\/version>/
    if (versionTagMatcher.matches()) {
        return versionTagMatcher[0][1]
    }
    //no version tag, version should be inherited from parent version
    def parentVersionMatcher = pomWithoutProject =~ /(?s)\s*<parent>.*<version>(.*)<\/version>.*<\/parent>\s*/
    return parentVersionMatcher[0][1]
}

I have tested this solution against files where parent was declared, version was first statement, version was last statement and with presence of version in nested tags like dependency, plugin etc.