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-03-13 14:36:34 +01:00
commit 201b075cb5
50 changed files with 1006 additions and 538 deletions

2
.gitignore vendored
View File

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

View File

@ -1,6 +1,7 @@
branches:
only:
- master
- /^it\/.*$/
language: groovy
sudo: required
services:
@ -35,7 +36,10 @@ jobs:
cp -r documentation/docs documentation/docs-tmp
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
- name: Consumer Tests
if: repo = "SAP/jenkins-library" && ( (type != pull_request && branch =~ /^master$|^it\/.*$/) || (type == pull_request && head_repo = "SAP/jenkins-library" && head_branch =~ /^it\/.*$/) )
script: cd consumer-test && chmod +x integrationTestController.sh && ./integrationTestController.sh
- stage: Docs
name: Deploy
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]
before opening a pull request.
# [License][piper-library-license]
# License
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

View File

@ -0,0 +1,138 @@
#!/bin/bash
function fail() {
local message="$1"
local returnCode=${2:-1}
echo "[ERROR] ${message}" >&2
exit "${returnCode}"
}
function notify() {
local state=${1}
local description=${2}
local hash=${3}
echo "[INFO] Notifying about state \"${state}\" for commit \"${hash}\"."
curl -X POST \
--fail \
--silent \
--output /dev/null \
--data "{\"state\": \"${state}\", \"target_url\": \"${TRAVIS_BUILD_WEB_URL}\", \"description\": \"${description}\", \"context\": \"integration-tests\"}" \
--user "${INTEGRATION_TEST_VOTING_USER}:${INTEGRATION_TEST_VOTING_TOKEN}" \
"https://api.github.com/repos/SAP/jenkins-library/statuses/${hash}" || fail "Cannot send notification. curl return code: $?"
}
function cleanup() {
[ -z "${notificationThreadPid}" ] || kill -PIPE "${notificationThreadPid}" &>/dev/null
}
trap cleanup EXIT
#
# In case the build is performed for a pull request TRAVIS_COMMIT is a merge
# commit between the base branch and the PR branch HEAD. That commit is actually built.
# But for notifying about a build status we need the commit which is currenty
# the HEAD of the PR branch.
#
# In case the build is performed for a simple branch (not associated with a PR)
# In this case there is no merge commit between any base branch and HEAD of a PR branch.
# The commit which we need for notifying about a build status is in this case simply
# TRAVIS_COMMIT itself.
#
COMMIT_HASH_FOR_STATUS_NOTIFICATIONS="${TRAVIS_PULL_REQUEST_SHA}"
[ -z "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}" ] && COMMIT_HASH_FOR_STATUS_NOTIFICATIONS="${TRAVIS_COMMIT}"
notify "pending" "Integration tests in progress." "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}"
WORKSPACES_ROOT=workspaces
[ -e "${WORKSPACES_ROOT}" ] && rm -rf ${WORKSPACES_ROOT}
TEST_CASES=$(find testCases -name '*.yml')
# This auxiliar thread is needed in order to produce some output while the
# test are running. Otherwise the job will be canceled after 10 minutes without
# output.
while true; do sleep 10; echo "[INFO] Integration tests still running."; done &
notificationThreadPid=$!
declare -a processes
i=0
for f in ${TEST_CASES}
do
testCase=$(basename "${f%.*}")
area=$(dirname "${f#*/}")
echo "[INFO] Running test case \"${testCase}\" in area \"${area}\"."
TEST_CASE_ROOT="${WORKSPACES_ROOT}/${area}/${testCase}"
[ -e "${TEST_CASE_ROOT}" ] && rm -rf "${TEST_CASE_ROOT}"
mkdir -p "${TEST_CASE_ROOT}" || fail "Cannot create test case root directory for test case \"${testCase}\"." 1
source ./runTest.sh "${testCase}" "${TEST_CASE_ROOT}" &> "${TEST_CASE_ROOT}/log.txt" &
pid=$!
processes[$i]="${area}/${testCase}:${pid}"
echo "[INFO] Test case \"${testCase}\" in area \"${area}\" launched. (PID: \"${pid}\")."
let i=i+1
done
[ "${i}" == 0 ] && fail "No tests has been executed." 1
#
# wait for the test cases and cat the log
for p in "${processes[@]}"
do
area=$(dirname "${p%:*}")
testCase=$(basename "${p%:*}")
processId="${p#*:}"
echo "[INFO] Waiting for test case \"${testCase}\" in area \"${area}\" (PID: \"${processId}\")."
wait "${processId}"
echo "[INFO] Test case \"${testCase}\" in area \"${area}\" finished (PID: \"${processId}\")."
done
kill -PIPE "${notificationThreadPid}" &>/dev/null && notificationThreadPid=""
#
# provide the logs
for p in "${processes[@]}"
do
area=$(dirname "${p%:*}")
testCase=$(basename "${p%:*}")
echo "[INFO] === START === Logs for test case \"${testCase}\" ===."
cat "${TEST_CASE_ROOT}/log.txt"
echo "[INFO] === END === Logs for test case \"${testCase}\" ===."
done
#
# list test case status
echo "[INFO] Build status:"
failure="false"
for p in "${processes[@]}"
do
status="UNDEFINED"
area=$(dirname "${p%:*}")
testCase=$(basename "${p%:*}")
TEST_CASE_ROOT="${WORKSPACES_ROOT}/${area}/${testCase}"
if [ -f "${TEST_CASE_ROOT}/SUCCESS" ]
then
status="SUCCESS"
else
status="FAILURE"
failure="true"
fi
printf "[INFO] %-30s: %s\n" "${testCase}" "${status}"
done
STATUS_DESCRIPTION="The integration tests failed."
STATUS_STATE="failure"
if [ "${failure}" == "false" ]
then
STATUS_DESCRIPTION="The integration tests succeeded."
STATUS_STATE="success"
fi
notify "${STATUS_STATE}" "${STATUS_DESCRIPTION}" "${COMMIT_HASH_FOR_STATUS_NOTIFICATIONS}"
[ "${failure}" != "false" ] && fail "Integration tests failed." 1
echo "[INFO] Integration tests succeeded."
exit 0

29
consumer-test/jenkins.yml Normal file
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"

30
consumer-test/runTest.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
TEST_CASE=$1
TEST_CASE_ROOT=$2
TEST_CASE_WORKSPACE="${TEST_CASE_ROOT}/workspace"
LIBRARY_VERSION_UNDER_TEST=$(git log --format="%H" -n 1)
REPOSITORY_UNDER_TEST=${TRAVIS_REPO_SLUG:-SAP/jenkins-library}
git clone -b "${TEST_CASE}" https://github.com/sap/cloud-s4-sdk-book "${TEST_CASE_WORKSPACE}"
cp -f jenkins.yml "${TEST_CASE_WORKSPACE}"
cd "${TEST_CASE_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"
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="${TEST_CASE}" ppiper/jenkinsfile-runner
RC=$?
cd - &> /dev/null || { echo "[ERROR] change directory back into integration test root folder failed."; exit 1; }
[ "${RC}" == 0 ] && touch "${TEST_CASE_ROOT}/SUCCESS"

View File

@ -0,0 +1,2 @@
# Empty for the moment.
# Might contain test configuration in the future.

View File

@ -0,0 +1,2 @@
# Empty for the moment.
# Might contain test configuration in the future.

View File

@ -0,0 +1,65 @@
# Build and Deploy Applications with Jenkins and the SAP Cloud Application Programming Model
Set up a basic continuous delivery process for developing applications according to the SAP Cloud Application Programming Model.
## Prerequisites
* You have an account on SAP Cloud Platform in the Cloud Foundry environment. See [Accounts](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/8ed4a705efa0431b910056c0acdbf377.html).
* You have downloaded and installed the Cloud Foundry command line interface (CLI). See [Download and Install the Cloud Foundry Command Line Interface](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/afc3f643ec6942a283daad6cdf1b4936.html).
* You have installed the multi-target application plug-in for the Cloud Foundry command line interface. See [Install the Multi-Target Application Plug-in in the Cloud Foundry Environment](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/27f3af39c2584d4ea8c15ba8c282fd75.html).
* You have installed the Java Runtime Environment 8.
* You have installed Jenkins 2.60.3 or higher.
* You have set up Project “Piper”. See [README](https://github.com/SAP/jenkins-library/blob/master/README.md).
* You have installed the Multi-Target Application (MTA) Archive Builder 1.0.6 or newer. See [SAP Development Tools](https://tools.hana.ondemand.com/#cloud).
* You have installed Node.js including node and npm. See [Node.js](https://nodejs.org/en/download/).
## Context
The Application Programming Model for SAP Cloud Platform is an end-to-end best practice guide for developing applications on SAP Cloud Platform and provides a supportive set of APIs, languages, and libraries. For more information about the SAP Cloud Application Programming Model, see [Working with the SAP Cloud Application Programming Model](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/00823f91779d4d42aa29a498e0535cdf.html).
In this scenario, we want to show how to implement a basic continuous delivery process for developing applications according to this programming model with the help of project "Piper" on Jenkins. This basic scenario can be adapted and enriched according to your specific needs.
## Example
### Jenkinsfile
```groovy
@Library('piper-library-os') _
node(){
stage('Prepare') {
deleteDir()
checkout scm
setupCommonPipelineEnvironment script:this
}
stage('Build') {
mtaBuild script:this
}
stage('Deploy') {
cloudFoundryDeploy script:this, deployTool:'mtaDeployPlugin'
}
}
```
### Configuration (`.pipeline/config.yml`)
```yaml
steps:
mtaBuild
buildTarget: 'CF'
cloudFoundryDeploy:
cloudFoundry:
credentialsId: 'CF'
apiEndpoint: '<CF Endpoint>'
org: '<CF Organization>'
space: '<CF Space>'
```
### Parameters
For the detailed description of the relevant parameters, see:
* [mtaBuild](https://sap.github.io/jenkins-library/steps/mtaBuild/)
* [cloudFoundryDeploy](https://sap.github.io/jenkins-library/steps/cloudFoundryDeploy/)

View File

@ -38,7 +38,7 @@ The basic workflow is as follows:
5. As soon as the development process is completed, the change document in SAP Solution Manager can be set to status `to be tested` and all components can be transported to the test system.
![Hybrid Application Development Workflow](../images/Scenario_SolMan.png "Hybrid Application Development Workflow")
##### Hybrid Application Development Worflow
###### Hybrid Application Development Workflow
## Example

View File

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

View File

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

View File

@ -2,7 +2,7 @@
## Description
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## Prerequisites
@ -13,11 +13,11 @@ Content here is generated from corresponnding step, see `vars`.
## Parameters
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## Step configuration
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## 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.
* `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:
1. Step beginning: `--- BEGIN LIBRARY STEP: ${stepName}.groovy ---`
2. Step end: `--- END LIBRARY STEP: ${stepName}.groovy ---`
1. Step beginning: `--- Begin library step: ${stepName}.groovy ---`
2. Step end: `--- End library step: ${stepName}.groovy ---`
3. Step errors:
```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}
***
ERROR WAS:
The error was:
***
${err}
***
FURTHER INFORMATION:
Further information:
* Documentation of step ${stepName}: .../${stepName}/
* Pipeline documentation: https://...
* GitHub repository for pipeline steps: https://...

View File

@ -2,82 +2,20 @@
## Description
In this step the ([Karma test runner](http://karma-runner.github.io)) is executed.
The step is using the `seleniumExecuteTest` step to spins up two containers in a Docker network:
- a Selenium/Chrome container (`selenium/standalone-chrome`)
- a NodeJS container (`node:8-stretch`)
In the Docker network, the containers can be referenced by the values provided in `dockerName` and `sidecarName`, the default values are `karma` and `selenium`. These values must be used in the `hostname` properties of the test configuration ([Karma](https://karma-runner.github.io/1.0/config/configuration-file.html) and [WebDriver](https://github.com/karma-runner/karma-webdriver-launcher#usage)).
!!! note
In a Kubernetes environment, the containers both need to be referenced with `localhost`.
Content here is generated from corresponnding step, see `vars`.
## Prerequisites
- **running Karma tests** - have a NPM module with running tests executed with Karma
- **configured WebDriver** - have the [`karma-webdriver-launcher`](https://github.com/karma-runner/karma-webdriver-launcher) package installed and a custom, WebDriver-based browser configured in Karma
* **running Karma tests** - have a NPM module with running tests executed with Karma
* **configured WebDriver** - have the [`karma-webdriver-launcher`](https://github.com/karma-runner/karma-webdriver-launcher) package installed and a custom, WebDriver-based browser configured in Karma
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|containerPortMappings|no|`[node:8-stretch: [[containerPort: 9876, hostPort: 9876]]]`||
|dockerEnvVars|no|`[ NO_PROXY: 'localhost,karma,$NO_PROXY', no_proxy: 'localhost,karma,$no_proxy']`||
|dockerImage|no|`node:8-stretch`||
|dockerName|no|`karma`||
|dockerWorkspace|no|`/home/node`||
|failOnError|no|||
|installCommand|no|`npm install --quiet`||
|modules|no|`['.']`||
|runCommand|no|`npm run karma`||
|sidecarEnvVars|no|`[ NO_PROXY: 'localhost,selenium,$NO_PROXY', no_proxy: 'localhost,selenium,$no_proxy']`||
|sidecarImage|no|||
|sidecarName|no|||
|sidecarVolumeBind|no|||
|stashContent|no|`['buildDescriptor', 'tests']`||
- `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.
- `containerPortMappings` - see step [dockerExecute](dockerExecute.md)
- `dockerEnvVars` - see step [dockerExecute](dockerExecute.md)
- `dockerImage` - see step [dockerExecute](dockerExecute.md)
- `dockerName` - see step [dockerExecute](dockerExecute.md)
- `dockerWorkspace` - see step [dockerExecute](dockerExecute.md)
- `failOnError` - see step [seleniumExecuteTests](seleniumExecuteTests.md)
- `installCommand` - the command that is executed to install dependencies
- `modules` - define the paths of the modules to execute tests on
- `runCommand` - the command that is executed to start the tests
- `sidecarEnvVars` - see step [dockerExecute](dockerExecute.md)
- `sidecarImage` - see step [dockerExecute](dockerExecute.md)
- `sidecarName` - see step [dockerExecute](dockerExecute.md)
- `sidecarVolumeBind` - see step [dockerExecute](dockerExecute.md)
- `stashContent` - pass specific stashed that should be considered for the tests
Content here is generated from corresponnding step, see `vars`.
## 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||||
|containerPortMappings|X|X|X|
|dockerEnvVars|X|X|X|
|dockerImage|X|X|X|
|dockerName|X|X|X|
|dockerWorkspace|X|X|X|
|failOnError|X|X|X|
|installCommand|X|X|X|
|modules|X|X|X|
|runCommand|X|X|X|
|sidecarEnvVars|X|X|X|
|sidecarImage|X|X|X|
|sidecarName|X|X|X|
|sidecarVolumeBind|X|X|X|
|stashContent|X|X|X|
Content here is generated from corresponnding step, see `vars`.
## Side effects

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.
## 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
| parameter | mandatory | default | possible values |
| -----------------|-----------|--------------------------------------------------------|--------------------|
| `script` | yes | | |
| `dockerImage` | yes | | |
| `dockerImage` | no | `ppiper/mta-archive-builder` | |
| `dockerOptions` | no | '' | |
| `buildTarget` | yes | `'NEO'` | 'CF', 'NEO', 'XSA' |
| `extension` | no | | |
| `mtaJarLocation` | no | `'mta.jar'` | |
| `extension` | no | | |
| `mtaJarLocation` | no | `'/opt/sap/mta/lib/mta.jar'` | |
| `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.
* `dockerImage` - The Docker image to execute the MTA build.
A custom built image needs to include Multi-target Application Archive Builder.
Refer to [SAP Help Portal](https://help.sap.com/viewer/58746c584026430a890170ac4d87d03b/Cloud/en-US/ba7dd5a47b7a4858a652d15f9673c28d.html) for information on how to set it up.
Note that you can provide your own image if required, but for most cases, the default should be fine.
* `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.
* `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

@ -33,6 +33,7 @@ Available parameters:
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
| script | yes | | |
| `failOnError` | no | `false` | `true`, `false` |
| junit | no | `false` | true, false |
| jacoco | no | `false` | true, false |
| cobertura | no | `false` | true, false |
@ -43,6 +44,7 @@ Available parameters:
with the `this` parameter, as in `script: this`.
This allows the function to access the [`commonPipelineEnvironment`](commonPipelineEnvironment.md)
for retrieving, for example, configuration parameters.
* `failOnError` - If `failOnError` it set to `true` the step will fail the build if JUnit detected any failing tests.
* `junit` - Publishes test results files in JUnit format with the [JUnit Plugin](https://plugins.jenkins.io/junit).
* `jacoco` - Publishes code coverage with the [JaCoCo plugin](https://plugins.jenkins.io/jacoco) .
* `cobertura` - Publishes code coverage with the [Cobertura plugin](https://plugins.jenkins.io/cobertura).

View File

@ -2,18 +2,21 @@
## Description
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## Prerequisites
## Parameters
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## Step configuration
Content here is generated from corresponnding step, see `vars`.
Content here is generated from corresponding step, see `vars`.
## Exceptions
If you see an error like `fatal: Not a git repository (or any parent up to mount point /home/jenkins)` it is likely that your test description cannot be found.<br />
Please make sure to point parameter `testOptions` to your `conf.js` file like `testOptions: './path/to/my/tests/conf.js'`
## Examples

View File

@ -24,12 +24,14 @@ nav:
- mtaBuild: steps/mtaBuild.md
- neoDeploy: steps/neoDeploy.md
- newmanExecute: steps/newmanExecute.md
- npmExecute: steps/npmExecute.md
- pipelineExecute: steps/pipelineExecute.md
- pipelineRestartSteps: steps/pipelineRestartSteps.md
- pipelineStashFiles: steps/pipelineStashFiles.md
- prepareDefaultValues: steps/prepareDefaultValues.md
- seleniumExecuteTests: steps/seleniumExecuteTests.md
- setupCommonPipelineEnvironment: steps/setupCommonPipelineEnvironment.md
- slackSendNotification: steps/slackSendNotification.md
- testsPublishResults: steps/testsPublishResults.md
- toolValidate: steps/toolValidate.md
- transportRequestCreate: steps/transportRequestCreate.md
@ -38,7 +40,8 @@ nav:
- uiVeri5ExecuteTests: steps/uiVeri5ExecuteTests.md
- 'Scenarios':
- 'Build and Deploy Hybrid Applications with Jenkins and SAP Solution Manager': scenarios/changeManagement.md
- 'Create a Pipeline for SAP UI5 or SAP Fiori on SAP Cloud Platform': scenarios/ui5-sap-cp/Readme.md
- 'Build and Deploy SAP UI5 or SAP Fiori Applications on SAP Cloud Platform with Jenkins': scenarios/ui5-sap-cp/Readme.md
- 'Build and Deploy Applications with Jenkins and the SAP Cloud Application Programming Model': scenarios/CAP_Scenario.md
- 'Required Plugins': jenkins/requiredPlugins.md
theme:

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}
***
ERROR WAS:
The error was:
***
${error}
***
FURTHER INFORMATION:
Further information:
* 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
* Library documentation: https://sap.github.io/jenkins-library/

View File

@ -238,7 +238,8 @@ steps:
logSuccessfulMavenTransfers: false
mtaBuild:
buildTarget: 'NEO'
mtaJarLocation: 'mta.jar'
mtaJarLocation: '/opt/sap/mta/lib/mta.jar'
dockerImage: 'ppiper/mta-archive-builder'
neoDeploy:
dockerImage: 's4sdk/docker-neo-cli'
deployMode: 'mta'
@ -257,6 +258,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'"
stashContent:
- 'tests'
npmExecute:
dockerImage: 'node:8-stretch'
pipelineRestartSteps:
sendMail: true
timeoutInSeconds: 900
@ -311,6 +314,9 @@ steps:
dockerImage: 'node:8-stretch'
dockerName: 'npm'
dockerWorkspace: '/home/node'
slackSendNotification:
color: "${buildStatus == 'SUCCESS'?'#008000':'#E60000'}"
defaultMessage: "${buildStatus}: Job ${env.JOB_NAME} <${env.BUILD_URL}|#${env.BUILD_NUMBER}>"
snykExecute:
buildDescriptorFile: './package.json'
dockerImage: 'node:8-stretch'
@ -323,6 +329,7 @@ steps:
toJson: false
toHtml: false
testsPublishResults:
failOnError: false
junit:
pattern: '**/TEST-*.xml'
updateResults: false

View File

@ -3,6 +3,7 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
import hudson.tasks.junit.TestResultAction
@API
@NonCPS
@ -10,6 +11,14 @@ static def isPluginActive(pluginId) {
return Jenkins.instance.pluginManager.plugins.find { p -> p.isActive() && p.getShortName() == pluginId }
}
static boolean hasTestFailures(build){
//build: https://javadoc.jenkins.io/plugin/workflow-support/org/jenkinsci/plugins/workflow/support/steps/build/RunWrapper.html
//getRawBuild: https://javadoc.jenkins.io/plugin/workflow-job/org/jenkinsci/plugins/workflow/job/WorkflowRun.html
//getAction: http://www.hudson-ci.org/javadoc/hudson/tasks/junit/TestResultAction.html
def action = build?.getRawBuild()?.getAction(TestResultAction.class)
return action && action.getFailCount() != 0
}
def nodeAvailable() {
try {
sh "echo 'Node is available!'"

View File

@ -2,6 +2,7 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
import com.sap.piper.analytics.Telemetry
import groovy.text.SimpleTemplateEngine
import java.nio.charset.StandardCharsets
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.
}
}
@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){
//allow opt-out via configuration
if (!config?.collectTelemetryData) {
steps.echo "[${payload.step}] Telemetry reporting disabled!"
steps.echo "[${payload.step}] Sending telemetry data is disabled."
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

@ -9,25 +9,23 @@ class NeoCommandHelper {
private Script step
private DeployMode deployMode
private Map deploymentConfiguration
private String pathToNeoExecutable
private String user
private String password
private String source
//Warning: Commands generated with this class can contain passwords and should only be used within the step withCredentials
NeoCommandHelper(Script step, DeployMode deployMode, Map deploymentConfiguration, String pathToNeoExecutable,
NeoCommandHelper(Script step, DeployMode deployMode, Map deploymentConfiguration,
String user, String password, String source) {
this.step = step
this.deployMode = deployMode
this.deploymentConfiguration = deploymentConfiguration
this.pathToNeoExecutable = pathToNeoExecutable
this.user = user
this.password = password
this.source = source
}
private String prolog() {
return "\"${pathToNeoExecutable}\""
return 'neo.sh'
}
String statusCommand() {

View File

@ -115,9 +115,11 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest {
.commonPipelineEnvironment
.configuration = [steps:
[neoDeploy:
[ host: 'hana.example.com',
account: 'myTestAccount',
]
[neo:
[ host: 'hana.example.com',
account: 'myTestAccount',
]
]
]
]
@ -139,7 +141,7 @@ class FioriOnCloudPlatformPipelineTest extends BasePiperTest {
// the neo deploy call:
Assert.assertThat(shellRule.shell,
new CommandLineMatcher()
.hasProlog("\"/opt/sap/neo/tools/neo.sh\" deploy-mta")
.hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('host', 'hana\\.example\\.com')
.hasSingleQuotedOption('account', 'myTestAccount')
.hasSingleQuotedOption('password', 'terceSpot')

View File

@ -37,8 +37,8 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
}
// asserts
assertThat(isExecuted, is(true))
assertThat(loggingRule.log, containsString('--- BEGIN LIBRARY STEP: testStep'))
assertThat(loggingRule.log, containsString('--- END LIBRARY STEP: testStep'))
assertThat(loggingRule.log, containsString('--- Begin library step of: testStep'))
assertThat(loggingRule.log, containsString('--- End library step of: testStep'))
}
@Test
@ -54,9 +54,9 @@ class HandlePipelineStepErrorsTest extends BasePiperTest {
} catch (ignore) {
} finally {
// asserts
assertThat(loggingRule.log, not(containsString('--- BEGIN 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('--- Begin library step of: testStep')))
assertThat(loggingRule.log, not(containsString('--- End 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 {
// asserts
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]'))
}
}

View File

@ -72,8 +72,6 @@ user3@domain.com noreply+github@domain.com'''
],
'master',
2)
println("LOGS: ${loggingRule.log}")
println("RESULT: ${result}")
// asserts
assertThat(result, containsString('user2@domain.com'))
assertThat(result, containsString('user3@domain.com'))

View File

@ -73,7 +73,7 @@ public class MtaBuildTest extends BasePiperTest {
@Test
void mtarFilePathFromCommonPipelineEnviromentTest() {
void mtarFilePathFromCommonPipelineEnvironmentTest() {
stepRule.step.mtaBuild(script: nullScript,
buildTarget: 'NEO')
@ -163,9 +163,9 @@ public class MtaBuildTest extends BasePiperTest {
stepRule.step.mtaBuild(script: nullScript,
buildTarget: 'NEO')
assert shellRule.shell.find(){ c -> c.contains("-jar mta.jar --mtar")}
assert loggingRule.log.contains("SAP Multitarget Application Archive Builder file 'mta.jar' retrieved from configuration.")
assert loggingRule.log.contains("Using SAP Multitarget Application Archive Builder 'mta.jar'.")
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 '/opt/sap/mta/lib/mta.jar' retrieved from configuration.")
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')
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)
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
@ -211,7 +211,7 @@ public class MtaBuildTest extends BasePiperTest {
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')
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)
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

@ -84,109 +84,10 @@ class NeoDeployTest extends BasePiperTest {
helper.registerAllowedMethod('dockerExecute', [Map, Closure], null)
helper.registerAllowedMethod('fileExists', [String], { s -> return new File(workspacePath, s).exists() })
helper.registerAllowedMethod('pwd', [], { return workspacePath })
mockShellCommands()
nullScript.commonPipelineEnvironment.configuration = [steps: [neoDeploy: [neo: [host: 'test.deploy.host.com', account: 'trialuser123']]]]
}
@Test
void straightForwardTestCompatibilityConfiguration(){
shellRule.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.* status .*', 'Status: STARTED')
nullScript.commonPipelineEnvironment.configuration = [
steps: [
neoDeploy: [
host: 'test.deploy.host.com',
account: 'trialuser123',
neoCredentialsId: 'myCredentialsId'
]]]
stepRule.step.neoDeploy(script: nullScript,
archivePath: warArchiveName,
deployMode: 'warParams',
applicationName: 'testApp',
runtime: 'neo-javaee6-wp',
runtimeVersion: '2.125',
warAction: 'rolling-update',
vmSize: 'lite')
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasSingleQuotedOption('application', 'testApp')
.hasSingleQuotedOption('runtime', 'neo-javaee6-wp')
.hasSingleQuotedOption('runtime-version', '2\\.125')
.hasSingleQuotedOption('size', 'lite')
.hasSingleQuotedOption('user', 'anonymous')
.hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*')
.hasSingleQuotedOption('source', '.*\\.war'))
}
@Test
void straightForwardTestConfigViaConfigProperties() {
boolean buildStatusHasBeenSet = false
boolean notifyOldConfigFrameworkUsed = false
nullScript.commonPipelineEnvironment.setConfigProperty('DEPLOY_HOST', 'test.deploy.host.com')
nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'trialuser123')
nullScript.commonPipelineEnvironment.configuration = [:]
nullScript.currentBuild = [setResult: { buildStatusHasBeenSet = true }]
def utils = new Utils() {
void pushToSWA(Map parameters, Map config) {
notifyOldConfigFrameworkUsed = parameters.stepParam4
}
}
stepRule.step.neoDeploy(script: nullScript,
source: archiveName,
neo: [credentialsId: 'myCredentialsId'],
utils: utils
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasOption('synchronous', '')
.hasSingleQuotedOption('user', 'anonymous')
.hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*')
.hasSingleQuotedOption('source', '.*'))
assert !buildStatusHasBeenSet
assert notifyOldConfigFrameworkUsed
}
@Test
void testConfigViaConfigPropertiesSetsBuildToUnstable() {
def buildStatus = 'SUCCESS'
nullScript.commonPipelineEnvironment.setConfigProperty('DEPLOY_HOST', 'test.deploy.host.com')
nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'trialuser123')
nullScript.commonPipelineEnvironment.configuration = [:]
nullScript.currentBuild = [setResult: { r -> buildStatus = r }]
System.setProperty('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy',
Boolean.TRUE.toString())
try {
stepRule.step.neoDeploy(script: nullScript,
source: archiveName,
neo:[credentialsId: 'myCredentialsId'],
utils: utils
)
} finally {
System.clearProperty('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy')
}
assert buildStatus == 'UNSTABLE'
}
@Test
void straightForwardTestConfigViaConfiguration() {
@ -205,7 +106,7 @@ class NeoDeployTest extends BasePiperTest {
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasOption('synchronous', '')
@ -231,7 +132,7 @@ class NeoDeployTest extends BasePiperTest {
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('host', 'configuration-frwk\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'configurationFrwkUser123')
.hasOption('synchronous', '')
@ -246,7 +147,7 @@ class NeoDeployTest extends BasePiperTest {
stepRule.step.neoDeploy(script: nullScript)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('source', 'archive.mtar'))
}
@ -257,7 +158,7 @@ class NeoDeployTest extends BasePiperTest {
source: "archive.mtar")
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('source', 'archive.mtar'))
}
@ -282,7 +183,7 @@ class NeoDeployTest extends BasePiperTest {
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasOption('synchronous', '')
@ -292,69 +193,6 @@ class NeoDeployTest extends BasePiperTest {
)
}
@Test
void neoHomeNotSetTest() {
mockHomeVariablesNotSet()
stepRule.step.neoDeploy(script: nullScript,
source: archiveName
)
assert shellRule.shell.find { c -> c.contains('"neo.sh" deploy-mta') }
assert loggingRule.log.contains('SAP Cloud Platform Console Client is on PATH.')
assert loggingRule.log.contains("Using SAP Cloud Platform Console Client 'neo.sh'.")
}
@Test
void neoHomeAsParameterTest() {
mockHomeVariablesNotSet()
stepRule.step.neoDeploy(script: nullScript,
source: archiveName,
neo:[credentialsId: 'myCredentialsId'],
neoHome: '/param/neo'
)
assert shellRule.shell.find { c -> c = "\"/param/neo/tools/neo.sh\" deploy-mta" }
assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/param/neo' retrieved from configuration.")
assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/param/neo/tools/neo.sh'.")
}
@Test
void neoHomeFromEnvironmentTest() {
stepRule.step.neoDeploy(script: nullScript,
source: archiveName
)
assert shellRule.shell.find { c -> c.contains("\"/opt/neo/tools/neo.sh\" deploy-mta") }
assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/opt/neo' retrieved from environment.")
assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/opt/neo/tools/neo.sh'.")
}
@Test
void neoHomeFromCustomStepConfigurationTest() {
mockHomeVariablesNotSet()
nullScript.commonPipelineEnvironment.configuration = [steps: [neoDeploy: [neo: [host: 'test.deploy.host.com', account: 'trialuser123'], neoHome: '/config/neo']]]
stepRule.step.neoDeploy(script: nullScript,
source: archiveName
)
assert shellRule.shell.find { c -> c = "\"/config/neo/tools/neo.sh\" deploy-mta" }
assert loggingRule.log.contains("SAP Cloud Platform Console Client home '/config/neo' retrieved from configuration.")
assert loggingRule.log.contains("Using SAP Cloud Platform Console Client '/config/neo/tools/neo.sh'.")
}
@Test
void archiveNotProvidedTest() {
@ -392,7 +230,7 @@ class NeoDeployTest extends BasePiperTest {
stepRule.step.neoDeploy(script: nullScript, source: archiveName, deployMode: 'mta')
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy-mta")
new CommandLineMatcher().hasProlog("neo.sh deploy-mta")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasOption('synchronous', '')
@ -417,7 +255,7 @@ class NeoDeployTest extends BasePiperTest {
source: warArchiveName)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy")
new CommandLineMatcher().hasProlog("neo.sh deploy")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasSingleQuotedOption('application', 'testApp')
@ -448,7 +286,7 @@ class NeoDeployTest extends BasePiperTest {
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update")
new CommandLineMatcher().hasProlog("neo.sh rolling-update")
.hasSingleQuotedOption('host', 'test\\.deploy\\.host\\.com')
.hasSingleQuotedOption('account', 'trialuser123')
.hasSingleQuotedOption('application', 'testApp')
@ -478,7 +316,7 @@ class NeoDeployTest extends BasePiperTest {
Assert.assertThat(shellRule.shell,
new CommandLineMatcher()
.hasProlog("\"/opt/neo/tools/neo.sh\" deploy")
.hasProlog("neo.sh deploy")
.hasSingleQuotedOption('application', 'testApp'))
}
@ -541,7 +379,7 @@ class NeoDeployTest extends BasePiperTest {
)
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" deploy")
new CommandLineMatcher().hasProlog("neo.sh deploy")
.hasArgument("config.properties")
.hasSingleQuotedOption('user', 'defaultUser')
.hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*')
@ -566,7 +404,7 @@ class NeoDeployTest extends BasePiperTest {
])
Assert.assertThat(shellRule.shell,
new CommandLineMatcher().hasProlog("\"/opt/neo/tools/neo.sh\" rolling-update")
new CommandLineMatcher().hasProlog("neo.sh rolling-update")
.hasArgument('config.properties')
.hasSingleQuotedOption('user', 'defaultUser')
.hasSingleQuotedOption('password', '\\*\\*\\*\\*\\*\\*\\*\\*')
@ -654,57 +492,4 @@ class NeoDeployTest extends BasePiperTest {
size: 'lite'
])
}
@Test
void deployHostProvidedAsDeprecatedParameterTest() {
nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'configPropsUser123')
stepRule.step.neoDeploy(script: nullScript,
source: archiveName,
deployHost: "my.deploy.host.com"
)
assert loggingRule.log.contains("[WARNING][neoDeploy] Deprecated parameter 'deployHost' is used. This will not work anymore in future versions. Use parameter 'host' instead.")
}
@Test
void deployAccountProvidedAsDeprecatedParameterTest() {
nullScript.commonPipelineEnvironment.setConfigProperty('CI_DEPLOY_ACCOUNT', 'configPropsUser123')
stepRule.step.neoDeploy(script: nullScript,
source: archiveName,
neo: [
host: "my.deploy.host.com",
],
deployAccount: "myAccount"
)
assert loggingRule.log.contains("Deprecated parameter 'deployAccount' is used. This will not work anymore in future versions. Use parameter 'account' instead.")
}
private mockShellCommands() {
String javaVersion = '''openjdk version \"1.8.0_121\"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-1~bpo8+1-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)'''
shellRule.setReturnValue(Type.REGEX, '.*java -version.*', javaVersion)
String neoVersion = '''SAP Cloud Platform Console Client
SDK version : 3.39.10
Runtime : neo-java-web'''
shellRule.setReturnValue(Type.REGEX, '.*neo.sh version.*', neoVersion)
shellRule.setReturnValue(Type.REGEX, '.*JAVA_HOME.*', '/opt/java')
shellRule.setReturnValue(Type.REGEX, '.*NEO_HOME.*', '/opt/neo')
shellRule.setReturnValue(Type.REGEX, '.*which java.*', 0)
shellRule.setReturnValue(Type.REGEX, '.*which neo.*', 0)
}
private mockHomeVariablesNotSet() {
shellRule.setReturnValue(Type.REGEX, '.*JAVA_HOME.*', '')
shellRule.setReturnValue(Type.REGEX, '.*NEO_HOME.*', '')
shellRule.setReturnValue(Type.REGEX, '.*which java.*', 0)
shellRule.setReturnValue(Type.REGEX, '.*which neo.*', 0)
}
}

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

@ -3,6 +3,7 @@ import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.rules.ExpectedException
import util.BasePiperTest
import util.JenkinsReadYamlRule
@ -16,12 +17,14 @@ class TestsPublishResultsTest extends BasePiperTest {
Map publisherStepOptions
List archiveStepPatterns
private ExpectedException thrown = ExpectedException.none()
private JenkinsStepRule stepRule = new JenkinsStepRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(thrown)
.around(stepRule)
@Before
@ -126,4 +129,40 @@ class TestsPublishResultsTest extends BasePiperTest {
assertTrue('Cobertura options are not empty', publisherStepOptions.cobertura == null)
assertTrue('JMeter options are not empty', publisherStepOptions.jmeter == null)
}
@Test
void testBuildResultStatus() throws Exception {
stepRule.step.testsPublishResults(script: nullScript)
assertJobStatusSuccess()
}
@Test
void testBuildWithTestFailuresAndWithoutFailOnError() throws Exception {
nullScript.currentBuild.getRawBuild = {
return [getAction: { type ->
return [getFailCount: {
return 6
}]
}]
}
stepRule.step.testsPublishResults(script: nullScript)
assertJobStatusSuccess()
}
@Test
void testBuildWithTestFailuresAndWithFailOnError() throws Exception {
nullScript.currentBuild.getRawBuild = {
return [getAction: { type ->
return [getFailCount: {
return 6
}]
}]
}
thrown.expect(hudson.AbortException)
thrown.expectMessage('[testsPublishResults] Some tests failed!')
stepRule.step.testsPublishResults(script: nullScript, failOnError: true)
}
}

View File

@ -82,7 +82,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, [collectTelemetryData: false], [step: 'anyStep', anything: 'something'])
// asserts
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()))
}
@ -99,7 +99,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, [:], [step: 'anyStep', anything: 'something'])
// asserts
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()))
}
@ -116,7 +116,7 @@ class TelemetryTest extends BasePiperTest {
Telemetry.notify(nullScript, null, [step: 'anyStep', anything: 'something'])
// asserts
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()))
}

View File

@ -34,7 +34,6 @@ class NeoCommandHelperTest extends BasePiperTest {
String source = (deployMode == DeployMode.MTA) ? 'file.mta' : 'file.war'
String username = 'username'
String password = 'password'
String neoExecutable = '/path/tools/neo.sh';
nullScript.STEP_NAME="neoDeploy"
@ -42,7 +41,6 @@ class NeoCommandHelperTest extends BasePiperTest {
nullScript,
deployMode,
deploymentConfiguration,
neoExecutable,
username,
password,
source
@ -52,7 +50,7 @@ class NeoCommandHelperTest extends BasePiperTest {
@Test
void testStatusCommand() {
String actual = getTestFixture(DeployMode.WAR_PARAMS).statusCommand()
String expected = "\"/path/tools/neo.sh\" status --host 'host_value' --account 'account_value' " +
String expected = "neo.sh status --host 'host_value' --account 'account_value' " +
"--application 'application_value' --user 'username' --password 'password'"
Assert.assertEquals(expected, actual)
}
@ -60,14 +58,14 @@ class NeoCommandHelperTest extends BasePiperTest {
@Test
void testStatusCommandForProperties() {
String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).statusCommand()
String expected = "\"/path/tools/neo.sh\" status file.properties --user 'username' --password 'password'"
String expected = "neo.sh status file.properties --user 'username' --password 'password'"
Assert.assertEquals(expected, actual)
}
@Test
void testRollingUpdateCommand() {
String actual = getTestFixture(DeployMode.WAR_PARAMS).rollingUpdateCommand()
String basicCommand = "\"/path/tools/neo.sh\" rolling-update --host 'host_value' --account 'account_value' " +
String basicCommand = "neo.sh rolling-update --host 'host_value' --account 'account_value' " +
"--application 'application_value' --user 'username' --password 'password' --source 'file.war'"
Assert.assertTrue(actual.contains(basicCommand))
@ -81,14 +79,14 @@ class NeoCommandHelperTest extends BasePiperTest {
@Test
void testRollingUpdateCommandForProperties() {
String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).rollingUpdateCommand()
String expected = "\"/path/tools/neo.sh\" rolling-update file.properties --user 'username' --password 'password' --source 'file.war' "
String expected = "neo.sh rolling-update file.properties --user 'username' --password 'password' --source 'file.war' "
Assert.assertEquals(expected, actual)
}
@Test
void testDeployCommand() {
String actual = getTestFixture(DeployMode.WAR_PARAMS).deployCommand()
String basicCommand = "\"/path/tools/neo.sh\" deploy --host 'host_value' --account 'account_value' " +
String basicCommand = "neo.sh deploy --host 'host_value' --account 'account_value' " +
"--application 'application_value' --user 'username' --password 'password' --source 'file.war'"
Assert.assertTrue(actual.contains(basicCommand))
@ -102,14 +100,14 @@ class NeoCommandHelperTest extends BasePiperTest {
@Test
void testDeployCommandForProperties() {
String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).deployCommand()
String expected = "\"/path/tools/neo.sh\" deploy file.properties --user 'username' --password 'password' --source 'file.war' "
String expected = "neo.sh deploy file.properties --user 'username' --password 'password' --source 'file.war' "
Assert.assertEquals(expected, actual)
}
@Test
void testRestartCommand() {
String actual = getTestFixture(DeployMode.WAR_PARAMS).restartCommand()
String expected = "\"/path/tools/neo.sh\" restart --synchronous --host 'host_value' --account 'account_value' " +
String expected = "neo.sh restart --synchronous --host 'host_value' --account 'account_value' " +
"--application 'application_value' --user 'username' --password 'password'"
Assert.assertEquals(expected, actual)
}
@ -117,14 +115,14 @@ class NeoCommandHelperTest extends BasePiperTest {
@Test
void testRestartCommandForProperties() {
String actual = getTestFixture(DeployMode.WAR_PROPERTIES_FILE).restartCommand()
String expected = "\"/path/tools/neo.sh\" restart --synchronous file.properties --user 'username' --password 'password'"
String expected = "neo.sh restart --synchronous file.properties --user 'username' --password 'password'"
Assert.assertEquals(expected, actual)
}
@Test
void deployMta() {
String actual = getTestFixture(DeployMode.MTA).deployMta()
String expected = "\"/path/tools/neo.sh\" deploy-mta --synchronous --host 'host_value' --account 'account_value' " +
String expected = "neo.sh deploy-mta --synchronous --host 'host_value' --account 'account_value' " +
"--user 'username' --password 'password' --source 'file.mta'"
Assert.assertEquals(expected, actual)
}

View File

@ -152,11 +152,9 @@ class LibraryLoadingTestExecutionListener extends AbstractTestExecutionListener
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)

View File

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

View File

@ -10,10 +10,10 @@ void call(Map parameters = [:], body) {
def message = ''
try {
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)
echo "--- BEGIN LIBRARY STEP: ${stepName} ---"
echo "--- Begin library step of: ${stepName} ---"
body()
} catch (Throwable err) {
@ -28,7 +28,7 @@ void call(Map parameters = [:], body) {
throw err
} finally {
if (verbose)
message += "--- END LIBRARY STEP: ${stepName} ---"
message += "--- End library step of: ${stepName} ---"
echo message
}
}

View File

@ -35,7 +35,10 @@ void call(Map parameters = [:]) {
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin([
artifactVersion: script.commonPipelineEnvironment.getArtifactVersion()
artifactVersion: script.commonPipelineEnvironment.getArtifactVersion(),
influxPrefix: script.commonPipelineEnvironment.getGithubOrg() && script.commonPipelineEnvironment.getGithubRepo()
? "${script.commonPipelineEnvironment.getGithubOrg()}_${script.commonPipelineEnvironment.getGithubRepo()}"
: null
])
.mixin(parameters, PARAMETER_KEYS)
.addIfNull('customData', script.commonPipelineEnvironment.getInfluxCustomData())

View File

@ -1,6 +1,7 @@
import static com.sap.piper.Prerequisites.checkScript
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GenerateDocumentation
import com.sap.piper.GitUtils
import com.sap.piper.Utils
@ -9,34 +10,67 @@ import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = [
/** port mappings required for containers. This will only take effect inside a Kubernetes pod, format [[containerPort: 1111, hostPort: 1111]] */
/**
* Map which defines per docker image the port mappings, e.g. `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]`.
*/
'containerPortMappings',
/** envVars to be set in the execution container if required */
/** A map of environment variables to set in the container, e.g. [http_proxy:'proxy:8080']. */
'dockerEnvVars',
/** Docker image for code execution */
/** The name of the docker image that should be used. If empty, Docker is not used and the command is executed directly on the Jenkins system. */
'dockerImage',
/** name of the Docker container. If not on Kubernetes pod, this will define the network-alias to the NPM container and is thus required for accessing the server, example http://karma:9876 (default). */
/**
* Kubernetes only:
* Name of the container launching `dockerImage`.
* SideCar only:
* Name of the container in local network.
*/
'dockerName',
/** user home directory for Docker execution. This will only take effect inside a Kubernetes pod. */
/**
* Kubernetes only:
* Specifies a dedicated user home directory for the container which will be passed as value for environment variable `HOME`.
*/
'dockerWorkspace',
/**
* With `failOnError` the behavior in case tests fail can be defined.
* @possibleValues `true`, `false`
*/
'failOnError',
/** The command that is executed to install the test tool. */
'installCommand',
/** Define the paths of the modules to execute tests on. */
'modules',
/** The command that is executed to start the tests. */
'runCommand',
/** envVars to be set in Selenium container if required */
/** A map of environment variables to set in the sidecar container, similar to `dockerEnvVars`. */
'sidecarEnvVars',
/** image for Selenium execution which runs as sidecar to dockerImage */
/** The name of the docker image of the sidecar container. If empty, no sidecar container is started. */
'sidecarImage',
/** name of the Selenium container. If not on Kubernetes pod, this will define the network-alias to the Selenium container and is thus required for accessing the server, example http://selenium:4444 (default) */
/**
* as `dockerName` for the sidecar container
*/
'sidecarName',
/** volume bind. This will not take effect in Kubernetes pod. */
/** Volumes that should be mounted into the sidecar container. */
'sidecarVolumeBind',
/** list of stash names which are required to be unstashed before test run */
/** If specific stashes should be considered for the tests, their names need to be passed via the parameter `stashContent`. */
'stashContent'
]
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
/**
* In this step the ([Karma test runner](http://karma-runner.github.io)) is executed.
*
* The step is using the `seleniumExecuteTest` step to spin up two containers in a Docker network:
*
* * a Selenium/Chrome container (`selenium/standalone-chrome`)
* * a NodeJS container (`node:8-stretch`)
*
* In the Docker network, the containers can be referenced by the values provided in `dockerName` and `sidecarName`, the default values are `karma` and `selenium`. These values must be used in the `hostname` properties of the test configuration ([Karma](https://karma-runner.github.io/1.0/config/configuration-file.html) and [WebDriver](https://github.com/karma-runner/karma-webdriver-launcher#usage)).
*
* !!! note
* In a Kubernetes environment, the containers both need to be referenced with `localhost`.
*/
@GenerateDocumentation
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = checkScript(this, parameters) ?: this

View File

@ -1,6 +1,5 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import com.sap.piper.tools.ToolDescriptor
import com.sap.piper.tools.neo.DeployMode
import com.sap.piper.tools.neo.NeoCommandHelper
import com.sap.piper.tools.neo.WarAction
@ -25,21 +24,6 @@ import static com.sap.piper.Prerequisites.checkScript
'warAction'
])
@Field Map CONFIG_KEY_COMPATIBILITY = [
neo : [
host : 'host',
account : 'account',
application : 'applicationName',
credentialsId : 'neoCredentialsId',
propertiesFile: 'propertiesFile',
runtime : 'runtime',
runtimeVersion: 'runtimeVersion',
size : 'vmSize'
],
source: 'archivePath'
]
void call(parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
@ -49,19 +33,17 @@ void call(parameters = [:]) {
prepareDefaultValues script: script
Map stepCompatibilityConfiguration = handleCompatibility(script, parameters)
// load default & individual configuration
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixin(stepCompatibilityConfiguration)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName ?: env.STAGE_NAME, STEP_CONFIG_KEYS)
.addIfEmpty('source', script.commonPipelineEnvironment.getMtarFilePath())
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS)
.withMandatoryProperty('neo')
.withMandatoryProperty('source')
.withMandatoryProperty('neo/credentialsId')
.withPropertyInValues('deployMode', DeployMode.stringValues())
.use()
@ -73,52 +55,37 @@ void call(parameters = [:]) {
stepParam2: configuration.warAction == 'rolling-update'?'blue-green':'standard', // ['deploy', 'deploy-mta', 'rolling-update']
stepParamKey3: 'scriptMissing',
stepParam3: parameters?.script == null,
stepParamKey4: 'legacyConfig',
stepParam4: !stepCompatibilityConfiguration.isEmpty(),
], configuration)
ToolDescriptor neo = new ToolDescriptor('SAP Cloud Platform Console Client', 'NEO_HOME', 'neoHome', '/tools/', 'neo.sh', null, 'version')
ToolDescriptor java = new ToolDescriptor('Java', 'JAVA_HOME', '', '/bin/', 'java', '1.8.0', '-version 2>&1')
if (configuration.neo.credentialsId) {
withCredentials([usernamePassword(
credentialsId: configuration.neo.credentialsId,
passwordVariable: 'NEO_PASSWORD',
usernameVariable: 'NEO_USERNAME')]) {
withCredentials([usernamePassword(
credentialsId: configuration.neo.credentialsId,
passwordVariable: 'NEO_PASSWORD',
usernameVariable: 'NEO_USERNAME')]) {
assertPasswordRules(NEO_PASSWORD)
assertPasswordRules(NEO_PASSWORD)
dockerExecute(
script: script,
dockerImage: configuration.dockerImage,
dockerEnvVars: configuration.dockerEnvVars,
dockerOptions: configuration.dockerOptions
) {
dockerExecute(
script: script,
dockerImage: configuration.dockerImage,
dockerEnvVars: configuration.dockerEnvVars,
dockerOptions: configuration.dockerOptions
) {
DeployMode deployMode = DeployMode.fromString(configuration.deployMode)
neo.verify(this, configuration)
java.verify(this, configuration)
NeoCommandHelper neoCommandHelper = new NeoCommandHelper(
this,
deployMode,
configuration.neo,
NEO_USERNAME,
NEO_PASSWORD,
configuration.source
)
String neoExecutable = neo.getToolExecutable(script, configuration)
DeployMode deployMode = DeployMode.fromString(configuration.deployMode)
NeoCommandHelper neoCommandHelper = new NeoCommandHelper(
this,
deployMode,
configuration.neo,
neoExecutable,
NEO_USERNAME,
NEO_PASSWORD,
configuration.source
)
lock("$STEP_NAME :${neoCommandHelper.resourceLock()}") {
deploy(script, utils, configuration, neoCommandHelper, configuration.dockerImage, deployMode)
}
lock("$STEP_NAME :${neoCommandHelper.resourceLock()}") {
deploy(script, utils, configuration, neoCommandHelper, configuration.dockerImage, deployMode)
}
}
} else {
error("[neoDeploy] No credentials defined for the deployment. Please specify the value for credentialsId for neo.")
}
}
}
@ -168,61 +135,6 @@ private boolean isAppRunning(NeoCommandHelper commandHelper) {
return status.contains('Status: STARTED')
}
private handleCompatibility(script, parameters) {
final Map neoCompatibilityConfiguration = [:]
// Backward compatibility: ensure old configuration is taken into account
// The old configuration in not stage / step specific
def defaultDeployHost = script.commonPipelineEnvironment.getConfigProperty('DEPLOY_HOST')
if (defaultDeployHost) {
echo "[WARNING][${STEP_NAME}] A deprecated configuration framework is used for configuring parameter 'DEPLOY_HOST'. This configuration framework will be removed in future versions."
neoCompatibilityConfiguration.put('host', defaultDeployHost)
}
def defaultDeployAccount = script.commonPipelineEnvironment.getConfigProperty('CI_DEPLOY_ACCOUNT')
if (defaultDeployAccount) {
echo "[WARNING][${STEP_NAME}] A deprecated configuration framework is used for configuring parameter 'DEPLOY_ACCOUNT'. This configuration framekwork will be removed in future versions."
neoCompatibilityConfiguration.put('account', defaultDeployAccount)
}
if (parameters.deployHost && !parameters.host) {
echo "[WARNING][${STEP_NAME}] Deprecated parameter 'deployHost' is used. This will not work anymore in future versions. Use parameter 'host' instead."
parameters.put('host', parameters.deployHost)
}
if (parameters.deployAccount && !parameters.account) {
echo "[WARNING][${STEP_NAME}] Deprecated parameter 'deployAccount' is used. This will not work anymore in future versions. Use parameter 'account' instead."
parameters.put('account', parameters.deployAccount)
}
def credId = script.commonPipelineEnvironment.getConfigProperty('neoCredentialsId')
if (credId && !parameters.neoCredentialsId) {
echo "[WARNING][${STEP_NAME}] Deprecated parameter 'neoCredentialsId' from old configuration framework is used. This will not work anymore in future versions."
parameters.put('neoCredentialsId', credId)
}
if (!neoCompatibilityConfiguration.isEmpty()) {
echo "[WARNING][$STEP_NAME] You are using a deprecated configuration framework. This will be removed in " +
'futureVersions.\nAdd snippet below to \'./pipeline/config.yml\' and remove ' +
'file \'.pipeline/configuration.properties\'.\n' +
"""|steps:
| neoDeploy:
| neo:
| host: ${neoCompatibilityConfiguration.get('host', '<Add host here>')}
| account: ${neoCompatibilityConfiguration.get('account', '<Add account here>')}
""".stripMargin()
if (Boolean.getBoolean('com.sap.piper.featureFlag.buildUnstableWhenOldConfigFrameworkIsUsedByNeoDeploy')) {
script.currentBuild.setResult('UNSTABLE')
echo "[WARNING][$STEP_NAME] Build has been set to unstable since old config framework is used."
}
return [neo: neoCompatibilityConfiguration]
}
return [:]
}
private assertPasswordRules(String password) {
if (password.startsWith("@")) {
error("Your password for the deployment to SAP Cloud Platform contains characters which are not " +

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

@ -6,7 +6,7 @@ import groovy.transform.Field
@Field STEP_NAME = getClass().getName()
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters) {
handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters, echoDetails: false) {
if(!DefaultValueCache.getInstance() || parameters.customDefaults) {
def defaultValues = [:]
def configFileList = ['default_pipeline_environment.yml']

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)
}
}

View File

@ -3,6 +3,7 @@ import static com.sap.piper.Prerequisites.checkScript
import com.cloudbees.groovy.cps.NonCPS
import com.sap.piper.ConfigurationHelper
import com.sap.piper.JenkinsUtils
import com.sap.piper.MapUtils
import com.sap.piper.Utils
import groovy.transform.Field
@ -13,7 +14,9 @@ import groovy.transform.Field
@Field def STEP_NAME = getClass().getName()
@Field Set GENERAL_CONFIG_KEYS = TOOLS
@Field Set STEP_CONFIG_KEYS = TOOLS
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
'failOnError'
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
/**
@ -24,10 +27,7 @@ import groovy.transform.Field
*/
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = checkScript(this, parameters)
if (script == null)
script = this
def script = checkScript(this, parameters) ?: this
prepare(parameters)
@ -46,13 +46,15 @@ void call(Map parameters = [:]) {
stepParam1: parameters?.script == null
], configuration)
// UNIT TESTS
publishJUnitReport(configuration.get('junit'))
// CODE COVERAGE
publishJacocoReport(configuration.get('jacoco'))
publishCoberturaReport(configuration.get('cobertura'))
// PERFORMANCE
publishJMeterReport(configuration.get('jmeter'))
if (configuration.failOnError && JenkinsUtils.hasTestFailures(script.currentBuild)) {
script.currentBuild.result = 'FAILURE'
error "[${STEP_NAME}] Some tests failed!"
}
}
}

View File

@ -14,7 +14,7 @@ void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'toolValidate', stepParameters: parameters) {
echo '[WARNING][toolValidate] This step is deprecated, and it will be removed in future versions. Validation is automatically done inside the steps.'
echo '[WARNING][toolValidate] This step is deprecated, and it will be removed in future versions.'
def tool = parameters.tool
def home = parameters.home