1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00

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

This commit is contained in:
Marcus Holl 2019-02-27 16:02:45 +01:00
commit baad41167a
31 changed files with 625 additions and 85 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ hs_err_pid*
target/ target/
targets/ targets/
documentation/docs-gen documentation/docs-gen
consumer-test/**/workspace

View File

@ -35,7 +35,11 @@ jobs:
cp -r documentation/docs documentation/docs-tmp cp -r documentation/docs documentation/docs-tmp
documentation/bin/createDocu.sh vars documentation/docs-tmp/steps documentation/bin/createDocu.sh vars documentation/docs-tmp/steps
docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material:3.0.4 build --clean --verbose --strict docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material:3.0.4 build --clean --verbose --strict
- name: Consumer Tests for s4sdk pipeline (CloudFoundry)
script: cd consumer-test/s4sdk/CloudFoundry && chmod +x runTests.sh && ./runTests.sh
- name: Consumer Tests for s4sdk pipeline (Neo Environment)
script: cd consumer-test/s4sdk/NeoEnvironment && chmod +x runTests.sh && ./runTests.sh
- stage: Docs - stage: Docs
name: Deploy name: Deploy
if: repo = "SAP/jenkins-library" AND branch = master AND NOT type = pull_request if: repo = "SAP/jenkins-library" AND branch = master AND NOT type = pull_request

19
Jenkinsfile vendored
View File

@ -1,19 +0,0 @@
node {
try {
lock(resource: "sap-jenkins-library/10", inversePrecedence: true) {
milestone 10
deleteDir()
stage ('Checkout'){
checkout scm
}
stage ('Test') {
sh "mvn clean test --batch-mode"
}
}
} catch (Throwable err) {
echo "Error occured: ${err}"
currentBuild.result = 'FAILURE'
mail subject: '[Build failed] SAP/jenkins-library', body: 'Fix the build.', to: 'marcus.holl@sap.com,oliver.nocon@sap.com'
throw err
}
}

View File

@ -108,7 +108,7 @@ Register to our [google group][google-group] in order to get updates or for aski
Read and understand our [contribution guidelines][piper-library-contribution] Read and understand our [contribution guidelines][piper-library-contribution]
before opening a pull request. before opening a pull request.
# [License][piper-library-license] # License
Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved. Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved.
This file is licensed under the Apache Software License, v. 2 except as noted This file is licensed under the Apache Software License, v. 2 except as noted

View File

@ -0,0 +1,6 @@
#!/bin/bash -e
source ../prepareTests.sh consumer-test
docker run -v /var/run/docker.sock:/var/run/docker.sock -v "${PWD}":/workspace -v /tmp -e CASC_JENKINS_CONFIG=/workspace/jenkins.yml \
-e CX_INFRA_IT_CF_USERNAME -e CX_INFRA_IT_CF_PASSWORD -e BRANCH_NAME=consumer-test ppiper/jenkinsfile-runner

View File

@ -0,0 +1,6 @@
#!/bin/bash -e
source ../prepareTests.sh consumer-test-neo
docker run -v /var/run/docker.sock:/var/run/docker.sock -v "${PWD}":/workspace -v /tmp -e CASC_JENKINS_CONFIG=/workspace/jenkins.yml \
-e CX_INFRA_IT_CF_USERNAME -e CX_INFRA_IT_CF_PASSWORD -e BRANCH_NAME=consumer-test-neo ppiper/jenkinsfile-runner

View File

@ -0,0 +1,29 @@
jenkins:
numExecutors: 10
unclassified:
globallibraries:
libraries:
- defaultVersion: "master"
name: "s4sdk-pipeline-library"
retriever:
modernSCM:
scm:
git:
remote: "https://github.com/SAP/cloud-s4-sdk-pipeline-lib.git"
- defaultVersion: "master"
name: "piper-library-os"
retriever:
modernSCM:
scm:
git:
remote: "https://github.com/__REPO_SLUG__.git"
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
scope: GLOBAL
id: "devops-docker-images-IT-cf"
username: ${CX_INFRA_IT_CF_USERNAME}
password: ${CX_INFRA_IT_CF_PASSWORD}
description: "SAP CP Trail account for test deployment"

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
EXAMPLE_PROJECT_BRANCH=$1
LIBRARY_VERSION_UNDER_TEST=$(git log --format="%H" -n 1)
REPOSITORY_UNDER_TEST=${TRAVIS_REPO_SLUG:-SAP/jenkins-library}
rm -rf workspace
git clone -b "${EXAMPLE_PROJECT_BRANCH}" https://github.com/sap/cloud-s4-sdk-book workspace
cp -f ../jenkins.yml workspace
cd workspace || exit 1
# Configure path to library-repository under test in Jenkins config
sed -i -e "s:__REPO_SLUG__:${REPOSITORY_UNDER_TEST}:g" jenkins.yml
# Force usage of library version under test by setting it in the Jenkinsfile which is then the first definition and thus has the highest precedence
echo "@Library(\"piper-library-os@$LIBRARY_VERSION_UNDER_TEST\") _" | cat - Jenkinsfile > temp && mv temp Jenkinsfile
# Commit the changed version because artifactSetVersion expects the git repo not to be dirty
git commit --all --author="piper-testing-bot <piper-testing-bot@example.com>" --message="Set piper lib version for test"

View File

@ -2,7 +2,7 @@
## Description ## Description
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Prerequisites ## Prerequisites
@ -10,11 +10,11 @@ Content here is generated from corresponnding step, see `vars`.
## Parameters ## Parameters
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Step configuration ## Step configuration
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Exceptions ## Exceptions

View File

@ -2,11 +2,11 @@
## Description ## Description
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Parameters ## Parameters
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Kubernetes support ## Kubernetes support
@ -14,7 +14,7 @@ If the Jenkins is setup on a Kubernetes cluster, then you can execute the closur
## Step configuration ## Step configuration
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Side effects ## Side effects

View File

@ -2,7 +2,7 @@
## Description ## Description
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Prerequisites ## Prerequisites
@ -13,11 +13,11 @@ Content here is generated from corresponnding step, see `vars`.
## Parameters ## Parameters
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Step configuration ## Step configuration
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Side effects ## Side effects

View File

@ -19,23 +19,23 @@ none
* `stepParameters` - The parameters from the step to be executed. The list of parameters is then shown in the console output. * `stepParameters` - The parameters from the step to be executed. The list of parameters is then shown in the console output.
* `stepName` - The name of the step executed to be shown in the console output. * `stepName` - The name of the step executed to be shown in the console output.
* `echoDetails` - If set to true the following will be output to the console: * `echoDetails` - If set to true the following will be output to the console:
1. Step beginning: `--- BEGIN LIBRARY STEP: ${stepName}.groovy ---` 1. Step beginning: `--- Begin library step: ${stepName}.groovy ---`
2. Step end: `--- END LIBRARY STEP: ${stepName}.groovy ---` 2. Step end: `--- End library step: ${stepName}.groovy ---`
3. Step errors: 3. Step errors:
```log ```log
---------------------------------------------------------- ----------------------------------------------------------
--- ERROR OCCURED IN LIBRARY STEP: ${stepName} --- An error occurred in the library step: ${stepName}
---------------------------------------------------------- ----------------------------------------------------------
FOLLOWING PARAMETERS WERE AVAILABLE TO THIS STEP: The following parameters were available to the step:
*** ***
${stepParameters} ${stepParameters}
*** ***
ERROR WAS: The error was:
*** ***
${err} ${err}
*** ***
FURTHER INFORMATION: Further information:
* Documentation of step ${stepName}: .../${stepName}/ * Documentation of step ${stepName}: .../${stepName}/
* Pipeline documentation: https://... * Pipeline documentation: https://...
* GitHub repository for pipeline steps: https://... * GitHub repository for pipeline steps: https://...

View File

@ -8,29 +8,21 @@ Before doing this, validates that SAP Multitarget Application Archive Builder ex
Note that a version is formed by `major.minor.patch`, and a version is compatible to another version if the minor and patch versions are higher, but the major version is not, e.g. if 3.39.10 is the expected version, 3.39.11 and 3.40.1 would be compatible versions, but 4.0.1 would not be a compatible version. Note that a version is formed by `major.minor.patch`, and a version is compatible to another version if the minor and patch versions are higher, but the major version is not, e.g. if 3.39.10 is the expected version, 3.39.11 and 3.40.1 would be compatible versions, but 4.0.1 would not be a compatible version.
## Prerequisites
* A docker image meeting the following requirements
* **SAP MTA Archive Builder 1.0.6 or compatible version** - can be downloaded from [SAP Development Tools](https://tools.hana.ondemand.com/#cloud).
* **Java 8 or compatible version** - necessary to run the `mta.jar` file.
* **NodeJS installed** - the MTA Builder uses `npm` to download node module dependencies such as `grunt`.
## Parameters ## Parameters
| parameter | mandatory | default | possible values | | parameter | mandatory | default | possible values |
| -----------------|-----------|--------------------------------------------------------|--------------------| | -----------------|-----------|--------------------------------------------------------|--------------------|
| `script` | yes | | | | `script` | yes | | |
| `dockerImage` | yes | | | | `dockerImage` | no | `ppiper/mta-archive-builder` | |
| `dockerOptions` | no | '' | | | `dockerOptions` | no | '' | |
| `buildTarget` | yes | `'NEO'` | 'CF', 'NEO', 'XSA' | | `buildTarget` | yes | `'NEO'` | 'CF', 'NEO', 'XSA' |
| `extension` | no | | | | `extension` | no | | |
| `mtaJarLocation` | no | `'mta.jar'` | | | `mtaJarLocation` | no | `'/opt/sap/mta/lib/mta.jar'` | |
| `applicationName`| no | | | | `applicationName`| no | | |
* `script` - The common script environment of the Jenkinsfile running. Typically the reference to the script calling the pipeline step is provided with the `this` parameter, as in `script: this`. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving, for example, configuration parameters. * `script` - The common script environment of the Jenkinsfile running. Typically the reference to the script calling the pipeline step is provided with the `this` parameter, as in `script: this`. This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md) for retrieving, for example, configuration parameters.
* `dockerImage` - The Docker image to execute the MTA build. * `dockerImage` - The Docker image to execute the MTA build.
A custom built image needs to include Multi-target Application Archive Builder. Note that you can provide your own image if required, but for most cases, the default should be fine.
Refer to [SAP Help Portal](https://help.sap.com/viewer/58746c584026430a890170ac4d87d03b/Cloud/en-US/ba7dd5a47b7a4858a652d15f9673c28d.html) for information on how to set it up.
* `dockerOptions` Docker options to be set when starting the container. It can be a list or a string. * `dockerOptions` Docker options to be set when starting the container. It can be a list or a string.
* `buildTarget` - The target platform to which the mtar can be deployed. * `buildTarget` - The target platform to which the mtar can be deployed.
* `extension` - The path to the extension descriptor file. * `extension` - The path to the extension descriptor file.

View File

@ -0,0 +1,23 @@
# npmExecute
## Description
Content here is generated from corresponding step, see `vars`.
## Parameters
Content here is generated from corresponding step, see `vars`.
## Step configuration
Content here is generated from corresponding step, see `vars`.
## Exceptions
None
## Examples
```groovy
npmExecute script: this, dockerImage: 'node:8-stretch', npmCommand: 'run build'
```

View File

@ -0,0 +1,73 @@
# slackSendNotification
## Description
Sends notifications to the Slack channel about the build status.
Notification contains:
* Build status;
* Repo Owner;
* Repo Name;
* Branch Name;
* Jenkins Build Number;
* Jenkins Build URL.
## Prerequisites
Installed and configured [Jenkins Slack plugin](https://github.com/jenkinsci/slack-plugin).
## Example
Usage of pipeline step:
```groovy
try {
stage('..') {..}
stage('..') {..}
stage('..') {..}
currentBuild.result = 'SUCCESS'
} catch (Throwable err) {
currentBuild.result = 'FAILURE'
throw err
} finally {
stage('report') {
slackSendNotification script: this
}
}
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|baseUrl|no|||
|channel|no|||
|color|no|`${buildStatus == 'SUCCESS'?'#008000':'#E60000'}`||
|credentialsId|no|||
|message|no|||
### Details
* `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 storing the measured duration.
* `baseUrl` allows overriding the Slack Plugin Integration Base Url specified in the global configuration.
* `color` defines the message color.
* If `channel` is defined another than the default channel will be used.
* `credentialsId` defines the Jenkins credentialId which holds the Slack token
* With parameter `message` a custom message can be defined which is sent into the Slack channel.
## Step configuration
We recommend to define values of step parameters via [config.yml file](../configuration.md).
In following sections the configuration is possible:
| parameter | general | step | stage |
| ----------|-----------|---------|-----------------|
|script||||
|baseUrl||X|X|
|channel||X|X|
|color||X|X|
|credentialsId||X|X|
|message||X|X|

View File

@ -2,17 +2,17 @@
## Description ## Description
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Prerequisites ## Prerequisites
## Parameters ## Parameters
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Step configuration ## Step configuration
Content here is generated from corresponnding step, see `vars`. Content here is generated from corresponding step, see `vars`.
## Exceptions ## Exceptions

View File

@ -24,12 +24,14 @@ nav:
- mtaBuild: steps/mtaBuild.md - mtaBuild: steps/mtaBuild.md
- neoDeploy: steps/neoDeploy.md - neoDeploy: steps/neoDeploy.md
- newmanExecute: steps/newmanExecute.md - newmanExecute: steps/newmanExecute.md
- npmExecute: steps/npmExecute.md
- pipelineExecute: steps/pipelineExecute.md - pipelineExecute: steps/pipelineExecute.md
- pipelineRestartSteps: steps/pipelineRestartSteps.md - pipelineRestartSteps: steps/pipelineRestartSteps.md
- pipelineStashFiles: steps/pipelineStashFiles.md - pipelineStashFiles: steps/pipelineStashFiles.md
- prepareDefaultValues: steps/prepareDefaultValues.md - prepareDefaultValues: steps/prepareDefaultValues.md
- seleniumExecuteTests: steps/seleniumExecuteTests.md - seleniumExecuteTests: steps/seleniumExecuteTests.md
- setupCommonPipelineEnvironment: steps/setupCommonPipelineEnvironment.md - setupCommonPipelineEnvironment: steps/setupCommonPipelineEnvironment.md
- slackSendNotification: steps/slackSendNotification.md
- testsPublishResults: steps/testsPublishResults.md - testsPublishResults: steps/testsPublishResults.md
- toolValidate: steps/toolValidate.md - toolValidate: steps/toolValidate.md
- transportRequestCreate: steps/transportRequestCreate.md - transportRequestCreate: steps/transportRequestCreate.md

View File

@ -1,18 +1,18 @@
---------------------------------------------------------- ----------------------------------------------------------
--- ERROR OCCURRED IN LIBRARY STEP: ${stepName} --- An error occurred in the library step: ${stepName}
---------------------------------------------------------- ----------------------------------------------------------
FOLLOWING PARAMETERS WERE AVAILABLE TO THIS STEP: The following parameters were available to the step:
*** ***
${stepParameters} ${stepParameters}
*** ***
ERROR WAS: The error was:
*** ***
${error} ${error}
*** ***
FURTHER INFORMATION: Further information:
* Documentation of library step ${stepName}: https://sap.github.io/jenkins-library/steps/${stepName}/ * Documentation of library step ${stepName}: https://sap.github.io/jenkins-library/steps/${stepName}/
* Source code of library step ${stepName}: https://github.com/SAP/jenkins-library/blob/master/vars/${stepName}.groovy * Source code of library step ${stepName}: https://github.com/SAP/jenkins-library/blob/master/vars/${stepName}.groovy
* Library documentation: https://sap.github.io/jenkins-library/ * Library documentation: https://sap.github.io/jenkins-library/

View File

@ -232,7 +232,8 @@ steps:
logSuccessfulMavenTransfers: false logSuccessfulMavenTransfers: false
mtaBuild: mtaBuild:
buildTarget: 'NEO' buildTarget: 'NEO'
mtaJarLocation: 'mta.jar' mtaJarLocation: '/opt/sap/mta/lib/mta.jar'
dockerImage: 'ppiper/mta-archive-builder'
neoDeploy: neoDeploy:
dockerImage: 's4sdk/docker-neo-cli' dockerImage: 's4sdk/docker-neo-cli'
deployMode: 'mta' deployMode: 'mta'
@ -251,6 +252,8 @@ steps:
newmanRunCommand: "run '${config.newmanCollection}' --environment '${config.newmanEnvironment}' --globals '${config.newmanGlobals}' --reporters junit,html --reporter-junit-export 'target/newman/TEST-${collectionDisplayName}.xml' --reporter-html-export 'target/newman/TEST-${collectionDisplayName}.html'" newmanRunCommand: "run '${config.newmanCollection}' --environment '${config.newmanEnvironment}' --globals '${config.newmanGlobals}' --reporters junit,html --reporter-junit-export 'target/newman/TEST-${collectionDisplayName}.xml' --reporter-html-export 'target/newman/TEST-${collectionDisplayName}.html'"
stashContent: stashContent:
- 'tests' - 'tests'
npmExecute:
dockerImage: 'node:8-stretch'
pipelineRestartSteps: pipelineRestartSteps:
sendMail: true sendMail: true
timeoutInSeconds: 900 timeoutInSeconds: 900
@ -305,6 +308,9 @@ steps:
dockerImage: 'node:8-stretch' dockerImage: 'node:8-stretch'
dockerName: 'npm' dockerName: 'npm'
dockerWorkspace: '/home/node' dockerWorkspace: '/home/node'
slackSendNotification:
color: "${buildStatus == 'SUCCESS'?'#008000':'#E60000'}"
defaultMessage: "${buildStatus}: Job ${env.JOB_NAME} <${env.BUILD_URL}|#${env.BUILD_NUMBER}>"
snykExecute: snykExecute:
buildDescriptorFile: './package.json' buildDescriptorFile: './package.json'
dockerImage: 'node:8-stretch' dockerImage: 'node:8-stretch'

View File

@ -2,6 +2,7 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS import com.cloudbees.groovy.cps.NonCPS
import com.sap.piper.analytics.Telemetry import com.sap.piper.analytics.Telemetry
import groovy.text.SimpleTemplateEngine
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.security.MessageDigest import java.security.MessageDigest
@ -95,3 +96,10 @@ void pushToSWA(Map parameters, Map config) {
// some error occured in telemetry reporting. This should not break anything though. // some error occured in telemetry reporting. This should not break anything though.
} }
} }
@NonCPS
static String fillTemplate(String templateText, Map binding){
def engine = new SimpleTemplateEngine()
String result = engine.createTemplate(templateText).make(binding)
return result
}

View File

@ -30,7 +30,7 @@ class Telemetry implements Serializable{
static notify(Script steps, Map config, Map payload){ static notify(Script steps, Map config, Map payload){
//allow opt-out via configuration //allow opt-out via configuration
if (!config?.collectTelemetryData) { if (!config?.collectTelemetryData) {
steps.echo "[${payload.step}] Telemetry reporting disabled!" steps.echo "[${payload.step}] Sending telemetry data is disabled."
return return
} }

View File

@ -0,0 +1,122 @@
package com.sap.piper.jenkins
import com.cloudbees.groovy.cps.NonCPS
class JenkinsController implements Serializable {
def script
String jenkinsUrl
def timeout
JenkinsController(script, String jenkinsUrl = "http://localhost:8080", timeout = 3600) {
this.script = script
this.jenkinsUrl = jenkinsUrl
this.timeout = timeout
}
def waitForJenkinsStarted() {
def timeout = 120
def timePerLoop = 5
for (int i = 0; i < timeout; i += timePerLoop) {
script.sleep timePerLoop
try {
if (retrieveJenkinsStatus() == 'NORMAL') {
return true
}
} catch (Exception e) {
script.echo "Could not retrieve status for Jenkins at ${jenkinsUrl}/api/json. Message: ${e.getMessage()}. Retrying..."
e.printStackTrace()
continue
}
return false
}
script.error("Timeout: Jenkins did not start within the expected time frame.")
}
private retrieveJenkinsStatus() {
def apiUrl = "${jenkinsUrl}/api/json"
script.echo "Checking Jenkins Status"
def response = getTextFromUrl(apiUrl)
def result = script.readJSON text: response
return result.mode
}
//Trigger scanning of the multi branch builds
def buildJob(String jobName) {
script.sh "curl -s -X POST ${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/build"
}
def waitForSuccess(String jobName, String branch) {
if (this.waitForJobStatus(jobName, branch, 'SUCCESS')) {
this.printConsoleText(jobName, branch)
script.echo "Build was successful"
} else {
this.printConsoleText(jobName, branch)
script.error("Build of ${jobName} ${branch} was not successfull")
}
}
def getBuildUrl(String jobName, String branch) {
return "${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/job/${URLEncoder.encode(branch, 'UTF-8')}/lastBuild/"
}
def waitForJobStatus(String jobName, String branch, String status) {
def buildUrl = getBuildUrl(jobName, branch)
def timePerLoop = 10
for (int i = 0; i < timeout; i += timePerLoop) {
script.sleep timePerLoop
try {
script.echo "Checking Build Status of ${jobName} ${branch}"
def buildInformation = retrieveBuildInformation(jobName, branch)
if (buildInformation.building) {
script.echo "Build is still in progress"
continue
}
if (buildInformation.result == status) {
return true
}
} catch (Exception e) {
script.echo "Could not retrieve status for ${buildUrl}. Message: ${e.getMessage()}. Retrying..."
continue
}
return false
}
script.error("Timeout: Build of job ${jobName}, branch ${branch} did not finish in the expected time frame.")
}
def getConsoleText(String jobName, String branch) {
def consoleUrl = this.getBuildUrl(jobName, branch) + "/consoleText"
return getTextFromUrl(consoleUrl)
}
def printConsoleText(String jobName, String branch) {
String consoleOutput = getConsoleText(jobName, branch)
script.echo '***********************************************'
script.echo '** Begin Output of Example Application Build **'
script.echo '***********************************************'
script.echo consoleOutput
script.echo '*********************************************'
script.echo '** End Output of Example Application Build **'
script.echo '*********************************************'
}
def retrieveBuildInformation(String jobName, String branch) {
def buildUrl = getBuildUrl(jobName, branch)
def url = "${buildUrl}/api/json"
script.echo "Checking Build Status of ${jobName} ${branch}"
script.echo "${jenkinsUrl}/job/${URLEncoder.encode(jobName, 'UTF-8')}/job/${URLEncoder.encode(branch, 'UTF-8')}/"
def response = getTextFromUrl(url)
def result = script.readJSON text: response
return result
}
@NonCPS
private static String getTextFromUrl(url) {
return new URL(url).getText()
}
}

View File

@ -37,8 +37,8 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
} }
// asserts // asserts
assertThat(isExecuted, is(true)) assertThat(isExecuted, is(true))
assertThat(loggingRule.log, containsString('--- BEGIN LIBRARY STEP: testStep')) assertThat(loggingRule.log, containsString('--- Begin library step of: testStep'))
assertThat(loggingRule.log, containsString('--- END LIBRARY STEP: testStep')) assertThat(loggingRule.log, containsString('--- End library step of: testStep'))
} }
@Test @Test
@ -54,9 +54,9 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
} catch (ignore) { } catch (ignore) {
} finally { } finally {
// asserts // asserts
assertThat(loggingRule.log, not(containsString('--- BEGIN LIBRARY STEP: testStep'))) assertThat(loggingRule.log, not(containsString('--- Begin library step of: testStep')))
assertThat(loggingRule.log, not(containsString('--- END LIBRARY STEP: testStep'))) assertThat(loggingRule.log, not(containsString('--- End library step: testStep')))
assertThat(loggingRule.log, not(containsString('--- ERROR OCCURRED IN LIBRARY STEP: testStep'))) assertThat(loggingRule.log, not(containsString('--- An error occurred in the library step: testStep')))
} }
} }
@ -75,7 +75,7 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
} finally { } finally {
// asserts // asserts
assertThat(isReported, is(true)) assertThat(isReported, is(true))
assertThat(loggingRule.log, containsString('--- ERROR OCCURRED IN LIBRARY STEP: testStep')) assertThat(loggingRule.log, containsString('--- An error occurred in the library step: testStep'))
assertThat(loggingRule.log, containsString('[something:anything]')) assertThat(loggingRule.log, containsString('[something:anything]'))
} }
} }

View File

@ -73,7 +73,7 @@ public class MtaBuildTest extends BasePiperTest {
@Test @Test
void mtarFilePathFromCommonPipelineEnviromentTest() { void mtarFilePathFromCommonPipelineEnvironmentTest() {
stepRule.step.mtaBuild(script: nullScript, stepRule.step.mtaBuild(script: nullScript,
buildTarget: 'NEO') buildTarget: 'NEO')
@ -163,9 +163,9 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript, stepRule.step.mtaBuild(script: nullScript,
buildTarget: 'NEO') buildTarget: 'NEO')
assert shellRule.shell.find(){ c -> c.contains("-jar mta.jar --mtar")} assert shellRule.shell.find(){ c -> c.contains("-jar /opt/sap/mta/lib/mta.jar --mtar")}
assert loggingRule.log.contains("SAP Multitarget Application Archive Builder file 'mta.jar' retrieved from configuration.") assert loggingRule.log.contains("SAP Multitarget Application Archive Builder file '/opt/sap/mta/lib/mta.jar' retrieved from configuration.")
assert loggingRule.log.contains("Using SAP Multitarget Application Archive Builder 'mta.jar'.") assert loggingRule.log.contains("Using SAP Multitarget Application Archive Builder '/opt/sap/mta/lib/mta.jar'.")
} }
@ -174,7 +174,7 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript, buildTarget: 'NEO') stepRule.step.mtaBuild(script: nullScript, buildTarget: 'NEO')
assert shellRule.shell.find { c -> c.contains('java -jar mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')} assert shellRule.shell.find { c -> c.contains('java -jar /opt/sap/mta/lib/mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')}
} }
@ -185,7 +185,7 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript) stepRule.step.mtaBuild(script: nullScript)
assert shellRule.shell.find(){ c -> c.contains('java -jar mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')} assert shellRule.shell.find(){ c -> c.contains('java -jar /opt/sap/mta/lib/mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')}
} }
@Test @Test
@ -211,7 +211,7 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript) stepRule.step.mtaBuild(script: nullScript)
assert shellRule.shell.find { c -> c.contains('java -jar mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')} assert shellRule.shell.find { c -> c.contains('java -jar /opt/sap/mta/lib/mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO build')}
} }
@ -220,7 +220,7 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript, buildTarget: 'NEO', extension: 'param_extension') stepRule.step.mtaBuild(script: nullScript, buildTarget: 'NEO', extension: 'param_extension')
assert shellRule.shell.find { c -> c.contains('java -jar mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO --extension=param_extension build')} assert shellRule.shell.find { c -> c.contains('java -jar /opt/sap/mta/lib/mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO --extension=param_extension build')}
} }
@ -231,7 +231,7 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript) stepRule.step.mtaBuild(script: nullScript)
assert shellRule.shell.find(){ c -> c.contains('java -jar mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO --extension=config_extension build')} assert shellRule.shell.find(){ c -> c.contains('java -jar /opt/sap/mta/lib/mta.jar --mtar com.mycompany.northwind.mtar --build-target=NEO --extension=config_extension build')}
} }

View File

@ -0,0 +1,56 @@
import static org.junit.Assert.assertEquals
import hudson.AbortException
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.BasePiperTest
import util.JenkinsDockerExecuteRule
import util.JenkinsReadYamlRule
import util.JenkinsShellCallRule
import util.JenkinsStepRule
import util.Rules
class NpmExecuteTest extends BasePiperTest {
private ExpectedException thrown = new ExpectedException().none()
private JenkinsShellCallRule shellRule = new JenkinsShellCallRule(this)
private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
private JenkinsReadYamlRule yamlRule = new JenkinsReadYamlRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(thrown)
.around(yamlRule)
.around(dockerExecuteRule)
.around(shellRule)
.around(stepRule)
@Before
void init() {
helper.registerAllowedMethod 'fileExists', [String], { s -> s == 'package.json' }
}
@Test
void testNpmExecute() {
stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch')
assertEquals 'node:8-stretch', dockerExecuteRule.dockerParams.dockerImage
}
@Test
void testNpmExecuteWithClosure() {
stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch', npmCommand: 'run build') { }
assert shellRule.shell.find { c -> c.contains('npm run build') }
}
@Test
void testNoPackageJson() {
helper.registerAllowedMethod 'fileExists', [String], { false }
thrown.expect AbortException
thrown.expectMessage '[npmExecute] package.json is not found.'
stepRule.step.npmExecute(script: nullScript, dockerImage: 'node:8-stretch', npmCommand: 'run build')
}
}

View File

@ -0,0 +1,88 @@
#!groovy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.BasePiperTest
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsStepRule
import util.Rules
import static org.junit.Assert.*
class SlackSendNotificationTest extends BasePiperTest {
def slackCallMap = [:]
private JenkinsLoggingRule loggingRule = new JenkinsLoggingRule(this)
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(loggingRule)
.around(stepRule)
@Before
void init() throws Exception {
helper.registerAllowedMethod("slackSend", [Map.class], {m -> slackCallMap = m})
}
@Test
void testNotificationBuildSuccessDefaultChannel() throws Exception {
stepRule.step.slackSendNotification(script: [currentBuild: [result: 'SUCCESS']])
// asserts
assertEquals('Message not set correctly', 'SUCCESS: Job p <http://build.url|#1>', slackCallMap.message.toString())
assertNull('Channel not set correctly', slackCallMap.channel)
assertEquals('Color not set correctly', '#008000', slackCallMap.color)
assertJobStatusSuccess()
}
@Test
void testNotificationBuildSuccessCustomChannel() throws Exception {
stepRule.step.slackSendNotification(script: [currentBuild: [result: 'SUCCCESS']], channel: 'Test')
// asserts
assertEquals('Channel not set correctly', 'Test', slackCallMap.channel)
assertJobStatusSuccess()
}
@Test
void testNotificationBuildFailed() throws Exception {
stepRule.step.slackSendNotification(script: [currentBuild: [result: 'FAILURE']])
// asserts
assertEquals('Message not set correctly', 'FAILURE: Job p <http://build.url|#1>', slackCallMap.message.toString())
assertEquals('Color not set correctly', '#E60000', slackCallMap.color)
}
@Test
void testNotificationBuildStatusNull() throws Exception {
stepRule.step.slackSendNotification(script: [currentBuild: [:]])
// asserts
assertTrue('Missing build status not detected', loggingRule.log.contains('currentBuild.result is not set. Skipping Slack notification'))
assertJobStatusSuccess()
}
@Test
void testNotificationCustomMessageAndColor() throws Exception {
stepRule.step.slackSendNotification(script: [currentBuild: [:]], message: 'Custom Message', color: '#AAAAAA')
// asserts
assertEquals('Custom message not set correctly', 'Custom Message', slackCallMap.message.toString())
assertEquals('Custom color not set correctly', '#AAAAAA', slackCallMap.color)
assertJobStatusSuccess()
}
@Test
void testNotificationWithCustomCredentials() throws Exception {
stepRule.step.slackSendNotification(
script: [currentBuild: [:]],
message: 'I am no Message',
baseUrl: 'https://my.base.url',
credentialsId: 'MY_TOKEN_ID'
)
// asserts
assertEquals('Custom base url not set correctly', 'https://my.base.url', slackCallMap.baseUrl)
assertEquals('Custom token id not set correctly', 'MY_TOKEN_ID', slackCallMap.tokenCredentialId)
assertJobStatusSuccess()
}
}

View File

@ -82,7 +82,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, [collectTelemetryData: false], [step: 'anyStep', anything: 'something']) Telemetry.notify(nullScript, [collectTelemetryData: false], [step: 'anyStep', anything: 'something'])
// asserts // asserts
assertThat(Telemetry.getInstance().listenerList, is(not(empty()))) assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!")) assertThat(jlr.log, containsString("[anyStep] Sending telemetry data is disabled."))
assertThat(notificationPayload.keySet(), is(empty())) assertThat(notificationPayload.keySet(), is(empty()))
} }
@ -99,7 +99,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, [:], [step: 'anyStep', anything: 'something']) Telemetry.notify(nullScript, [:], [step: 'anyStep', anything: 'something'])
// asserts // asserts
assertThat(Telemetry.getInstance().listenerList, is(not(empty()))) assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!")) assertThat(jlr.log, containsString("[anyStep] Sending telemetry data is disabled."))
assertThat(notificationPayload.keySet(), is(empty())) assertThat(notificationPayload.keySet(), is(empty()))
} }
@ -116,7 +116,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, null, [step: 'anyStep', anything: 'something']) Telemetry.notify(nullScript, null, [step: 'anyStep', anything: 'something'])
// asserts // asserts
assertThat(Telemetry.getInstance().listenerList, is(not(empty()))) assertThat(Telemetry.getInstance().listenerList, is(not(empty())))
assertThat(jlr.log, containsString("[anyStep] Telemetry reporting disabled!")) assertThat(jlr.log, containsString("[anyStep] Sending telemetry data is disabled."))
assertThat(notificationPayload.keySet(), is(empty())) assertThat(notificationPayload.keySet(), is(empty()))
} }

View File

@ -16,17 +16,13 @@ import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrati
@Field def STEP_NAME = getClass().getName() @Field def STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS @Field Set GENERAL_CONFIG_KEYS = ['changeManagement']
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus(
@Field Set STEP_CONFIG_KEYS = [
'changeManagement',
/** /**
* When set to `false` the step will not fail in case the step is not in status 'in development'. * When set to `false` the step will not fail in case the step is not in status 'in development'.
* @possibleValues `true`, `false` * @possibleValues `true`, `false`
*/ */
'failIfStatusIsNotInDevelopment' 'failIfStatusIsNotInDevelopment')
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus('changeDocumentId') @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus('changeDocumentId')
/** /**

View File

@ -10,10 +10,10 @@ void call(Map parameters = [:], body) {
def message = '' def message = ''
try { try {
if (stepParameters == null && stepName == null) if (stepParameters == null && stepName == null)
error "step handlePipelineStepErrors requires following mandatory parameters: stepParameters, stepName" error "The step handlePipelineStepErrors requires following mandatory parameters: stepParameters, stepName"
if (verbose) if (verbose)
echo "--- BEGIN LIBRARY STEP: ${stepName} ---" echo "--- Begin library step of: ${stepName} ---"
body() body()
} catch (Throwable err) { } catch (Throwable err) {
@ -28,7 +28,7 @@ void call(Map parameters = [:], body) {
throw err throw err
} finally { } finally {
if (verbose) if (verbose)
message += "--- END LIBRARY STEP: ${stepName} ---" message += "--- End library step of: ${stepName} ---"
echo message echo message
} }
} }

73
vars/npmExecute.groovy Normal file
View File

@ -0,0 +1,73 @@
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.GenerateDocumentation
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.transform.Field
@Field def STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = [
/**
* Name of the docker image that should be used, in which node should be installed and configured. Default value is 'node:8-stretch'.
*/
'dockerImage',
/**
* URL of default NPM registry
*/
'defaultNpmRegistry',
/**
* Which NPM command should be executed.
*/
'npmCommand']
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS + [
/**
* Docker options to be set when starting the container.
*/
'dockerOptions']
/**
* Executes NPM commands inside a docker container.
* Docker image, docker options and npm commands can be specified or configured.
*/
@GenerateDocumentation
void call(Map parameters = [:], body = null) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = checkScript(this, parameters) ?: this
// load default & individual configuration
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.use()
new Utils().pushToSWA([
step: STEP_NAME,
stepParamKey1: 'scriptMissing',
stepParam1: parameters?.script == null
], configuration)
try {
if (!fileExists('package.json')) {
error "[${STEP_NAME}] package.json is not found."
}
dockerExecute(script: script, dockerImage: configuration.dockerImage, dockerOptions: configuration.dockerOptions) {
if (configuration.defaultNpmRegistry) {
sh "npm config set registry ${configuration.defaultNpmRegistry}"
}
if (configuration.npmCommand) {
sh "npm ${configuration.npmCommand}"
}
if (body) {
body()
}
}
} catch (Exception e) {
println "Error while executing npm. Here are the logs:"
sh "cat ~/.npm/_logs/*"
throw e
}
}
}

View File

@ -0,0 +1,53 @@
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.transform.Field
import groovy.text.SimpleTemplateEngine
@Field String STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = []
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
'baseUrl',
'channel',
'color',
'credentialsId',
'message'
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def utils = parameters.juStabUtils ?: new Utils()
def script = checkScript(this, parameters) ?: this
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.use()
new Utils().pushToSWA([step: STEP_NAME], config)
def buildStatus = script.currentBuild.result
// resolve templates
config.color = SimpleTemplateEngine.newInstance().createTemplate(config.color).make([buildStatus: buildStatus]).toString()
if (!config?.message){
if (!buildStatus) {
echo "[${STEP_NAME}] currentBuild.result is not set. Skipping Slack notification"
return
}
config.message = SimpleTemplateEngine.newInstance().createTemplate(config.defaultMessage).make([buildStatus: buildStatus, env: env]).toString()
}
Map options = [:]
if(config.credentialsId)
options.put('tokenCredentialId', config.credentialsId)
for(String entry : STEP_CONFIG_KEYS.minus('credentialsId'))
if(config.get(entry))
options.put(entry, config.get(entry))
slackSend(options)
}
}