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:
parent
832a55f817
commit
fbd03a88da
75
documentation/docs/steps/artifactSetVersion.md
Normal file
75
documentation/docs/steps/artifactSetVersion.md
Normal 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'
|
||||
```
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
7
src/com/sap/piper/GitUtils.groovy
Normal file
7
src/com/sap/piper/GitUtils.groovy
Normal 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()
|
||||
}
|
30
src/com/sap/piper/versioning/ArtifactVersioning.groovy
Normal file
30
src/com/sap/piper/versioning/ArtifactVersioning.groovy
Normal 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}")
|
||||
}
|
||||
}
|
50
src/com/sap/piper/versioning/DockerArtifactVersioning.groovy
Normal file
50
src/com/sap/piper/versioning/DockerArtifactVersioning.groovy
Normal 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()
|
||||
}
|
||||
}
|
18
src/com/sap/piper/versioning/MavenArtifactVersioning.groovy
Normal file
18
src/com/sap/piper/versioning/MavenArtifactVersioning.groovy
Normal 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}"
|
||||
}
|
||||
}
|
115
test/groovy/ArtifactSetVersionTest.groovy
Normal file
115
test/groovy/ArtifactSetVersionTest.groovy
Normal 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()
|
||||
}
|
||||
|
||||
|
||||
}
|
47
test/groovy/com/sap/piper/GitUtilsTest.groovy
Normal file
47
test/groovy/com/sap/piper/GitUtilsTest.groovy
Normal 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())
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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, [:])
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
41
test/groovy/util/JenkinsReadFileRule.groovy
Normal file
41
test/groovy/util/JenkinsReadFileRule.groovy
Normal 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)
|
||||
}
|
||||
|
||||
}
|
66
test/groovy/util/JenkinsReadMavenPomRule.groovy
Normal file
66
test/groovy/util/JenkinsReadMavenPomRule.groovy
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
34
test/groovy/util/JenkinsWriteFileRule.groovy
Normal file
34
test/groovy/util/JenkinsWriteFileRule.groovy
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
test/resources/DockerArtifactVersioning/Dockerfile
Normal file
5
test/resources/DockerArtifactVersioning/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM rootImage:1.2.3
|
||||
|
||||
USER root
|
||||
|
||||
ENV TEST 2.3.4
|
8
test/resources/MavenArtifactVersioning/pom.xml
Normal file
8
test/resources/MavenArtifactVersioning/pom.xml
Normal 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>
|
8
test/resources/MavenArtifactVersioning/snapshot/pom.xml
Normal file
8
test/resources/MavenArtifactVersioning/snapshot/pom.xml
Normal 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>
|
138
vars/artifactSetVersion.groovy
Normal file
138
vars/artifactSetVersion.groovy
Normal 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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user