How to download the latest artifact from Artifactory repository? - shell

I need the latest artifact (for example, a snapshot) from a repository in Artifactory. This artifact needs to be copied to a server (Linux) via a script.
What are my options? Something like Wget / SCP? And how do I get the path of the artifact?
I found some solutions which require Artifactory Pro. But I just have Artifactory, not Artifactory Pro.
Is it possible at all to download from Artifactory without the UI and not having the Pro-Version? What is the experience?
I'm on OpenSUSE 12.1 (x86_64) if that matters.

Something like the following bash script will retrieve the lastest com.company:artifact snapshot from the snapshot repo:
# Artifactory location
server=http://artifactory.company.com/artifactory
repo=snapshot
# Maven artifact location
name=artifact
artifact=com/company/$name
path=$server/$repo/$artifact
version=$(curl -s $path/maven-metadata.xml | grep latest | sed "s/.*<latest>\([^<]*\)<\/latest>.*/\1/")
build=$(curl -s $path/$version/maven-metadata.xml | grep '<value>' | head -1 | sed "s/.*<value>\([^<]*\)<\/value>.*/\1/")
jar=$name-$build.jar
url=$path/$version/$jar
# Download
echo $url
wget -q -N $url
It feels a bit dirty, yes, but it gets the job done.

Artifactory has a good extensive REST-API and almost anything that can be done in the UI (perhaps even more) can also be done using simple HTTP requests.
The feature that you mention - retrieving the latest artifact, does indeed require the Pro edition; but it can also be achieved with a bit of work on your side and a few basic scripts.
Option 1 - Search:
Perform a GAVC search on a set of group ID and artifact ID coordinates to retrieve all existing versions of that set; then you can use any version string comparison algorithm to determine the latest version.
Option 2 - the Maven way:
Artifactory generates a standard XML metadata that is to be consumed by Maven, because Maven is faced with the same problem - determining the latest version; The metadata lists all available versions of an artifact and is generated for every artifact level folder; with a simple GET request and some XML parsing, you can discover the latest version.

Using shell/unix tools
curl 'http://$artiserver/artifactory/api/storage/$repokey/$path/$version/?lastModified'
The above command responds with a JSON with two elements - "uri" and "lastModified"
Fetching the link in the uri returns another JSON which has the "downloadUri" of the artifact.
Fetch the link in the "downloadUri" and you have the latest artefact.
Using Jenkins Artifactory plugin
(Requires Pro) to resolve and download latest artifact, if Jenkins Artifactory plugin was used to publish to artifactory in another job:
Select Generic Artifactory Integration
Use Resolved Artifacts as
${repokey}:**/${component}*.jar;status=${STATUS}#${PUBLISH_BUILDJOB}#LATEST=>${targetDir}

You could also use Artifactory Query Language to get the latest artifact.
The following shell script is just an example. It uses 'items.find()' (which is available in the non-Pro version), e.g. items.find({ "repo": {"$eq":"my-repo"}, "name": {"$match" : "my-file*"}}) that searches for files that have a repository name equal to "my-repo" and match all files that start with "my-file". Then it uses the shell JSON parser ./jq to extract the latest file by sorting by the date field 'updated'. Finally it uses wget to download the artifact.
#!/bin/bash
# Artifactory settings
host="127.0.0.1"
username="downloader"
password="my-artifactory-token"
# Use Artifactory Query Language to get the latest scraper script (https://www.jfrog.com/confluence/display/RTF/Artifactory+Query+Language)
resultAsJson=$(curl -u$username:"$password" -X POST http://$host/artifactory/api/search/aql -H "content-type: text/plain" -d 'items.find({ "repo": {"$eq":"my-repo"}, "name": {"$match" : "my-file*"}})')
# Use ./jq to pars JSON
latestFile=$(echo $resultAsJson | jq -r '.results | sort_by(.updated) [-1].name')
# Download the latest scraper script
wget -N -P ./libs/ --user $username --password $password http://$host/artifactory/my-repo/$latestFile

You can use the REST-API's "Item last modified". From the docs, it retuns something like this:
GET /api/storage/libs-release-local/org/acme?lastModified
{
"uri": "http://localhost:8081/artifactory/api/storage/libs-release-local/org/acme/foo/1.0-SNAPSHOT/foo-1.0-SNAPSHOT.pom",
"lastModified": ISO8601
}
Example:
# Figure out the URL of the last item modified in a given folder/repo combination
url=$(curl \
-H 'X-JFrog-Art-Api: XXXXXXXXXXXXXXXXXXXX' \
'http://<artifactory-base-url>/api/storage/<repo>/<folder>?lastModified' | jq -r '.uri')
# Figure out the name of the downloaded file
downloaded_filename=$(echo "${url}" | sed -e 's|[^/]*/||g')
# Download the file
curl -L -O "${url}"

With recent versions of artifactory, you can query this through the api.
https://www.jfrog.com/confluence/display/RTF/Artifactory+REST+API#ArtifactoryRESTAPI-RetrieveLatestArtifact
If you have a maven artifact with 2 snapshots
name => 'com.acme.derp'
version => 0.1.0
repo name => 'foo'
snapshot 1 => derp-0.1.0-20161121.183847-3.jar
snapshot 2 => derp-0.1.0-20161122.00000-0.jar
Then the full paths would be
https://artifactory.example.com/artifactory/foo/com/acme/derp/0.1.0-SNAPSHOT/derp-0.1.0-20161121.183847-3.jar
and
https://artifactory.example.com/artifactory/foo/com/acme/derp/0.1.0-SNAPSHOT/derp-0.1.0-20161122.00000-0.jar
You would fetch the latest like so:
curl https://artifactory.example.com/artifactory/foo/com/acme/derp/0.1.0-SNAPSHOT/derp-0.1.0-SNAPSHOT.jar

The role of Artifactory is to provide files for Maven (as well as other build tools such as Ivy, Gradle or sbt). You can just use Maven together with the maven-dependency-plugin to copy the artifacts out. Here's a pom outline to start you off...
<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>A group id</groupId>
<artifactId>An artifact id</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>The group id of your artifact</groupId>
<artifactId>The artifact id</artifactId>
<version>The snapshot version</version>
<type>Whatever the type is, for example, JAR</type>
<outputDirectory>Where you want the file to go</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Just run mvn install to do the copy.

You can use the wget --user=USER --password=PASSWORD .. command, but before you can do that, you must allow artifactory to force authentication, which can be done by unchecking the "Hide Existence of Unauthorized Resources" box at Security/General tab in artifactory admin panel. Otherwise artifactory sends a 404 page and wget can not authenticate to artifactory.

For me the easiest way was to read the last versions of the project with a combination of curl, grep, sort and tail.
My format: service-(version: 1.9.23)-(buildnumber)156.tar.gz
versionToDownload=$(curl -u$user:$password 'https://$artifactory/artifactory/$project/' | grep -o 'service-[^"]*.tar.gz' | sort | tail -1)

This may be new:
https://artifactory.example.com/artifactory/repo/com/example/foo/1.0.[RELEASE]/foo-1.0.[RELEASE].tgz
For loading module foo from example.com . Keep the [RELEASE] parts verbatim. This is mentioned in the docs but it's not made abundantly clear that you can actually put [RELEASE] into the URL (as opposed to a substitution pattern for the developer).

If you want to download the latest jar between 2 repositores, you can use this solution. I actually use it within my Jenkins pipeline, it works perfectly. Let's say you have a plugins-release-local and plugins-snapshot-local and you want to download the latest jar between these. Your shell script should look like this
NOTE : I use jfrog cli and it's configured with my Artifactory server.
Use case : Shell script
# your repo, you can change it then or pass an argument to the script
# repo = $1 this get the first arg passed to the script
repo=plugins-snapshot-local
# change this by your artifact path, or pass an argument $2
artifact=kshuttle/ifrs16
path=$repo/$artifact
echo $path
~/jfrog rt download --flat $path/maven-metadata.xml version/
version=$(cat version/maven-metadata.xml | grep latest | sed "s/.*<latest>\([^<]*\)<\/latest>.*/\1/")
echo "VERSION $version"
~/jfrog rt download --flat $path/$version/maven-metadata.xml build/
build=$(cat build/maven-metadata.xml | grep '<value>' | head -1 | sed "s/.*<value>\([^<]*\)<\/value>.*/\1/")
echo "BUILD $build"
# change this by your app name, or pass an argument $3
name=ifrs16
jar=$name-$build.jar
url=$path/$version/$jar
# Download
echo $url
~/jfrog rt download --flat $url
Use case : Jenkins Pipeline
def getLatestArtifact(repo, pkg, appName, configDir){
sh """
~/jfrog rt download --flat $repo/$pkg/maven-metadata.xml $configDir/version/
version=\$(cat $configDir/version/maven-metadata.xml | grep latest | sed "s/.*<latest>\\([^<]*\\)<\\/latest>.*/\\1/")
echo "VERSION \$version"
~/jfrog rt download --flat $repo/$pkg/\$version/maven-metadata.xml $configDir/build/
build=\$(cat $configDir/build/maven-metadata.xml | grep '<value>' | head -1 | sed "s/.*<value>\\([^<]*\\)<\\/value>.*/\\1/")
echo "BUILD \$build"
jar=$appName-\$build.jar
url=$repo/$pkg/\$version/\$jar
# Download
echo \$url
~/jfrog rt download --flat \$url
"""
}
def clearDir(dir){
sh """
rm -rf $dir/*
"""
}
node('mynode'){
stage('mysstage'){
def repos = ["plugins-snapshot-local","plugins-release-local"]
for (String repo in repos) {
getLatestArtifact(repo,"kshuttle/ifrs16","ifrs16","myConfigDir/")
}
//optional
clearDir("myConfigDir/")
}
}
This helps alot when you want to get the latest package between 1 or more repos. Hope it helps u too!
For more Jenkins scripted pipelines info, visit Jenkins docs.

With awk:
curl -sS http://the_repo/com/stackoverflow/the_artifact/maven-metadata.xml | grep latest | awk -F'<latest>' '{print $2}' | awk -F'</latest>' '{print $1}'
With sed:
curl -sS http://the_repo/com/stackoverflow/the_artifact/maven-metadata.xml | grep latest | sed 's:<latest>::' | sed 's:</latest>::'

In case you need to download an artifact in a Dockerfile, instead of using wget or curl or the likes you can simply use the 'ADD' directive:
ADD ${ARTIFACT_URL} /opt/app/app.jar
Of course, the tricky part is determining the ARTIFACT_URL, but there's enough about that in all the other answers.
However, Docker best practises strongly discourage using ADD for this purpose and recommend using wget or curl.

No one mention xmllint aka proper xml parser, install it with:
sudo apt-get update -qq
sudo apt-get install -y libxml2-utils
use it:
ART_URL="https://artifactory.internal.babycorp.com/artifactory/api-snapshot/com/babycorp/baby-app/CD-684-my-branch-SNAPSHOT"
ART_VERSION=`curl -s $ART_URL/maven-metadata.xml | xmllint --xpath '//snapshotVersion[1]/value/text()' -`
and finally:
curl -s -o baby-app.jar ${ART_URL}/baby-app-${ART_VERSION}.jar
or
wget ${ART_URL}/baby-app-${ART_VERSION}.jar
to keep the filename

If using the JFrog CLI, this can be done in a single line as follows:
jf rt dl "path/to/artifacts/-/" --sort-by=created --sort-order=desc --limit=1
(The above is for v2 but think the v1 command should be the same but substituting jf with jfrog.)

Related

Exporting the variables in Gitlab to properties file

I am using a Spring boot based application and a pipeline on gitlab. I need to export the tag and version to my variables in the properties file in the repo.
In before-script i need to take the value of tag from gitlab-ci.yml and put it in my config.properties file in the project.
export APP_VERSION=${CI_COMMIT_TAG:-$(git describe --tags)}
need to pass the values in app version to
version.major=1
version.minor=0
version.patch=0
any other approaches are welcome to do this.
End Goal to pass the version to application configuration before the script runs.
Get major, minor and patch by using sed and git describe
Let's start with getting major,minor and patch from the latest tag. You can use sed to parse the tag. I won't go into detail about the syntax, but this should help you get everything you need.
//fetch latest tag
> VERSION=$(git describe --tags $(git rev-list --tags --max-count=1))
> echo $VERSION
v0.2.2
// remove prefix and suffix from tag
> VERSION=$(echo "$VERSION" | sed "s/^.*[^0-9]\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/")
> echo $VERSION
0.2.2
// get major, minor, patch
> MAJOR_VERSION=$(echo "$VERSION" | sed "s/^\([0-9]*\).*/\1/")
> echo $MAJOR_VERSION
0
> MINOR_VERSION=$(echo "$VERSION" | sed "s/[0-9]*\.\([0-9]*\).*/\1/")
> echo $MINOR_VERSION
2
> PATCH_VERSION=$(echo "$VERSION" | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/")
> echo $PATCH_VERSION
2
mavens automatic property expansion
As you are using spring boot, you might consider mavens automatic property expansion which allows you to automatically override properties (https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.properties-and-configuration) .
Add this to the build section of your pom.xml
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>#</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
...
</plugins>
</build>
This tells the maven-resources plugin to automatically update values in files in your src/main/resources directory that are delimited by #. So now your change your property file that it looks like this:
major.version=#application.version#
minor.version=#minor.version#
patch.version=#patch.version#
Finally add your desired properties to your pom.xml:
<properties>
...
<version.major>1</version.major>
<version.minor>0</version.minor>
<version.patch>0</version.patch>
</properties>
To dynamically update the version you now only need to pass the variables to maven / JVM when building your application.
build:
stage: build
script:
- mvn clean package -Dversion.major=$MAJOR_VERSION -Dversion.minor=$MINOR_VERSION -Dversion.patch=$PATCH_VERSION
artifacts:
paths:
- pom.xml
- target/*.jar
except:
- tags
optional: use sed to write to properties file
If you are not using maven, you could also directly write your app version to the config file via sed
build:
stage: build
before_script:
- sed -i "s/^\(major\.version\s*=\s*\).*\$/\1$MAJOR_VERSION/" src/main/resources/config.properties
- sed -i "s/^\(minor\.version\s*=\s*\).*\$/\1$MINOR_VERSION/" src/main/resources/config.properties
- sed -i "s/^\(patch\.version\s*=\s*\).*\$/\1$PATCH_VERSION/" src/main/resources/config.properties
script:
- ...
artifacts:
paths:
- pom.xml
- target/*.jar
except:
- tags

How to extract the artifactId from all pom.xml files using in a multimodule project (using shell script)?

I need to extract the artifactId from all the poms in a multimodule project.
The project structure is similar to this but the structure can be different (It's depends of the project):
project
|------->pom.xml
|------->subproject1
|------->pom.xml
|------->folder1
|------->pom.xml
|------->subproject2
|------->pom.xml
|------->folder2
|------->pom.xml
|------->subproject3
|------->pom.xml
|------->folder3
|------->pom.xml
|------->subproject4
|------->pom.xml
|------->folder4
|------->pom.xml
|------->subproject5
|------->pom.xml
|------->folder5
|------->pom.xml
I'm using this command for extract the info in single module projects:
MVN_ARTIFACTID=$(mvn -q \
-Dexec.executable="echo" \
-Dexec.args='${project.artifactId}' \
--non-recursive \
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec)
Then I use this variable for other actions in a Bamboo Plan.
How I can do similar tasks with multimodule projects? I need to extract the artifactId of all poms in variables.
I know about this post but It's not the same problem:
How to extract the GAV from a pom.xml file in a shell script
Thanks.
Just remove non recursive option :
MVN_ARTIFACTID=$(mvn -q -Dexec.executable="echo" -Dexec.args='${project.artifactId} ##' org.codehaus.mojo:exec-maven-plugin:1.3.1:exec)
echo $MVN_ARTIFACTID| sed 's/##/\n/g'
Added ## as a small hack so that second line can give artifactId names in more readable format. Else all modules will be echoed in same line.
Module-1
Module-2
Module-3
This should do it (adapted from the answer referenced in the question):
grep -v '\[' <( find . -type f -name 'pom.xml' -exec mvn -f{} help:evaluate -Dexpression="project.artifactId" \; )
Note that the other answer didn't work for me in Maven 3.5.4.

The target deployment path '...' does not match the POM's expected path prefix

I am trying to upload a pom.xml file to a Maven repository hosted on an Artifactory server. The <project> section of the pom.xml looks like this:
<groupId>com.x.y.z</groupId>
<artifactId>common</artifactId>
<version>2.3.0-RELEASE</version>
<packaging>jar</packaging>
I am using the Artifactory plugin for Jenkins in a Pipeline script and here is the uploadSpec
{
"files": [
{
"pattern": "target/common-2.3.0-RELEASE.jar",
"target": "REPOSITORY/com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.jar"
},
{
"pattern": "pom.xml",
"target": "REPOSITORY/com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.pom"
}
]
}
When I now try to upload the artifact, I'm getting the following error message:
java.io.IOException: Failed to deploy file.
Status code: 409
Response message: Artifactory returned the following errors:
The target deployment path 'com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.pom'
does not match the POM's expected path prefix 'com/x/y/z/common/2.2.7'.
Please verify your POM content for correctness and make sure the source path is a valid Maven repository root path. Status code: 409
Before I upload the RELEASE, I upload a SNAPSHOT which (in this case) had the version 2.2.7-SNAPSHOT. After that I bump the version to 2.3.0, re-build the project with mvn clean install and then start another upload to Artifactory. Somehow Artifactory still seems to expect the "old" version, when I try to upload the new version.
Edit
When I upload the file with curl, everything works as expected:
curl -user:password-T pom.xml \
"http://DOMAIN/artifactory/REPOSITORY/com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.pom"
So it seems like this is related to the Jenkins Artifactory Plugin.
You upload your pom file to an incorrect location. You use
REPOSITORY/com/x/y/z/common-2.3.0-RELEASE.pom
as a path, when the path should be
REPOSITORY/com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.pom
Note version-named directory that is missing.
The good news are you don't even need to bother with it. When you use our Artifactory.newMavenBuild for Maven builds, we'll take care of the correct deployment. See the example.
Can you try the below code in pipeline script?
{
"pattern": "pom.xml",
"target": "REPOSITORY/com/x/y/z/common/2.3.0-RELEASE/common-2.3.0-RELEASE.pom"
}
or if it doesn't work you can utilize maven deploy in pipeline script using
def mvnHome = tool mvnName
sh "$mvnHome/bin/mvn deploy -deploy-file -Durl=file:///C:/m2-repo \
-DrepositoryId=some.id \
-Dfile=path-to-your-artifact-jar \
-DpomFile=path-to-your-pom.xml

How do you extract a version number from nuspec into TeamCity?

How do you extract a version number from nuspec into TeamCity?
We have a csproj file with this corresponding nuspec node:
1.3.2
In TeamCity I'd like to do a build of this project and create a NuGet package with version 1.3.2.%build.counter%. For example, 1.3.2.322. It is important the version number is coming from a file within source control (the NuSpec) and I don't want to duplicate this as a variable in TeamCity. Is there a way to extract 1.3.2 from the NuSpec file and use it as a TeamCity variable?
This approach works with version 10+ of TeamCity and gets around issues with Select-Xml and namespaces.
$Version = ([xml](Get-Content .\MyProject.nuspec)).package.metadata.version
$BuildCounter = %build.counter%
echo "##teamcity[setParameter name='PackageVersion' value='$Version.$BuildCounter']"
A couple of approaches I can think of spring to mind, both using TeamCity service messages:
Use a PowerShell build step, something like this (apologies my PS isn't great):
$buildcounter = %build.counter%
$node = Select-Xml -XPath "/package/metadata/version" -Path /path/to/nuspec.nuspec
$version = $node.Node.InnerText
Write-Output "##teamcity[buildNumber '$version.$buildcounter']"
Or, similarly, bootstrap a tool like XmlStarlet:
$buildcounter = "1231"
$version = xml sel -T -t -v "/package/metadata/version" Package.nuspec
Write-Output "##teamcity[buildNumber '$version.$buildcounter']"
This step would need to be added before any other step requiring the build number.

Getting Project Version from Maven POM in Jenkins

Is there any way a Jenkins build can be aware of the Maven version number of a project after processing the POM?
I've got some projects where versioning is controlled by Maven, and in a post-build job we'd like to create a Debian package and call some shell scripts. What I need is for the version number that Maven used to be available as a Jenkins environment variable so I can pass it to post-build actions.
To be clear, I'm not needing to know how to get Jenkins to pass a version number to Maven; instead I want Maven to pass a version number to Jenkins!
You can use the ${POM_VERSION} variable, which was introduced with https://issues.jenkins-ci.org/browse/JENKINS-18272
After a lot of digging around (I never realised how poorly-documented Jenkins is!) I found a quite trivial solution.
Install the Groovy plugin
Add a Post Step to your Maven build of type Execute **system** Groovy script
Paste in the following snippet of Groovy:
Script:
import hudson.model.*;
import hudson.util.*;
def thr = Thread.currentThread();
def currentBuild = thr?.executable;
def mavenVer = currentBuild.getParent().getModules().toArray()[0].getVersion();
def newParamAction = new hudson.model.ParametersAction(new hudson.model.StringParameterValue("MAVEN_VERSION", mavenVer));
currentBuild.addAction(newParamAction);
The build environment variable called MAVEN_VERSION will now be available for substitution into other post-build steps in the usual manner (${MAVEN_VERSION}). I'm using it for Git tagging amongst other things.
As other answers already pointed out, if you are using the Maven project type, you have access to the $POM_VERSION variable. But if you are not, you can use this sequence of steps (ugly but reliable). Doing it this way relies on the same version of maven to determine the pom version (while handling complex parent/child pom inheritance where <version> may not even be present for the child).
Maven step with this goal:
org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version -l version.log
Shell step: (You may need to adjust the path to version.log depending on your hierarchy)
echo "POM_VERSION=$(grep -v '\[' version.log)" > props.properties
Inject Environment Variables step (Environment Injector Plugin):
Properties File Path: props.properties
Now you can use $POM_VERSION as if this were a Maven project.
What this does: Uses maven to print out the version together with a mess of output, then greps out the mess of output leaving just the version, writes it to a file using properties file format and then injects it into the build environment. The reason this is better than a one-liner like mvn ..... | grep -v '\[' is that using a Maven step does not make assumptions about the installed maven versions and will be handled by the same auto-installation as any other maven steps.
I used Pipeline Utility Steps plugin in a declarative pipeline job to get Maven version. In the example below I use script variable instead of environment variable, because that can be modified and passed between stages.
def TAG_SELECTOR = "UNINTIALIZED"
pipeline {
agent any
stages {
stage('Build') {
steps {
sh "mvn --batch-mode -U deploy"
script {
TAG_SELECTOR = readMavenPom().getVersion()
}
echo("TAG_SELECTOR=${TAG_SELECTOR}")
}
}
}
}
Note: You must approve the getVersion() method after creating the job in Manage jenkins > In-process Script Approval.
See also:
readMavenPom documentation
We used the Groovy Postbuild Plugin.
String regex = '.*\\[INFO\\] Building .+ (.+)';
def matcher = manager.getLogMatcher(regex);
if (matcher == null) {
version = null;
} else {
version = matcher.group(1);
}
Adding this to Jenkins for use later is a bit tricky. Give this a shot, although I remember this causing us some headaches. (Sorry, we did this a long time ago)
def addBuildParameter(String key, String value) {
manager.build.addAction(new hudson.model.ParametersAction(new hudson.model.StringParameterValue(key,value)));
}
Had the same need and solved as suggested with Groovy parsing the pom.
import jenkins.util.*;
import jenkins.model.*;
def thr = Thread.currentThread();
def currentBuild = thr?.executable;
def workspace = currentBuild.getModuleRoot().absolutize().toString();
def project = new XmlSlurper().parse(new File("$workspace/pom.xml"))
def param = new hudson.model.StringParameterValue("project.version", project.version.toString())
currentBuild.addAction(new hudson.model.ParametersAction(param));
Add this script as a post step of type "Execute system Groovy script" (so it's not needed to install Groovy) and paste the code in the "Groovy command".
Execute the Maven Plugin "exec-maven-plugin" in "Execute Shell" as a "Conditional step" worked for me:
mvn -q -Dexec.executable="echo" -Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec
Integrate in Jenkins:
-> "Add post-build step"
-> "Conditional steps (single or multiple)"
-> "Execute Shell:"
export MY_POM_VERSION=`mvn -q -Dexec.executable="echo"
-Dexec.args='${projects.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.3.1:exec` && [[
"${MY_POM_VERSION}" == "THE_VERSION_TO_BE_MATCHED" ]] && echo
"CONDITION_IS_MET"
-> "Steps to run if condition is met"
-> Add any build step you need
Notes:
THE_VERSION_TO_BE_MATCHED has to exchanged with your version
'&& echo "CONDITION_IS_MET"' is only for debugging purposes. For the same purpose you can add a '&& echo "MY_POM_VERSION=${MY_POM_VERSION}"' after the mvn command in order to understand what's going on.
This approach is more reliable than a "grep" and it could be an alternative if the Jenkins Ruby Plugin is not installed.
You could also do :
MAVEN_VERSION=`grep A -2 -B 2 "<your_project_name>" pom.xml | grep version | cut -d\> -f 2 | cut -d\< -f 1`-commit-"`echo $GIT_COMMIT`"
Explanation: assuming that you have your project name within a line or two above/below version like a normal pom:
<groupId>org.apache.bigtop</groupId>
<artifactId>bigpetstore</artifactId>
<version>1.0-SNAPSHOT</version>
Then you can easily grep for the artifactId, use the "before/after" grep actions to suck in the version with it, and then grep the version out and use the simple unix "cut" command to splice out the content between "version" tags.
I like the Jenkins-groovy integration, but this is alot easier and will work even on a build server which you dont have control over (i.e. because bash is universal).
Solution:
POM_VERSION=$( \
xmlstarlet sel \
-N x='http://maven.apache.org/POM/4.0.0' \
-t \
-v '//x:project/x:version/text()' \
pom.xml \
)
Explanation:
You can do this in a one-liner using a command-line XPath tool, such as those mentioned at "How to execute XPath one-liners from shell?". I chose XMLStarlet, but they all have similar syntax.
When parsing a POM, you have to account for namespaces. The docs here helped me figure this out.
In order to get the text for an element in XPath, you use the text() function as explained at XPath: select text node.
My POM looks like this:
<?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.foo.bar</groupId>
<artifactId>foobar</artifactId>
<version>1.0.6-SNAPSHOT</version>
<packaging>jar</packaging>
The downside here is that if the namespace changes, you have to change the command.
Using 'Execute System Groovy Script' as follows:
import jenkins.util.*;
import jenkins.model.*;
def thr = Thread.currentThread();
def currentBuild = thr?.executable;
def projectManager = build.getProject()
def file = projectManager.getWorkspace().child("pom.xml");
def project = new XmlSlurper().parseText(file.readToString())
def param = new hudson.model.StringParameterValue("currentVersion", project.version.toString())
currentBuild.addAction(new hudson.model.ParametersAction(param));
By using Execute System Groovy script you have direct access to the build, from which you can get the project and thus the "child" file in this case pom.xml.
You won't have to create a new file and as you can see it offers very powerful access to every file within the workspace.
Based on #Akom`s answer the pre steps to have POM_VERSION are:
"Inject environment variables" with property
file your_property_file. Note if you select "Inject environment variables to the build process" the file needs to exist in the jenkins workspace.
run in a pre step execute shell the follwing
bash script.
Script
mvn org.apache.maven.plugins:maven-help-plugin:evaluate -Dexpression=project.version -l project_version
# grep for the version pattern rather than not mentioning '\['
echo "POM_VERSION=$(grep -E '^[0-9.]+(-SNAPSHOT)?$' project_version)" > your_property_file

Resources