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:
commit
201b075cb5
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ hs_err_pid*
|
||||
target/
|
||||
targets/
|
||||
documentation/docs-gen
|
||||
|
||||
consumer-test/**/workspace
|
||||
|
@ -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
19
Jenkinsfile
vendored
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
|
138
consumer-test/integrationTestController.sh
Executable file
138
consumer-test/integrationTestController.sh
Executable 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
29
consumer-test/jenkins.yml
Normal 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
30
consumer-test/runTest.sh
Executable 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"
|
2
consumer-test/testCases/s4sdk/consumer-test-neo.yml
Normal file
2
consumer-test/testCases/s4sdk/consumer-test-neo.yml
Normal file
@ -0,0 +1,2 @@
|
||||
# Empty for the moment.
|
||||
# Might contain test configuration in the future.
|
2
consumer-test/testCases/s4sdk/consumer-test.yml
Normal file
2
consumer-test/testCases/s4sdk/consumer-test.yml
Normal file
@ -0,0 +1,2 @@
|
||||
# Empty for the moment.
|
||||
# Might contain test configuration in the future.
|
65
documentation/docs/scenarios/CAP_Scenario.md
Normal file
65
documentation/docs/scenarios/CAP_Scenario.md
Normal 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/)
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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://...
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
23
documentation/docs/steps/npmExecute.md
Normal file
23
documentation/docs/steps/npmExecute.md
Normal 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'
|
||||
```
|
73
documentation/docs/steps/slackSendNotification.md
Normal file
73
documentation/docs/steps/slackSendNotification.md
Normal 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|
|
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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!'"
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
122
src/com/sap/piper/testutils/JenkinsController.groovy
Normal file
122
src/com/sap/piper/testutils/JenkinsController.groovy
Normal 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()
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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')
|
||||
|
@ -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]'))
|
||||
}
|
||||
}
|
||||
|
@ -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'))
|
||||
|
@ -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')}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
56
test/groovy/NpmExecuteTest.groovy
Normal file
56
test/groovy/NpmExecuteTest.groovy
Normal 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')
|
||||
}
|
||||
}
|
88
test/groovy/SlackSendNotificationTest.groovy
Normal file
88
test/groovy/SlackSendNotificationTest.groovy
Normal 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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
|
||||
|
@ -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
73
vars/npmExecute.groovy
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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']
|
||||
|
53
vars/slackSendNotification.groovy
Normal file
53
vars/slackSendNotification.groovy
Normal 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)
|
||||
}
|
||||
}
|
@ -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!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user