Skip to end of metadata
Go to start of metadata

Overview

Pipeline jobs simplify building continuous delivery workflows with Jenkins by creating a script that defines the steps of your build. For those not familiar with Jenkins Pipeline, please refer to the Pipeline Tutorial or the Getting Started With Pipeline documentation.

The Jenkins Artifactory Plugin supports Artifactory operations pipeline APIs. You have the added option of downloading dependencies, uploading artifacts, and publishing build-info to Artifactory from a pipeline script.

This page describes how to use scripted pipeline syntax with Artifactory.

Declarative syntax is also supported. Read more about it here.

Examples

The Jenkins Pipeline Examples can help get you started creating your pipeline jobs with Artifactory.

Page contents


Creating an Artifactory Server Instance

To upload or download files to and from your Artifactory server, you need to create an Artifactory server instance in your Pipeline script.

If your Artifactory server is already defined in Jenkins, you only need its server ID which can be obtained  under Manage | Configure System.

Then, to create your Artifactory server instance, add the following line to your script:

def server = Artifactory.server 'my-server-id'

If your Artifactory is not defined in Jenkins you can still create it as follows:

def server = Artifactory.newServer url: 'artifactory-url', username: 'username', password: 'password'

You can also user Jenkins Credential ID instead of username and password:

def server = Artifactory.newServer url: 'artifactory-url', credentialsId: 'ccrreeddeennttiiaall'

You can modify the server object using the following methods:

// If Jenkins is configured to use an http proxy, you can bypass the proxy when using this Artifactory server:  
server.bypassProxy = true
// If you're using username and password:
server.username = 'new-user-name'
server.password = 'new-password'
// If you're using Credentials ID:
server.credentialsId = 'ccrreeddeennttiiaall'
// Configure the connection timeout (in seconds).
// The default value (if not configured) is 300 seconds:  
server.connection.timeout = 300

Use variables

We recommend using variables rather than plain text to specify the Artifactory server details.


Uploading and Downloading Files

To upload or download files you first need to create a spec which is a JSON file that specifies which files should be uploaded or downloaded and the target path.
For example:
def downloadSpec = """{
 "files": [
  {
      "pattern": "bazinga-repo/*.zip",
      "target": "bazinga/"
    }
 ]
}"""
The above spec specifies that all ZIP files in the bazinga-repo Artifactory repository should be downloaded into the bazinga directory on your Jenkins agent file system.

"files" is an array

Since the "files" element is an array, you can specify several patterns and corresponding targets in a single download spec.
To download the files, add the following line to your script:
server.download spec: downloadSpec 
Uploading files is very similar. The following example uploads all ZIP files that include froggy in their names into the froggy-files folder in the bazinga-repo Artifactory repository.
def uploadSpec = """{
  "files": [
    {
      "pattern": "bazinga/*froggy*.zip",
      "target": "bazinga-repo/froggy-files/"
    }
 ]
}"""
server.upload spec: uploadSpec 
You can read about using File Specs for downloading and uploading files here.
If you'd like the build to fail, in case no files are uploaded or downloaded, add the failNoOp argume to the upload or download methods as follows:
server.download spec: downloadSpec, failNoOp: true
server.upload spec: uploadSpec, failNoOp: true


Setting and Deleting Properties on Files in Artifactory

When uploading files to Artifactory using the server.upload method, you have the option of setting properties on the files. The properties are defined as part of the File Spec sent to the method. These properties can be later used to filter and download those files.

In some cases, you may want want to set properties on files that are already in Artifactory. Hereroperties to be set are sent outside the File Spec. To define which file to set the properties, a File Spec is used. Here's an example:

def setPropsSpec = """{
 "files": [
  {
       "pattern": "my-froggy-local-repo/dir/*.zip",
       "props": "filter-by-this-prop=yes"
    }
 ]
}"""


server.setProps spec: setPropsSpec, props: “p1=v1;p2=v2”

In the above example, the p1 and p2 properties will be set with the v1 and v2 values respectively. The properties will be set on all the zip files inside the dir directory under the my-froggy-local-repo repository. Only files which already have the filter-by-this-prop property set to yes will be affected.

The failNoOp argument is optional. Setting it to true will cause the job to fail, if no properties have been set. Here's how you use it:

server.setProps spec: setPropsSpec, props: “p1=v1;p2=v2”, failNoOp: true

The server.deleteProps method can be used to delete properties from files in Artifactory, Like the server.setProps method, it also uses a File Spec. The only difference between the two methods, is that for deleteProps, we specify only the names of the properties to delete. The names are comma separated. The properties values should not be specified. The failNoOp argument is optional. Setting it to true will cause the job to fail, if no properties have been deleted.

Here's an example:

server.deleteProps spec: deletePropsSpec, props: “p1,p2,p3”, failNoOp: true



Publishing Build-Info to Artifactory

Both the download and upload methods return a build-info object which can be published to Artifactory as shown in the following examples:

def buildInfo1 = server.download downloadSpec 
def buildInfo2 = server.upload uploadSpec 
buildInfo1.append buildInfo2 
server.publishBuildInfo buildInfo1
def buildInfo = Artifactory.newBuildInfo()
server.download spec: downloadSpec, buildInfo: buildInfo
server.upload spec: uploadSpec, buildInfo: buildInfo
server.publishBuildInfo buildInfo 

Getting Dependencies and Artifacts from the Build-Info

The build-info instance stores the build-info locally. It can be later published to Artifactory. As shown above, the server.upload method adds artifacts to the build-info and the server.download method adds dependencies to the build-info.

You have the option of getting the list of dependencies and artifacts stored in the build-info instance. You can do this at any time, before or after the build-info is published to Artifactory. In the following example, ww first check if there are any dependencies stored in the build info, and if there are, we access the properties of one of the dependencies. We then do the same for artifacts.

if (buildInfo.getDependencies().size() > 0) {
	def localPath = buildInfo.getDependencies()[0].getLocalPath()
	def remotePath = buildInfo.getDependencies()[0].getRemotePath()
	def md5 = buildInfo.getDependencies()[0].getMd5()
	def sha1 = buildInfo.getDependencies()[0].getSha1()
}

if (buildInfo.getArtifacts().size() > 0) {
	def localPath = buildInfo.getArtifacts()[0].getLocalPath()
	def remotePath = buildInfo.getArtifacts()[0].getRemotePath()
	def md5 = buildInfo.getArtifacts()[0].getMd5()
	def sha1 = buildInfo.getArtifacts()[0].getSha1()
}

Modifying the Default Build Name and Build Number

You can modify the default build name and build number set by Jenkins. Here's how you do it:

def buildInfo = Artifactory.newBuildInfo()
buildInfo.name = 'super-frog'
buildInfo.number = 'v1.2.3'
server.publishBuildInfo buildInfo

If you're setting the build name or number as shown above, it is important to do so before you're using this buildInfo instance for uploading files. 

Here's the reason for this: The server.upload method also tags the uploaded files with the build name and build number (using the build.name and build.number properties). Setting a new build name or number after the files are already uploaded to Artifactory, will not update the properties attached to the files.


Capturing Environment Variables

To set the Build-Info object to automatically capture environment variables while downloading and uploading files, add the following to your script:

def buildInfo = Artifactory.newBuildInfo()
buildInfo.env.capture = true

By default, environment variables names which include "password", "psw", "secret", or "key" (case insensitive) are excluded and will not be published to Artifactory.

You can add more include/exclude patterns with wildcards as follows:

def buildInfo = Artifactory.newBuildInfo()
buildInfo.env.filter.addInclude("*a*")
buildInfo.env.filter.addExclude("DONT_COLLECT*")

Here's how you reset to the include/exclude patterns default values:

buildInfo.env.filter.reset()

You can also completely clear the include/exclude patterns:

buildInfo.env.filter.clear()

To collect environment variables at any point in the script, use:

buildInfo.env.collect()

You can get the value of an environment variable collected as follows:

value = buildInfo.env.vars['env-var-name']


Triggering Build Retention

To trigger build retention when publishing build-info to Artifactory, use the following method:

buildInfo.retention maxBuilds: 10
buildInfo.retention maxDays: 7

To have the build retention also delete the build artifacts, add the deleteBuildArtifacts with true value as shown below:

buildInfo.retention maxBuilds: 10, maxDays: 7, doNotDiscardBuilds: ["3", "4"], deleteBuildArtifacts: true

It is possible to trigger an asynchronous build retention. To do this, add the async argument with true as shown below:

buildInfo.retention maxBuilds: 10, deleteBuildArtifacts: true, async: true



Collecting Build Issues

The build-info can include the issues which were handled as part of the build. The list of issues is automatically collected by Jenkins from the git commit messages. This requires the project developers to use a consistent commit message format, which includes the issue ID and issue summary, for example:
HAP-1364 - Replace tabs with spaces
The list of issues can be then viewed in the Builds UI in Artifactory, along with a link to the issue in the issues tracking system.
The information required for collecting the issues is provided through a JSON configuration. This configuration can be provided as a file or as a JSON string.
Here's an example for issues collection configuration.

{
    "version": 1,
    "issues": {
        "trackerName": "JIRA",
        "regexp": "(.+-[0-9]+)\\s-\\s(.+)",
        "keyGroupIndex": 1,
        "summaryGroupIndex": 2,
        "trackerUrl": "http://my-jira.com/issues",
        "aggregate": "true",
        "aggregationStatus": "RELEASED"
    }
}

Configuration file properties:

Property name

Description

VersionThe schema version is intended for internal use. Do not change!
trackerNameThe name (type) of the issue tracking system. For example, JIRA. This property can take any value.
trackerUrlThe issue tracking URL. This value is used for constructing a direct link to the issues in the Artifactory build UI.
keyGroupIndex

The capturing group index in the regular expression used for retrieving the issue key. In the example above, setting the index to "1" retrieves HAP-1364 from this commit message:

HAP-1364 - Replace tabs with spaces

summaryGroupIndex

The capturing group index in the regular expression for retrieving the issue summary. In the example above, setting the index to "2" retrieves the sample issue from this commit message:

HAP-1364 - Replace tabs with spaces

aggregate

Set to true, if you wish all builds to include issues from previous builds.

aggregationStatus

If aggregate is set to true, this property indicates how far in time should the issues be aggregated. In the above example, issues will be aggregated from previous builds, until a build with a RELEASE status is found. Build statuses are set when a build is promoted using the jfrog rt build-promote command.
regexp

A regular expression used for matching the git commit messages. The expression should include two capturing groups - for the issue key (ID) and the issue summary. In the example above, the regular expression matches the commit messages as displayed in the following example:

HAP-1364 - Replace tabs with spaces

Here's how you set issues collection in the pipeline script.

server = Artifactory.server 'my-server-id'

config = """{
    "version": 1,
    "issues": {
        "trackerName": "JIRA",
        "regexp": "(.+-[0-9]+)\\s-\\s(.+)",
        "keyGroupIndex": 1,
        "summaryGroupIndex": 2,
        "trackerUrl": "http://my-jira.com/issues",
        "aggregate": "true",
        "aggregationStatus": "RELEASED"
    }
}"""

buildInfo.issues.collect(server, config)
server.publishBuildInfo buildInfo

To help you get started, we recommend using the Github Examples.



Promoting Builds in Artifactory

To promote a build between repositories in Artifactory, define the promotion parameters in a promotionConfig object and promote that. For example:

    def promotionConfig = [
        // Mandatory parameters
        'targetRepo'         : 'libs-prod-ready-local',

        // Optional parameters

		// The build name and build number to promote. If not specified, the Jenkins job's build name and build number are used
        'buildName'          : buildInfo.name,
        'buildNumber'        : buildInfo.number,
		// Comment and Status to be displayed in the Build History tab in Artifactory
        'comment'            : 'this is the promotion comment',
        'status'             : 'Released',
		// Specifies the source repository for build artifacts. 
        'sourceRepo'         : 'libs-staging-local',
		// Indicates whether to promote the build dependencies, in addition to the artifacts. False by default
        'includeDependencies': true,
		// Indicates whether to copy the files. Move is the default
        'copy'               : true,
        // Indicates whether to fail the promotion process in case of failing to move or copy one of the files. False by default.
        'failFast'           : true
    ]

    // Promote build
    server.promote promotionConfig

Allowing Interactive Promotion for Published Builds

The 'Promoting Builds in Artifactory' section in this article describes how your Pipeline script can promote builds in Artifactory. In some cases however, you'd like the build promotion to be performed after the build finished. You can configure your Pipeline job to expose some or all the builds it publishes to Artifactory, so that they can be later promoted interactively using a GUI. Here's how the Interactive Promotions looks like:


When the build finishes, the promotion window will be accessible by clicking on the promotion icon, next to the build run.

Here's how you do this.

First you need to create a 'promotionConfig' instance, the same way it is shown in the 'Promoting Builds in Artifactory' section.

Next, you can use it, to expose a build for interactive promotion as follows:

Artifactory.addInteractivePromotion server: server, promotionConfig: promotionConfig, displayName: "Promote me please"

You can add as many builds as you like, by using the method multiple times. All the builds added will be displayed in the promotion window. 

The 'addInteractivePromotion' methods expects the following arguments:

  1. "server" is the Artifactory on which the build promotions is done. You can create the server instance as described in the beginning of this article. 
  2. "promotionConfig" includes the promotion details. The "Promoting Builds in Artifactory" section describes how to create a promotionConfig instance.
  3.  "displayName" is an optional argument. If you add it, the promotion window will display it instead of the build name and number.

Maven Builds with Artifactory

Maven builds can resolve dependencies, deploy artifacts and publish build-info to Artifactory. To run Maven builds with Artifactory from your Pipeline script, you first need to create an Artifactory server instance, as described at the beginning of this article.
Here's an example:

    def server = Artifactory.server 'my-server-id'

The next step is to create an Artifactory Maven Build instance:

    def rtMaven = Artifactory.newMavenBuild()

Now let's define where the Maven build should download its dependencies from. Let's say you want the release dependencies to be resolved from the 'libs-release' repository and the snapshot dependencies from the 'libs-snapshot' repository. Both repositories are located on the Artifactory server instance you defined above. Here's how you define this, using the Artifactory Maven Build instance we created:

    rtMaven.resolver server: server, releaseRepo: 'libs-release', snapshotRepo: 'libs-snapshot'

Now let's define where our build artifacts should be deployed to. Once again, we define the Artifactory server and repositories on the 'rtMaven' instance:

    rtMaven.deployer server: server, releaseRepo: 'libs-release-local', snapshotRepo: 'libs-snapshot-local'

By default, all the build artifacts are deployed to Artifactory. In case you want to deploy only some artifacts, you can filter them based on their names, using the 'addInclude' method. In the following example, we are deploying only artifacts with names that start with 'frog'

    rtMaven.deployer.artifactDeploymentPatterns.addInclude("frog*")

You can also exclude artifacts from being deployed. In the following example, we are deploying all artifacts, except for those that are zip files:

    rtMaven.deployer.artifactDeploymentPatterns.addExclude("*.zip")

And to make things more interesting, you can combine both methods. For example, to deploy all artifacts with names that start with 'frog', but are not zip files, do the following:

    rtMaven.deployer.artifactDeploymentPatterns.addInclude("frog*").addExclude("*.zip")

If you'd like to add custom properties to the deployed artifacts, you can do that as follows:

    rtMaven.deployer.addProperty("status", "in-qa").addProperty("compatibility", "1", "2", "3")

In some cases, you want to disable artifacts deployment to Artifactory or make the deployment conditional. Here's how you do it:

    rtMaven.deployer.deployArtifacts = false

To select a Maven installation for our build, we should define a Maven Tool through Jenkins Manage, and then, set the tool name as follows:

    rtMaven.tool = 'maven tool name'

Instead of using rtMaven.tool, you can set the path to the Maven installation directory using the MAVEN_HOME environment variable as follows:

env.MAVEN_HOME = '/tools/apache-maven-3.3.9'

Here's how you define Maven options for your build:

    rtMaven.opts = '-Xms1024m -Xmx4096m'

In case you'd like Maven to use a different JDK than your build agent's default, no problem.
Simply set the JAVA_HOME environment variable to the desired JDK path (the path to the directory above the bin directory, which includes the java executable).
Here's you do it:

    env.JAVA_HOME = 'full/path/to/JDK'


OK, we're ready to run our build. Here's how we define the pom file path (relative to the workspace) and the Maven goals. The deployment to Artifactory is performed during the 'install' phase:

    def buildInfo = rtMaven.run pom: 'maven-example/pom.xml', goals: 'clean install'

The above method runs the Maven build. Notice that the method returns a buildInfo instance, which can be later published to Artifactory.

In some cases though, you'd like to pass an existing buildInfo instance to be used by the build. This can come in handy is when you want to set custom build name or build number on the build-info instance, or when you'd like to aggregate multiple builds into the same build-info instance. Here's how you pass an existing build-info instance to the rtMaven.run method:


    rtMaven.run pom: 'maven-example/pom.xml', goals: 'clean install', buildInfo: existingBuildInfo


By default, the build artifacts will be deployed to Artifactory, unless rtMaven.deployer.deployArtifacts property was set to false. This can come in handy in two cases:

  1. You do not wish to publish the artifacts.
  2. You'd like to publish the artifacts later down the road. Here's how you can publish the artifacts at a later stage:
 	rtMaven.deployer.deployArtifacts buildInfo 

Make sure to use the same buildInfo instance you received from the rtMaven.run method. Also make sure to run the above method on the same agent that ran the rtMaven.run method, because the artifacts were built and stored on the file-system of this agent.

By default, Maven uses the local Maven repository inside the .m2 directory under the user home. In case you'd like Maven to create the local repository in your job's workspace, add the -Dmaven.repo.local=.m2 system property to the goals value as shown here:

	def buildInfo = rtMaven.run pom: 'maven-example/pom.xml', goals: 'clean install -Dmaven.repo.local=.m2'

What about the build information?

The build information has not yet been published to Artifactory, but it is stored locally in the 'buildInfo' instance returned by the 'run' method. You can now publish it to Artifactory as follows:

    server.publishBuildInfo buildInfo

You can also merge multiple buildInfo instances into one buildInfo instance and publish it to Artifactory as one build, as described in the Publishing Build-Info to Artifactory section in this article.


Gradle Builds with Artifactory

Gradle builds can resolve dependencies, deploy artifacts and publish build-info to Artifactory. To run Gradle builds with Artifactory from your Pipeline script, you first need to create an Artifactory server instance, as described at the beginning of this article.
Here's an example:

    def server = Artifactory.server 'my-server-id'

The next step is to create an Artifactory Gradle Build instance:

    def rtGradle = Artifactory.newGradleBuild()

Now let's define where the Gradle build should download its dependencies from. Let's say you want the dependencies to be resolved from the 'libs-release' repository, located on the Artifactory server instance you defined above. Here's how you define this, using the Artifactory Gradle Build instance we created:

    rtGradle.resolver server: server, repo: 'libs-release'

Now let's define where our build artifacts should be deployed to. Once again, we define the Artifactory server and repositories on the 'rtGradle' instance:

    rtGradle.deployer server: server, repo: 'libs-release-local'

By default, all the build artifacts are deployed to Artifactory. In case you want to deploy only some artifacts, you can filter them based on their names, using the 'addInclude' method. In the following example, we are deploying only artifacts with names that start with 'frog'

    rtGradle.deployer.artifactDeploymentPatterns.addInclude("frog*")

You can also exclude artifacts from being deployed. In the following example, we are deploying all artifacts, except for those that are zip files:

    rtGradle.deployer.artifactDeploymentPatterns.addExclude("*.zip")

And to make things more interesting, you can combine both methods. For example, to deploy all artifacts with names that start with 'frog', but are not zip files, do the following:

    rtGradle.deployer.artifactDeploymentPatterns.addInclude("frog*").addExclude("*.zip")

If you'd like to add custom properties to the deployed artifacts, you can do that as follows:

    rtGradle.deployer.addProperty("status", "in-qa").addProperty("compatibility", "1", "2", "3")

In some cases, you want to disable artifacts deployment to Artifactory or make the deployment conditional. Here's how you do it:

    rtGradle.deployer.deployArtifacts = false

In case the "com.jfrog.artifactory" Gradle Plugin is already applied in your Gradle script, we need to let Jenkins know it shouldn't apply it. Here's how we do it:

    rtGradle.usesPlugin = true

In case you'd like to use the Gradle Wrapper for this build, add this:

    rtGradle.useWrapper = true

If you don't want to use the Gradle Wrapper, and set a Gradle installation instead, you should define a Gradle Tool through Jenkins Manage, and then, set the tool name as follows:

    rtGradle.tool = 'gradle tool name'

In case you'd like Gradle to use a different JDK than your build agent's default, no problem.
Simply set the JAVA_HOME environment variable to the desired JDK path (the path to the directory above the bin directory, which includes the java executable).
Here's you do it:

    env.JAVA_HOME = 'path to JDK'

OK, looks like we're ready to run our Gradle build. Here's how we define the build.gradle file path (relative to the workspace) and the Gradle tasks. The deployment to Artifactory is performed as part of the 'artifactoryPublish' task:

    def buildInfo = rtGradle.run rootDir: "projectDir/", buildFile: 'build.gradle', tasks: 'clean artifactoryPublish'

The above method runs the Gradle build. Notice that the method returns a buildInfo instance, which can be later published to Artifactory.

In some cases though, you'd like to pass an existing buildInfo instance to be used by the build. This can come in handy is when you want to set custom build name or build number on the build-info instance, or when you'd like to aggregate multiple builds into the same build-info instance. Here's how you pass an existing build-info instance to the rtGradle.run method:

	rtGradle.run rootDir: "projectDir/", buildFile: 'build.gradle', tasks: 'clean artifactoryPublish', buildInfo: existingBuildInfo

By default, the build artifacts will be deployed to Artifactory, unless the rtGradle.deployer.deployArtifacts property was set to false. This can come in handy in two cases:

  1. You do not wish to publish the artifacts.
  2. You'd like to publish the artifacts later down the road. Here's how you can publish the artifacts at a later stage:
rtGradle.deployer.deployArtifacts buildInfo

Make sure to use the same buildInfo instance you received from the rtGradle.run method. Also make sure to run the above method on the same agent that ran the rtGradle.run method, because the artifacts were built and stored on the file-system of this agent.

What about the build information?
The build information has not yet been published to Artifactory, but it is stored locally in the 'buildInfo' instance returned by the 'run' method. You can now publish it to Artifactory as follows:

    server.publishBuildInfo buildInfo

You can also merge multiple buildInfo instances into one buildInfo instance and publish it to Artifactory as one build, as described in the Publishing Build-Info to Artifactory section in this article.

That's it! We're all set.

The rtGradle instance supports additional configuration APIs. You can use these APIs as follows:

    def rtGradle = Artifactory.newGradleBuild()
    // Deploy Maven descriptors to Artifactory:
    rtGradle.deployer.deployMavenDescriptors = true
    // Deploy Ivy descriptors (pom.xml files) to Artifactory:
    rtGradle.deployer.deployIvyDescriptors = true

    // The following properties are used for Ivy publication configuration.
    // The values below are the defaults.

    // Set the deployed Ivy descriptor pattern:
    rtGradle.deployer.ivyPattern = '[organisation]/[module]/ivy-[revision].xml'
    // Set the deployed Ivy artifacts pattern:
    rtGradle.deployer.artifactPattern = '[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]'
    // Set mavenCompatible to true, if you wish to replace dots with slashes in the Ivy layout path, to match the Maven layout:
    rtGradle.deployer.mavenCompatible = true

You also have the option of defining default values in the gradle build script. Read more about it here.


Maven Release Management with Artifactory

With the Artifactory Pipeline DSL you can easily manage and run a release build for your Maven project by following the instructions below:

First, clone the code from your source control:

git url: 'https://github.com/eyalbe4/project-examples.git'

If the pom file has a snapshot version, Maven will create snapshot artifacts, because the pom files include a snapshot version (for example, 1.0.0-SNAPSHOT).
Since you want your build to create release artifacts, you need to change the version in the pom file to 1.0.0.
To do that, create a mavenDescriptor instance, and set the version to 1.0.0:

def descriptor = Artifactory.mavenDescriptor()
descriptor.version = '1.0.0'

If the project's pom file is not located at the root of the cloned project, but inside a sub-directory, add it to the mavenDescriptor instance:

descriptor.pomFile = 'maven-example/pom.xml'

In most cases, you want to verify that your release build does not include snapshot dependencies. The are two ways to do that.

The first way, is to configure the descriptor to fail the build if snapshot dependencies are found in the pom files. In this case, the job will fail before the new version is set to the pom files.
Here's how you configure this:

descriptor.failOnSnapshot = true

The second way to verify this is by using the hasSnapshots method, which returns a boolean true value if snapshot dependencies are found:

 def snapshots = descriptor.hasSnapshots()
 if (snapshots) {
     ....
 }

That's it. Using the mavenDescriptor as it is now will change the version inside the root pom file. In addition, if the project includes sub-modules with pom files, which include a version, it will change them as well.
Sometimes however, some sub-modules should use different release versions. For example, suppose there's one module whose version should change to 1.0.1, instead of 1.0.0. The other modules should still have their versions changed to 1.0.0. Here's how to do that:

descriptor.setVersion "the.group.id:the.artifact.id", "1.0.1"

The above setVersion method receives two arguments: the module name and its new release version. The module name is composed of the group ID and the artifact ID with a colon between them.
Now you can transform the pom files to include the new versions:

descriptor.transform()

The transform method changed the versions on the local pom files.
You can now build the code and deploy the release Maven artifacts to Artifactory as described in the "Maven Builds with Artifactory" section in this article. 

The next step is to commit the changes made to the pom files to the source control, and also tag the new release version in the source control repository. If you're using git, you can use the git client installed on your build agent and run a few shell commands from inside the Pipeline script to do that.
The last thing you'll probably want to do is to change the pom files version to the next development version and commit the changes. You can do that again by using a mavenDescriptor instance.


NPM Builds with Artifactory

NPM builds can resolve dependencies, deploy artifacts and publish build-info to Artifactory. To run NPM builds with Artifactory from your Pipeline script, you first need to create an Artifactory server instance, as described in the Creating an Artifactory Server Instance section.
Here's an example:

def server = Artifactory.server 'my-server-id'

The next step is to create an Artifactory NPM Build instance:

def rtNpm = Artifactory.newNpmBuild()

Now let's define where the NPM build should download its dependencies from. We set the Artifactory server instance we created earlier and the repository name on the resolver:

rtNpm.resolver server: server, repo: 'npm-virtual'

The build uses the npm executable to install (download the dependencies) and publish. By default, Jenkins uses the npm executable present in the agent's PATH. You can also reference a tool defined in Jenkins, and set the script to use it as follows:

// Set the name of a tool defined in Jenkins configuration
rtNpm.tool = 'nodejs-tool-name'
// or set the tool as an environment variable
env.NODEJS_HOME = "${tool 'nodejs-tool-name'}"
// or set a path to the NodeJS home directory (not the npm executable)
env.NODEJS_HOME = 'full/path/to/the/nodeJS/home'
// or
nodejs(nodeJSInstallationName: 'nodejs-tool-name') {
	// Only in this code scope, the npm defined by 'nodejs-tool-name' is used.
}

Now we can download our project's npm dependencies. The following method runs npm install behind the scenes:

def buildInfo = rtNpm.install path: 'npm-example'

You can also add npm flags or arguments as follows:

def buildInfo = rtNpm.install path: 'npm-example', args: '--verbose'

The above method returns a buildInfo instance. If we already have a buildInfo instance we'd like to reuse, we can alternatively send the buildInfo as an argument as shown below. Read the Publishing Build-Info to Artifactory section for more details.

rtNpm.install path: 'npm-example', buildInfo: my-build-info

The action of publishing the NPM package to Artifactory is very similar. We start by defining the deployer:

rtNpm.deployer server: server, repo: 'npm-local'

The following method will do two things: package the code (by running npm pack) and publish it to Artifactory:

def buildInfo = rtNpm.publish path: 'npm-example'

Similarly to the install method, the following is also supported:

rtNpm.publish path: 'npm-example', buildInfo: my-build-info

You can now publish the build-info to Artifactory as described in the Publishing Build-Info to Artifactory section


Conan Builds with Artifactory

Conan is a C/C++ Package Manager. The Artifactory Pipeline DSL includes APIs that make it easy for you to run Conan builds, using the Conan Client installed on your build agents. Here's what you need to do before you create your first Conan build job with Jenkins:

1, Install the latest Conan Client on your Jenkins build agent. Please refer to the Conan documentation for installation instructions.

2. Add the Conan Client executable to the PATH environment variable on your build agent, to make sure Jenkins is able to use the client.

3. Create a Conan repository in Artifactory as described in the Conan Repositories Artifactory documentation.

OK. Let's start coding your first Conan Pipeline script.

We'll start by creating an Artifactory server instance, as described at the beginning of this article.
Here's an example:

def server = Artifactory.server 'my-server-id'

Now let's create a Conan Client instance

def conanClient = Artifactory.newConanClient()

When creating the Conan client, you can also specify the Conan user home directory as shown below:

def conanClient = Artifactory.newConanClient userHome: "conan/my-conan-user-home"

We can now configure our new conanClient instance by adding an Artifactory repository to it. In our example, we're adding the 'conan-local' repository, located in the Artifactory server, referenced by the server instance we obtained:

String remoteName = conanClient.remote.add server: server, repo: "conan-local"

The above method also accepts the additional optional argument force: true. Adding this argument will make the command not to raise an error. If an existing remote exists with the provided name.

As you can see in the above example, the 'conanClient.remote.add' method returns a string variable - 'remoteName'.

What is this 'remoteName' variable? What is it for?

Well, a 'Conan remote' is a repository, which can be used to download dependencies from and upload artifacts to. When we added the 'conan-local' Artifactory repository to our Conan Client, we actually added a Conan remote. The 'remoteName' variable contains the name of the new Conan remote we added.

OK. We're ready to start running Conan commands. You'll need to be familiar with the Conan commands syntax, exposed by the Conan Client to run the commands. You can read about the commands syntax in the Conan documentation.

Let's run the first command:

def buildInfo1 = conanClient.run command: "install --build missing"

The 'conanClient.run' method returns a buildInfo instance, that we can later publish to Artifactory. If you already have a buildInfo instance, and you'd like the 'conanClient.run' method to aggregate the build information to it, you can also send the buildInfo instance to the run command as and an argument as shown below:


conanClient.run command: "install --build missing", buildInfo: buildInfo

The next thing we want to do is to use the Conan remote we created. For example, let's upload our artifacts to the Conan remote. Notice how we use the 'remoteName' variable we got earlier, when building the Conan command:

String command = "upload * --all -r ${remoteName} --confirm"
conanClient.run command: command, buildInfo: buildInfo

We can now publish the the buildInfo to Artifactory, as described in the Publishing Build-Info to Artifactory section. For example:

server.publishBuildInfo buildInfo

Docker Builds with Artifactory

The Jenkins Artifactory Plugin supports a Pipeline DSL that allows collecting and publishing build-info to Artifactory for your Docker builds. To setup your Jenkins build agents to collect build-info for your Docker builds, please refer to the setup instructions

    // Create an Artifactory server instance, as described above in this article:
    def server = Artifactory.server 'my-server-id'

    // Create an Artifactory Docker instance. The instance stores the Artifactory credentials and the Docker daemon host address:
    def rtDocker = Artifactory.docker server: server, host: "tcp://<daemon IP>:<daemon port>"

    // If the docker daemon host is not specified, "/var/run/docker.sock" is used as a default value:
    def rtDocker = Artifactory.docker server: server
 
    // Attach custom properties to the published artifacts:
    rtDocker.addProperty("project-name", "docker1").addProperty("status", "stable")

    // Push a docker image to Artifactory (here we're pushing hello-world:latest). The push method also expects
    // Artifactory repository name (<target-artifactory-repository>).
    // Please make sure that <artifactoryDockerRegistry> is configured to reference the <target-artifactory-repository> Artifactory repository. In case it references a different repository, your build will fail with "Could not find manifest.json in Artifactory..." following the push.
    def buildInfo = rtDocker.push '<artifactory-docker-registry-url>/hello-world:latest', '<target-artifactory-repository>'

    // Publish the build-info to Artifactory:
    server.publishBuildInfo buildInfo

Scanning Builds with JFrog Xray

From version 2.9.0, Jenkins Artifactory Plugin is integrated with JFrog Xray through JFrog Artifactory allowing you to have build artifacts scanned for vulnerabilities and other issues. If issues or vulnerabilities are found, you may choose to fail a build job or perform other actions according to the Pipeline script you write. This integration requires JFrog Artifactory v4.16 and above and JFrog Xray v1.6 and above. 

You may scan any build that has been published to Artifactory. It does not matter when the build was published, as long as it was published before triggering the scan by JFrog Xray.

The following instructions show you how to configure your Pipeline script to have a build scanned.

First, for Xray to scan builds, you need to configure a Watch with the right filters that specify which artifacts and vulnerabilities should trigger an alert, and set a Fail Build Job Action for that Watch. You can read more about CI/CD integration with Xray here.

Now you can configure your Jenkins Pipeline job to scan the build.. Start by creating a scanConfig instance with the build name and build number you wish to scan:

  def scanConfig = [
      'buildName'     : 'my-build-name',
      'buildNumber'   : '17'
    ]

If you're scanning a build which has already been published to Artifactory in the same job, you can use the build name and build number stored on the buildInfo instance you used to publish the build. For example:

  server.publishBuildInfo buildInfo
  def scanConfig = [
      'buildName'      : buildInfo.name,
      'buildNumber'    : buildInfo.number
    ]

Before you trigger the scan, there's one more thing you need to be aware of. By default, if the Xray scan finds vulnerabilities or issues in the build that trigger an alert, the build job will fail. If you don't want the build job to fail, you can add the 'failBuild' property to the scanConfig instance and set it to 'false' as shown here:

  def scanConfig = [
      'buildName'      : buildInfo.name,
      'buildNumber'    : buildInfo.number,
      'failBuild'      : false
    ]

OK, we're ready to initiate the scan. The scan should be initiated on the same Artifactory server instance, to which the build was published:

 def scanResult = server.xrayScan scanConfig

That's it. The build will now be scanned. If the scan is not configured to fail the build job, you can use the scanResult instance returned from the xrayScan method to see some details about the scan.
For example, to print the result to the log, you could use the following code snippet:

 echo scanResult as String

For more details on the integration with JFrog Xray and JFrog Artifactory to scan builds for issues and vulnerabilities, please refer to CI/CD Integration in the JFrog Xray documentation.


Distributing Build Artifacts

From version 2.11.0, you can easily distribute your build artifacts to the world or to your community using Pipeline. Your artifacts are distributed to JFrog Bintray, using a Distribution Repository in Artifactory. You can read more about Distribution Repositories and the steps for setting them up here.

In order to distribute a build, it should be first published to Artifactory, as described in the Publishing Build-Info to Artifactory section in this article.

Once you have your Distribution Repository set up and your build is published to Artifactory, define a distributionConfig instance in your Pipeline script as shown here:

def distributionConfig = [
	// Mandatory parameters
	'buildName'             : buildInfo.name,
	'buildNumber'           : buildInfo.number,
	'targetRepo'            : 'dist-repo',
       
	// Optional parameters
	'publish'               : true, // Default: true. If true, artifacts are published when deployed to Bintray.
    'overrideExistingFiles' : false, // Default: false. If true, Artifactory overwrites builds already existing in the target path in Bintray. 
    'gpgPassphrase'         : 'passphrase', // If specified, Artifactory will GPG sign the build deployed to Bintray and apply the specified passphrase.
    'async'                 : false, // Default: false. If true, the build will be distributed asynchronously. Errors and warnings may be viewed in the Artifactory log.
    "sourceRepos"           : ["yum-local"], // An array of local repositories from which build artifacts should be collected.
    'dryRun'                : false, // Default: false. If true, distribution is only simulated. No files are actually moved.
]

OK, we're ready to distribute the build. The distribution should be donw on the same Artifactory server instance, to which the build was published:

server.distribute distributionConfig
  • No labels