Merge remote-tracking branch 'github/master' into HEAD

This commit is contained in:
Marcus Holl
2018-09-27 08:24:04 +02:00
16 changed files with 125 additions and 71 deletions
+4
View File
@@ -25,6 +25,10 @@ All pipeline library coding _must_ come with automated unit tests.
The contract of functionality exposed by a library functionality needs to be documented, so it can be properly used.
Implementation of a functionality and its documentation shall happen within the same commit(s).
### Coding pattern
Pipeline steps must not make use of return values. The pattern for sharing parameters between pipeline steps or between a pipeline step and a pipeline script is sharing values via the [`commonPipelineEnvironment`](../vars/commonPipelineEnvironment.groovy). Since there is no return value from a pipeline step the return value of a pipeline step is already `void` rather than `def`.
### Code Style
The code should follow any stylistic and architectural guidelines prescribed by the project. In the absence of guidelines, mimic the styles and patterns in the existing code-base.
+11 -7
View File
@@ -2,22 +2,27 @@ branches:
only:
- master
language: groovy
sudo: false
install:
- pip install --user mkdocs==0.17.5 mkdocs-material==2.9.4
sudo: required
services:
- docker
before_install:
- docker pull squidfunk/mkdocs-material
script:
- mvn clean test --batch-mode
- |
if [[ "${TRAVIS_PULL_REQUEST}" != "false" ]]
then
cd documentation && mkdocs build --clean --verbose --strict && cd ..
docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material build --clean --verbose --strict
else
# Only in case we are in master branch of the leading SAP repo we would like to deploy,
# not from the forks.
if [[ "${TRAVIS_BRANCH}" == "master" && "${TRAVIS_REPO_SLUG}" == "SAP/jenkins-library" ]]
then
openssl aes-256-cbc -K $encrypted_12c8071d2874_key -iv $encrypted_12c8071d2874_iv -in cfg/id_rsa.enc -out cfg/id_rsa -d
./gh-pages-deploy.sh
echo "Found change on master: Deployment of documentation"
PRIVATE_KEY="cfg/id_rsa"
openssl aes-256-cbc -K $encrypted_12c8071d2874_key -iv $encrypted_12c8071d2874_iv -in cfg/id_rsa.enc -out "${PRIVATE_KEY}" -d
chmod a+x gh-pages-deploy.sh
docker run --rm -it --entrypoint "./gh-pages-deploy.sh" -e "TRAVIS_REPO_SLUG=${TRAVIS_REPO_SLUG}" -v ${TRAVIS_BUILD_DIR}:/docs -w /docs squidfunk/mkdocs-material
else
echo "Publishing documentation skipped."
fi
@@ -25,7 +30,6 @@ script:
cache:
directories:
- $HOME/.m2
- $HOME/.cache/pip
after_success:
- mvn -DrepoToken=$COVERALLS_REPO_TOKEN org.jacoco:jacoco-maven-plugin:report org.eluder.coveralls:coveralls-maven-plugin:report
#notifications:
+1 -1
View File
@@ -117,7 +117,7 @@ otherwise in the [LICENSE file][piper-library-license]
[piper-library-pages-plugins]: https://sap.github.io/jenkins-library/jenkins/requiredPlugins
[piper-library-issues]: https://github.com/SAP/jenkins-library/issues
[piper-library-license]: ./LICENSE
[piper-library-contribution]: ./CONTRIBUTING.md
[piper-library-contribution]: .github/CONTRIBUTING.md
[jenkins-doc-pipelines]: https://jenkins.io/solutions/pipeline
[jenkins-doc-libraries]: https://jenkins.io/doc/book/pipeline/shared-libraries
[jenkins-doc-steps]: https://jenkins.io/doc/pipeline/steps
+1 -1
View File
@@ -1,5 +1,5 @@
site_name: Jenkins 2.0 Pipelines
pages:
nav:
- Home: index.md
- Configuration: configuration.md
- 'Library steps':
Executable → Regular
+4 -3
View File
@@ -1,15 +1,16 @@
#!/bin/bash
echo "Found change on master: Deployment of documentation"
PRIVATE_KEY="cfg/id_rsa"
chmod 600 "${PRIVATE_KEY}"
eval `ssh-agent -s`
ssh-add "${PRIVATE_KEY}"
mkdir ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan github.com >> ~/.ssh/known_hosts
git config user.name "Travis CI Publisher"
git remote add docu "git@github.com:$TRAVIS_REPO_SLUG.git";
git fetch docu gh-pages:gh-pages
echo "Pushing to gh-pages of repository $TRAVIS_REPO_SLUG"
cd $TRAVIS_BUILD_DIR/documentation
cd documentation
mkdocs gh-deploy -v --clean --remote-name docu
+8 -1
View File
@@ -1,5 +1,7 @@
package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
class ConfigurationHelper implements Serializable {
static ConfigurationHelper loadStepDefaults(Script step){
return new ConfigurationHelper(step)
@@ -96,7 +98,12 @@ class ConfigurationHelper implements Serializable {
return this
}
Map use(){ return config }
@NonCPS // required because we have a closure in the
// method body that cannot be CPS transformed
Map use(){
MapUtils.traverse(config, { v -> (v instanceof GString) ? v.toString() : v })
return config
}
ConfigurationHelper(Map config = [:]){
this.config = config
+24
View File
@@ -38,4 +38,28 @@ class MapUtils implements Serializable {
return result
}
/**
* @param m The map to which the changed denoted by closure <code>strategy</code>
* should be applied.
* The strategy is also applied to all sub-maps contained as values
* in <code>m</code> in a recursive manner.
* @param strategy Strategy applied to all non-map entries
*/
@NonCPS
static void traverse(Map m, Closure strategy) {
def updates = [:]
for(def e : m.entrySet()) {
if(isMap(e.value)) {
traverse(e.getValue(), strategy)
}
else {
// do not update the map while it is traversed. Depending
// on the map implementation the behavior is undefined.
updates.put(e.getKey(), strategy(e.getValue()))
}
}
m.putAll(updates)
}
}
+2 -20
View File
@@ -6,6 +6,7 @@ import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import org.yaml.snakeyaml.Yaml
import util.BasePiperTest
import util.JenkinsCredentialsRule
import util.JenkinsEnvironmentRule
import util.JenkinsDockerExecuteRule
import util.JenkinsLoggingRule
@@ -42,28 +43,9 @@ class CloudFoundryDeployTest extends BasePiperTest {
.around(jwfr)
.around(jedr)
.around(jer)
.around(new JenkinsCredentialsRule(this).withCredentials('test_cfCredentialsId', 'test_cf', '********'))
.around(jsr) // needs to be activated after jedr, otherwise executeDocker is not mocked
@Before
void init() throws Throwable {
helper.registerAllowedMethod('usernamePassword', [Map], { m -> return m })
helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c ->
if(l[0].credentialsId == 'test_cfCredentialsId') {
binding.setProperty('username', 'test_cf')
binding.setProperty('password', '********')
} else if(l[0].credentialsId == 'test_camCredentialsId') {
binding.setProperty('username', 'test_cam')
binding.setProperty('password', '********')
}
try {
c()
} finally {
binding.setProperty('username', null)
binding.setProperty('password', null)
}
})
}
@Test
void testNoTool() throws Exception {
nullScript.commonPipelineEnvironment.configuration = [
+7 -20
View File
@@ -5,8 +5,10 @@ import org.junit.rules.TemporaryFolder
import org.junit.BeforeClass
import org.junit.ClassRule
import org.junit.Ignore
import org.hamcrest.BaseMatcher
import org.hamcrest.Description
import org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException
import org.junit.Assert
import org.junit.Before
import org.junit.Rule
@@ -15,6 +17,7 @@ import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.BasePiperTest
import util.JenkinsCredentialsRule
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsShellCallRule
@@ -40,6 +43,9 @@ class NeoDeployTest extends BasePiperTest {
.around(thrown)
.around(jlr)
.around(jscr)
.around(new JenkinsCredentialsRule(this)
.withCredentials('myCredentialsId', 'anonymous', '********')
.withCredentials('CI_CREDENTIALS_ID', 'defaultUser', '********'))
.around(jsr)
private static workspacePath
@@ -66,24 +72,6 @@ class NeoDeployTest extends BasePiperTest {
helper.registerAllowedMethod('dockerExecute', [Map, Closure], null)
helper.registerAllowedMethod('fileExists', [String], { s -> return new File(workspacePath, s).exists() })
helper.registerAllowedMethod('usernamePassword', [Map], { m -> return m })
helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c ->
if(l[0].credentialsId == 'myCredentialsId') {
binding.setProperty('username', 'anonymous')
binding.setProperty('password', '********')
} else if(l[0].credentialsId == 'CI_CREDENTIALS_ID') {
binding.setProperty('username', 'defaultUser')
binding.setProperty('password', '********')
}
try {
c()
} finally {
binding.setProperty('username', null)
binding.setProperty('password', null)
}
})
helper.registerAllowedMethod('sh', [Map], { Map m -> getVersionWithEnvVars(m) })
nullScript.commonPipelineEnvironment.configuration = [steps:[neoDeploy: [host: 'test.deploy.host.com', account: 'trialuser123']]]
@@ -179,8 +167,7 @@ class NeoDeployTest extends BasePiperTest {
@Test
void badCredentialsIdTest() {
thrown.expect(MissingPropertyException)
thrown.expectMessage('No such property: username')
thrown.expect(CredentialNotFoundException)
jsr.step.call(script: nullScript,
archivePath: archiveName,
+13 -9
View File
@@ -30,16 +30,14 @@ public class PrepareDefaultValuesTest extends BasePiperTest {
@Before
public void setup() {
helper.registerAllowedMethod("libraryResource", [String], { fileName-> return fileName })
helper.registerAllowedMethod("readYaml", [Map], { m ->
switch(m.text) {
case 'default_pipeline_environment.yml': return [default: 'config']
case 'custom.yml': return [custom: 'myConfig']
helper.registerAllowedMethod("libraryResource", [String], { fileName ->
switch(fileName) {
case 'default_pipeline_environment.yml': return "default: 'config'"
case 'custom.yml': return "custom: 'myConfig'"
case 'not_found': throw new hudson.AbortException('No such library resource not_found could be found')
default: return [the:'end']
default: return "the:'end'"
}
})
}
@Test
@@ -54,11 +52,16 @@ public class PrepareDefaultValuesTest extends BasePiperTest {
@Test
public void testReInitializeOnCustomConfig() {
DefaultValueCache.createInstance([key:'value'])
def instance = DefaultValueCache.createInstance([key:'value'])
// existing instance is dropped in case a custom config is provided.
jsr.step.call(script: nullScript, customDefaults: 'custom.yml')
// this check is for checking we have another instance
assert ! instance.is(DefaultValueCache.getInstance())
// some additional checks that the configuration represented by the new
// config is fine
assert DefaultValueCache.getInstance().getDefaultValues().size() == 2
assert DefaultValueCache.getInstance().getDefaultValues().default == 'config'
assert DefaultValueCache.getInstance().getDefaultValues().custom == 'myConfig'
@@ -67,10 +70,11 @@ public class PrepareDefaultValuesTest extends BasePiperTest {
@Test
public void testNoReInitializeWithoutCustomConfig() {
DefaultValueCache.createInstance([key:'value'])
def instance = DefaultValueCache.createInstance([key:'value'])
jsr.step.call(script: nullScript)
assert instance.is(DefaultValueCache.getInstance())
assert DefaultValueCache.getInstance().getDefaultValues().size() == 1
assert DefaultValueCache.getInstance().getDefaultValues().key == 'value'
}
@@ -241,4 +241,32 @@ class ConfigurationHelperTest {
Assert.assertThat(configuration, hasEntry('collectTelemetryData', false))
}
@Test
public void testGStringsAreReplacedByJavaLangStrings() {
//
// needed in order to ensure we have real GStrings.
// a GString not containing variables might be optimized to
// a java.lang.String from the very beginning.
def dummy = 'Dummy',
aGString = "a${dummy}",
bGString = "b${dummy}",
cGString = "c${dummy}"
assert aGString instanceof GString
assert bGString instanceof GString
assert cGString instanceof GString
def config = new ConfigurationHelper([a: aGString,
nextLevel: [b: bGString]])
.mixin([c : cGString])
.use()
assert config == [a: 'aDummy',
c: 'cDummy',
nextLevel: [b: 'bDummy']]
assert config.a instanceof java.lang.String
assert config.c instanceof java.lang.String
assert config.nextLevel.b instanceof java.lang.String
}
}
@@ -19,8 +19,13 @@ import static org.junit.Assert.assertNotNull
import static org.junit.Assert.assertNull
import static org.junit.Assert.assertThat
import org.springframework.beans.factory.annotation.Autowired
class GitUtilsTest extends BasePiperTest {
@Autowired
GitUtils gitUtils
JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
ExpectedException thrown = ExpectedException.none()
@@ -43,4 +43,11 @@ class MapUtilsTest {
c: [ d: 'abc',
e: '']]
}
@Test
void testTraverse() {
Map m = [a: 'x1', m:[b: 'x2', c: 'otherString']]
MapUtils.traverse(m, { s -> (s.startsWith('x')) ? "replaced" : s})
assert m == [a: 'replaced', m: [b: 'replaced', c: 'otherString']]
}
}
-3
View File
@@ -20,9 +20,6 @@ abstract class BasePiperTest extends BasePipelineTest {
@Autowired
Script nullScript
@Autowired
GitUtils gitUtils
@Autowired
Utils utils
+2 -2
View File
@@ -30,8 +30,8 @@ class BasePiperTestContext {
Utils mockUtils() {
def mockUtils = new Utils()
mockUtils.steps = [
stash : { m -> println "stash name = ${m.name}" },
unstash: { println "unstash called ..." }
stash : { },
unstash: { }
]
LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockUtils)
return mockUtils
@@ -14,6 +14,7 @@ import static org.hamcrest.Matchers.nullValue
import static org.junit.Assert.assertThat;
import org.hamcrest.Matchers
import org.jenkinsci.plugins.credentialsbinding.impl.CredentialNotFoundException
/**
* By default a user &quot;anonymous&quot; with password &quot;********&quot;
@@ -47,16 +48,19 @@ class JenkinsCredentialsRule implements TestRule {
@Override
void evaluate() throws Throwable {
testInstance.helper.registerAllowedMethod('usernamePassword', [Map.class], {m -> return m})
testInstance.helper.registerAllowedMethod('usernamePassword', [Map.class],
{ m -> if (credentials.keySet().contains(m.credentialsId)) return m;
// this is what really happens in case of an unknown credentials id,
// checked with reality using credentials plugin 2.1.18.
throw new CredentialNotFoundException(
"Could not find credentials entry with ID '${m.credentialsId}'")
})
testInstance.helper.registerAllowedMethod('withCredentials', [List, Closure], { l, c ->
def credsId = l[0].credentialsId
def creds = credentials.get(credsId)
assertThat("Unexpected credentialsId received: '${credsId}'.",
creds, is(not(nullValue())))
binding.setProperty('username', creds?.user)
binding.setProperty('password', creds?.passwd)
try {