1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-03 15:02:35 +02:00

Step for automatic versioning (#65)

It contains:

* versioning step artifactSetVersion
* versioning implementation for Maven & Docker
* enhancements to commonPipelineEnvironment
* extended default configuration
* new utils object for git-related tasks
* automated tests incl. new Rules and resources
* incorporated PR feedback
* step documentation
This commit is contained in:
Oliver Nocon 2018-02-07 13:17:33 +01:00 committed by GitHub
parent 832a55f817
commit fbd03a88da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 850 additions and 1 deletions

View File

@ -0,0 +1,75 @@
# artifactSetVersion
## Description
The continuous delivery process requires that each build is done with a unique version number.
The version generated using this step will contain:
* Version (major.minor.patch) from descriptor file in master repository is preserved. Developers should be able to autonomously decide on increasing either part of this version number.
* Timestamp
* CommitId (by default the long version of the hash)
After conducting automatic versioning the new version is pushed as a new tag into the source code repository (e.g. GitHub)
## Prerequsites
none
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
| script | no | empty `commonPipelineEnvironment` | |
| buildTool | no | maven | maven, docker |
| dockerVersionSource | no | `''` | FROM, (ENV name),appVersion |
| filePath | no | buildTool=`maven`: pom.xml <br />docker: Dockerfile | |
| gitCommitId | no | `GitUtils.getGitCommitId()` | |
| gitCredentialsId | yes | as defined in custom configuration | |
| gitUserEMail | no | | |
| gitUserName | no | | |
| gitSshUrl | yes | | |
| tagPrefix | no | 'build_' | |
| timestamp | no | current time in format according to `timestampTemplate` | |
| timestampTemplate | no | `%Y%m%d%H%M%S` | |
| versioningTemplate | no | depending on `buildTool`<br />maven: `${version}-${timestamp}${commitId?"_"+commitId:""}` | |
* `script` defines the global script environment of the Jenkinsfile run. Typically `this` is passed to this parameter. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving e.g. configuration parameters.
* `buildTool` defines the tool which is used for building the artifact.
* `dockerVersionSource` specifies the source to be used for the main version which is used for generating the automatic version.
* This can either be the version of the base image - as retrieved from the `FROM` statement within the Dockerfile, e.g. `FROM jenkins:2.46.2`
* Alternatively the name of an environment variable defined in the Docker image can be used which contains the version number, e.g. `ENV MY_VERSION 1.2.3`
* The third option `appVersion` applies only to the artifactType `appContainer`. Here the version of the app which is packaged into the container will be used as version for the container itself.
* Using `filePath` you could define a custom path to the descriptor file.
* `gitCommitId` defines the version prefix of the automatically generated version. By default it will take the long commitId hash. You could pass any other string (e.g. the short commitId hash) to be used. In case you don't want to have the gitCommitId added to the automatic versioning string you could set the value to an empty string: `''`.
* `gitCredentialsId`defines the ssh git credentials to be used for writing the tag.
* The parameters `gitUserName` and `gitUserEMail` allow to overwrite the global git settings available on your Jenkins server
* `gitSshUrl` defines the git ssh url to the source code repository.
* `tagPrefix` defines the prefix wich is used for the git tag which is written during the versioning run.
* `timestamp` defines the timestamp to be used in the automatic version string. You could overwrite the default behavior by explicitly setting this string.
## Step configuration
Following parameters can also be specified as step parameters using the global configuration file:
* `artifactType`
* `buildTool`
* `dockerVersionSource`
* `filePath`
* `gitCredentialsId`
* `gitUserEMail`
* `gitUserName`
* `gitSshUrl`
* `tagPrefix`
* `timestamp`
* `timestampTemplate`
* `versioningTemplate`
## Explanation of pipeline step
Pipeline step:
```groovy
artifactSetVersion script: this, buildTool: 'maven'
```

View File

@ -2,6 +2,7 @@ site_name: Jenkins 2.0 Pipelines
pages:
- Home: index.md
- 'Library steps':
- artifactSetVersion: steps/artifactSetVersion.md
- commonPipelineEnvironment: steps/commonPipelineEnvironment.md
- dockerExecute: steps/dockerExecute.md
- durationMeasure: steps/durationMeasure.md

View File

@ -4,6 +4,15 @@ general:
#Steps Specific Configuration
steps:
artifactSetVersion:
timestampTemplate: '%Y%m%d%H%M%S'
tagPrefix: 'build_'
maven:
filePath: 'pom.xml'
versioningTemplate: '${version}-${timestamp}${commitId?"_"+commitId:""}'
docker:
filePath: 'Dockerfile'
versioningTemplate: '${version}-${timestamp}${commitId?"_"+commitId:""}'
mavenExecute:
dockerImage: 'maven:3.5-jdk-7'
influxWriteData:

View File

@ -0,0 +1,7 @@
package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
def getGitCommitId() {
return sh(returnStdout: true, script: 'git rev-parse HEAD').trim()
}

View File

@ -0,0 +1,30 @@
package com.sap.piper.versioning
abstract class ArtifactVersioning implements Serializable {
final protected script
final protected Map configuration
protected ArtifactVersioning(script, configuration) {
this.script = script
this.configuration = configuration
}
public static getArtifactVersioning(buildTool, script, configuration) {
switch (buildTool) {
case 'maven':
return new MavenArtifactVersioning(script, configuration)
case 'docker':
return new DockerArtifactVersioning(script, configuration)
default:
throw new IllegalArgumentException("No versioning implementation for buildTool: ${buildTool} available.")
}
}
abstract setVersion(version)
abstract getVersion()
protected echo(msg){
script.echo("[${this.getClass().getSimpleName()}] ${msg}")
}
}

View File

@ -0,0 +1,50 @@
package com.sap.piper.versioning
class DockerArtifactVersioning extends ArtifactVersioning {
protected DockerArtifactVersioning(script, configuration) {
super(script, configuration)
}
@Override
def getVersion() {
if (configuration.dockerVersionSource == 'FROM')
return getVersionFromDockerBaseImageTag(configuration.filePath)
else
//standard assumption: version is assigned to an env variable
return getVersionFromDockerEnvVariable(configuration.filePath, configuration.dockerVersionSource)
}
@Override
def setVersion(version) {
def dockerVersionDir = (configuration.dockerVersionDir?dockerVersionDir:'')
script.dir(dockerVersionDir) {
script.writeFile file:'VERSION', text: version
}
}
def getVersionFromDockerEnvVariable(filePath, envVarName) {
def lines = script.readFile(filePath).split('\n')
def version = ''
for (def i = 0; i < lines.size(); i++) {
if (lines[i].startsWith('ENV') && lines[i].split(' ')[1] == envVarName) {
version = lines[i].split(' ')[2]
break
}
}
echo("Version from Docker environment variable ${envVarName}: ${version}")
return version.trim()
}
def getVersionFromDockerBaseImageTag(filePath) {
def lines = script.readFile(filePath).split('\n')
def version = null
for (def i = 0; i < lines.size(); i++) {
if (lines[i].startsWith('FROM') && lines[i].indexOf(':') > 0) {
version = lines[i].split(':')[1]
break
}
}
echo("Version from Docker base image tag: ${version}")
return version.trim()
}
}

View File

@ -0,0 +1,18 @@
package com.sap.piper.versioning
class MavenArtifactVersioning extends ArtifactVersioning {
protected MavenArtifactVersioning (script, configuration) {
super(script, configuration)
}
@Override
def getVersion() {
def mavenPom = script.readMavenPom (file: configuration.filePath)
return mavenPom.getVersion().replaceAll(/-SNAPSHOT$/, "")
}
@Override
def setVersion(version) {
script.sh "mvn versions:set -DnewVersion=${version} --file ${configuration.filePath}"
}
}

View File

@ -0,0 +1,115 @@
#!groovy
import com.lesfurets.jenkins.unit.BasePipelineTest
import com.sap.piper.DefaultValueCache
import com.sap.piper.GitUtils
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.JenkinsLoggingRule
import util.JenkinsReadMavenPomRule
import util.JenkinsShellCallRule
import util.JenkinsWriteFileRule
import util.Rules
import static org.junit.Assert.assertEquals
class ArtifactSetVersionTest extends BasePipelineTest {
Script artifactSetVersionScript
def cpe
def gitUtils
def sshAgentList = []
ExpectedException thrown = ExpectedException.none()
JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
JenkinsWriteFileRule jwfr = new JenkinsWriteFileRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(thrown)
.around(jlr)
.around(jscr)
.around(new JenkinsReadMavenPomRule(this, 'test/resources/MavenArtifactVersioning'))
.around(jwfr)
@Before
void init() throws Throwable {
helper.registerAllowedMethod("sshagent", [List.class, Closure.class], { list, closure ->
sshAgentList = list
return closure()
})
jscr.setReturnValue('git rev-parse HEAD', 'testCommitId')
jscr.setReturnValue("date +'%Y%m%d%H%M%S'", '20180101010203')
jscr.setReturnValue('git diff --quiet HEAD', 0)
cpe = loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment
artifactSetVersionScript = loadScript("artifactSetVersion.groovy")
gitUtils = new GitUtils()
prepareObjectInterceptors(gitUtils)
}
@Test
void testVersioning() {
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils, buildTool: 'maven', gitSshUrl: 'myGitSshUrl')
assertEquals('1.2.3-20180101010203_testCommitId', cpe.getArtifactVersion())
assertEquals('testCommitId', cpe.getGitCommitId())
assertEquals('mvn versions:set -DnewVersion=1.2.3-20180101010203_testCommitId --file pom.xml', jscr.shell[3])
assertEquals('git add .', jscr.shell[4])
assertEquals ("git commit -m 'update version 1.2.3-20180101010203_testCommitId'", jscr.shell[5])
assertEquals ("git remote set-url origin myGitSshUrl", jscr.shell[6])
assertEquals ("git tag build_1.2.3-20180101010203_testCommitId", jscr.shell[7])
assertEquals ("git push origin build_1.2.3-20180101010203_testCommitId", jscr.shell[8])
}
@Test
void testVersioningCustomGitUserAndEMail() {
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils, buildTool: 'maven', gitSshUrl: 'myGitSshUrl', gitUserEMail: 'test@test.com', gitUserName: 'test')
assertEquals ('git -c user.email="test@test.com" -c user.name "test" commit -m \'update version 1.2.3-20180101010203_testCommitId\'', jscr.shell[5])
}
@Test
void testVersioningWithTimestamp() {
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils, buildTool: 'maven', timestamp: '2018')
assertEquals('1.2.3-2018_testCommitId', cpe.getArtifactVersion())
}
@Test
void testVersioningNoBuildTool() {
thrown.expect(Exception)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR buildTool')
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils)
}
@Test
void testVersioningWithCustomTemplate() {
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils, buildTool: 'maven', versioningTemplate: '${version}-xyz')
assertEquals('1.2.3-xyz', cpe.getArtifactVersion())
}
@Test
void testVersioningWithTypeAppContainer() {
cpe.setArtifactVersion('1.2.3-xyz')
artifactSetVersionScript.call(script: [commonPipelineEnvironment: cpe], juStabGitUtils: gitUtils, buildTool: 'docker', artifactType: 'appContainer', dockerVersionSource: 'appVersion')
assertEquals('1.2.3-xyz', cpe.getArtifactVersion())
assertEquals('1.2.3-xyz', jwfr.files['VERSION'])
}
void prepareObjectInterceptors(object) {
object.metaClass.invokeMethod = helper.getMethodInterceptor()
object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
}
}

View File

@ -0,0 +1,47 @@
package com.sap.piper
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.JenkinsReadMavenPomRule
import util.JenkinsShellCallRule
import util.Rules
import util.SharedLibraryCreator
import static org.junit.Assert.assertEquals
class GitUtilsTest extends BasePipelineTest {
JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this).around(jscr).around(thrown)
GitUtils gitUtils
@Before
void init() throws Exception {
gitUtils = new GitUtils()
prepareObjectInterceptors(gitUtils)
jscr.setReturnValue('git rev-parse HEAD', 'testCommitId')
}
void prepareObjectInterceptors(object) {
object.metaClass.invokeMethod = helper.getMethodInterceptor()
object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
}
@Test
void testGetGitCommitId() {
assertEquals('testCommitId', gitUtils.getGitCommitId())
}
}

View File

@ -0,0 +1,28 @@
package com.sap.piper.versioning
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import static org.junit.Assert.assertTrue
import static org.junit.Assert.assertEquals
class ArtifactVersioningTest {
@Rule
public ExpectedException thrown = ExpectedException.none()
@Test
void testInstatiateFactoryMethod() {
def versionObj = ArtifactVersioning.getArtifactVersioning( 'maven', this, [:])
assertTrue(versionObj instanceof MavenArtifactVersioning)
}
@Test
void testInstatiateFactoryMethodWithInvalidToolId() {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('No versioning implementation for buildTool: invalid available.')
ArtifactVersioning.getArtifactVersioning('invalid', this, [:])
}
}

View File

@ -0,0 +1,71 @@
package com.sap.piper.versioning
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.JenkinsLoggingRule
import util.JenkinsReadFileRule
import util.JenkinsReadMavenPomRule
import util.JenkinsShellCallRule
import util.JenkinsWriteFileRule
import util.Rules
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertTrue
class DockerArtifactVersioningTest extends BasePipelineTest{
DockerArtifactVersioning av
String passedDir
JenkinsReadFileRule jrfr = new JenkinsReadFileRule(this, 'test/resources/DockerArtifactVersioning')
JenkinsWriteFileRule jwfr = new JenkinsWriteFileRule(this)
JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(jrfr)
.around(jwfr)
.around(jlr)
.around(thrown)
@Before
public void init() {
helper.registerAllowedMethod("dir", [String.class, Closure.class], { s, closure ->
passedDir = s
return closure()
})
prepareObjectInterceptors(this)
}
@Test
void testVersioningFrom() {
av = new DockerArtifactVersioning(this, [filePath: 'Dockerfile', dockerVersionSource: 'FROM'])
assertEquals('1.2.3', av.getVersion())
av.setVersion('1.2.3-20180101')
assertEquals('1.2.3-20180101', jwfr.files['VERSION'])
assertTrue(jlr.log.contains('[DockerArtifactVersioning] Version from Docker base image tag: 1.2.3'))
}
@Test
void testVersioningEnv() {
av = new DockerArtifactVersioning(this, [filePath: 'Dockerfile', dockerVersionSource: 'TEST'])
assertEquals('2.3.4', av.getVersion())
assertTrue(jlr.log.contains('[DockerArtifactVersioning] Version from Docker environment variable TEST: 2.3.4'))
}
void prepareObjectInterceptors(object) {
object.metaClass.invokeMethod = helper.getMethodInterceptor()
object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
}
}

View File

@ -0,0 +1,54 @@
package com.sap.piper.versioning
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.JenkinsReadMavenPomRule
import util.JenkinsShellCallRule
import util.Rules
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertTrue
class MavenArtifactVersioningTest extends BasePipelineTest{
MavenArtifactVersioning av
JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this).around(jscr).around(thrown).around(new JenkinsReadMavenPomRule(this, 'test/resources/MavenArtifactVersioning'))
@Before
public void init() {
prepareObjectInterceptors(this)
}
@Test
void testVersioning() {
av = new MavenArtifactVersioning(this, [filePath: 'pom.xml'])
assertEquals('1.2.3', av.getVersion())
av.setVersion('1.2.3-20180101')
assertEquals('mvn versions:set -DnewVersion=1.2.3-20180101 --file pom.xml', jscr.shell[0])
}
@Test
void testVersioningCustomFilePathSnapshot() {
av = new MavenArtifactVersioning(this, [filePath: 'snapshot/pom.xml'])
assertEquals('1.2.3', av.getVersion())
av.setVersion('1.2.3-20180101')
assertEquals('mvn versions:set -DnewVersion=1.2.3-20180101 --file snapshot/pom.xml', jscr.shell[0])
}
void prepareObjectInterceptors(object) {
object.metaClass.invokeMethod = helper.getMethodInterceptor()
object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
}
}

View File

@ -0,0 +1,41 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsReadFileRule implements TestRule {
final BasePipelineTest testInstance
final String testRoot
JenkinsReadFileRule(BasePipelineTest testInstance, String testRoot) {
this.testInstance = testInstance
this.testRoot = testRoot
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod( 'readFile', [String.class], {s -> return (loadFile("${testRoot}/${s}")).getText('UTF-8')} )
testInstance.helper.registerAllowedMethod( 'readFile', [Map.class], {m -> return (loadFile("${testRoot}/${m.file}")).getText(m.encoding?m.encoding:'UTF-8')} )
base.evaluate()
}
}
}
File loadFile(String path){
return new File(path)
}
}

View File

@ -0,0 +1,66 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsReadMavenPomRule implements TestRule {
final BasePipelineTest testInstance
final String testRoot
JenkinsReadMavenPomRule(BasePipelineTest testInstance, String testRoot) {
this.testInstance = testInstance
this.testRoot = testRoot
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod('readMavenPom', [Map.class], {m -> return loadPom("${testRoot}/${m.file}")})
base.evaluate()
}
}
}
MockPom loadPom( String path ){
return new MockPom( path )
}
class MockPom {
def pom
MockPom(String path){
def f = new File( path )
if ( f.exists() ){
this.pom = new XmlSlurper().parse(f)
}
else {
throw new FileNotFoundException( 'Failed to find file: ' + path )
}
}
String getVersion(){
return pom.version
}
String getGroupId(){
return pom.groupId
}
String getArtifactId(){
return pom.artifactId
}
String getPackaging(){
return pom.packaging
}
String getName(){
return pom.name
}
}
}

View File

@ -11,10 +11,16 @@ class JenkinsShellCallRule implements TestRule {
List shell = []
def returnValues = [:]
JenkinsShellCallRule(BasePipelineTest testInstance) {
this.testInstance = testInstance
}
def setReturnValue(script, value) {
returnValues[script] = value
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
@ -26,10 +32,17 @@ class JenkinsShellCallRule implements TestRule {
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod("sh", [String.class], {
command ->
command ->
shell.add(command.replaceAll(/\s+/," ").trim())
})
testInstance.helper.registerAllowedMethod("sh", [Map.class], {
m ->
shell.add(m.script.replaceAll(/\s+/," ").trim())
if (m.returnStdout || m.returnStatus)
return returnValues[m.script]
})
base.evaluate()
}
}

View File

@ -0,0 +1,34 @@
package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class JenkinsWriteFileRule implements TestRule {
final BasePipelineTest testInstance
Map files = [:]
JenkinsWriteFileRule(BasePipelineTest testInstance) {
this.testInstance = testInstance
}
@Override
Statement apply(Statement base, Description description) {
return statement(base)
}
private Statement statement(final Statement base) {
return new Statement() {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod( 'writeFile', [Map.class], {m -> files[m.file] = m.text.toString()})
base.evaluate()
}
}
}
}

View File

@ -0,0 +1,5 @@
FROM rootImage:1.2.3
USER root
ENV TEST 2.3.4

View File

@ -0,0 +1,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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sap.piper</groupId>
<artifactId>library-test</artifactId>
<packaging>war</packaging>
<version>1.2.3</version>
<name>library-test</name>
</project>

View File

@ -0,0 +1,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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sap.piper</groupId>
<artifactId>library-test</artifactId>
<packaging>war</packaging>
<version>1.2.3-SNAPSHOT</version>
<name>library-test</name>
</project>

View File

@ -0,0 +1,138 @@
import com.sap.piper.ConfigurationLoader
import com.sap.piper.ConfigurationMerger
import com.sap.piper.GitUtils
import com.sap.piper.Utils
import com.sap.piper.versioning.ArtifactVersioning
import groovy.text.SimpleTemplateEngine
def call(Map parameters = [:]) {
def stepName = 'artifactSetVersion'
handlePipelineStepErrors (stepName: stepName, stepParameters: parameters) {
def gitUtils = parameters.juStabGitUtils
if (gitUtils == null) {
gitUtils = new GitUtils()
}
if (sh(returnStatus: true, script: 'git diff --quiet HEAD') != 0)
error "[${stepName}] Files in the workspace have been changed previously - aborting ${stepName}"
def script = parameters.script
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
prepareDefaultValues script: script
final Map stepDefaults = ConfigurationLoader.defaultStepConfiguration(script, stepName)
final Map stepConfiguration = ConfigurationLoader.stepConfiguration(script, stepName)
List parameterKeys = [
'artifactType',
'buildTool',
'dockerVersionSource',
'filePath',
'gitCommitId',
'gitCredentialsId',
'gitUserEMail',
'gitUserName',
'gitSshUrl',
'tagPrefix',
'timestamp',
'timestampTemplate',
'versioningTemplate'
]
Map pipelineDataMap = [
gitCommitId: gitUtils.getGitCommitId()
]
List stepConfigurationKeys = [
'artifactType',
'buildTool',
'dockerVersionSource',
'filePath',
'gitCredentialsId',
'gitUserEMail',
'gitUserName',
'gitSshUrl',
'tagPrefix',
'timestamp',
'timestampTemplate',
'versioningTemplate'
]
Map configuration = ConfigurationMerger.mergeWithPipelineData(parameters, parameterKeys, pipelineDataMap, stepConfiguration, stepConfigurationKeys, stepDefaults)
def utils = new Utils()
def buildTool = utils.getMandatoryParameter(configuration, 'buildTool')
if (!configuration.filePath)
configuration.filePath = configuration[buildTool].filePath //use default configuration
def newVersion
def artifactVersioning = ArtifactVersioning.getArtifactVersioning(buildTool, this, configuration)
if(configuration.artifactType == 'appContainer' && configuration.dockerVersionSource == 'appVersion'){
if (script.commonPipelineEnvironment.getArtifactVersion())
//replace + sign if available since + is not allowed in a Docker tag
newVersion = script.commonPipelineEnvironment.getArtifactVersion().replace('+', '_')
else
error ("[${stepName}] No artifact version available for 'dockerVersionSource: appVersion' -> executeBuild needs to run for the application artifact first to set the artifactVersion for the application artifact.'")
} else {
def currentVersion = artifactVersioning.getVersion()
def timestamp = configuration.timestamp ? configuration.timestamp : getTimestamp(configuration.timestampTemplate)
def versioningTemplate = configuration.versioningTemplate ? configuration.versioningTemplate : configuration[configuration.buildTool].versioningTemplate
//defined in default configuration
def binding = [version: currentVersion, timestamp: timestamp, commitId: configuration.gitCommitId]
def templatingEngine = new SimpleTemplateEngine()
def template = templatingEngine.createTemplate(versioningTemplate).make(binding)
newVersion = template.toString()
}
artifactVersioning.setVersion(newVersion)
sh 'git add .'
def gitCommitId
sshagent([configuration.gitCredentialsId]) {
def gitUserMailConfig = ''
if (configuration.gitUserName && configuration.gitUserEMail)
gitUserMailConfig = "-c user.email=\"${configuration.gitUserEMail}\" -c user.name \"${configuration.gitUserName}\""
try {
sh "git ${gitUserMailConfig} commit -m 'update version ${newVersion}'"
} catch (e) {
error "[${stepName}]git commit failed: ${e}"
}
sh "git remote set-url origin ${configuration.gitSshUrl}"
sh "git tag ${configuration.tagPrefix}${newVersion}"
sh "git push origin ${configuration.tagPrefix}${newVersion}"
gitCommitId = gitUtils.getGitCommitId()
}
if(buildTool == 'docker' && configuration.artifactType == 'appContainer') {
script.commonPipelineEnvironment.setAppContainerProperty('artifactVersion', newVersion)
script.commonPipelineEnvironment.setAppContainerProperty('gitCommitId', gitCommitId)
} else {
//standard case
script.commonPipelineEnvironment.setArtifactVersion(newVersion)
script.commonPipelineEnvironment.setGitCommitId(gitCommitId)
}
echo "[${stepName}]New version: ${newVersion}"
}
}
def getTimestamp(pattern){
return sh(returnStdout: true, script: "date +'${pattern}'").trim()
}

View File

@ -4,6 +4,13 @@ class commonPipelineEnvironment implements Serializable {
//stores version of the artifact which is build during pipeline run
def artifactVersion
//stores the gitCommitId as well as additional git information for the build during pipeline run
private String gitCommitId
private String gitSshUrl
//stores properties for a pipeline which build an artifact and then bundles it into a container
private Map appContainerProperties = [:]
Map configuration = [:]
Map defaultConfiguration = [:]
@ -14,6 +21,14 @@ class commonPipelineEnvironment implements Serializable {
private String mtarFilePath
def setAppContainerProperty(property, value) {
appContainerProperties[property] = value
}
def getAppContainerProperty(property) {
return appContainerProperties[property]
}
def setArtifactVersion(version) {
artifactVersion = version
}
@ -41,6 +56,22 @@ class commonPipelineEnvironment implements Serializable {
return configProperties[property]
}
def setGitCommitId(commitId) {
gitCommitId = commitId
}
def getGitCommitId() {
return gitCommitId
}
def setGitSshUrl(url) {
gitSshUrl = url
}
def getGitSshUrl() {
return gitSshUrl
}
def getInfluxCustomData() {
return influxCustomData
}