diff --git a/pom.xml b/pom.xml
index cab1ae1e1..7675703ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -109,6 +109,26 @@
1.19
test
+
+
+ org.springframework
+ spring-context
+ 4.3.12.RELEASE
+ test
+
+
+ org.springframework
+ spring-context-support
+ 4.3.10.RELEASE
+ test
+
+
+ org.springframework
+ spring-test
+ 4.3.5.RELEASE
+ test
+
+
src
diff --git a/resources/default_pipeline_environment.yml b/resources/default_pipeline_environment.yml
index 462488714..49167e31b 100644
--- a/resources/default_pipeline_environment.yml
+++ b/resources/default_pipeline_environment.yml
@@ -14,18 +14,6 @@ steps:
docker:
filePath: 'Dockerfile'
versioningTemplate: '${version}-${timestamp}${commitId?"_"+commitId:""}'
- mavenExecute:
- dockerImage: 'maven:3.5-jdk-7'
- influxWriteData:
- influxServer: 'jenkins'
- mtaBuild:
- buildTarget: 'NEO'
- mtaJarLocation: 'mta.jar'
- neoDeploy:
- deployMode: 'mta'
- warAction: 'deploy'
- vmSize: 'lite'
- neoCredentialsId: 'CI_CREDENTIALS_ID'
checksPublishResults:
aggregation:
active: true
@@ -85,6 +73,48 @@ steps:
fail:
high: '0'
archive: false
+ influxWriteData:
+ influxServer: 'jenkins'
+ mavenExecute:
+ dockerImage: 'maven:3.5-jdk-7'
+ mtaBuild:
+ buildTarget: 'NEO'
+ mtaJarLocation: 'mta.jar'
+ neoDeploy:
+ deployMode: 'mta'
+ warAction: 'deploy'
+ vmSize: 'lite'
+ neoCredentialsId: 'CI_CREDENTIALS_ID'
+ pipelineStashFilesAfterBuild:
+ stashIncludes:
+ checkmarx: '**/*.js, **/*.scala, **/*.py, **/*.go, **/*.xml, **/*.html'
+ classFiles: '**/target/classes/**/*.class, **/target/test-classes/**/*.class'
+ sonar: '**/jacoco*.exec, **/sonar-project.properties'
+ stashExcludes:
+ checkmarx: '**/*.mockserver.js, node_modules/**/*.js'
+ classFiles: ''
+ sonar: ''
+ pipelineStashFilesBeforeBuild:
+ stashIncludes:
+ buildDescriptor: '**/pom.xml, **/.mvn/**, **/assembly.xml, **/.swagger-codegen-ignore, **/package.json, **/requirements.txt, **/setup.py, **/whitesource_config.py, **/mta*.y*ml, **/.npmrc, **/whitesource.*.json, **/whitesource-fs-agent.config, .xmake.cfg, Dockerfile, **/VERSION, **/version.txt, **/build.sbt, **/sbtDescriptor.json, **/project/*'
+ deployDescriptor: '**/manifest*.y*ml, **/*.mtaext.y*ml, **/*.mtaext, **/xs-app.json, helm/**, *.y*ml'
+ git: '**/gitmetadata/**'
+ opa5: '**/*.*'
+ 'opensource configuration': '**/srcclr.yml, **/vulas-custom.properties, **/.nsprc, **/.retireignore, **/.retireignore.json, **/'
+ pipelineConfigAndTests: '.pipeline/*.*'
+ securityDescriptor: '**/xs-security.json'
+ 'snyk configuration': '**/.snyk'
+ tests: '**/pom.xml, **/*.json, **/*.xml, **/src/**, **/node_modules/**, **/specs/**, **/env/**, **/*.js'
+ stashExcludes:
+ buildDescriptor: '**/node_modules/**/package.json'
+ deployDescriptor: ''
+ git: ''
+ opa5: ''
+ 'opensource configuration': ''
+ pipelineConfigAndTests: ''
+ securityDescriptor: ''
+ 'snyk configuration': ''
+ tests: ''
testsPublishResults:
junit:
pattern: '**/target/surefire-reports/*.xml'
diff --git a/src/com/sap/piper/Utils.groovy b/src/com/sap/piper/Utils.groovy
index 5b6792db0..11221baf9 100644
--- a/src/com/sap/piper/Utils.groovy
+++ b/src/com/sap/piper/Utils.groovy
@@ -15,3 +15,39 @@ def getMandatoryParameter(Map map, paramName, defaultValue = null) {
return paramValue
}
+
+def stash(name, include = '**/*.*', exclude = '') {
+ echo "Stash content: ${name} (include: ${include}, exclude: ${exclude})"
+ steps.stash name: name, includes: include, excludes: exclude
+}
+
+def stashWithMessage(name, msg, include = '**/*.*', exclude = '') {
+ try {
+ stash(name, include, exclude)
+ } catch (e) {
+ echo msg + name + " (${e.getMessage()})"
+ }
+}
+
+def unstash(name, msg = "Unstash failed:") {
+ def unstashedContent = []
+ try {
+ echo "Unstash content: ${name}"
+ steps.unstash name
+ unstashedContent += name
+ } catch (e) {
+ echo "$msg $name (${e.getMessage()})"
+ }
+ return unstashedContent
+}
+
+def unstashAll(stashContent) {
+ def unstashedContent = []
+ if (stashContent) {
+ for (i = 0; i < stashContent.size(); i++) {
+ unstashedContent += unstash(stashContent[i])
+ }
+ }
+ return unstashedContent
+}
+
diff --git a/test/groovy/InfluxWriteDataTest.groovy b/test/groovy/InfluxWriteDataTest.groovy
index 23d693895..95dd03516 100644
--- a/test/groovy/InfluxWriteDataTest.groovy
+++ b/test/groovy/InfluxWriteDataTest.groovy
@@ -63,7 +63,7 @@ class InfluxWriteDataTest extends BasePipelineTest {
assertEquals('testInflux', stepMap.selectedTarget)
assertEquals(null, stepMap.customPrefix)
assertEquals([:], stepMap.customData)
- assertEquals([pipeline_data:[:]], stepMap.customDataMap)
+ assertEquals([pipeline_data: [:], step_data: [:]], stepMap.customDataMap)
assertTrue(fileMap.containsKey('jenkins_data.json'))
assertTrue(fileMap.containsKey('pipeline_data.json'))
diff --git a/test/groovy/MavenExecuteTest.groovy b/test/groovy/MavenExecuteTest.groovy
index 7318190fb..cd80de9d4 100644
--- a/test/groovy/MavenExecuteTest.groovy
+++ b/test/groovy/MavenExecuteTest.groovy
@@ -9,10 +9,11 @@ import com.lesfurets.jenkins.unit.BasePipelineTest
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertTrue
+import util.BasePiperTest
import util.JenkinsShellCallRule
import util.Rules
-class MavenExecuteTest extends BasePipelineTest {
+class MavenExecuteTest extends BasePiperTest {
Map dockerParameters
@@ -23,7 +24,6 @@ class MavenExecuteTest extends BasePipelineTest {
.around(jscr)
def mavenExecuteScript
- def cpe
@Before
void init() {
@@ -37,13 +37,12 @@ class MavenExecuteTest extends BasePipelineTest {
})
mavenExecuteScript = loadScript("mavenExecute.groovy").mavenExecute
- cpe = loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment
}
@Test
void testExecuteBasicMavenCommand() throws Exception {
- mavenExecuteScript.call(script: [commonPipelineEnvironment: cpe], goals: 'clean install')
+ mavenExecuteScript.call(script: nullScript, goals: 'clean install')
assertEquals('maven:3.5-jdk-7', dockerParameters.dockerImage)
assert jscr.shell[0] == 'mvn clean install'
@@ -53,7 +52,7 @@ class MavenExecuteTest extends BasePipelineTest {
void testExecuteMavenCommandWithParameter() throws Exception {
mavenExecuteScript.call(
- script: [commonPipelineEnvironment: cpe],
+ script: nullScript,
dockerImage: 'maven:3.5-jdk-8-alpine',
goals: 'clean install',
globalSettingsFile: 'globalSettingsFile.xml',
@@ -70,7 +69,7 @@ class MavenExecuteTest extends BasePipelineTest {
@Test
void testMavenCommandForwardsDockerOptions() throws Exception {
- mavenExecuteScript.call(script: [commonPipelineEnvironment: cpe], goals: 'clean install')
+ mavenExecuteScript.call(script: nullScript, goals: 'clean install')
assertEquals('maven:3.5-jdk-7', dockerParameters.dockerImage)
assert jscr.shell[0] == 'mvn clean install'
diff --git a/test/groovy/PipelineStashFilesAfterBuildTest.groovy b/test/groovy/PipelineStashFilesAfterBuildTest.groovy
new file mode 100644
index 000000000..86ea41185
--- /dev/null
+++ b/test/groovy/PipelineStashFilesAfterBuildTest.groovy
@@ -0,0 +1,72 @@
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import util.*
+
+import static org.hamcrest.Matchers.containsString
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertThat
+
+class PipelineStashFilesAfterBuildTest extends BasePiperTest {
+ JenkinsStepRule jsr = new JenkinsStepRule(this)
+ JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
+ JenkinsReadJsonRule jrj = new JenkinsReadJsonRule(this)
+
+ @Rule
+ public RuleChain rules = Rules
+ .getCommonRules(this)
+ .around(jrj)
+ .around(jlr)
+ .around(jsr)
+
+ @Test
+ void testStashAfterBuild() {
+ helper.registerAllowedMethod("fileExists", [String.class], {
+ searchTerm ->
+ return false
+ })
+ jsr.step.call(
+ script: nullScript,
+ juStabUtils: utils
+ )
+ // asserts
+ assertFalse(jlr.log.contains('Stash content: checkmarx'))
+ assertThat(jlr.log, containsString('Stash content: classFiles (include: **/target/classes/**/*.class, **/target/test-classes/**/*.class, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: sonar (include: **/jacoco*.exec, **/sonar-project.properties, exclude: )'))
+ assertFalse(jlr.log.contains('Stash content: postStagedFiles'))
+ }
+
+ @Test
+ void testStashAfterBuildWithCheckmarx() {
+ helper.registerAllowedMethod("fileExists", [String.class], {
+ searchTerm ->
+ return true
+ })
+ jsr.step.call(
+ script: nullScript,
+ juStabUtils: utils,
+ runCheckmarx: true
+ )
+ // asserts
+ assertThat(jlr.log, containsString('Stash content: checkmarx (include: **/*.js, **/*.scala, **/*.py, **/*.go, **/*.xml, **/*.html, exclude: **/*.mockserver.js, node_modules/**/*.js)'))
+ assertThat(jlr.log, containsString('Stash content: classFiles (include: **/target/classes/**/*.class, **/target/test-classes/**/*.class, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: sonar (include: **/jacoco*.exec, **/sonar-project.properties, exclude: )'))
+ }
+
+ @Test
+ void testStashAfterBuildWithCheckmarxConfig() {
+ helper.registerAllowedMethod("fileExists", [String.class], {
+ searchTerm ->
+ return true
+ })
+ jsr.step.call(
+ script: [commonPipelineEnvironment: [configuration: [steps: [executeCheckmarxScan: [checkmarxProject: 'TestProject']]]]],
+ juStabUtils: utils,
+ )
+ // asserts
+ assertThat(jlr.log, containsString('Stash content: checkmarx (include: **/*.js, **/*.scala, **/*.py, **/*.go, **/*.xml, **/*.html, exclude: **/*.mockserver.js, node_modules/**/*.js)'))
+ assertThat(jlr.log, containsString('Stash content: classFiles (include: **/target/classes/**/*.class, **/target/test-classes/**/*.class, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: sonar (include: **/jacoco*.exec, **/sonar-project.properties, exclude: )'))
+ }
+
+}
diff --git a/test/groovy/PipelineStashFilesBeforeBuildTest.groovy b/test/groovy/PipelineStashFilesBeforeBuildTest.groovy
new file mode 100644
index 000000000..e93a3963a
--- /dev/null
+++ b/test/groovy/PipelineStashFilesBeforeBuildTest.groovy
@@ -0,0 +1,76 @@
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import util.*
+
+import static org.hamcrest.Matchers.containsString
+import static org.junit.Assert.*
+
+class PipelineStashFilesBeforeBuildTest extends BasePiperTest {
+ JenkinsStepRule jsr = new JenkinsStepRule(this)
+ JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
+ JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
+ //JenkinsReadJsonRule jrj = new JenkinsReadJsonRule(this)
+
+ @Rule
+ public RuleChain rules = Rules
+ .getCommonRules(this)
+ //.around(jrj)
+ .around(jlr)
+ .around(jscr)
+ .around(jsr)
+
+ @Test
+ void testStashBeforeBuildNoOpa() {
+
+ jsr.step.call(script: nullScript, juStabUtils: utils)
+
+ // asserts
+ assertEquals('mkdir -p gitmetadata', jscr.shell[0])
+ assertEquals('cp -rf .git/* gitmetadata', jscr.shell[1])
+ assertEquals('chmod -R u+w gitmetadata', jscr.shell[2])
+ assertFalse(jlr.log.contains('Stash content: opa5'))
+ assertThat(jlr.log, containsString('Stash content: git (include: **/gitmetadata/**, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: tests (include: **/pom.xml, **/*.json, **/*.xml, **/src/**, **/node_modules/**, **/specs/**, **/env/**, **/*.js, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: buildDescriptor (include: **/pom.xml, **/.mvn/**, **/assembly.xml, **/.swagger-codegen-ignore, **/package.json, **/requirements.txt, **/setup.py, **/whitesource_config.py, **/mta*.y*ml, **/.npmrc, **/whitesource.*.json, **/whitesource-fs-agent.config, .xmake.cfg, Dockerfile, **/VERSION, **/version.txt, **/build.sbt, **/sbtDescriptor.json, **/project/*, exclude: **/node_modules/**/package.json)'))
+ assertThat(jlr.log, containsString('Stash content: deployDescriptor (include: **/manifest*.y*ml, **/*.mtaext.y*ml, **/*.mtaext, **/xs-app.json, helm/**, *.y*ml, exclude: )'))
+ assertThat(jlr.log, containsString('Stash content: opensource configuration (include: **/srcclr.yml'))
+ assertThat(jlr.log, containsString('Stash content: snyk configuration (include: **/.snyk'))
+ assertThat(jlr.log, containsString('Stash content: pipelineConfigAndTests (include: .pipeline/*.*'))
+ assertThat(jlr.log, containsString('Stash content: securityDescriptor (include: **/xs-security.json'))
+ }
+
+ @Test
+ void testStashBeforeBuildOpa() {
+
+ jsr.step.call(script: nullScript, juStabUtils: utils, runOpaTests: true)
+
+ // asserts
+ assertThat(jlr.log, containsString('Stash content: opa5'))
+ assertThat(jlr.log, containsString('Stash content: git'))
+ assertThat(jlr.log, containsString('Stash content: tests'))
+ assertThat(jlr.log, containsString('Stash content: buildDescriptor'))
+ assertThat(jlr.log, containsString('Stash content: deployDescriptor'))
+ assertThat(jlr.log, containsString('Stash content: opensource configuration'))
+ assertThat(jlr.log, containsString('Stash content: snyk configuration'))
+ assertThat(jlr.log, containsString('Stash content: pipelineConfigAndTests'))
+ assertThat(jlr.log, containsString('Stash content: securityDescriptor'))
+ }
+
+ @Test
+ void testStashBeforeBuildOpaCompatibility() {
+
+ jsr.step.call(script: nullScript, juStabUtils: utils, runOpaTests: 'true')
+
+ // asserts
+ assertThat(jlr.log, containsString('Stash content: opa5'))
+ assertThat(jlr.log, containsString('Stash content: git'))
+ assertThat(jlr.log, containsString('Stash content: tests'))
+ assertThat(jlr.log, containsString('Stash content: buildDescriptor'))
+ assertThat(jlr.log, containsString('Stash content: deployDescriptor'))
+ assertThat(jlr.log, containsString('Stash content: opensource configuration'))
+ assertThat(jlr.log, containsString('Stash content: snyk configuration'))
+ assertThat(jlr.log, containsString('Stash content: pipelineConfigAndTests'))
+ assertThat(jlr.log, containsString('Stash content: securityDescriptor'))
+ }
+}
diff --git a/test/groovy/com/sap/piper/versioning/MavenArtifactVersioningTest.groovy b/test/groovy/com/sap/piper/versioning/MavenArtifactVersioningTest.groovy
index 19068baf3..aec4c810a 100644
--- a/test/groovy/com/sap/piper/versioning/MavenArtifactVersioningTest.groovy
+++ b/test/groovy/com/sap/piper/versioning/MavenArtifactVersioningTest.groovy
@@ -6,13 +6,14 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
+import util.BasePiperTest
import util.JenkinsReadMavenPomRule
import util.JenkinsShellCallRule
import util.Rules
import static org.junit.Assert.assertEquals
-class MavenArtifactVersioningTest extends BasePipelineTest{
+class MavenArtifactVersioningTest extends BasePiperTest{
Map dockerParameters
def mavenExecuteScript
@@ -21,10 +22,12 @@ 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'))
+ public RuleChain ruleChain = Rules
+ .getCommonRules(this)
+ .around(jscr)
+ .around(new JenkinsReadMavenPomRule(this, 'test/resources/MavenArtifactVersioning'))
@Before
void init() {
@@ -35,16 +38,11 @@ class MavenArtifactVersioningTest extends BasePipelineTest{
dockerParameters = parameters
closure()
})
-
- mavenExecuteScript = loadScript("mavenExecute.groovy").mavenExecute
- commonPipelineEnvironment = loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment
-
- prepareObjectInterceptors(this)
}
@Test
void testVersioning() {
- av = new MavenArtifactVersioning(this, [filePath: 'pom.xml'])
+ av = new MavenArtifactVersioning(nullScript, [filePath: 'pom.xml'])
assertEquals('1.2.3', av.getVersion())
av.setVersion('1.2.3-20180101')
assertEquals('mvn --file \'pom.xml\' versions:set -DnewVersion=1.2.3-20180101', jscr.shell[0])
@@ -52,15 +50,9 @@ class MavenArtifactVersioningTest extends BasePipelineTest{
@Test
void testVersioningCustomFilePathSnapshot() {
- av = new MavenArtifactVersioning(this, [filePath: 'snapshot/pom.xml'])
+ av = new MavenArtifactVersioning(nullScript, [filePath: 'snapshot/pom.xml'])
assertEquals('1.2.3', av.getVersion())
av.setVersion('1.2.3-20180101')
assertEquals('mvn --file \'snapshot/pom.xml\' versions:set -DnewVersion=1.2.3-20180101', jscr.shell[0])
}
-
- void prepareObjectInterceptors(object) {
- object.metaClass.invokeMethod = helper.getMethodInterceptor()
- object.metaClass.static.invokeMethod = helper.getMethodInterceptor()
- object.metaClass.methodMissing = helper.getMethodMissingInterceptor()
- }
}
diff --git a/test/groovy/util/BasePiperTest.groovy b/test/groovy/util/BasePiperTest.groovy
new file mode 100644
index 000000000..e90da1c35
--- /dev/null
+++ b/test/groovy/util/BasePiperTest.groovy
@@ -0,0 +1,52 @@
+#!groovy
+
+package util
+
+import com.lesfurets.jenkins.unit.BasePipelineTest
+import com.sap.piper.Utils
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.test.context.ContextConfiguration
+import org.springframework.test.context.TestExecutionListeners
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
+
+@RunWith(SpringJUnit4ClassRunner)
+@ContextConfiguration(classes = [BasePiperTestContext.class])
+@TestExecutionListeners(listeners = [LibraryLoadingTestExecutionListener.class], mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
+abstract class BasePiperTest extends BasePipelineTest {
+
+ @Autowired
+ MockHelper mockHelper
+
+ @Autowired
+ Script nullScript
+
+ @Autowired
+ Utils utils
+
+ @Override
+ @Before
+ void setUp() throws Exception {
+ helper = LibraryLoadingTestExecutionListener.singletonInstance
+ if(!isHelperInitialized()) {
+ super.setScriptRoots('.', 'vars')
+ super.setUp()
+ }
+ }
+
+ boolean isHelperInitialized() {
+ try {
+ helper.loadScript('dummy.groovy')
+ } catch (Exception e) {
+ if (e.getMessage().startsWith('GroovyScriptEngine is not initialized:'))
+ return false
+ }
+ return true
+ }
+
+ @Deprecated
+ void prepareObjectInterceptors(Object object) {
+ LibraryLoadingTestExecutionListener.prepareObjectInterceptors(object)
+ }
+}
diff --git a/test/groovy/util/BasePiperTestContext.groovy b/test/groovy/util/BasePiperTestContext.groovy
new file mode 100644
index 000000000..6f840bfdd
--- /dev/null
+++ b/test/groovy/util/BasePiperTestContext.groovy
@@ -0,0 +1,37 @@
+#!groovy
+
+package util
+
+import com.sap.piper.Utils
+import org.codehaus.groovy.runtime.InvokerHelper
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+
+@Configuration
+class BasePiperTestContext {
+
+ @Bean
+ Script nullScript() {
+ def nullScript = InvokerHelper.createScript(null, new Binding())
+ nullScript.currentBuild = [:]
+ LibraryLoadingTestExecutionListener.prepareObjectInterceptors(nullScript)
+ return nullScript
+ }
+
+ @Bean
+ Utils mockUtils() {
+ def mockUtils = new Utils()
+ mockUtils.steps = [
+ stash : { m -> println "stash name = ${m.name}" },
+ unstash: { println "unstash called ..." }
+ ]
+ LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockUtils)
+ return mockUtils
+ }
+
+ @Bean
+ MockHelper mockHelper() {
+ return new MockHelper()
+ }
+
+}
diff --git a/test/groovy/util/JenkinsEnvironmentRule.groovy b/test/groovy/util/JenkinsEnvironmentRule.groovy
index 00cd9c841..6944f4455 100644
--- a/test/groovy/util/JenkinsEnvironmentRule.groovy
+++ b/test/groovy/util/JenkinsEnvironmentRule.groovy
@@ -21,6 +21,11 @@ class JenkinsEnvironmentRule implements TestRule {
@Override
void evaluate() throws Throwable {
env = testInstance.loadScript('commonPipelineEnvironment.groovy').commonPipelineEnvironment
+ try {
+ testInstance?.nullScript.commonPipelineEnvironment = env
+ } catch (MissingPropertyException e) {
+ //kept for backward compatibility before all tests inherit from BasePiperTest
+ }
base.evaluate()
}
}
diff --git a/test/groovy/util/JenkinsReadJsonRule.groovy b/test/groovy/util/JenkinsReadJsonRule.groovy
new file mode 100644
index 000000000..58a6f8ab6
--- /dev/null
+++ b/test/groovy/util/JenkinsReadJsonRule.groovy
@@ -0,0 +1,45 @@
+package util
+
+import com.lesfurets.jenkins.unit.BasePipelineTest
+import groovy.json.JsonSlurper
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+class JenkinsReadJsonRule implements TestRule {
+
+ final BasePipelineTest testInstance
+ final String testRoot
+
+ JenkinsReadJsonRule(BasePipelineTest testInstance, 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("readJSON", [Map], { Map m ->
+ if(m.text) {
+ def js = new JsonSlurper()
+ return js.parseText(m.text)
+ } else if(m.file) {
+ def js = new JsonSlurper()
+ def reader = new BufferedReader(new FileReader( "${this.testRoot}${m.file}" ))
+ return js.parse(reader)
+ } else {
+ throw new IllegalArgumentException("Key 'text' is missing in map ${m}.")
+ }
+ })
+
+ base.evaluate()
+ }
+ }
+ }
+}
diff --git a/test/groovy/util/LibraryLoadingTestExecutionListener.groovy b/test/groovy/util/LibraryLoadingTestExecutionListener.groovy
new file mode 100644
index 000000000..d8dd628f6
--- /dev/null
+++ b/test/groovy/util/LibraryLoadingTestExecutionListener.groovy
@@ -0,0 +1,253 @@
+package util
+
+import com.lesfurets.jenkins.unit.InterceptingGCL
+import com.lesfurets.jenkins.unit.MethodSignature
+import com.lesfurets.jenkins.unit.PipelineTestHelper
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.springframework.test.context.TestContext
+import org.springframework.test.context.support.AbstractTestExecutionListener
+import org.springframework.test.context.support.DependencyInjectionTestExecutionListener
+
+import static com.lesfurets.jenkins.unit.MethodSignature.method
+
+class LibraryLoadingTestExecutionListener extends AbstractTestExecutionListener {
+
+ static PipelineTestHelper singletonInstance
+
+ static CompilerConfiguration configuration
+
+ static GroovyClassLoader cLoader
+
+ static {
+ configuration = new CompilerConfiguration()
+ cLoader = new InterceptingGCL(singletonInstance, LibraryLoadingTestExecutionListener.class.getClassLoader(), configuration)
+ }
+
+ static List TRACKED_ON_CLASS = []
+ static List TRACKED_ON_METHODS = []
+
+ static HashMap RESTORE_ON_CLASS = [:]
+ static HashMap RESTORE_ON_METHODS = [:]
+
+ static boolean START_METHOD_TRACKING = false
+ static boolean START_CLASS_TRACKING = false
+
+ @Override
+ int getOrder() {
+ return 2500
+ }
+
+ static void setSingletonInstance(PipelineTestHelper helper) {
+ if(null != helper) {
+ helper.metaClass.invokeMethod = {
+ String name, Object[] args ->
+ if ((LibraryLoadingTestExecutionListener.START_METHOD_TRACKING || LibraryLoadingTestExecutionListener.START_CLASS_TRACKING)
+ && name.equals("registerAllowedMethod")) {
+
+ List list
+ HashMap restore
+ if (LibraryLoadingTestExecutionListener.START_METHOD_TRACKING) {
+ list = LibraryLoadingTestExecutionListener.TRACKED_ON_METHODS
+ restore = LibraryLoadingTestExecutionListener.RESTORE_ON_METHODS
+ } else if (LibraryLoadingTestExecutionListener.START_CLASS_TRACKING) {
+ list = LibraryLoadingTestExecutionListener.TRACKED_ON_CLASS
+ restore = LibraryLoadingTestExecutionListener.RESTORE_ON_CLASS
+ }
+
+ Object methodName = args[0]
+ def key
+ if (args[1] instanceof List) {
+ List parameters = args[1]
+ key = method(methodName, parameters.toArray(new Class[parameters.size()]))
+ } else if (!(args[0] instanceof MethodSignature)) {
+ key = method(methodName, args[1])
+ }
+
+ if (null != key) {
+ list.add(key)
+ def existingValue = helper.removeAllowedMethodCallback(key)
+ if (!restore.containsKey(key)) {
+ restore.put(key, existingValue)
+ }
+ }
+ }
+ def m = delegate.metaClass.getMetaMethod(name, *args)
+ if( null != m)
+ return m.invoke(delegate, *args)
+ }
+ }
+ singletonInstance = helper
+ }
+
+ static PipelineTestHelper getSingletonInstance() {
+ if (singletonInstance == null) {
+ setSingletonInstance(new LibraryLoadingTestExecutionListener.PipelineTestHelperHook().helper)
+ }
+
+ return singletonInstance
+ }
+
+ @Override
+ void beforeTestClass(TestContext testContext) throws Exception {
+ super.beforeTestClass(testContext)
+ def helper = LibraryLoadingTestExecutionListener.getSingletonInstance()
+ registerDefaultAllowedMethods(helper)
+ LibraryLoadingTestExecutionListener.START_CLASS_TRACKING = true
+ }
+
+ @Override
+ void afterTestClass(TestContext testContext) throws Exception {
+ super.afterTestClass(testContext)
+ PipelineTestHelper helper = LibraryLoadingTestExecutionListener.getSingletonInstance()
+ helper.clearAllowedMethodCallbacks(LibraryLoadingTestExecutionListener.TRACKED_ON_CLASS)
+ LibraryLoadingTestExecutionListener.TRACKED_ON_CLASS.clear()
+
+ helper.putAllAllowedMethodCallbacks(LibraryLoadingTestExecutionListener.RESTORE_ON_CLASS)
+ LibraryLoadingTestExecutionListener.RESTORE_ON_CLASS.clear()
+
+ LibraryLoadingTestExecutionListener.START_CLASS_TRACKING = false
+
+ if (Boolean.TRUE.equals(testContext.getAttribute(DependencyInjectionTestExecutionListener.REINJECT_DEPENDENCIES_ATTRIBUTE))) {
+ LibraryLoadingTestExecutionListener.singletonInstance = null
+ PipelineTestHelper newHeiper = LibraryLoadingTestExecutionListener.getSingletonInstance()
+
+ def applicationContext = testContext.getApplicationContext()
+ def beanNames = applicationContext.getBeanDefinitionNames()
+ beanNames.each { name ->
+ LibraryLoadingTestExecutionListener.prepareObjectInterceptors(applicationContext.getBean(name))
+ }
+ }
+ }
+
+ @Override
+ void beforeTestMethod(TestContext testContext) throws Exception {
+ super.beforeTestMethod(testContext)
+ def testInstance = testContext.getTestInstance()
+ testInstance.binding.setVariable('currentBuild', [result: 'SUCCESS'])
+ PipelineTestHelper helper = LibraryLoadingTestExecutionListener.getSingletonInstance()
+ LibraryLoadingTestExecutionListener.START_METHOD_TRACKING = true
+ }
+
+ @Override
+ void afterTestMethod(TestContext testContext) throws Exception {
+ super.afterTestMethod(testContext)
+ def testInstance = testContext.getTestInstance()
+ PipelineTestHelper helper = LibraryLoadingTestExecutionListener.getSingletonInstance()
+
+ helper.clearCallStack()
+ helper.clearAllowedMethodCallbacks(LibraryLoadingTestExecutionListener.TRACKED_ON_METHODS)
+ LibraryLoadingTestExecutionListener.TRACKED_ON_METHODS.clear()
+
+ LibraryLoadingTestExecutionListener.START_METHOD_TRACKING = false
+
+ testInstance.getNullScript().commonPipelineEnvironment.reset()
+ }
+
+ def registerDefaultAllowedMethods(helper) {
+ helper.registerAllowedMethod("stage", [String.class, Closure.class], null)
+ helper.registerAllowedMethod("stage", [String.class, Closure.class], null)
+ helper.registerAllowedMethod("node", [String.class, Closure.class], null)
+ helper.registerAllowedMethod("node", [Closure.class], null)
+ helper.registerAllowedMethod( method('sh', Map.class), {m ->
+ println "sh-command: $m.script"
+ return ""
+ } )
+ helper.registerAllowedMethod( method('sh', String.class), {s ->
+ println "sh-command: $s"
+ return ""
+ } )
+ helper.registerAllowedMethod("checkout", [Map.class], null)
+ helper.registerAllowedMethod("echo", [String.class], null)
+ helper.registerAllowedMethod("timeout", [Map.class, Closure.class], null)
+ helper.registerAllowedMethod("step", [Map.class], null)
+ helper.registerAllowedMethod("input", [String.class], null)
+ helper.registerAllowedMethod("gitlabCommitStatus", [String.class, Closure.class], { String name, Closure c ->
+ c.delegate = delegate
+ helper.callClosure(c)
+ })
+ helper.registerAllowedMethod("gitlabBuilds", [Map.class, Closure.class], null)
+ helper.registerAllowedMethod("logRotator", [Map.class], null)
+ helper.registerAllowedMethod("buildDiscarder", [Object.class], null)
+ helper.registerAllowedMethod("pipelineTriggers", [List.class], null)
+ helper.registerAllowedMethod("properties", [List.class], null)
+ helper.registerAllowedMethod("dir", [String.class, Closure.class], null)
+ helper.registerAllowedMethod("archiveArtifacts", [Map.class], null)
+ helper.registerAllowedMethod("junit", [String.class], null)
+ helper.registerAllowedMethod("readFile", [String.class], null)
+ helper.registerAllowedMethod("disableConcurrentBuilds", [], null)
+ helper.registerAllowedMethod("gatlingArchive", [], null)
+
+ helper.registerAllowedMethod("unstash", [String.class], null)
+ helper.registerAllowedMethod("unstash", [Object.class, String.class], null)
+ helper.registerAllowedMethod("stash", [Map.class], null)
+ helper.registerAllowedMethod("echo", [String.class], null)
+ }
+
+ static def prepareObjectInterceptors(Object object) {
+ object.metaClass.invokeMethod = LibraryLoadingTestExecutionListener.singletonInstance.getMethodInterceptor()
+ object.metaClass.static.invokeMethod = LibraryLoadingTestExecutionListener.singletonInstance.getMethodInterceptor()
+ object.metaClass.methodMissing = LibraryLoadingTestExecutionListener.singletonInstance.getMethodMissingInterceptor()
+ }
+
+ static class PipelineTestHelperHook {
+ def helper = new PipelineTestHelper() {
+ def clearAllowedMethodCallbacks(Collection c = []) {
+ List itemsToRemove = []
+ c.each {
+ key ->
+ allowedMethodCallbacks.entrySet().each {
+ entry ->
+ if (entry?.getKey().equals(key))
+ itemsToRemove.add(entry.getKey())
+ }
+ }
+ allowedMethodCallbacks.keySet().removeAll(itemsToRemove)
+ }
+
+ def removeAllowedMethodCallback(Object key) {
+ def itemToRemove
+ allowedMethodCallbacks.entrySet().each {
+ entry ->
+ if (entry?.getKey().equals(key)) {
+ itemToRemove = entry.getKey()
+ }
+ }
+ if (null != itemToRemove) {
+ def itemValue = allowedMethodCallbacks.remove(itemToRemove)
+ return itemValue
+ }
+ return null
+ }
+
+ def putAllAllowedMethodCallbacks(HashMap m) {
+ m.entrySet().each {
+ entry ->
+ if(null != entry.getValue())
+ allowedMethodCallbacks.put(entry.getKey(), entry.getValue())
+ else
+ allowedMethodCallbacks.remove(entry.getKey())
+ }
+ }
+
+ protected void setGlobalVars(Binding binding) {
+ libLoader.libRecords.values().stream()
+ .flatMap { it.definedGlobalVars.entrySet().stream() }
+ .forEach { e ->
+ if (e.value instanceof Script) {
+ Script script = Script.cast(e.value)
+ // invoke setBinding from method to avoid interception
+ SCRIPT_SET_BINDING.invoke(script, binding)
+ LibraryLoadingTestExecutionListener.prepareObjectInterceptors(script)
+ script.metaClass.getMethods().findAll { it.name == 'call' }.forEach { m ->
+ LibraryLoadingTestExecutionListener.singletonInstance.registerAllowedMethod(method(e.value.class.name, m.getNativeParameterTypes()),
+ { args ->
+ m.doMethodInvoke(e.value, args)
+ })
+ }
+ }
+ binding.setVariable(e.key, e.value)
+ }
+ }
+ }
+ }
+}
diff --git a/test/groovy/util/Rules.groovy b/test/groovy/util/Rules.groovy
index e9c5791de..6a3b37a96 100644
--- a/test/groovy/util/Rules.groovy
+++ b/test/groovy/util/Rules.groovy
@@ -13,8 +13,9 @@ public class Rules {
public static RuleChain getCommonRules(BasePipelineTest testCase, LibraryConfiguration libConfig) {
return RuleChain.outerRule(new JenkinsSetupRule(testCase, libConfig))
- .around(new JenkinsReadYamlRule(testCase))
- .around(new JenkinsResetDefaultCacheRule())
- .around(new JenkinsErrorRule(testCase))
+ .around(new JenkinsReadYamlRule(testCase))
+ .around(new JenkinsResetDefaultCacheRule())
+ .around(new JenkinsErrorRule(testCase))
+ .around(new JenkinsEnvironmentRule(testCase))
}
}
diff --git a/vars/commonPipelineEnvironment.groovy b/vars/commonPipelineEnvironment.groovy
index 1ff5bdea6..cb9634b79 100644
--- a/vars/commonPipelineEnvironment.groovy
+++ b/vars/commonPipelineEnvironment.groovy
@@ -15,12 +15,28 @@ class commonPipelineEnvironment implements Serializable {
Map defaultConfiguration = [:]
//each Map in influxCustomDataMap represents a measurement in Influx. Additional measurements can be added as a new Map entry of influxCustomDataMap
- private Map influxCustomDataMap = [pipeline_data: [:]]
+ private Map influxCustomDataMap = [pipeline_data: [:], step_data: [:]]
//influxCustomData represents measurement jenkins_custom_data in Influx. Metrics can be written into this map
private Map influxCustomData = [:]
private String mtarFilePath
+ def reset() {
+ appContainerProperties = [:]
+ artifactVersion = null
+
+ configProperties = [:]
+ configuration = [:]
+
+ gitCommitId = null
+ gitSshUrl = null
+
+ influxCustomData = [:]
+ influxCustomDataMap = [pipeline_data: [:], step_data: [:]]
+
+ mtarFilePath = null
+ }
+
def setAppContainerProperty(property, value) {
appContainerProperties[property] = value
}
diff --git a/vars/pipelineStashFiles.groovy b/vars/pipelineStashFiles.groovy
new file mode 100644
index 000000000..eb2ea5214
--- /dev/null
+++ b/vars/pipelineStashFiles.groovy
@@ -0,0 +1,7 @@
+def call(Map parameters = [:], body) {
+ handlePipelineStepErrors (stepName: 'pipelineStashFiles', stepParameters: parameters) {
+ pipelineStashFilesBeforeBuild(parameters)
+ body() //execute build
+ pipelineStashFilesAfterBuild(parameters)
+ }
+}
diff --git a/vars/pipelineStashFilesAfterBuild.groovy b/vars/pipelineStashFilesAfterBuild.groovy
new file mode 100644
index 000000000..96649f3ef
--- /dev/null
+++ b/vars/pipelineStashFilesAfterBuild.groovy
@@ -0,0 +1,51 @@
+import com.sap.piper.Utils
+import com.sap.piper.ConfigurationHelper
+import groovy.transform.Field
+
+@Field String STEP_NAME = 'pipelineStashFilesAfterBuild'
+@Field Set STEP_CONFIG_KEYS = ['runCheckmarx', 'stashIncludes', 'stashExcludes']
+@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
+
+def call(Map parameters = [:]) {
+
+ handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
+ def utils = parameters.juStabUtils
+ if (utils == null) {
+ utils = new Utils()
+ }
+ def script = parameters.script
+ if (script == null)
+ script = [commonPipelineEnvironment: commonPipelineEnvironment]
+
+ //additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
+ //additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
+
+ Map config = ConfigurationHelper
+ .loadStepDefaults(this)
+ .mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
+ .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
+ .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
+ .mixin(parameters, PARAMETER_KEYS)
+ .addIfEmpty('runCheckmarx', (script.commonPipelineEnvironment.configuration?.steps?.executeCheckmarxScan?.checkmarxProject != null && script.commonPipelineEnvironment.configuration.steps.executeCheckmarxScan.checkmarxProject.length()>0))
+ .use()
+
+ // store files to be checked with checkmarx
+ if (config.runCheckmarx) {
+ utils.stash('checkmarx', config.stashIncludes?.get('checkmarx')?config.stashIncludes.checkmarx:'**/*.js, **/*.scala, **/*.py, **/*.go, **/*.xml, **/*.html', config.stashExcludes?.get('checkmarx')?config.stashExcludes.checkmarx:'**/*.mockserver.js, node_modules/**/*.js')
+ }
+
+ utils.stashWithMessage(
+ 'classFiles',
+ '[${STEP_NAME}] Failed to stash class files.',
+ config.stashIncludes.classFiles,
+ config.stashExcludes.classFiles
+ )
+
+ utils.stashWithMessage(
+ 'sonar',
+ '[${STEP_NAME}] Failed to stash sonar files.',
+ config.stashIncludes.sonar,
+ config.stashExcludes.sonar
+ )
+ }
+}
diff --git a/vars/pipelineStashFilesBeforeBuild.groovy b/vars/pipelineStashFilesBeforeBuild.groovy
new file mode 100644
index 000000000..a1569b801
--- /dev/null
+++ b/vars/pipelineStashFilesBeforeBuild.groovy
@@ -0,0 +1,97 @@
+import com.sap.piper.Utils
+import com.sap.piper.ConfigurationHelper
+import groovy.transform.Field
+
+@Field String STEP_NAME = 'pipelineStashFilesBeforeBuild'
+@Field Set STEP_CONFIG_KEYS = ['runOpaTests', 'stashIncludes', 'stashExcludes']
+@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
+
+def call(Map parameters = [:]) {
+
+ handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
+
+ def utils = parameters.juStabUtils
+ if (utils == null) {
+ utils = new Utils()
+ }
+
+ def script = parameters.script
+ if (script == null)
+ script = [commonPipelineEnvironment: commonPipelineEnvironment]
+
+ //additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
+ //additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
+
+ Map config = ConfigurationHelper
+ .loadStepDefaults(this)
+ .mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
+ .mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
+ .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
+ .mixin(parameters, PARAMETER_KEYS)
+ .use()
+
+ if (config.runOpaTests){
+ utils.stash('opa5', config.stashIncludes?.get('opa5')?config.stashIncludes.opa5:'**/*.*', config.stashExcludes?.get('opa5')?config.stashExcludes.opa5:'')
+ }
+
+ //store git metadata for SourceClear agent
+ sh "mkdir -p gitmetadata"
+ sh "cp -rf .git/* gitmetadata"
+ sh "chmod -R u+w gitmetadata"
+ utils.stashWithMessage(
+ 'git',
+ '[${STEP_NAME}] no git repo files detected: ',
+ config.stashIncludes.git,
+ config.stashExcludes.git
+ )
+
+ //store files required for tests, e.g. Gauge, SUT, ...
+ utils.stashWithMessage(
+ 'tests',
+ '[${STEP_NAME}] no files for tests provided: ',
+ config.stashIncludes.tests,
+ config.stashExcludes.tests
+ )
+ //store build descriptor files depending on technology, e.g. pom.xml, package.json
+ utils.stash(
+ 'buildDescriptor',
+ config.stashIncludes.buildDescriptor,
+ config.stashExcludes.buildDescriptor
+ )
+ //store deployment descriptor files depending on technology, e.g. *.mtaext.yml
+ utils.stashWithMessage(
+ 'deployDescriptor',
+ '[${STEP_NAME}] no deployment descriptor files provided: ',
+ config.stashIncludes.deployDescriptor,
+ config.stashExcludes.deployDescriptor
+ )
+ //store nsp & retire exclusion file for future use
+ utils.stashWithMessage(
+ 'opensource configuration',
+ '[${STEP_NAME}] no opensource configuration files provided: ',
+ config.stashIncludes.get('opensource configuration'),
+ config.stashExcludes.get('opensource configuration')
+ )
+ //store snyk config file for future use
+ utils.stashWithMessage(
+ 'snyk configuration',
+ '[${STEP_NAME}] no snyk configuration files provided: ',
+ config.stashIncludes.get('snyk configuration'),
+ config.stashExcludes.get('snyk configuration')
+ )
+ //store pipeline configuration including additional groovy test scripts for future use
+ utils.stashWithMessage(
+ 'pipelineConfigAndTests',
+ '[${STEP_NAME}] no pipeline configuration and test files found: ',
+ config.stashIncludes.pipelineConfigAndTests,
+ config.stashExcludes.pipelineConfigAndTests
+ )
+
+ utils.stashWithMessage(
+ 'securityDescriptor',
+ '[${STEP_NAME}] no security descriptor found: ',
+ config.stashIncludes.securityDescriptor,
+ config.stashExcludes.securityDescriptor
+ )
+ }
+}