mirror of
https://github.com/SAP/jenkins-library.git
synced 2026-06-19 22:58:55 +02:00
Merge remote-tracking branch 'github/master' into HEAD
This commit is contained in:
@@ -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
@@ -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:
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
site_name: Jenkins 2.0 Pipelines
|
||||
pages:
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Configuration: configuration.md
|
||||
- 'Library steps':
|
||||
|
||||
Executable → Regular
+4
-3
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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']]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ abstract class BasePiperTest extends BasePipelineTest {
|
||||
@Autowired
|
||||
Script nullScript
|
||||
|
||||
@Autowired
|
||||
GitUtils gitUtils
|
||||
|
||||
@Autowired
|
||||
Utils utils
|
||||
|
||||
|
||||
@@ -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 "anonymous" with password "********"
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user