1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-01-30 05:59:39 +02:00

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

This commit is contained in:
Marcus Holl 2018-11-05 09:02:21 +01:00
commit 4b8b1abb1c
81 changed files with 2986 additions and 456 deletions

17
.codeclimate.yml Normal file
View File

@ -0,0 +1,17 @@
plugins:
codenarc:
enabled: true
editorconfig:
enabled: true
config:
editorconfig: .editorconfig
fixme:
enabled: true
config:
strings:
- TODO
- FIXME
markdownlint:
enabled: true
shellcheck:
enabled: true

View File

@ -4,34 +4,39 @@ branches:
language: groovy
sudo: required
services:
- docker
before_install:
- docker pull squidfunk/mkdocs-material
script:
- mvn clean test --batch-mode
- |
if [[ "${TRAVIS_PULL_REQUEST}" != "false" ]]
then
docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material build --clean --verbose --strict
else
# Only in case we are in master branch of the leading SAP repo we would like to deploy,
# not from the forks.
if [[ "${TRAVIS_BRANCH}" == "master" && "${TRAVIS_REPO_SLUG}" == "SAP/jenkins-library" ]]
then
echo "Found change on master: Deployment of documentation"
PRIVATE_KEY="cfg/id_rsa"
openssl aes-256-cbc -K $encrypted_12c8071d2874_key -iv $encrypted_12c8071d2874_iv -in cfg/id_rsa.enc -out "${PRIVATE_KEY}" -d
chmod a+x gh-pages-deploy.sh
docker run --rm -it --entrypoint "./gh-pages-deploy.sh" -e "TRAVIS_REPO_SLUG=${TRAVIS_REPO_SLUG}" -v ${TRAVIS_BUILD_DIR}:/docs -w /docs squidfunk/mkdocs-material
else
echo "Publishing documentation skipped."
fi
fi
- docker
cache:
directories:
- $HOME/.m2
after_success:
- mvn -DrepoToken=$COVERALLS_REPO_TOKEN org.jacoco:jacoco-maven-plugin:report org.eluder.coveralls:coveralls-maven-plugin:report
# Travis Lifecycle: https://docs.travis-ci.com/user/job-lifecycle#the-job-lifecycle
# Travis Stages: https://docs.travis-ci.com/user/build-stages/
# Travis Conditions: https://docs.travis-ci.com/user/conditional-builds-stages-jobs
jobs:
include:
- stage: Tests
name: Unit Tests
script: mvn clean test --batch-mode
after_success: mvn -DrepoToken=$COVERALLS_REPO_TOKEN org.jacoco:jacoco-maven-plugin:report org.eluder.coveralls:coveralls-maven-plugin:report
- stage: Docs
name: Build
if: type = pull_request
install: docker pull squidfunk/mkdocs-material:3.0.4
script: docker run --rm -it -v ${TRAVIS_BUILD_DIR}:/docs -w /docs/documentation squidfunk/mkdocs-material:3.0.4 build --clean --verbose --strict
- name: Deploy
if: repo = "SAP/jenkins-library" AND branch = master AND NOT type = pull_request
install:
- docker pull squidfunk/mkdocs-material:3.0.4
- |
echo "Found change on master: Deployment of documentation"
PRIVATE_KEY="cfg/id_rsa"
openssl aes-256-cbc -K $encrypted_12c8071d2874_key -iv $encrypted_12c8071d2874_iv -in cfg/id_rsa.enc -out "${PRIVATE_KEY}" -d
chmod a+x gh-pages-deploy.sh
script: docker run --rm -it --entrypoint "./gh-pages-deploy.sh" -e "TRAVIS_REPO_SLUG=${TRAVIS_REPO_SLUG}" -v ${TRAVIS_BUILD_DIR}:/docs -w /docs squidfunk/mkdocs-material:3.0.4
# TODO: make use of GHPages deploy provider: https://docs.travis-ci.com/user/deployment/pages/
#notifications:
# slack:
# secure: UYzfd4QYLtAX39r8LzV1dYp7cKMhYRRjI/xswMEkR+RgdMWxVPPH3kcsNLwkdNGSPn1b8Aidz8YLss9JolrepWjwI283dK8EUthZAOw03+PmL5X/3nOJ7aGv0sxwYqF5ypltBrerTf6jtPUTcQdtao+0O8bgnzShc6nWWE4MLXonjOm1pZLRUo81un+0bzm8C2ABIeHC6xuZCRycXP5u1mW1nDLK3900uY1rxIDTSZKEzA0IzLQhE9uROvI1r48fW8cKJQQjMMO5PPorq+0eDl2YTE8rQr9ldvuRE7A/ubsOQR0N5F8iAv1JTZXuXGt62fw6eKDQ1h94suEk7X+baV0EwlfhsHXcI1MxRFwxNSr9k1WaVFfA4TrM8XYBAcW3JGRA51ZK3q4EcjpuxpupaA7kZDtH53W7ePzH2TIp6yknln1q+yfcsP7cGv38sSKpKwOyMgAPRElkZzcoo31kw/PLzKPXYJEovRqx/0lWzczbFSscsroNaGCavC02++bUnyUXW2W+PG4gDSBFVZjtrvTPKnZ6DpHXV97x6xC/CzyhFj/Nf+ao/J9IIfocnc4vXJojwS550KIvM7xCDJwa/+29dajj2l6dQqrcOe3UT3O5UGU9I0KkGEDMfkLOD71eRy58qiYz3y953e52DvvzWQJbvfuk8ubMO+Fmn4GyRz8=

View File

@ -84,14 +84,14 @@ class TemplateHelper {
//
class Helper {
static getConfigHelper(classLoader, roots) {
static getConfigHelper(classLoader, roots, script) {
def compilerConfig = new CompilerConfiguration()
compilerConfig.setClasspathList( roots )
new GroovyClassLoader(classLoader, compilerConfig, true)
.parseClass(new File('src/com/sap/piper/ConfigurationHelper.groovy'))
.newInstance()
.newInstance(script, [:])
}
@ -439,7 +439,9 @@ def handleStep(stepName, prepareDefaultValuesStep, gse) {
System.err << "[INFO] Handling step '${stepName}'.\n"
def defaultConfig = Helper.getConfigHelper(getClass().getClassLoader(), roots).loadStepDefaults(Helper.getDummyScript(prepareDefaultValuesStep, stepName)).use()
def defaultConfig = Helper.getConfigHelper(getClass().getClassLoader(),
roots,
Helper.getDummyScript(prepareDefaultValuesStep, stepName)).use()
def params = [] as Set

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -61,6 +61,10 @@ Deployment can be done
- cfOrg
- cfSpace
!!! note
Due to [an incompatible change](https://github.com/cloudfoundry/cli/issues/1445) in the Cloud Foundry CLI, multiple buildpacks are not supported by this step.
If your `application` contains a list of `buildpacks` instead a single `buildpack`, this will be automatically re-written by the step when blue-green deployment is used.
* `deployTool` defines the tool which should be used for deployment.
* `deployType` defines the type of deployment, either `standard` deployment which results in a system downtime or a zero-downtime `blue-green` deployment.
* `dockerImage` defines the Docker image containing the deployment tools (like cf cli, ...) and `dockerWorkspace` defines the home directory of the default user of the `dockerImage`
@ -106,7 +110,11 @@ The following parameters can also be specified as step/stage/general parameters
## Example
```groovy
artifactSetVersion script: this, buildTool: 'maven'
cloudFoundryDeploy(
script: script,
deployType: 'blue-green',
cloudFoundry: [apiEndpoint: 'https://test.server.com', appName:'cfAppName', credentialsId: 'cfCredentialsId', manifest: 'cfManifest', org: 'cfOrg', space: 'cfSpace'],
deployTool: 'cf_native'
)
```

View File

@ -30,7 +30,7 @@ Proxy environment variables defined on the Jenkins machine are also available in
* `containerPortMappings`: Map which defines per docker image the port mappings, like `containerPortMappings: ['selenium/standalone-chrome': [[name: 'selPort', containerPort: 4444, hostPort: 4444]]]`
* `dockerEnvVars`: Environment variables to set in the container, e.g. [http_proxy:'proxy:8080']
* `dockerImage`: 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.
* `dockerName`: only relevant for Kubernetes case: Name of the container launching `dockerImage`
* `dockerName`: Kubernetes case: Name of the container launching `dockerImage`, SideCar: Name of the container in local network
* `dockerOptions` Docker options to be set when starting the container. It can be a list or a string.
* `dockerVolumeBind` Volumes that should be mounted into the container.
* `dockerWorkspace`: only relevant for Kubernetes case: specifies a dedicated user home directory for the container which will be passed as value for environment variable `HOME`

View File

@ -0,0 +1,102 @@
# gaugeExecuteTests
## Description
In this step Gauge ([getgauge.io](http:getgauge.io)) acceptance tests are executed.
Using Gauge it will be possible to have a three-tier test layout:
* Acceptance Criteria
* Test implemenation layer
* Application driver layer
This layout is propagated by Jez Humble and Dave Farley in their book "Continuous Delivery" as a way to create maintainable acceptance test suites (see "Continuous Delivery", p. 190ff).
Using Gauge it is possible to write test specifications in [Markdown syntax](http://daringfireball.net/projects/markdown/syntax) and therefore allow e.g. product owners to write the relevant acceptance test specifications. At the same time it allows the developer to implement the steps described in the specification in her development environment.
You can use the sample projects of Gauge, for example: https://github.com/getgauge/gauge-mvn-archetypes
!!! note "Make sure to run against a Selenium Hub configuration"
In the test example of _gauge-archetype-selenium_ please make sure to allow it to run against a Selenium hub:
Please extend DriverFactory.java for example in following way:
``` java
String hubUrl = System.getenv("HUB_URL");
//when running on a Docker deamon (and not using Kubernetes plugin), Docker images will be linked
//in this case hubUrl will be http://selenium:4444/wd/hub due to the linking of the containers
hubUrl = (hubUrl == null) ? "http://localhost:4444/wd/hub" : hubUrl;
Capabilities chromeCapabilities = DesiredCapabilities.chrome();
System.out.println("Running on Selenium Hub: " + hubUrl);
return new RemoteWebDriver(new URL(hubUrl), chromeCapabilities);
```
## Prerequsites
none
## Example
Pipeline step:
```groovy
gaugeExecuteTests script: this, testServerUrl: 'http://test.url'
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildTool|no|`maven`||
|dockerEnvVars|no|`[HUB:TRUE, HUB_URL:http://localhost:4444/wd/hub]`||
|dockerImage|no|buildTool=`maven`: `maven:3.5-jdk-8`<br />buildTool=`npm`: `node:8-stretch`<br />||
|dockerName|no|buildTool=`maven`: `maven`<br />buildTool=`npm`: `npm`<br />||
|dockerWorkspace|no|buildTool=`maven`: ``<br />buildTool=`npm`: `/home/node`<br />||
|failOnError|no|`false`||
|gitBranch|no|||
|gitSshKeyCredentialsId|no|``||
|installCommand|no|`curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge`||
|languageRunner|no|buildTool=`maven`: `java`<br />buildTool=`npm`: `js`<br />||
|runCommand|no|buildTool=`maven`: `mvn test-compile gauge:execute`<br />buildTool=`npm`: `gauge run`<br />||
|stashContent|no|<ul><li>`buildDescriptor`</li><li>`tests`</li></ul>||
|testOptions|no|buildTool=`maven`: `-DspecsDir=specs`<br />buildTool=`npm`: `specs`<br />||
|testRepository|no|||
|testServerUrl|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.
* `buildTool` defines the build tool to be used for the test execution.
* `dockerEnvVars`, see step [dockerExecute](dockerExecute.md)
* `dockerImage`, see step [dockerExecute](dockerExecute.md)
* `dockerName`, see step [dockerExecute](dockerExecute.md)
* `dockerWorkspace`, see step [dockerExecute](dockerExecute.md)
* With `failOnError` you can define the behavior, in case tests fail. When this is set to `true` test results cannot be recorded using the `publishTestResults` step afterwards.
* `installCommand` defines the command for installing Gauge. In case the `dockerImage` already contains Gauge it can be set to empty: ``.
* `languageRunner` defines the Gauge language runner to be used.
* `runCommand` defines the command which is used for executing Gauge.
* If specific stashes should be considered for the tests, you can pass this via parameter `stashContent`
* `testOptions` allows to set specific options for the Gauge execution. Details can be found for example [in the Gauge Maven plugin documentation](https://github.com/getgauge/gauge-maven-plugin#executing-specs)
* In case the test implementation is stored in a different repository than the code itself, you can define the repository containing the tests using parameter `testRepository` and if required `gitBranch` (for a different branch than master) and `gitSshKeyCredentialsId` (for protected repositories). For protected repositories the `testRepository` needs to contain the ssh git url.
* `testServerUrl` is passed as environment variable `TARGET_SERVER_URL` to the test execution. Tests running against the system should read the host information from this environment variable in order to be infrastructure agnostic.
## 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||||
|buildTool||X|X|
|dockerEnvVars||X|X|
|dockerImage||X|X|
|dockerName||X|X|
|dockerWorkspace||X|X|
|failOnError||X|X|
|gitBranch||X|X|
|gitSshKeyCredentialsId||X|X|
|stashContent||X|X|
|testOptions||X|X|
|testRepository||X|X|
|testServerUrl||X|X|

View File

@ -0,0 +1,77 @@
# githubPublishRelease
## Description
This step creates a tag in your GitHub repository together with a release.
The release can be filled with text plus additional information like:
* Closed pull request since last release
* Closed issues since last release
* link to delta information showing all commits since last release
The result looks like
![Example release](../images/githubRelease.png)
## Prerequisites
You need to create a personal access token within GitHub and add this to the Jenkins credentials store.
Please see [GitHub documentation for details about creating the personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/).
## Example
Usage of pipeline step:
```groovy
githubPublishRelease script: this, releaseBodyHeader: "**This is the latest success!**<br />"
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|addClosedIssues|no|`false`||
|addDeltaToLastRelease|no|`false`||
|customFilterExtension|no|``||
|excludeLabels|no|<ul><li>`duplicate`</li><li>`invalid`</li><li>`question`</li><li>`wontfix`</li></ul>||
|githubApiUrl|no|`//https://api.github.com`||
|githubOrg|yes|`script.commonPipelineEnvironment.getGitFolder()`||
|githubRepo|yes|`script.commonPipelineEnvironment.getGitRepo()`||
|githubServerUrl|no|`https://github.com`||
|githubTokenCredentialsId|yes|||
|releaseBodyHeader|no|||
|version|yes|`script.commonPipelineEnvironment.getArtifactVersion()`||
### 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.
* All GitHub related properties allow you to overwrite the default behavior of identifying e.g. GitHub organization, GitHub repository.
* `version` defines the version number which will be written as tag as well as release name
* By defining the `releaseBodyHeader` you can specify the content which will appear for the release
* If you set `addClosedIssues` to `true`, a list of all closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`
* If you set `addDeltaToLastRelease` to `true`, a link will be added to the relese information that brings up all commits since the last release.
* By passing the parameter `customFilterExtension` it is possible to pass additional filter criteria for retrieving closed issues since the last release. Additional criteria could be for example specific `label`, or `filter` according to [GitHub API documentation](https://developer.github.com/v3/issues/).
* It is possible to exclude issues with dedicated labels using parameter `excludeLabels`. Usage is like `excludeLabels: ['label1', 'label2']`
## 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||||
|addClosedIssues||X|X|
|addDeltaToLastRelease||X|X|
|customFilterExtension||X|X|
|excludeLabels||X|X|
|githubApiUrl|X|X|X|
|githubOrg||X|X|
|githubRepo||X|X|
|githubServerUrl|X|X|X|
|githubTokenCredentialsId|X|X|X|
|releaseBodyHeader||X|X|
|version||X|X|

View File

@ -0,0 +1,96 @@
# mailSendNotification
## Description
Sends notifications to all potential culprits of a current or previous build failure plus to fixed list of recipients.
It will attach the current build log to the email.
Notifications are sent in following cases:
* current build failed or is unstable
* current build is successful and previous build failed or was unstable
## Prerequsites
none
## Example
Usage of pipeline step:
```groovy
mailSendNotification script: this
```
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildResult|no|||
|gitCommitId|no|`script.commonPipelineEnvironment.getGitCommitId()`||
|gitSshKeyCredentialsId|no|``||
|gitUrl|no|||
|notificationAttachment|no|`true`||
|notificationRecipients|no|||
|notifyCulprits|no|`true`||
|numLogLinesInBody|no|`100`||
|projectName|no|||
|wrapInNode|no|`false`||
### 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.
* `buildResult` may be used to overrule the build result coming from `currentBuild.result`. This is for example used in the step `pipelineRestartSteps`
* `gitCommitId` defines a dedicated git commitId for culprit retrieval.
* `gitUrl` and `gitCommitId` are used to retrieve culprit information.
* `gitSshKeyCredentialsId` only required if your git repository is protected. It defines the credentialsId for the git ssh credentials.
* `notificationAttachment` defines if the console log file should be attached to the notification mail.
* `notificationRecipients` defines the fixed list of recipient that always get the notification. In case you want to send the notification to the culprits only set it to an empty string `''`.
!!! note
Multiple recipients need to be separated with the `space` character.
In case you do not want to have any fixed recipients of the notifications leave the property empty.
* `notifyCulprits` defines if potential culprits should receive an email.
* `numLogLinesInBody` defines the number of log lines (=last lines of the log) which are included into the body of the notification email.
* `projectName` may be used to specify a different name in the email subject.
* `wrapInNode` needs to be set to `true` if step is used outside of a node context, e.g. post actions in a declarative pipeline script.
## 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||||
|buildResult||X|X|
|gitCommitId||X|X|
|gitSshKeyCredentialsId|X|X|X|
|gitUrl||X|X|
|notificationAttachment||X|X|
|notificationRecipients||X|X|
|notifyCulprits||X|X|
|numLogLinesInBody||X|X|
|projectName||X|X|
|wrapInNode||X|X|
## Return value
none
## Side effects
none
## Exceptions
none

View File

@ -45,7 +45,7 @@ The following parameters can also be specified as step parameters using the glob
* `applicationName`
## Return value
The file name of the resulting archive is returned with this step. The file name is extracted from the key `ID` defined in `mta.yaml`.
none
## Side effects
1. The file name of the resulting archive is written to the `commonPipelineEnvironment` with variable name `mtarFileName`.

View File

@ -0,0 +1,75 @@
# pipelineRestartSteps
## Description
Support of restarting failed stages or steps in a pipeline is limited in Jenkins.
This has been documented in the [Jenkins Jira issue JENKINS-33846](https://issues.jenkins-ci.org/browse/JENKINS-33846).
For declarative pipelines there is a solution available which partially addresses this topic:
https://jenkins.io/doc/book/pipeline/running-pipelines/#restart-from-a-stage.
Nonetheless, still features are missing, so it can't be used in all cases.
The more complex Piper pipelines which share a state via [`commonPipelineEnvironment`](commonPipelineEnvironment.md) will for example not work with the standard _restart-from-stage_.
The step `pipelineRestartSteps` aims to address this gap and allows individual parts of a pipeline (e.g. a failed deployment) to be restarted.
This is done in a way that the pipeline waits for user input to restart the pipeline in case of a failure. In case this user input is not provided the pipeline stops after a timeout which can be configured.
## Prerequisites
none
## Example
Usage of pipeline step:
```groovy
pipelineRestartSteps (script: this) {
node {
//your steps ...
}
}
```
!!! caution
Use `node` inside the step. If a `node` exists outside the step context, the `input` step which is triggered in the process will block a Jenkins executor.
In case you cannot use `node` inside this step, please choose the parameter `timeoutInSeconds` carefully!
## Parameters
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|sendMail|no|`true`||
|timeoutInSeconds|no|`900`||
### 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.
* If `sendMail: true` the step `mailSendNotification` will be triggered in case of an error
* `timeoutInSeconds` defines the time period where the job waits for input. Default is 15 minutes. Once this time is passed the job enters state FAILED.
## 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||||
|sendMail|X|X|X|
|timeoutInSeconds|X|X|X|
## Return value
none
## Side effects
none
## Exceptions
none

View File

@ -77,8 +77,10 @@ webdriverio
| parameter | mandatory | default | possible values |
| ----------|-----------|---------|-----------------|
|script|yes|||
|buildTool|no|`npm`|`maven`, `npm`|
|containerPortMappings|no|`[selenium/standalone-chrome:[[containerPort:4444, hostPort:4444]]]`||
|dockerImage|no|buildTool=`maven`: `maven:3.5-jdk-7`<br />buildTool=`npm`: `node:8-stretch`<br />||
|dockerEnvVars|no|||
|dockerImage|no|buildTool=`maven`: `maven:3.5-jdk-8`<br />buildTool=`npm`: `node:8-stretch`<br />||
|dockerName|no|buildTool=`maven`: `maven`<br />buildTool=`npm`: `npm`<br />||
|dockerWorkspace|no|buildTool=`maven`: ``<br />buildTool=`npm`: `/home/node`<br />||
|failOnError|no|`true`||
@ -92,7 +94,9 @@ webdriverio
|testRepository|no|||
* `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.
* `buildTool` defines the build tool to be used for the test execution.
* `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)
@ -113,7 +117,9 @@ In following sections the configuration is possible:
| parameter | general | step | stage |
| ----------|-----------|---------|-----------------|
|script||||
|buildTool||X|X|
|containerPortMappings|X|X|X|
|dockerEnvVars|X|X|X|
|dockerImage|X|X|X|
|dockerName|X|X|X|
|dockerWorkspace|X|X|X|

View File

@ -1,7 +1,10 @@
# transportRequestCreate
## Description
Creates a Transport Request for a Change Document on the Solution Manager.
Creates
* a Transport Request for a Change Document on the Solution Manager (type `SOLMAN`) or
* a Transport Request inside an ABAP system (type`CTS`)
## Prerequisites
* **[Change Management Client 2.0.0 or compatible version](http://central.maven.org/maven2/com/sap/devops/cmclient/dist.cli/)** - available for download on Maven Central.
@ -10,7 +13,10 @@ Creates a Transport Request for a Change Document on the Solution Manager.
| parameter | mandatory | default | possible values |
| -----------------|-----------|--------------------------------------------------------|--------------------|
| `script` | yes | | |
| `changeDocumentId` | yes | | |
| `changeDocumentId` | for `SOLMAN` | | |
| `transportType` | for `CTS` | no | |
| `targetSystem` | for `CTS` | no | |
| `description` | for `CTS` | no | |
| `changeManagement/credentialsId` | yes | | |
| `changeManagement/endpoint` | yes | | |
| `changeManagement/clientOpts` | no | | |
@ -18,16 +24,21 @@ Creates a Transport Request for a Change Document on the Solution Manager.
| `changeManagement/git/to` | no | `HEAD` | |
| `changeManagement/changeDocumentLabel` | no | `ChangeDocument\s?:` | regex pattern |
| `changeManagement/git/format` | no | `%b` | see `git log --help` |
| `changeManagement/type` | no | `SOLMAN` | `SOLMAN`, `CTS` |
* `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.
* `changeDocumentId` - The id of the change document to transport.
* `changeManagement/credentialsId` - The credentials to connect to the Solution Manager.
* `changeManagement/endpoint` - The address of the Solution Manager.
* `changeDocumentId` - for `SOLMAN` only. The id of the change document to that the transport request is bound to. Typically this value is provided via commit message in the commit history.
* `changeManagement/type` Where/how the transport request is created (via SAP Solution Manager, ABAP).
* `changeManagement/credentialsId` - The credentials to connect to the service endpoint (Solution Manager, ABAP System).
* `changeManagement/endpoint` - The service endpoint (Solution Manager, ABAP System).
* `changeManagement/clientOpts`- Options forwarded to JVM used by the CM client, like `JAVA_OPTS`
* `changeManagement/git/from` - The starting point for retrieving the change document id
* `changeManagement/git/to` - The end point for retrieving the change document id
* `changeManagement/changeDocumentLabel` - A pattern used for identifying lines holding the change document id.
* `changeManagement/changeDocumentLabel` - For type `SOLMAN` only. A pattern used for identifying lines holding the change document id.
* `changeManagement/git/format` - Specifies what part of the commit is scanned. By default the body of the commit message is scanned.
* `description` - for `CTS` only. The description of the transport request.
* `targetSystem` - for `CTS` only. The system receiving the transport request.
* `transportType` - for type `CTS` only. Typically `W` (workbench) or `C` customizing.
## Step configuration
The step is configured using a customer configuration file provided as
@ -55,6 +66,7 @@ general:
changeDocumentLabel: 'ChangeDocument\s?:'
cmClientOpts: '-Djavax.net.ssl.trustStore=<path to truststore>'
credentialsId: 'CM'
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
git:
from: 'HEAD~1'
@ -72,6 +84,7 @@ The properties can also be configured on a per-step basis:
steps:
transportRequestCreate:
changeManagement:
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
[...]
```
@ -89,9 +102,20 @@ The id of the Transport Request that has been created.
## Example
```groovy
// SOLMAN
def transportRequestId = transportRequestCreate script:this,
changeDocumentId: '001,'
changeManagement: [
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
]
// CTS
def transportRequestId = transportRequestCreate script:this,
transportType: 'W',
targetSystem: 'XYZ',
description: 'the description',
changeManagement: [
type: 'CTS'
endpoint: 'https://example.org/cm'
]
```

View File

@ -1,7 +1,7 @@
# transportRequestRelease
## Description
Releases a Transport Request for a Change Document on the Solution Manager.
Releases a Transport Request.
## Prerequisites
* **[Change Management Client 2.0.0 or compatible version](http://central.maven.org/maven2/com/sap/devops/cmclient/dist.cli/)** - available for download on Maven Central.
@ -10,7 +10,7 @@ Releases a Transport Request for a Change Document on the Solution Manager.
| parameter | mandatory | default | possible values |
| -----------------|-----------|--------------------------------------------------------|--------------------|
| `script` | yes | | |
| `changeDocumentId` | yes | | |
| `changeDocumentId` | `SOLMAN` only | | |
| `transportRequestId`| yes | | |
| `changeManagement/changeDocumentLabel` | no | `ChangeDocument\s?:` | regex pattern |
| `changeManagment/transportRequestLabel` | no | `TransportRequest\s?:` | regex pattern |
@ -19,16 +19,17 @@ Releases a Transport Request for a Change Document on the Solution Manager.
| `changeManagement/git/from` | no | `origin/master` | |
| `changeManagement/git/to` | no | `HEAD` | |
| `changeManagement/git/format` | no | `%b` | see `git log --help` |
| `changeManagement/type` | no | `SOLMAN` | `SOLMAN`, `CTS` |
* `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.
* `changeDocumentId` - The id of the change document related to the transport request to release.
* `changeDocumentId` - for `SOLMAN` only. The id of the change document related to the transport request to release.
* `transportRequestId` - The id of the transport request to release.
* `changeManagement/changeDocumentLabel` - A pattern used for identifying lines holding the change document id.
* `changeManagement/changeDocumentLabel` - for `SOLMAN` only. A pattern used for identifying lines holding the change document id.
* `changeManagment/transportRequestLabel` - A pattern used for identifying lines holding the transport request id.
* `changeManagement/credentialsId` - The id of the credentials to connect to the Solution Manager. The credentials needs to be maintained on Jenkins.
* `changeManagement/endpoint` - The address of the Solution Manager.
* `changeManagement/git/from` - The starting point for retrieving the change document id
* `changeManagement/git/to` - The end point for retrieving the change document id
* `changeManagement/credentialsId` - The credentials to connect to the service endpoint (Solution Manager, ABAP System).
* `changeManagement/endpoint` - The service endpoint (Solution Manager, ABAP System).
* `changeManagement/git/from` - The starting point for retrieving the change document id and/or transport request id
* `changeManagement/git/to` - The end point for retrieving the change document id and/or transport request id
* `changeManagement/git/format` - Specifies what part of the commit is scanned. By default the body of the commit message is scanned.
## Step configuration
@ -57,6 +58,7 @@ general:
changeDocumentLabel: 'ChangeDocument\s?:'
cmClientOpts: '-Djavax.net.ssl.trustStore=<path to truststore>'
credentialsId: 'CM'
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
git:
from: 'HEAD~1'
@ -73,6 +75,7 @@ The properties can also be configured on a per-step basis:
steps:
transportRequestRelease:
changeManagement:
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
[...]
```
@ -84,17 +87,26 @@ None.
## Exceptions
* `IllegalArgumentException`:
* If the change id is not provided.
* If the change id is not provided (`SOLMAN` only)
* If the transport request id is not provided.
* `AbortException`:
* If the release of the transport request fails.
## Example
```groovy
// SOLMAN
transportRequestRelease script:this,
changeDocumentId: '001',
transportRequestId: '001',
changeManagement: [
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
]
// CTS
transportRequestRelease script:this,
transportRequestId: '001',
changeManagement: [
type: 'CTS'
endpoint: 'https://example.org/cm'
]
```

View File

@ -1,7 +1,7 @@
# transportRequestUploadFile
## Description
Uploads a file to a Transport Request for a Change Document on the Solution Manager.
Uploads a file to a Transport Request.
## Prerequisites
* **[Change Management Client 2.0.0 or compatible version](http://central.maven.org/maven2/com/sap/devops/cmclient/dist.cli/)** - available for download on Maven Central.
@ -10,9 +10,9 @@ Uploads a file to a Transport Request for a Change Document on the Solution Mana
| parameter | mandatory | default | possible values |
| -----------------|-----------|--------------------------------------------------------|--------------------|
| `script` | yes | | |
| `changeDocumentId` | yes | | |
| `changeDocumentId` | `SOLMAN` only | | |
| `transportRequestId`| yes | | |
| `applicationId` | yes | | |
| `applicationId` | `SOLMAN` only | | |
| `filePath` | yes | | |
| `changeManagement/credentialsId` | yes | | |
| `changeManagement/endpoint` | yes | | |
@ -21,18 +21,20 @@ Uploads a file to a Transport Request for a Change Document on the Solution Mana
| `changeManagement/changeDocumentLabel` | no | `ChangeDocument\s?:` | regex pattern |
| `changeManagement/transportRequestLabel` | no | `TransportRequest\s?:` | regex pattern |
| `changeManagement/git/format` | no | `%b` | see `git log --help` |
| `changeManagement/type` | no | `SOLMAN` | `SOLMAN`, `CTS` |
* `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.
* `changeDocumentId` - The id of the change document related to the transport request to release.
* `transportRequestId` - The id of the transport request to release.
* `applicationId` - The id of the application.
* `changeDocumentId` - For type `SOLMAN` only. The id of the change document related to the transport request to release. Typically provided via commit history.
* `transportRequestId` - The id of the transport request to release. Typically provided via commit history.
* `applicationId` - For type `SOLMAN` only. The id of the application.
* `filePath` - The path of the file to upload.
* `changeManagement/credentialsId` - The credentials to connect to the Solution Manager.
* `changeManagement/endpoint` - The address of the Solution Manager.
* `changeManagement/git/from` - The starting point for retrieving the change document id
* `changeManagement/git/to` - The end point for retrieving the change document id
* `changeManagement/changeDocumentLabel` - A pattern used for identifying lines holding the change document id.
* `changeManagement/credentialsId` - The credentials to connect to the service endpoint (Solution Manager, ABAP System).
* `changeManagement/endpoint` - The service endpoint (Solution Manager, ABAP System).
* `changeManagement/git/from` - The starting point for retrieving the change document id and/or transport request id
* `changeManagement/git/to` - The end point for retrieving the change document id and/or transport request id
* `changeManagement/changeDocumentLabel` - For type `SOLMAN` only. A pattern used for identifying lines holding the change document id.
* `changeManagement/transportRequestLabel` - A pattern used for identifying lines holding the transport request id.
* `changeManagement/type` Where/how the transport request is created (via SAP Solution Manager, ABAP).
* `changeManagement/git/format` - Specifies what part of the commit is scanned. By default the body of the commit message is scanned.
## Step configuration
@ -61,6 +63,7 @@ general:
changeDocumentLabel: 'ChangeDocument\s?:'
cmClientOpts: '-Djavax.net.ssl.trustStore=<path to truststore>'
credentialsId: 'CM'
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
git:
from: 'HEAD~1'
@ -78,6 +81,7 @@ The properties can also be configured on a per-step basis:
transportRequestUploadFile:
applicationId: 'FOO'
changeManagement:
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
[...]
```
@ -89,21 +93,31 @@ None.
## Exceptions
* `IllegalArgumentException`:
* If the change id is not provided.
* If the change id is not provided (`SOLMAN` only).
* If the transport request id is not provided.
* If the application id is not provided.
* If the application id is not provided (`SOLMAN` only).
* If the file path is not provided.
* `AbortException`:
* If the upload fails.
## Example
```groovy
// SOLMAN
transportRequestUploadFile script:this,
changeDocumentId: '001',
transportRequestId: '001',
changeDocumentId: '001', // typically provided via git commit history
transportRequestId: '001', // typically provided via git commit history
applicationId: '001',
filePath: '/path',
changeManagement:[
type: 'SOLMAN'
endpoint: 'https://example.org/cm'
]
// CTS
transportRequestUploadFile script:this,
transportRequestId: '001', // typically provided via git commit history
filePath: '/path',
changeManagement:[
type: 'CTS'
endpoint: 'https://example.org/cm'
]
```

View File

@ -6,22 +6,28 @@ nav:
- artifactSetVersion: steps/artifactSetVersion.md
- batsExecuteTests: steps/batsExecuteTests.md
- checkChangeInDevelopment: steps/checkChangeInDevelopment.md
- checksPublishResults: steps/checksPublishResults.md
- cloudFoundryDeploy: steps/cloudFoundryDeploy.md
- commonPipelineEnvironment: steps/commonPipelineEnvironment.md
- dockerExecute: steps/dockerExecute.md
- dockerExecuteOnKubernetes: steps/dockerExecuteOnKubernetes.md
- durationMeasure: steps/durationMeasure.md
- githubPublishRelease: steps/githubPublishRelease.md
- gaugeExecuteTests: steps/gaugeExecuteTests.md
- handlePipelineStepErrors: steps/handlePipelineStepErrors.md
- healthExecuteCheck: steps/healthExecuteCheck.md
- influxWriteData: steps/influxWriteData.md
- mailSendNotification: steps/mailSendNotification.md
- mavenExecute: steps/mavenExecute.md
- mtaBuild: steps/mtaBuild.md
- neoDeploy: steps/neoDeploy.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
- testsPublishResults: steps/testsPublishResults.md
- toolValidate: steps/toolValidate.md
- transportRequestCreate: steps/transportRequestCreate.md
- transportRequestRelease: steps/transportRequestRelease.md

18
pom.xml
View File

@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.sap.cp.jenkins</groupId>
<artifactId>jenkins-library</artifactId>
<version>0.7</version>
<version>0.8</version>
<name>SAP CP Piper Library</name>
<description>Shared library containing steps and utilities to set up continuous deployment processes for SAP technologies.</description>
@ -132,7 +132,6 @@
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
@ -140,8 +139,21 @@
<version>1.12</version>
<executions>
<execution>
<id>add-test-source</id>
<id>add-groovy-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src</source>
<source>vars</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-groovy-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>

View File

@ -0,0 +1,21 @@
----------------------------------------------------------
--- ERROR OCCURRED IN LIBRARY STEP: ${stepName}
----------------------------------------------------------
FOLLOWING PARAMETERS WERE AVAILABLE TO THIS STEP:
***
${stepParameters}
***
ERROR WAS:
***
${error}
***
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/
* Library repository: https://github.com/SAP/jenkins-library
----------------------------------------------------------

View File

@ -0,0 +1,6 @@
<a href="${env.BUILD_URL}">${env.BUILD_URL}</a>
<br>
To have a detailed look at the different pipeline stages: <a href="${env.RUN_DISPLAY_URL}">${env.RUN_DISPLAY_URL}</a>
<br>
<h3>Last lines of output</h3>
<pre>${log}</pre>

View File

@ -0,0 +1,3 @@
<a href="${env.BUILD_URL}">${env.BUILD_URL}</a>
<br>
To have a detailed look at the different pipeline stages: <a href="${env.RUN_DISPLAY_URL}">${env.RUN_DISPLAY_URL}</a>

View File

@ -3,6 +3,7 @@ general:
productiveBranch: 'master'
collectTelemetryData: true
changeManagement:
type: 'NONE' # SOLMAN, CTS, NONE
transportRequestLabel: 'TransportRequest\s?:'
changeDocumentLabel: 'ChangeDocument\s?:'
clientOpts: ''
@ -11,6 +12,8 @@ general:
from: 'origin/master'
to: 'HEAD'
format: '%b'
githubApiUrl: 'https://api.github.com'
githubServerUrl: 'https://github.com'
gitSshKeyCredentialsId: '' #needed to allow sshagent to run with local ssh key
jenkinsKubernetes:
jnlpAgent: 's4sdk/jenkins-agent-k8s:latest'
@ -139,10 +142,48 @@ steps:
workspace: '**/*.*'
stashExcludes:
workspace: 'nohup.out'
githubPublishRelease:
addClosedIssues: false
addDeltaToLastRelease: false
customFilterExtension: ''
excludeLabels:
- 'duplicate'
- 'invalid'
- 'question'
- 'wontfix'
gaugeExecuteTests:
buildTool: 'maven'
dockerEnvVars:
HUB: 'TRUE'
HUB_URL: 'http://localhost:4444/wd/hub'
failOnError: false
installCommand: 'curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge'
stashContent:
- 'buildDescriptor'
- 'tests'
maven:
dockerImage: 'maven:3.5-jdk-8'
dockerName: 'maven'
dockerWorkspace: ''
languageRunner: 'java'
runCommand: 'mvn test-compile gauge:execute'
testOptions: '-DspecsDir=specs'
npm:
dockerImage: 'node:8-stretch'
dockerName: 'npm'
dockerWorkspace: '/home/node'
languageRunner: 'js'
runCommand: 'gauge run'
testOptions: 'specs'
healthExecuteCheck:
healthEndpoint: ''
influxWriteData:
influxServer: 'jenkins'
mailSendNotification:
notificationAttachment: true
notifyCulprits: true
numLogLinesInBody: 100
wrapInNode: false
mavenExecute:
dockerImage: 'maven:3.5-jdk-7'
logSuccessfulMavenTransfers: false
@ -164,6 +205,9 @@ 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'
pipelineRestartSteps:
sendMail: true
timeoutInSeconds: 900
pipelineStashFilesAfterBuild:
runOpaTests: false
stashIncludes:
@ -200,7 +244,6 @@ steps:
'selenium/standalone-chrome':
- containerPort: 4444
hostPort: 4444
dockerLinkAlias: 'selenium'
failOnError: true
sidecarImage: 'selenium/standalone-chrome'
sidecarName: 'selenium'
@ -209,7 +252,7 @@ steps:
stashContent:
- 'tests'
maven:
dockerImage: 'maven:3.5-jdk-7'
dockerImage: 'maven:3.5-jdk-8'
dockerName: 'maven'
dockerWorkspace: ''
npm:

View File

@ -0,0 +1,26 @@
package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
class CfManifestUtils {
@NonCPS
static Map transform(Map manifest) {
if (manifest.applications[0].buildpacks) {
manifest['applications'].each { Map application ->
def buildpacks = application['buildpacks']
if (buildpacks) {
if (buildpacks instanceof List) {
if (buildpacks.size > 1) {
throw new RuntimeException('More than one Cloud Foundry Buildpack is not supported. Please check your manifest.yaml file.')
}
application['buildpack'] = buildpacks[0]
application.remove('buildpacks')
} else {
throw new RuntimeException('"buildpacks" in manifest.yaml is not a list. Please check your manifest.yaml file.')
}
}
}
}
return manifest
}
}

View File

@ -3,31 +3,27 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
class ConfigurationHelper implements Serializable {
static ConfigurationHelper loadStepDefaults(Script step){
return new ConfigurationHelper(step)
.initDefaults(step)
.loadDefaults()
static ConfigurationHelper newInstance(Script step, Map config = [:]) {
new ConfigurationHelper(step, config)
}
private Map config = [:]
private String name
ConfigurationHelper loadStepDefaults() {
this.step.prepareDefaultValues()
this.config = ConfigurationLoader.defaultGeneralConfiguration()
mixin(ConfigurationLoader.defaultStepConfiguration(null, name))
}
private Map config
private Script step
private String name
private Map validationResults = null
ConfigurationHelper(Script step){
name = step.STEP_NAME
if(!name) throw new IllegalArgumentException('Step has no public name property!')
}
private final ConfigurationHelper initDefaults(Script step){
step.prepareDefaultValues()
return this
}
private final ConfigurationHelper loadDefaults(){
config = ConfigurationLoader.defaultGeneralConfiguration()
mixin(ConfigurationLoader.defaultStepConfiguration(null, name))
return this
private ConfigurationHelper(Script step, Map config){
this.config = config ?: [:]
this.step = step
this.name = step.STEP_NAME
if(!this.name) throw new IllegalArgumentException('Step has no public name property!')
}
ConfigurationHelper collectValidationFailures() {
@ -35,25 +31,24 @@ class ConfigurationHelper implements Serializable {
return this
}
ConfigurationHelper mixinGeneralConfig(commonPipelineEnvironment, Set filter = null, Script step = null, Map compatibleParameters = [:]){
ConfigurationHelper mixinGeneralConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){
Map stepConfiguration = ConfigurationLoader.generalConfiguration([commonPipelineEnvironment: commonPipelineEnvironment])
return mixin(stepConfiguration, filter, step, compatibleParameters)
return mixin(stepConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixinStageConfig(commonPipelineEnvironment, stageName, Set filter = null, Script step = null, Map compatibleParameters = [:]){
ConfigurationHelper mixinStageConfig(commonPipelineEnvironment, stageName, Set filter = null, Map compatibleParameters = [:]){
Map stageConfiguration = ConfigurationLoader.stageConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], stageName)
return mixin(stageConfiguration, filter, step, compatibleParameters)
return mixin(stageConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixinStepConfig(commonPipelineEnvironment, Set filter = null, Script step = null, Map compatibleParameters = [:]){
if(!name) throw new IllegalArgumentException('Step has no public name property!')
ConfigurationHelper mixinStepConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){
Map stepConfiguration = ConfigurationLoader.stepConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], name)
return mixin(stepConfiguration, filter, step, compatibleParameters)
return mixin(stepConfiguration, filter, compatibleParameters)
}
ConfigurationHelper mixin(Map parameters, Set filter = null, Script step = null, Map compatibleParameters = [:]){
final ConfigurationHelper mixin(Map parameters, Set filter = null, Map compatibleParameters = [:]){
if (parameters.size() > 0 && compatibleParameters.size() > 0) {
parameters = ConfigurationMerger.merge(handleCompatibility(step, compatibleParameters, parameters), null, parameters)
parameters = ConfigurationMerger.merge(handleCompatibility(compatibleParameters, parameters), null, parameters)
}
if (filter) {
filter.add('collectTelemetryData')
@ -62,12 +57,12 @@ class ConfigurationHelper implements Serializable {
return this
}
private Map handleCompatibility(Script step, Map compatibleParameters, String paramStructure = '', Map configMap ) {
private Map handleCompatibility(Map compatibleParameters, String paramStructure = '', Map configMap ) {
Map newConfig = [:]
compatibleParameters.each {entry ->
if (entry.getValue() instanceof Map) {
paramStructure = (paramStructure ? paramStructure + '.' : '') + entry.getKey()
newConfig[entry.getKey()] = handleCompatibility(step, entry.getValue(), paramStructure, configMap)
newConfig[entry.getKey()] = handleCompatibility(entry.getValue(), paramStructure, configMap)
} else {
def configSubMap = configMap
for(String key in paramStructure.tokenize('.')){
@ -76,8 +71,8 @@ class ConfigurationHelper implements Serializable {
if (configSubMap == null || (configSubMap != null && configSubMap[entry.getKey()] == null)) {
newConfig[entry.getKey()] = configMap[entry.getValue()]
def paramName = (paramStructure ? paramStructure + '.' : '') + entry.getKey()
if (step && configMap[entry.getValue()] != null) {
step.echo ("[INFO] The parameter '${entry.getValue()}' is COMPATIBLE to the parameter '${paramName}'")
if (configMap[entry.getValue()] != null) {
this.step.echo ("[INFO] The parameter '${entry.getValue()}' is COMPATIBLE to the parameter '${paramName}'")
}
}
}
@ -113,10 +108,6 @@ class ConfigurationHelper implements Serializable {
return config
}
ConfigurationHelper(Map config = [:]){
this.config = config
}
/* private */ def getConfigPropertyNested(key) {
return getConfigPropertyNested(config, key)
}

View File

@ -2,8 +2,19 @@ package com.sap.piper
import com.cloudbees.groovy.cps.NonCPS
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
@NonCPS
static def isPluginActive(pluginId) {
return Jenkins.instance.pluginManager.plugins.find { p -> p.isActive() && p.getShortName() == pluginId }
}
def nodeAvailable() {
try {
sh "echo 'Node is available!'"
} catch (MissingContextVariableException e) {
echo "No node context available."
return false
}
return true
}

View File

@ -0,0 +1,5 @@
package com.sap.piper.cm;
public enum BackendType {
SOLMAN, CTS, NONE
}

View File

@ -63,7 +63,8 @@ public class ChangeManagement implements Serializable {
}
boolean isChangeInDevelopment(String changeId, String endpoint, String credentialsId, String clientOpts = '') {
int rc = executeWithCredentials(endpoint, credentialsId, 'is-change-in-development', ['-cID', "'${changeId}'", '--return-code'],
int rc = executeWithCredentials(BackendType.SOLMAN, endpoint, credentialsId, 'is-change-in-development', ['-cID', "'${changeId}'", '--return-code'],
false,
clientOpts) as int
if (rc == 0) {
@ -75,22 +76,52 @@ public class ChangeManagement implements Serializable {
}
}
String createTransportRequest(String changeId, String developmentSystemId, String endpoint, String credentialsId, String clientOpts = '') {
String createTransportRequestCTS(String transportType, String targetSystemId, String description, String endpoint, String credentialsId, String clientOpts = '') {
try {
def transportRequest = executeWithCredentials(endpoint, credentialsId, 'create-transport', ['-cID', changeId, '-dID', developmentSystemId],
def transportRequest = executeWithCredentials(BackendType.CTS, endpoint, credentialsId, 'create-transport',
['-tt', transportType, '-ts', targetSystemId, '-d', "\"${description}\""],
true,
clientOpts)
return (transportRequest as String)?.trim()
}catch(AbortException e) {
throw new ChangeManagementException("Cannot create a transport request. $e.message.")
}
}
String createTransportRequestSOLMAN(String changeId, String developmentSystemId, String endpoint, String credentialsId, String clientOpts = '') {
try {
def transportRequest = executeWithCredentials(BackendType.SOLMAN, endpoint, credentialsId, 'create-transport', ['-cID', changeId, '-dID', developmentSystemId],
true,
clientOpts)
return transportRequest.trim() as String
return (transportRequest as String)?.trim()
}catch(AbortException e) {
throw new ChangeManagementException("Cannot create a transport request for change id '$changeId'. $e.message.")
}
}
void uploadFileToTransportRequest(BackendType type, String changeId, String transportRequestId, String applicationId, String filePath, String endpoint, String credentialsId, String cmclientOpts = '') {
void uploadFileToTransportRequest(String changeId, String transportRequestId, String applicationId, String filePath, String endpoint, String credentialsId, String cmclientOpts = '') {
int rc = executeWithCredentials(endpoint, credentialsId, 'upload-file-to-transport', ['-cID', changeId,
'-tID', transportRequestId,
applicationId, "\"$filePath\""],
cmclientOpts) as int
def args = null
if(type == BackendType.SOLMAN) {
args = ['-cID', changeId,
'-tID', transportRequestId,
applicationId, "\"$filePath\""]
} else if (type == BackendType.CTS) {
args = ['-tID', transportRequestId,
"\"$filePath\""]
} else {
throw new IllegalArgumentException("Invalid backend type: ${type}")
}
int rc = executeWithCredentials(type,
endpoint,
credentialsId,
'upload-file-to-transport',
args,
false,
cmclientOpts) as int
if(rc == 0) {
return
@ -100,27 +131,48 @@ public class ChangeManagement implements Serializable {
}
def executeWithCredentials(String endpoint, String credentialsId, String command, List<String> args, String clientOpts = '') {
def executeWithCredentials(BackendType type, String endpoint, String credentialsId, String command, List<String> args, boolean returnStdout = false, String clientOpts = '') {
script.withCredentials([script.usernamePassword(
credentialsId: credentialsId,
passwordVariable: 'password',
usernameVariable: 'username')]) {
def cmScript = getCMCommandLine(endpoint, script.username, script.password,
def cmScript = getCMCommandLine(type, endpoint, script.username, script.password,
command, args,
clientOpts)
Map shArgs = [:]
if(returnStdout)
shArgs.put('returnStdout', true)
else
shArgs.put('returnStatus', true)
shArgs.put('script', cmScript)
// user and password are masked by withCredentials
script.echo """[INFO] Executing command line: "${cmScript}"."""
def returnValue = script.sh(returnStatus: true,
script: cmScript)
return returnValue;
return script.sh(shArgs)
}
}
void releaseTransportRequest(String changeId, String transportRequestId, String endpoint, String credentialsId, String clientOpts = '') {
int rc = executeWithCredentials( endpoint, credentialsId, 'release-transport', ['-cID', changeId,
'-tID', transportRequestId], clientOpts) as int
void releaseTransportRequest(BackendType type,String changeId, String transportRequestId, String endpoint, String credentialsId, String clientOpts = '') {
def cmd
List args = []
if(type == BackendType.SOLMAN) {
cmd = 'release-transport'
args << '-cID'
args << changeId
} else if(type == BackendType.CTS) {
cmd = 'export-transport'
} else {
throw new IllegalStateException("Invalid backend type: '${type}'")
}
args << '-tID'
args << transportRequestId
int rc = executeWithCredentials(type, endpoint, credentialsId, cmd, args, false, clientOpts) as int
if(rc == 0) {
return
} else {
@ -128,7 +180,8 @@ public class ChangeManagement implements Serializable {
}
}
String getCMCommandLine(String endpoint,
String getCMCommandLine(BackendType type,
String endpoint,
String username,
String password,
String command,
@ -143,7 +196,7 @@ public class ChangeManagement implements Serializable {
cmclient -e '$endpoint' \
-u '$username' \
-p '$password' \
-t SOLMAN \
-t ${type} \
${command} ${(args as Iterable).join(' ')}
"""
return cmCommandLine

View File

@ -0,0 +1,29 @@
package com.sap.piper.cm;
import com.cloudbees.groovy.cps.NonCPS
public class StepHelpers {
@NonCPS
static BackendType getBackendTypeAndLogInfoIfCMIntegrationDisabled(def step, Map configuration) {
BackendType backendType
try {
backendType = configuration.changeManagement.type as BackendType
} catch(IllegalArgumentException e) {
step.error "Invalid backend type: '${configuration.changeManagement.type}'. " +
"Valid values: [${BackendType.values().join(', ')}]. " +
"Configuration: 'changeManagement/type'."
}
if (backendType == BackendType.NONE) {
step.echo "[INFO] Change management integration intentionally switched off. " +
"In order to enable it provide 'changeManagement/type with one of " +
"[${BackendType.values().minus(BackendType.NONE).join(', ')}] and maintain " +
"other required properties like 'endpoint', 'credentialsId'."
}
return backendType
}
}

View File

@ -11,6 +11,7 @@ import com.sap.piper.cm.ChangeManagementException
import hudson.AbortException
import util.BasePiperTest
import util.JenkinsCredentialsRule
import util.JenkinsLoggingRule
import util.JenkinsReadYamlRule
import util.JenkinsStepRule
import util.Rules
@ -19,6 +20,7 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
private ExpectedException thrown = ExpectedException.none()
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
@Rule
public RuleChain ruleChain = Rules
@ -26,6 +28,7 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
.around(new JenkinsReadYamlRule(this))
.around(thrown)
.around(jsr)
.around(jlr)
.around(new JenkinsCredentialsRule(this)
.withCredentials('CM', 'anonymous', '********'))
@ -40,17 +43,22 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
public void changeIsInStatusDevelopmentTest() {
ChangeManagement cm = getChangeManagementUtils(true)
boolean inDevelopment = jsr.step.checkChangeInDevelopment(
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [
type: 'SOLMAN',
endpoint: 'https://example.org/cm'],
failIfStatusIsNotInDevelopment: true)
assert inDevelopment
assert cmUtilReceivedParams == [
changeId: '001',
endpoint: 'https://example.org/cm',
credentialsId: 'CM',
cmclientOpts: ''
]
// no exception in thrown, so the change is in status 'in development'.
}
@Test
@ -61,8 +69,10 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(false)
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
changeManagement: [type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
}
@Test
@ -70,6 +80,7 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(false)
boolean inDevelopment = jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'],
failIfStatusIsNotInDevelopment: false)
@ -81,9 +92,11 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(true, '0815')
jsr.step.checkChangeInDevelopment(
script: nullScript,
changeDocumentId: '42',
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
changeManagement: [type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
assert cmUtilReceivedParams.changeId == '42'
}
@ -93,8 +106,10 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(true, '0815')
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement : [endpoint: 'https://example.org/cm'])
changeManagement : [type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
assert cmUtilReceivedParams.changeId == '0815'
}
@ -118,8 +133,10 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
}
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
changeManagement: [type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
}
@Test
@ -132,8 +149,10 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(false, null)
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
changeManagement: [endpoint: 'https://example.org/cm',
type: 'SOLMAN'])
}
@Test
@ -146,8 +165,21 @@ class CheckChangeInDevelopmentTest extends BasePiperTest {
ChangeManagement cm = getChangeManagementUtils(false, '')
jsr.step.checkChangeInDevelopment(
script: nullScript,
cmUtils: cm,
changeManagement: [endpoint: 'https://example.org/cm'])
changeManagement: [type: 'SOLMAN',
endpoint: 'https://example.org/cm'])
}
@Test
public void cmIntegrationSwichtedOffTest() {
jlr.expect('[INFO] Change management integration intentionally switched off.')
jsr.step.checkChangeInDevelopment(
script: nullScript,
changeManagement: [type: 'NONE'])
}
private ChangeManagement getChangeManagementUtils(boolean inDevelopment, String changeDocumentId = '001') {

View File

@ -105,6 +105,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppName() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -125,6 +130,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppNameCustomApi() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -142,6 +152,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeWithAppNameCompatible() {
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
juStabUtils: utils,
@ -165,7 +180,11 @@ class CloudFoundryDeployTest extends BasePiperTest {
@Test
void testCfNativeAppNameFromManifest() {
helper.registerAllowedMethod('fileExists', [String.class], { s -> return true })
jryr.registerYaml('test.yml', "[applications: [[name: 'manifestAppName']]]")
jryr.registerYaml('test.yml', "applications: [[name: 'manifestAppName']]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
jsr.step.cloudFoundryDeploy([
script: nullScript,
@ -185,6 +204,10 @@ class CloudFoundryDeployTest extends BasePiperTest {
void testCfNativeWithoutAppName() {
helper.registerAllowedMethod('fileExists', [String.class], { s -> return true })
jryr.registerYaml('test.yml', "applications: [[]]")
helper.registerAllowedMethod('writeYaml', [Map], { Map parameters ->
generatedFile = parameters.file
data = parameters.data
})
thrown.expect(hudson.AbortException)
thrown.expectMessage('[cloudFoundryDeploy] ERROR: No appName available in manifest test.yml.')

View File

@ -2,8 +2,10 @@ import static java.util.stream.Collectors.toList
import static org.hamcrest.Matchers.empty
import static org.hamcrest.Matchers.is
import static org.junit.Assert.assertThat
import static org.junit.Assert.fail
import java.lang.reflect.Field
import java.io.File;
import org.junit.Assert
import org.junit.Rule
@ -12,6 +14,7 @@ import org.junit.rules.RuleChain
import groovy.io.FileType
import util.BasePiperTest
import util.Rules
/*
@ -22,6 +25,69 @@ public class CommonStepsTest extends BasePiperTest{
@Rule
public RuleChain ruleChain = Rules.getCommonRules(this)
private static fieldRelatedWhitelist = [
'toolValidate', // step is intended to be configured by other steps
'durationMeasure', // only expects parameters via signature
'prepareDefaultValues', // special step (infrastructure)
'pipelineStashFilesAfterBuild', // intended to be called from pipelineStashFiles
'pipelineStashFilesBeforeBuild', // intended to be called from pipelineStashFiles
'pipelineStashFiles', // only forwards to before/after step
'pipelineExecute', // special step (infrastructure)
'commonPipelineEnvironment', // special step (infrastructure)
'handlePipelineStepErrors', // special step (infrastructure)
]
@Test
public void generalConfigKeysSetPresentTest() {
def fieldName = 'GENERAL_CONFIG_KEYS'
// the steps added to the fieldRelatedWhitelist do not take the general config at all
def stepsWithoutGeneralConfigKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus(['gaugeExecuteTests',
'pipelineRestartSteps']))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutGeneralConfigKeySet}",
stepsWithoutGeneralConfigKeySet, is(empty()))
}
@Test
public void stepConfigKeysSetPresentTest() {
def fieldName = 'STEP_CONFIG_KEYS'
def stepsWithoutStepConfigKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus('setupCommonPipelineEnvironment'))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutStepConfigKeySet}",
stepsWithoutStepConfigKeySet, is(empty()))
}
@Test
public void parametersKeysSetPresentTest() {
def fieldName = 'PARAMETER_KEYS'
def stepsWithoutParametersKeySet = fieldCheck(fieldName, fieldRelatedWhitelist.plus('setupCommonPipelineEnvironment'))
assertThat("Steps without ${fieldName} field (or that field is not a Set): ${stepsWithoutParametersKeySet}",
stepsWithoutParametersKeySet, is(empty()))
}
private fieldCheck(fieldName, whitelist) {
def stepsWithoutGeneralConfigKeySet = []
for(def step in getSteps()) {
if(whitelist.contains(step)) continue
def fields = loadScript("${step}.groovy").getClass().getDeclaredFields() as Set
Field generalConfigKeyField = fields.find{ it.getName() == fieldName}
if(! generalConfigKeyField ||
! generalConfigKeyField
.getType()
.isAssignableFrom(Set.class)) {
stepsWithoutGeneralConfigKeySet.add(step)
}
}
return stepsWithoutGeneralConfigKeySet
}
@Test
public void stepsWithWrongFieldNameTest() {
@ -64,6 +130,35 @@ public class CommonStepsTest extends BasePiperTest{
assertThat("Steps with wrong step name or without STEP_NAME field.: ${stepsWithWrongStepName}",
stepsWithWrongStepName, is(empty()))
}
/*
* With that test we ensure that all return types of the call methods of all the steps
* are void. Return types other than void are not possible when running inside declarative
* pipelines. Parameters shared between several steps needs to be shared via the commonPipelineEnvironment.
*/
@Test
public void returnTypeForCallMethodsIsVoidTest() {
def stepsWithCallMethodsOtherThanVoid = []
def whitelist = [
'transportRequestCreate',
'durationMeasure',
]
for(def step in getSteps()) {
def methods = loadScript("${step}.groovy").getClass().getDeclaredMethods() as List
Collection callMethodsWithReturnTypeOtherThanVoid =
methods.stream()
.filter { ! whitelist.contains(step) }
.filter { it.getName() == 'call' &&
it.getReturnType() != Void.TYPE }
.collect(toList())
if(!callMethodsWithReturnTypeOtherThanVoid.isEmpty()) stepsWithCallMethodsOtherThanVoid << step
}
assertThat("Steps with call methods with return types other than void: ${stepsWithCallMethodsOtherThanVoid}",
stepsWithCallMethodsOtherThanVoid, is(empty()))
}
private static getSteps() {
List steps = []

View File

@ -115,17 +115,6 @@ class DockerExecuteTest extends BasePiperTest {
assertTrue(bodyExecuted)
}
@Test
void testExecuteInsideDockerNoScript() throws Exception {
jsr.step.dockerExecute(dockerImage: 'maven:3.5-jdk-8-alpine') {
bodyExecuted = true
}
assertEquals('maven:3.5-jdk-8-alpine', docker.getImageName())
assertTrue(docker.isImagePulled())
assertEquals('--env http_proxy --env https_proxy --env no_proxy --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY', docker.getParameters().trim())
assertTrue(bodyExecuted)
}
@Test
void testExecuteInsideDockerContainerWithParameters() throws Exception {
jsr.step.dockerExecute(script: nullScript,
@ -172,6 +161,7 @@ class DockerExecuteTest extends BasePiperTest {
void testSidecarDefault(){
jsr.step.dockerExecute(
script: nullScript,
dockerName: 'maven',
dockerImage: 'maven:3.5-jdk-8-alpine',
sidecarEnvVars: ['testEnv':'testVal'],
sidecarImage: 'selenium/standalone-chrome',
@ -186,9 +176,14 @@ class DockerExecuteTest extends BasePiperTest {
assertThat(docker.imagePullCount, is(2))
assertThat(docker.sidecarParameters, allOf(
containsString('--env testEnv=testVal'),
containsString('--volume /dev/shm:/dev/shm')
containsString('--volume /dev/shm:/dev/shm'),
containsString('--network sidecar-'),
containsString('--network-alias testAlias')
))
assertThat(docker.parameters, allOf(
containsString('--network sidecar-'),
containsString('--network-alias maven')
))
assertThat(docker.parameters, containsString('--link uniqueId:testAlias'))
}
@Test
@ -217,8 +212,7 @@ class DockerExecuteTest extends BasePiperTest {
sidecarEnvVars: ['testEnv':'testVal'],
sidecarImage: 'selenium/standalone-chrome',
sidecarName: 'selenium',
sidecarVolumeBind: ['/dev/shm':'/dev/shm'],
dockerLinkAlias: 'testAlias',
sidecarVolumeBind: ['/dev/shm':'/dev/shm']
) {
bodyExecuted = true
}

View File

@ -0,0 +1,137 @@
#!groovy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class GaugeExecuteTestsTest extends BasePiperTest {
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
private JenkinsEnvironmentRule jer = new JenkinsEnvironmentRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jscr)
.around(jlr)
.around(jer)
.around(jsr)
.around(thrown)
def gitParams = [:]
def seleniumParams = [:]
@Before
void init() throws Exception {
helper.registerAllowedMethod("git", [Map.class], { map -> gitParams = map })
helper.registerAllowedMethod("unstash", [String.class], { s -> return [s]})
helper.registerAllowedMethod('seleniumExecuteTests', [Map.class, Closure.class], {map, body ->
seleniumParams = map
return body()
})
}
@Test
void testExecuteGaugeDefaultSuccess() throws Exception {
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testServerUrl: 'http://test.url'
)
assertThat(jscr.shell, hasItem(stringContainsInOrder([
'export HOME=${HOME:-$(pwd)}',
'if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi',
'export PATH=$HOME/bin/gauge:$PATH',
'mkdir -p $HOME/bin/gauge',
'curl -SsL https://downloads.gauge.org/stable | sh -s -- --location=$HOME/bin/gauge',
'gauge telemetry off',
'gauge install java',
'gauge install html-report',
'gauge install xml-report',
'mvn test-compile gauge:execute -DspecsDir=specs'
])))
assertThat(seleniumParams.dockerImage, is('maven:3.5-jdk-8'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://test.url'))
assertThat(seleniumParams.dockerName, is('maven'))
assertThat(seleniumParams.dockerWorkspace, is(''))
assertThat(seleniumParams.stashContent, hasSize(2))
assertThat(seleniumParams.stashContent, allOf(hasItem('buildDescriptor'), hasItem('tests')))
assertJobStatusSuccess()
}
@Test
void testExecuteGaugeNode() throws Exception {
jsr.step.gaugeExecuteTests(
script: nullScript,
buildTool: 'npm',
dockerEnvVars: ['TARGET_SERVER_URL':'http://custom.url'],
juStabUtils: utils,
testOptions: 'testSpec'
)
assertThat(jscr.shell, hasItem(stringContainsInOrder([
'gauge install js',
'gauge run testSpec'
])))
assertThat(seleniumParams.dockerImage, is('node:8-stretch'))
assertThat(seleniumParams.dockerEnvVars, hasEntry('TARGET_SERVER_URL', 'http://custom.url'))
assertThat(seleniumParams.dockerName, is('npm'))
assertThat(seleniumParams.dockerWorkspace, is('/home/node'))
assertJobStatusSuccess()
}
@Test
void testExecuteCustomWithError() throws Exception {
helper.registerAllowedMethod("sh", [String.class], { s ->
throw new RuntimeException('Test Error')
})
thrown.expect(RuntimeException)
thrown.expectMessage('Test Error')
try {
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
dockerImage: 'testImage',
dockerName: 'testImageName',
dockerWorkspace: '/home/test',
failOnError: true,
stashContent: ['testStash'],
)
} finally{
assertThat(seleniumParams.dockerImage, is('testImage'))
assertThat(seleniumParams.dockerName, is('testImageName'))
assertThat(seleniumParams.dockerWorkspace, is('/home/test'))
assertThat(seleniumParams.stashContent, hasSize(1))
assertThat(jlr.log, containsString('[gaugeExecuteTests] One or more tests failed'))
assertThat(nullScript.currentBuild.result, is('UNSTABLE'))
}
}
@Test
void testExecuteGaugeCustomRepo() throws Exception {
helper.registerAllowedMethod('git', [String.class], null)
helper.registerAllowedMethod('stash', [String.class], null)
jsr.step.gaugeExecuteTests(
script: nullScript,
juStabUtils: utils,
testRepository: 'myTestRepo',
failOnError: true
)
// nested matchers do not work correctly
assertThat(seleniumParams.stashContent, hasItem(startsWith('testContent-')))
assertJobStatusSuccess()
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,82 @@
#!groovy
import static org.hamcrest.Matchers.is
import static org.hamcrest.Matchers.not
import static org.hamcrest.Matchers.containsString
import org.junit.Rule
import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import static org.junit.Assert.assertThat
import util.BasePiperTest
import util.JenkinsLoggingRule
import util.JenkinsStepRule
import util.Rules
class HandlePipelineStepErrorsTest extends BasePiperTest {
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private ExpectedException thrown = ExpectedException.none()
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(jlr)
.around(jsr)
.around(thrown)
@Test
void testBeginAndEndMessage() {
def isExecuted
jsr.step.handlePipelineStepErrors([
stepName: 'testStep',
stepParameters: ['something': 'anything']
]) {
isExecuted = true
}
// asserts
assertThat(isExecuted, is(true))
assertThat(jlr.log, containsString('--- BEGIN LIBRARY STEP: testStep'))
assertThat(jlr.log, containsString('--- END LIBRARY STEP: testStep'))
}
@Test
void testNonVerbose() {
try {
jsr.step.handlePipelineStepErrors([
stepName: 'testStep',
stepParameters: ['something': 'anything'],
echoDetails: false
]) {
throw new Exception('TestError')
}
} catch (ignore) {
} finally {
// asserts
assertThat(jlr.log, not(containsString('--- BEGIN LIBRARY STEP: testStep')))
assertThat(jlr.log, not(containsString('--- END LIBRARY STEP: testStep')))
assertThat(jlr.log, not(containsString('--- ERROR OCCURRED IN LIBRARY STEP: testStep')))
}
}
@Test
void testErrorsMessage() {
def isReported
try {
jsr.step.handlePipelineStepErrors([
stepName: 'testStep',
stepParameters: ['something': 'anything']
]) {
throw new Exception('TestError')
}
} catch (ignore) {
isReported = true
} finally {
// asserts
assertThat(isReported, is(true))
assertThat(jlr.log, containsString('--- ERROR OCCURRED IN LIBRARY STEP: testStep'))
assertThat(jlr.log, containsString('[something:anything]'))
}
}
}

View File

@ -0,0 +1,240 @@
#!groovy
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class MailSendNotificationTest extends BasePiperTest {
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsStepRule jsr = new JenkinsStepRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
@Rule
public RuleChain ruleChain = Rules
.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jlr)
.around(jscr)
.around(jsr)
@Before
void init() throws Exception {
// register Jenkins commands with mock values
helper.registerAllowedMethod("deleteDir", [], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], null)
nullScript.commonPipelineEnvironment.configuration = nullScript.commonPipelineEnvironment.configuration ?: [:]
nullScript.commonPipelineEnvironment.configuration['general'] = nullScript.commonPipelineEnvironment.configuration['general'] ?: [:]
nullScript.commonPipelineEnvironment.configuration['steps'] = nullScript.commonPipelineEnvironment.configuration['steps'] ?: [:]
nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification'] = nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification'] ?: [:]
helper.registerAllowedMethod('requestor', [], { -> return [$class: 'RequesterRecipientProvider']})
}
@Test
void testGetDistinctRecipients() throws Exception {
// git log -10 --pretty=format:"%ae %ce"
def input = '''user1@domain.com noreply+github@domain.com
user1@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user2@domain.com user1@domain.com
user1@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com
user3@domain.com noreply+github@domain.com'''
def result = jsr.step.getDistinctRecipients(input)
// asserts
assertThat(result.split(' '), arrayWithSize(3))
assertThat(result, containsString('user1@domain.com'))
assertThat(result, containsString('user2@domain.com'))
assertThat(result, containsString('user3@domain.com'))
}
@Test
void testCulpritsFromGitCommit() throws Exception {
def gitCommand = "git log -2 --pretty=format:'%ae %ce'"
def expected = "user2@domain.com user3@domain.com"
jscr.setReturnValue("git log -2 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
def result = jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: 'f0973368a35a2b973612acb86f932c61f2635f6e'
],
'master',
2)
println("LOGS: ${jlr.log}")
println("RESULT: ${result}")
// asserts
assertThat(result, containsString('user2@domain.com'))
assertThat(result, containsString('user3@domain.com'))
}
@Test
void testCulpritsWithEmptyGitCommit() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: ''
],
'master',
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testCulpritsWithoutGitCommit() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: null
],
'master',
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testCulpritsWithoutBranch() throws Exception {
jscr.setReturnValue('git log > /dev/null 2>&1',1)
jsr.step.getCulprits(
[
gitSSHCredentialsId: '',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git',
gitCommitId: ''
],
null,
2)
// asserts
assertThat(jlr.log, containsString('[mailSendNotification] No git context available to retrieve culprits'))
}
@Test
void testSendNotificationMail() throws Exception {
def emailParameters = [:]
def buildMock = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [
getLog: { cnt -> return ['Setting http proxy: proxy.wdf.domain.com:8080',
' > git fetch --no-tags --progress https://github.com/SAP/jenkins-library.git +refs/heads/*:refs/remotes/origin/*',
'Checking out Revision myUniqueCommitId (master)',
' > git config core.sparsecheckout # timeout=10',
' > git checkout -f myUniqueCommitId',
'Commit message: "Merge pull request #147 from marcusholl/pr/useGitRevParseForInsideGitRepoCheck"',
' > git rev-list --no-walk myUniqueCommitId # timeout=10',
'[Pipeline] node',
'Running on Jenkins in /var/jenkins_home/workspace/Test/UserId/ECHO',
'[Pipeline] {',
'[Pipeline] stage',
'[Pipeline] { (A)',
'[Pipeline] script',
'[Pipeline] {']
}
],
getPreviousBuild: {
return null
}
]
nullScript.currentBuild = buildMock
nullScript.commonPipelineEnvironment.configuration['steps']['mailSendNotification']['notificationRecipients'] = 'piper@domain.com'
helper.registerAllowedMethod('emailext', [Map.class], { map ->
emailParameters = map
return ''
})
jsr.step.mailSendNotification(
script: nullScript,
notifyCulprits: false,
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(emailParameters.to, is('piper@domain.com'))
assertThat(emailParameters.subject, is('FAILURE: Build testProjectName testDisplayName'))
assertThat(emailParameters.body, startsWith('<a href="http://build.url">http://build.url</a>\n<br>\nTo have a detailed look at the different pipeline stages: <a href="null">null</a>\n<br>\n<h3>Last lines of output</h3>'))
assertThat(emailParameters.body, containsString(' > git fetch --no-tags --progress https://github.com/SAP/jenkins-library.git +refs/heads/*:refs/remotes/origin/*'))
assertJobStatusSuccess()
}
@Test
void testSendNotificationMailWithGeneralConfig() throws Exception {
def credentials
nullScript.currentBuild = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [ getLog: { cnt -> return ['empty'] } ],
getChangeSets: { return null },
getPreviousBuild: { return null }
]
nullScript.commonPipelineEnvironment.configuration['general']['gitSshKeyCredentialsId'] = 'myCredentialsId'
helper.registerAllowedMethod('emailext', [Map.class], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], { map, closure ->
credentials = map.credentials
return null
})
jscr.setReturnValue("git log -0 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
jsr.step.mailSendNotification(
script: nullScript,
gitCommitId: 'abcd1234',
//notifyCulprits: true,
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(credentials, hasItem('myCredentialsId'))
assertJobStatusSuccess()
}
@Test
void testSendNotificationMailWithEmptySshKey() throws Exception {
def credentials
nullScript.currentBuild = [
fullProjectName: 'testProjectName',
displayName: 'testDisplayName',
result: 'FAILURE',
rawBuild: [ getLog: { cnt -> return ['empty'] } ],
getChangeSets: { return null },
getPreviousBuild: { return null }
]
helper.registerAllowedMethod('emailext', [Map.class], null)
helper.registerAllowedMethod("sshagent", [Map.class, Closure.class], { map, closure ->
credentials = map.credentials
return null
})
jscr.setReturnValue("git log -0 --pretty=format:'%ae %ce'", 'user2@domain.com user3@domain.com')
jsr.step.mailSendNotification(
script: nullScript,
gitCommitId: 'abcd1234',
gitUrl: 'git@github.wdf.domain.com:IndustryCloudFoundation/pipeline-test-node.git'
)
// asserts
assertThat(credentials, hasItem(''))
assertJobStatusSuccess()
}
}

View File

@ -0,0 +1,126 @@
#!groovy
import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import util.*
import static org.hamcrest.CoreMatchers.containsString
import static org.hamcrest.CoreMatchers.is
import static org.junit.Assert.assertThat
class PipelineRestartStepsTest extends BasePiperTest {
private JenkinsErrorRule jer = new JenkinsErrorRule(this)
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsStepRule jsr = new JenkinsStepRule(this)
@Rule
public RuleChain chain = Rules.getCommonRules(this)
.around(new JenkinsReadYamlRule(this))
.around(jer)
.around(jlr)
.around(jsr)
@Test
void testError() throws Exception {
def mailBuildResult = ''
helper.registerAllowedMethod('mailSendNotification', [Map.class], { m ->
mailBuildResult = m.buildResult
return null
})
helper.registerAllowedMethod('timeout', [Map.class, Closure.class], { m, closure ->
assertThat(m.time, is(1))
assertThat(m.unit, is('SECONDS'))
return closure()
})
def iterations = 0
helper.registerAllowedMethod('input', [Map.class], { m ->
iterations ++
assertThat(m.message, is('Do you want to restart?'))
assertThat(m.ok, is('Restart'))
if (iterations > 1) {
throw new FlowInterruptedException()
} else {
return null
}
})
try {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: true,
timeoutInSeconds: 1
]) {
throw new hudson.AbortException('I just created an error')
}
} catch(err) {
assertThat(jlr.log, containsString('ERROR occured: hudson.AbortException: I just created an error'))
assertThat(mailBuildResult, is('UNSTABLE'))
}
}
@Test
void testErrorNoMail() throws Exception {
def mailBuildResult = ''
helper.registerAllowedMethod('mailSendNotification', [Map.class], { m ->
mailBuildResult = m.buildResult
return null
})
helper.registerAllowedMethod('timeout', [Map.class, Closure.class], { m, closure ->
assertThat(m.time, is(1))
assertThat(m.unit, is('SECONDS'))
return closure()
})
def iterations = 0
helper.registerAllowedMethod('input', [Map.class], { m ->
iterations ++
assertThat(m.message, is('Do you want to restart?'))
assertThat(m.ok, is('Restart'))
if (iterations > 1) {
throw new FlowInterruptedException()
} else {
return null
}
})
try {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: false,
timeoutInSeconds: 1
]) {
throw new hudson.AbortException('I just created an error')
}
} catch(err) {
assertThat(jlr.log, containsString('ERROR occured: hudson.AbortException: I just created an error'))
assertThat(mailBuildResult, is(''))
}
}
@Test
void testSuccess() throws Exception {
jsr.step.pipelineRestartSteps ([
script: nullScript,
jenkinsUtilsStub: jenkinsUtils,
sendMail: false,
timeoutInSeconds: 1
]) {
nullScript.echo 'This is a test'
}
assertThat(jlr.log, containsString('This is a test'))
}
}

View File

@ -47,6 +47,7 @@ class SeleniumExecuteTestsTest extends BasePiperTest {
}
assertThat(bodyExecuted, is(true))
assertThat(jedr.dockerParams.containerPortMappings, is(['selenium/standalone-chrome': [[containerPort: 4444, hostPort: 4444]]]))
assertThat(jedr.dockerParams.dockerEnvVars, is(null))
assertThat(jedr.dockerParams.dockerImage, is('node:8-stretch'))
assertThat(jedr.dockerParams.dockerName, is('npm'))
assertThat(jedr.dockerParams.dockerWorkspace, is('/home/node'))
@ -56,6 +57,20 @@ class SeleniumExecuteTestsTest extends BasePiperTest {
assertThat(jedr.dockerParams.sidecarVolumeBind, is(['/dev/shm': '/dev/shm']))
}
@Test
void testExecuteSeleniumCustomBuildTool() {
jsr.step.seleniumExecuteTests(
script: nullScript,
buildTool: 'maven',
juStabUtils: utils
) {
bodyExecuted = true
}
assertThat(bodyExecuted, is(true))
assertThat(jedr.dockerParams.dockerImage, is('maven:3.5-jdk-8'))
assertThat(jedr.dockerParams.dockerName, is('maven'))
assertThat(jedr.dockerParams.dockerWorkspace, is(''))
}
@Test
void testExecuteSeleniumError() {
thrown.expectMessage('Error occured')

View File

@ -48,7 +48,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishNothingWithDefaultSettings() throws Exception {
jsr.step.testsPublishResults()
jsr.step.testsPublishResults(script: nullScript)
// ensure nothing is published
assertTrue('WarningsPublisher options not empty', publisherStepOptions.junit == null)
@ -59,7 +59,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishNothingWithAllDisabled() throws Exception {
jsr.step.testsPublishResults(junit: false, jacoco: false, cobertura: false, jmeter: false)
jsr.step.testsPublishResults(script: nullScript, junit: false, jacoco: false, cobertura: false, jmeter: false)
// ensure nothing is published
assertTrue('WarningsPublisher options not empty', publisherStepOptions.junit == null)
@ -70,7 +70,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishUnitTestsWithDefaultSettings() throws Exception {
jsr.step.testsPublishResults(junit: true)
jsr.step.testsPublishResults(script: nullScript, junit: true)
assertTrue('JUnit options are empty', publisherStepOptions.junit != null)
// ensure default patterns are set
@ -84,7 +84,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishCoverageWithDefaultSettings() throws Exception {
jsr.step.testsPublishResults(jacoco: true, cobertura: true)
jsr.step.testsPublishResults(script: nullScript, jacoco: true, cobertura: true)
assertTrue('JaCoCo options are empty', publisherStepOptions.jacoco != null)
assertTrue('Cobertura options are empty', publisherStepOptions.cobertura != null)
@ -99,7 +99,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishJMeterWithDefaultSettings() throws Exception {
jsr.step.testsPublishResults(jmeter: true)
jsr.step.testsPublishResults(script: nullScript, jmeter: true)
assertTrue('JMeter options are empty', publisherStepOptions.jmeter != null)
assertEquals('JMeter default pattern not set',
@ -113,7 +113,7 @@ class TestsPublishResultsTest extends BasePiperTest {
@Test
void testPublishUnitTestsWithCustomSettings() throws Exception {
jsr.step.testsPublishResults(junit: [pattern: 'fancy/file/path', archive: true, active: true])
jsr.step.testsPublishResults(script: nullScript, junit: [pattern: 'fancy/file/path', archive: true, active: true])
assertTrue('JUnit options are empty', publisherStepOptions.junit != null)
// ensure default patterns are set

View File

@ -4,6 +4,7 @@ import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
@ -40,6 +41,7 @@ public class TransportRequestCreateTest extends BasePiperTest {
[
credentialsId: 'CM',
type: 'SOLMAN',
endpoint: 'https://example.org/cm',
clientOpts: '-DmyProp=myVal',
changeDocumentLabel: 'ChangeId\\s?:',
@ -84,7 +86,8 @@ public class TransportRequestCreateTest extends BasePiperTest {
ChangeManagement cm = new ChangeManagement(nullScript) {
String createTransportRequest(String changeId,
String createTransportRequestSOLMAN(
String changeId,
String developmentSystemId,
String cmEndpoint,
String credentialId,
@ -102,13 +105,14 @@ public class TransportRequestCreateTest extends BasePiperTest {
}
@Test
public void createTransportRequestSuccessTest() {
public void createTransportRequestSuccessSOLMANTest() {
def result = [:]
ChangeManagement cm = new ChangeManagement(nullScript) {
String createTransportRequest(String changeId,
String createTransportRequestSOLMAN(
String changeId,
String developmentSystemId,
String cmEndpoint,
String credentialId,
@ -118,7 +122,6 @@ public class TransportRequestCreateTest extends BasePiperTest {
result.developmentSystemId = developmentSystemId
result.cmEndpoint = cmEndpoint
result.credentialId = credentialId
result.clientOpts = clientOpts
return '001'
}
@ -137,4 +140,58 @@ public class TransportRequestCreateTest extends BasePiperTest {
assert jlr.log.contains("[INFO] Creating transport request for change document '001' and development system '001'.")
assert jlr.log.contains("[INFO] Transport Request '001' has been successfully created.")
}
@Test
public void createTransportRequestSuccessCTSTest() {
def result = [:]
ChangeManagement cm = new ChangeManagement(nullScript) {
String createTransportRequestCTS(
String transportType,
String targetSystemId,
String description,
String endpoint,
String credentialsId,
String clientOpts
) {
result.transportType = transportType
result.targetSystemId = targetSystemId
result.description = description
result.endpoint = endpoint
result.credentialsId = credentialsId
result.clientOpts = clientOpts
return '001'
}
}
def transportId = jsr.step.call(script: nullScript,
transportType: 'W',
targetSystem: 'XYZ',
description: 'desc',
changeManagement: [type: 'CTS'],
cmUtils: cm)
assert transportId == '001'
assert result == [transportType: 'W',
targetSystemId: 'XYZ',
description: 'desc',
endpoint: 'https://example.org/cm',
credentialsId: 'CM',
clientOpts: '-DmyProp=myVal'
]
assert jlr.log.contains("[INFO] Creating transport request.")
assert jlr.log.contains("[INFO] Transport Request '001' has been successfully created.")
}
@Test
public void cmIntegrationSwichtedOffTest() {
jlr.expect('[INFO] Change management integration intentionally switched off.')
jsr.step.call(
changeManagement: [type: 'NONE'])
}
}

View File

@ -4,6 +4,7 @@ import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
@ -39,6 +40,7 @@ public class TransportRequestReleaseTest extends BasePiperTest {
[changeManagement:
[
credentialsId: 'CM',
type: 'SOLMAN',
endpoint: 'https://example.org/cm'
]
]
@ -89,7 +91,8 @@ public class TransportRequestReleaseTest extends BasePiperTest {
ChangeManagement cm = new ChangeManagement(nullScript) {
void releaseTransportRequest(String changeId,
void releaseTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String endpoint,
String credentialsId,
@ -111,12 +114,14 @@ public class TransportRequestReleaseTest extends BasePiperTest {
Map receivedParams = [:]
ChangeManagement cm = new ChangeManagement(nullScript) {
void releaseTransportRequest(String changeId,
void releaseTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String endpoint,
String credentialsId,
String clientOpts) {
receivedParams.type = type
receivedParams.changeId = changeId
receivedParams.transportRequestId = transportRequestId
receivedParams.endpoint = endpoint
@ -127,10 +132,20 @@ public class TransportRequestReleaseTest extends BasePiperTest {
jsr.step.call(script: nullScript, changeDocumentId: '001', transportRequestId: '002', cmUtils: cm)
assert receivedParams == [changeId: '001',
assert receivedParams == [type: BackendType.SOLMAN,
changeId: '001',
transportRequestId: '002',
endpoint: 'https://example.org/cm',
credentialsId: 'CM',
clientOpts: '']
}
@Test
public void cmIntegrationSwichtedOffTest() {
jlr.expect('[INFO] Change management integration intentionally switched off.')
jsr.step.call(
changeManagement: [type: 'NONE'])
}
}

View File

@ -6,6 +6,7 @@ import org.junit.Test
import org.junit.rules.ExpectedException
import org.junit.rules.RuleChain
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
@ -44,6 +45,7 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
[changeManagement:
[
credentialsId: 'CM',
type: 'SOLMAN',
endpoint: 'https://example.org/cm'
]
]
@ -51,7 +53,11 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
}
@Test
public void changeDocumentIdNotProvidedTest() {
public void changeDocumentIdNotProvidedSOLMANTest() {
// we expect the failure only for SOLMAN (which is the default).
// Use case for CTS without change document id is checked by the
// straight forward test case for CTS
thrown.expect(IllegalArgumentException)
thrown.expectMessage("Change document id not provided (parameter: 'changeDocumentId' or via commit history).")
@ -91,7 +97,11 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
}
@Test
public void applicationIdNotProvidedTest() {
public void applicationIdNotProvidedSOLMANTest() {
// we expect the failure only for SOLMAN (which is the default).
// Use case for CTS without applicationId is checked by the
// straight forward test case for CTS
thrown.expect(IllegalArgumentException)
thrown.expectMessage("ERROR - NO VALUE AVAILABLE FOR applicationId")
@ -112,7 +122,8 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
public void uploadFileToTransportRequestFailureTest() {
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -135,13 +146,14 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
}
@Test
public void uploadFileToTransportRequestSuccessTest() {
public void uploadFileToTransportRequestCTSSuccessTest() {
jlr.expect("[INFO] Uploading file '/path' to transport request '002' of change document '001'.")
jlr.expect("[INFO] File '/path' has been successfully uploaded to transport request '002' of change document '001'.")
jlr.expect("[INFO] Uploading file '/path' to transport request '002'.")
jlr.expect("[INFO] File '/path' has been successfully uploaded to transport request '002'.")
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -149,6 +161,53 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
String credentialsId,
String cmclientOpts) {
cmUtilReceivedParams.type = type
cmUtilReceivedParams.changeId = changeId
cmUtilReceivedParams.transportRequestId = transportRequestId
cmUtilReceivedParams.applicationId = applicationId
cmUtilReceivedParams.filePath = filePath
cmUtilReceivedParams.endpoint = endpoint
cmUtilReceivedParams.credentialsId = credentialsId
cmUtilReceivedParams.cmclientOpts = cmclientOpts
}
}
jsr.step.call(script: nullScript,
changeManagement: [type: 'CTS'],
transportRequestId: '002',
filePath: '/path',
cmUtils: cm)
assert cmUtilReceivedParams ==
[
type: BackendType.CTS,
changeId: null,
transportRequestId: '002',
applicationId: null,
filePath: '/path',
endpoint: 'https://example.org/cm',
credentialsId: 'CM',
cmclientOpts: ''
]
}
@Test
public void uploadFileToTransportRequestSOLMANSuccessTest() {
jlr.expect("[INFO] Uploading file '/path' to transport request '002' of change document '001'.")
jlr.expect("[INFO] File '/path' has been successfully uploaded to transport request '002' of change document '001'.")
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
String endpoint,
String credentialsId,
String cmclientOpts) {
cmUtilReceivedParams.type = type
cmUtilReceivedParams.changeId = changeId
cmUtilReceivedParams.transportRequestId = transportRequestId
cmUtilReceivedParams.applicationId = applicationId
@ -168,6 +227,7 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
assert cmUtilReceivedParams ==
[
type: BackendType.SOLMAN,
changeId: '001',
transportRequestId: '002',
applicationId: 'app',
@ -186,7 +246,8 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
[applicationId: 'AppIdfromConfig']]])
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -215,7 +276,8 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
nullScript.commonPipelineEnvironment.setMtarFilePath('/path2')
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -244,7 +306,8 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
nullScript.commonPipelineEnvironment.setMtarFilePath('/path2')
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -272,7 +335,8 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
thrown.expectMessage('Upload failure.')
ChangeManagement cm = new ChangeManagement(nullScript) {
void uploadFileToTransportRequest(String changeId,
void uploadFileToTransportRequest(BackendType type,
String changeId,
String transportRequestId,
String applicationId,
String filePath,
@ -291,4 +355,26 @@ public class TransportRequestUploadFileTest extends BasePiperTest {
cmUtils: cm)
}
@Test
public void invalidBackendTypeTest() {
thrown.expect(AbortException)
thrown.expectMessage('Invalid backend type: \'DUMMY\'. Valid values: [SOLMAN, CTS, NONE]. ' +
'Configuration: \'changeManagement/type\'.')
jsr.step.call(script: nullScript,
applicationId: 'app',
filePath: '/path',
changeManagement: [type: 'DUMMY'])
}
@Test
public void cmIntegrationSwichtedOffTest() {
jlr.expect('[INFO] Change management integration intentionally switched off.')
jsr.step.call(
changeManagement: [type: 'NONE'])
}
}

View File

@ -0,0 +1,30 @@
package com.sap.piper
import org.junit.Test
import static org.junit.Assert.*
class CfManifestUtilsTest {
@Test
void testManifestTransform() {
Map testFixture = [applications: [[buildpacks: ['sap_java_buildpack']]]]
Map expected = [applications: [[buildpack: 'sap_java_buildpack']]]
def actual = CfManifestUtils.transform(testFixture)
assertEquals(expected, actual)
}
@Test(expected = RuntimeException)
void testManifestTransformMultipleBuildpacks() {
Map testFixture = [applications: [[buildpacks: ['sap_java_buildpack', 'another_buildpack']]]]
CfManifestUtils.transform(testFixture)
}
@Test
void testManifestTransformShouldNotChange() {
Map testFixture = [applications: [[buildpack: 'sap_java_buildpack']]]
Map expected = [applications: [[buildpack: 'sap_java_buildpack']]]
def actual = CfManifestUtils.transform(testFixture)
assertEquals(expected, actual)
}
}

View File

@ -14,6 +14,18 @@ import org.junit.rules.ExpectedException
class ConfigurationHelperTest {
Script mockScript = new Script() {
def run() {
// it never runs
throw new UnsupportedOperationException()
}
def STEP_NAME = 'mock'
def echo(message) {
}
}
@Rule
public ExpectedException thrown = ExpectedException.none()
@ -37,6 +49,7 @@ class ConfigurationHelperTest {
assertThat(ConfigurationHelper.getConfigPropertyNested([a:[b: 'c']], 'a/c'), is((nullValue())))
}
@Test
void testGetPropertyNestedPathStartsWithTokenizer() {
assertThat(ConfigurationHelper.getConfigPropertyNested([k:'v'], '/k'), is(('v')))
}
@ -53,14 +66,14 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithDefaults() {
Map config = new ConfigurationHelper([property1: '27']).use()
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27']).use()
// asserts
Assert.assertThat(config, hasEntry('property1', '27'))
}
@Test
void testConfigurationLoaderWithCustomSettings() {
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: '41'])
.use()
// asserts
@ -70,7 +83,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithFilteredCustomSettings() {
Set filter = ['property2']
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: '41', property2: '28', property3: '29'], filter)
.use()
// asserts
@ -81,7 +94,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithBooleanValue() {
Map config = new ConfigurationHelper([property1: '27'])
Map config = ConfigurationHelper.newInstance(mockScript, [property1: '27'])
.mixin([property1: false])
.mixin([property2: false])
.use()
@ -92,7 +105,7 @@ class ConfigurationHelperTest {
@Test
void testConfigurationLoaderWithMixinDependent() {
Map config = new ConfigurationHelper([
Map config = ConfigurationHelper.newInstance(mockScript, [
type: 'maven',
maven: [dockerImage: 'mavenImage', dockerWorkspace: 'mavenWorkspace'],
npm: [dockerImage: 'npmImage', dockerWorkspace: 'npmWorkspace', executeDocker: true, executeDocker3: false],
@ -122,8 +135,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibility() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(4))
@ -133,8 +146,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityFlat() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [new1: 'old1', new2: 'old2'])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [new1: 'old1', new2: 'old2'])
.use()
Assert.assertThat(configuration.size(), is(5))
@ -144,8 +157,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityDeep() {
def configuration = new ConfigurationHelper()
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, null, [deep:[deeper:[newStructure: [new1: 'old1', new2: 'old2']]]])
def configuration = ConfigurationHelper.newInstance(mockScript)
.mixin([old1: 'oldValue1', old2: 'oldValue2', test: 'testValue'], null, [deep:[deeper:[newStructure: [new1: 'old1', new2: 'old2']]]])
.use()
Assert.assertThat(configuration.size(), is(4))
@ -155,8 +168,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityNewAvailable() {
def configuration = new ConfigurationHelper([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'])
.mixin([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'])
.mixin([old1: 'oldValue1', newStructure: [new1: 'newValue1'], test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(3))
@ -165,8 +178,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityOldNotSet() {
def configuration = new ConfigurationHelper([old1: null, test: 'testValue'])
.mixin([old1: null, test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: null, test: 'testValue'])
.mixin([old1: null, test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(2))
@ -175,8 +188,8 @@ class ConfigurationHelperTest {
@Test
void testHandleCompatibilityNoneAvailable() {
def configuration = new ConfigurationHelper([old1: null, test: 'testValue'])
.mixin([test: 'testValue'], null, null, [newStructure: [new1: 'old1', new2: 'old2']])
def configuration = ConfigurationHelper.newInstance(mockScript, [old1: null, test: 'testValue'])
.mixin([test: 'testValue'], null, [newStructure: [new1: 'old1', new2: 'old2']])
.use()
Assert.assertThat(configuration.size(), is(2))
@ -189,7 +202,7 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR myKey')
new ConfigurationHelper().withMandatoryProperty('myKey')
ConfigurationHelper.newInstance(mockScript).withMandatoryProperty('myKey')
}
@Test
@ -198,22 +211,22 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('My error message')
new ConfigurationHelper().withMandatoryProperty('myKey', 'My error message')
ConfigurationHelper.newInstance(mockScript).withMandatoryProperty('myKey', 'My error message')
}
@Test
public void testWithMandoryParameterDefaultCustomFailureMessageProvidedSucceeds() {
new ConfigurationHelper([myKey: 'myValue']).withMandatoryProperty('myKey', 'My error message')
ConfigurationHelper.newInstance(mockScript, [myKey: 'myValue']).withMandatoryProperty('myKey', 'My error message')
}
@Test
public void testWithMandoryParameterDefaultCustomFailureMessageNotProvidedSucceeds() {
new ConfigurationHelper([myKey: 'myValue']).withMandatoryProperty('myKey')
ConfigurationHelper.newInstance(mockScript, [myKey: 'myValue']).withMandatoryProperty('myKey')
}
@Test
public void testWithMandoryWithFalseCondition() {
new ConfigurationHelper([verify: false])
ConfigurationHelper.newInstance(mockScript, [verify: false])
.withMandatoryProperty('missingKey', null, { c -> return c.get('verify') })
}
@ -222,20 +235,20 @@ class ConfigurationHelperTest {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR missingKey')
new ConfigurationHelper([verify: true])
ConfigurationHelper.newInstance(mockScript, [verify: true])
.withMandatoryProperty('missingKey', null, { c -> return c.get('verify') })
}
@Test
public void testWithMandoryWithTrueConditionExistingValue() {
new ConfigurationHelper([existingKey: 'anyValue', verify: true])
ConfigurationHelper.newInstance(mockScript, [existingKey: 'anyValue', verify: true])
.withMandatoryProperty('existingKey', null, { c -> return c.get('verify') })
}
@Test
public void testTelemetryConfigurationAvailable() {
Set filter = ['test']
def configuration = new ConfigurationHelper([test: 'testValue'])
def configuration = ConfigurationHelper.newInstance(mockScript, [test: 'testValue'])
.mixin([collectTelemetryData: false], filter)
.use()
@ -257,7 +270,7 @@ class ConfigurationHelperTest {
assert bGString instanceof GString
assert cGString instanceof GString
def config = new ConfigurationHelper([a: aGString,
def config = ConfigurationHelper.newInstance(mockScript, [a: aGString,
nextLevel: [b: bGString]])
.mixin([c : cGString])
.use()
@ -272,7 +285,7 @@ class ConfigurationHelperTest {
@Test
public void testWithMandatoryParameterCollectFailuresAllParamtersArePresentResultsInNoExceptionThrown() {
new ConfigurationHelper([myKey1: 'a', myKey2: 'b'])
ConfigurationHelper.newInstance(mockScript, [myKey1: 'a', myKey2: 'b'])
.collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
@ -281,7 +294,7 @@ class ConfigurationHelperTest {
@Test
public void testWithMandatoryParameterCollectFailuresMultipleMissingParametersDoNotResultInFailuresDuringWithMandatoryProperties() {
new ConfigurationHelper([:]).collectValidationFailures()
ConfigurationHelper.newInstance(mockScript, [:]).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
}
@ -290,7 +303,7 @@ class ConfigurationHelperTest {
public void testWithMandatoryParameterCollectFailuresMultipleMissingParametersResultsInFailureDuringUse() {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR: myKey2, myKey3')
new ConfigurationHelper([myKey1:'a']).collectValidationFailures()
ConfigurationHelper.newInstance(mockScript, [myKey1:'a']).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
.withMandatoryProperty('myKey3')
@ -301,7 +314,7 @@ class ConfigurationHelperTest {
public void testWithMandatoryParameterCollectFailuresOneMissingParametersResultsInFailureDuringUse() {
thrown.expect(IllegalArgumentException)
thrown.expectMessage('ERROR - NO VALUE AVAILABLE FOR myKey2')
new ConfigurationHelper([myKey1:'a']).collectValidationFailures()
ConfigurationHelper.newInstance(mockScript, [myKey1:'a']).collectValidationFailures()
.withMandatoryProperty('myKey1')
.withMandatoryProperty('myKey2')
.use()

View File

@ -0,0 +1,45 @@
package com.sap.piper
import org.jenkinsci.plugins.workflow.steps.MissingContextVariableException
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.JenkinsLoggingRule
import util.JenkinsShellCallRule
import util.Rules
import static org.hamcrest.Matchers.*
import static org.junit.Assert.assertThat
class JenkinsUtilsTest extends BasePiperTest {
private JenkinsLoggingRule jlr = new JenkinsLoggingRule(this)
private JenkinsShellCallRule jscr = new JenkinsShellCallRule(this)
@Rule
public RuleChain rules = Rules
.getCommonRules(this)
.around(jscr)
.around(jlr)
@Test
void testNodeAvailable() {
def result = jenkinsUtils.nodeAvailable()
assertThat(jscr.shell, contains("echo 'Node is available!'"))
assertThat(result, is(true))
}
@Test
void testNoNodeAvailable() {
helper.registerAllowedMethod('sh', [String.class], {s ->
throw new MissingContextVariableException(String.class)
})
def result = jenkinsUtils.nodeAvailable()
assertThat(jlr.log, containsString('No node context available.'))
assertThat(result, is(false))
}
}

View File

@ -130,7 +130,8 @@ public class ChangeManagementTest extends BasePiperTest {
@Test
public void testGetCommandLineWithoutCMClientOpts() {
String commandLine = new ChangeManagement(nullScript, null)
.getCMCommandLine('https://example.org/cm',
.getCMCommandLine(BackendType.SOLMAN,
'https://example.org/cm',
"me",
"topSecret",
"the-command",
@ -143,7 +144,8 @@ public class ChangeManagementTest extends BasePiperTest {
@Test
public void testGetCommandLineWithCMClientOpts() {
String commandLine = new ChangeManagement(nullScript, null)
.getCMCommandLine('https://example.org/cm',
.getCMCommandLine(BackendType.SOLMAN,
'https://example.org/cm',
"me",
"topSecret",
"the-command",
@ -154,10 +156,28 @@ public void testGetCommandLineWithCMClientOpts() {
}
@Test
public void testCreateTransportRequestSucceeds() {
public void testCreateTransportRequestSOLMANSucceeds() {
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, ".*cmclient.*create-transport -cID 001 -dID 002.*", '004')
def transportRequestId = new ChangeManagement(nullScript).createTransportRequest('001', '002', '003', 'me')
def transportRequestId = new ChangeManagement(nullScript).createTransportRequestSOLMAN( '001', '002', '003', 'me')
// the check for the transportRequestID is sufficient. This checks implicit the command line since that value is
// returned only in case the shell call matches.
assert transportRequestId == '004'
}
@Test
public void testCreateTransportRequestCTSSucceeds() {
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'cmclient.* -t CTS .*create-transport -tt W -ts XYZ -d "desc 123"$', '004')
def transportRequestId = new ChangeManagement(nullScript)
.createTransportRequestCTS(
'W', // transport type
'XYZ', // target system
'desc 123', // description
'https://example.org/cm',
'me')
// the check for the transportRequestID is sufficient. This checks implicit the command line since that value is
// returned only in case the shell call matches.
@ -174,7 +194,8 @@ public void testGetCommandLineWithCMClientOpts() {
thrown.expectMessage('Cannot upload file \'/path\' for change document \'001\''+
' with transport request \'002\'. Return code from cmclient: 1.')
new ChangeManagement(nullScript).uploadFileToTransportRequest('001',
new ChangeManagement(nullScript).uploadFileToTransportRequest(BackendType.SOLMAN,
'001',
'002',
'XXX',
'/path',
@ -183,12 +204,14 @@ public void testGetCommandLineWithCMClientOpts() {
}
@Test
public void testUploadFileToTransportSucceeds() {
public void testUploadFileToTransportSucceedsSOLMAN() {
// the regex provided below is an implicit check that the command line is fine.
script.setReturnValue(JenkinsShellCallRule.Type.REGEX,, 'upload-file-to-transport.*-cID 001 -tID 002 XXX "/path"', 0)
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'upload-file-to-transport.*-cID 001 -tID 002 XXX "/path"', 0)
new ChangeManagement(nullScript).uploadFileToTransportRequest('001',
new ChangeManagement(nullScript).uploadFileToTransportRequest(
BackendType.SOLMAN,
'001',
'002',
'XXX',
'/path',
@ -199,6 +222,25 @@ public void testGetCommandLineWithCMClientOpts() {
// the command line.
}
@Test
public void testUploadFileToTransportSucceedsCTS() {
// the regex provided below is an implicit check that the command line is fine.
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS upload-file-to-transport -tID 002 "/path"', 0)
new ChangeManagement(nullScript).uploadFileToTransportRequest(
BackendType.CTS,
null,
'002',
null,
'/path',
'https://example.org/cm',
'me')
// no assert required here, since the regex registered above to the script rule is an implicit check for
// the command line.
}
@Test
public void testUploadFileToTransportFails() {
@ -208,7 +250,8 @@ public void testGetCommandLineWithCMClientOpts() {
script.setReturnValue(JenkinsShellCallRule.Type.REGEX,, 'upload-file-to-transport', 1)
new ChangeManagement(nullScript).uploadFileToTransportRequest('001',
new ChangeManagement(nullScript).uploadFileToTransportRequest(BackendType.SOLMAN,
'001',
'002',
'XXX',
'/path',
@ -217,12 +260,32 @@ public void testGetCommandLineWithCMClientOpts() {
}
@Test
public void testReleaseTransportRequestSucceeds() {
public void testReleaseTransportRequestSucceedsSOLMAN() {
// the regex provided below is an implicit check that the command line is fine.
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'release-transport.*-cID 001.*-tID 002', 0)
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t SOLMAN release-transport.*-cID 001.*-tID 002', 0)
new ChangeManagement(nullScript).releaseTransportRequest('001',
new ChangeManagement(nullScript).releaseTransportRequest(
BackendType.SOLMAN,
'001',
'002',
'https://example.org',
'me',
'openSesame')
// no assert required here, since the regex registered above to the script rule is an implicit check for
// the command line.
}
@Test
public void testReleaseTransportRequestSucceedsCTS() {
// the regex provided below is an implicit check that the command line is fine.
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS export-transport.*-tID 002', 0)
new ChangeManagement(nullScript).releaseTransportRequest(
BackendType.CTS,
null,
'002',
'https://example.org',
'me',
@ -241,7 +304,9 @@ public void testGetCommandLineWithCMClientOpts() {
// the regex provided below is an implicit check that the command line is fine.
script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'release-transport.*-cID 001.*-tID 002', 1)
new ChangeManagement(nullScript).releaseTransportRequest('001',
new ChangeManagement(nullScript).releaseTransportRequest(
BackendType.SOLMAN,
'001',
'002',
'https://example.org',
'me',

View File

@ -4,6 +4,7 @@ package util
import com.lesfurets.jenkins.unit.BasePipelineTest
import com.sap.piper.GitUtils
import com.sap.piper.JenkinsUtils
import com.sap.piper.Utils
import org.junit.Before
import org.junit.runner.RunWith
@ -23,6 +24,9 @@ abstract class BasePiperTest extends BasePipelineTest {
@Autowired
Utils utils
@Autowired
JenkinsUtils jenkinsUtils
@Override
@Before
void setUp() throws Exception {

View File

@ -3,6 +3,7 @@
package util
import com.sap.piper.GitUtils
import com.sap.piper.JenkinsUtils
import com.sap.piper.Utils
import org.codehaus.groovy.runtime.InvokerHelper
import org.springframework.context.annotation.Bean
@ -36,4 +37,11 @@ class BasePiperTestContext {
LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockUtils)
return mockUtils
}
@Bean
JenkinsUtils mockJenkinsUtils() {
def mockJenkinsUtils = new JenkinsUtils()
LibraryLoadingTestExecutionListener.prepareObjectInterceptors(mockJenkinsUtils)
return mockJenkinsUtils
}
}

View File

@ -40,7 +40,7 @@ class JenkinsSetupRule implements TestRule {
testInstance.binding.setVariable('env', [
JOB_NAME : 'p',
BUILD_NUMBER: '1',
BUILD_URL : ''
BUILD_URL : 'http://build.url',
])
base.evaluate()

View File

@ -72,6 +72,7 @@ class JenkinsShellCallRule implements TestRule {
}
}
if(result instanceof Closure) result = result()
if (!result && m.returnStatus) result = 0
return result
}
})

View File

@ -8,6 +8,9 @@ import groovy.text.SimpleTemplateEngine
@Field String STEP_NAME = 'artifactSetVersion'
@Field Map CONFIG_KEY_COMPATIBILITY = [gitSshKeyCredentialsId: 'gitCredentialsId']
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'artifactType',
'buildTool',
@ -23,9 +26,10 @@ import groovy.text.SimpleTemplateEngine
'timestampTemplate',
'versioningTemplate'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus('gitCommitId')
def call(Map parameters = [:], Closure body = null) {
void call(Map parameters = [:], Closure body = null) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -41,13 +45,13 @@ def call(Map parameters = [:], Closure body = null) {
script = this
// load default & individual configuration
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
ConfigurationHelper configHelper = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixin(gitCommitId: gitUtils.getGitCommitIdOrNull())
.mixin(parameters, PARAMETER_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.withMandatoryProperty('buildTool')
.dependingOn('buildTool').mixin('filePath')
.dependingOn('buildTool').mixin('versioningTemplate')
@ -77,7 +81,7 @@ def call(Map parameters = [:], Closure body = null) {
}
if (config.commitVersion) {
config = new ConfigurationHelper(config)
config = ConfigurationHelper.newInstance(this, config)
.addIfEmpty('gitSshUrl', isAppContainer(config)
?script.commonPipelineEnvironment.getAppContainerProperty('gitSshUrl')
:script.commonPipelineEnvironment.getGitSshUrl())

View File

@ -5,6 +5,9 @@ import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'batsExecuteTests'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'dockerImage',
'dockerWorkspace',
@ -19,17 +22,18 @@ import groovy.transform.Field
'testPath',
'testRepository'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def utils = parameters.juStabUtils ?: new Utils()
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
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)

View File

@ -5,11 +5,16 @@ import hudson.AbortException
import com.sap.piper.ConfigurationHelper
import com.sap.piper.ConfigurationMerger
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrationDisabled
@Field def STEP_NAME = 'checkChangeInDevelopment'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'changeManagement',
/**
@ -21,8 +26,6 @@ import com.sap.piper.cm.ChangeManagementException
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus('changeDocumentId')
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
/**
* Checks if a Change Document in SAP Solution Manager is in status 'in development'. The change document id is retrieved from the git commit history. The change document id
* can also be provided via parameter `changeDocumentId`. Any value provided as parameter has a higher precedence than a value from the commit history.
@ -31,7 +34,7 @@ import com.sap.piper.cm.ChangeManagementException
* range and the pattern can be configured. For details see 'parameters' table.
*
*/
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -41,12 +44,21 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters?.cmUtils ?: new ChangeManagement(script, gitUtils)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = 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)
Map configuration = configHelper.use()
BackendType backendType = getBackendTypeAndLogInfoIfCMIntegrationDisabled(this, configuration)
if(backendType == BackendType.NONE) return
new Utils().pushToSWA([step: STEP_NAME], configuration)
configHelper
// for the following parameters we expect defaults
/**
* A pattern used for identifying lines holding the change document id.
@ -82,10 +94,6 @@ def call(parameters = [:]) {
.withMandatoryProperty('changeManagement/endpoint')
Map configuration = configHelper.use()
new Utils().pushToSWA([step: STEP_NAME], configuration)
def changeId = configuration.changeDocumentId
if(changeId?.trim()) {
@ -142,14 +150,12 @@ def call(parameters = [:]) {
if(isInDevelopment) {
echo "[INFO] Change '${changeId}' is in status 'in development'."
return true
} else {
if(configuration.failIfStatusIsNotInDevelopment.toBoolean()) {
throw new AbortException("Change '${changeId}' is not in status 'in development'.")
} else {
echo "[WARNING] Change '${changeId}' is not in status 'in development'. Failing the pipeline has been explicitly disabled."
return false
}
}
}

View File

@ -21,7 +21,7 @@ import groovy.transform.Field
*
* @param others document all parameters
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script
if (script == null)
@ -30,8 +30,8 @@ def call(Map parameters = [:]) {
prepare(parameters)
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
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)

View File

@ -1,9 +1,13 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.CfManifestUtils
import groovy.transform.Field
@Field String STEP_NAME = 'cloudFoundryDeploy'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'cloudFoundry',
'deployUser',
@ -18,9 +22,10 @@ import groovy.transform.Field
'smokeTestStatusCode',
'stashContent']
@Field Map CONFIG_KEY_COMPATIBILITY = [cloudFoundry: [apiEndpoint: 'cfApiEndpoint', appName:'cfAppName', credentialsId: 'cfCredentialsId', manifest: 'cfManifest', org: 'cfOrg', space: 'cfSpace']]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -33,12 +38,12 @@ def call(Map parameters = [:]) {
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, this, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, this, CONFIG_KEY_COMPATIBILITY)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS, CONFIG_KEY_COMPATIBILITY)
.mixin(parameters, PARAMETER_KEYS, CONFIG_KEY_COMPATIBILITY)
.dependingOn('deployTool').mixin('dockerImage')
.dependingOn('deployTool').mixin('dockerWorkspace')
.withMandatoryProperty('cloudFoundry/org')
@ -54,7 +59,7 @@ def call(Map parameters = [:]) {
if (config.deployTool == 'mtaDeployPlugin') {
// set default mtar path
config = new ConfigurationHelper(config)
config = ConfigurationHelper.newInstance(this, config)
.addIfEmpty('mtaPath', config.mtaPath?:findMtar())
.use()
@ -112,6 +117,7 @@ def deployCfNative (config) {
def deployCommand = 'push'
if (config.deployType == 'blue-green') {
deployCommand = 'blue-green-deploy'
handleLegacyCfManifest(config)
} else {
config.smokeTest = ''
}
@ -129,7 +135,7 @@ def deployCfNative (config) {
}
sh """#!/bin/bash
set +x
set +x
export HOME=${config.dockerWorkspace}
cf login -u \"${username}\" -p '${password}' -a ${config.cloudFoundry.apiEndpoint} -o \"${config.cloudFoundry.org}\" -s \"${config.cloudFoundry.space}\"
cf plugins
@ -166,3 +172,18 @@ def deployMta (config) {
sh "cf logout"
}
}
def handleLegacyCfManifest(config) {
def manifest = readYaml file: config.cloudFoundry.manifest
String originalManifest = manifest.toString()
manifest = CfManifestUtils.transform(manifest)
String transformedManifest = manifest.toString()
if (originalManifest != transformedManifest) {
echo """The file ${config.cloudFoundry.manifest} is not compatible with the Cloud Foundry blue-green deployment plugin. Re-writing inline.
See this issue if you are interested in the background: https://github.com/cloudfoundry/cli/issues/1445.\n
Original manifest file content: $originalManifest\n
Transformed manifest file content: $transformedManifest"""
sh "rm ${config.cloudFoundry.manifest}"
writeYaml file: config.cloudFoundry.manifest, data: manifest
}
}

View File

@ -4,9 +4,18 @@ class commonPipelineEnvironment implements Serializable {
//stores version of the artifact which is build during pipeline run
def artifactVersion
//Stores the current buildResult
String buildResult = 'SUCCESS'
//stores the gitCommitId as well as additional git information for the build during pipeline run
String gitCommitId
String gitSshUrl
String gitHttpsUrl
String gitBranch
//GiutHub specific information
String githubOrg
String githubRepo
//stores properties for a pipeline which build an artifact and then bundles it into a container
private Map appContainerProperties = [:]
@ -30,6 +39,11 @@ class commonPipelineEnvironment implements Serializable {
gitCommitId = null
gitSshUrl = null
gitHttpsUrl = null
gitBranch = null
githubOrg = null
githubRepo = null
influxCustomData = [:]
influxCustomDataMap = [pipeline_data: [:], step_data: [:]]

View File

@ -17,9 +17,9 @@ import groovy.transform.Field
'dockerOptions',
'dockerWorkspace',
'dockerVolumeBind',
'sidecarName',
'sidecarEnvVars',
'sidecarImage',
'sidecarName',
'sidecarOptions',
'sidecarWorkspace',
'sidecarVolumeBind'
@ -31,8 +31,8 @@ void call(Map parameters = [:], body) {
final script = parameters.script
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
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)
@ -111,15 +111,27 @@ void call(Map parameters = [:], body) {
body()
}
} else {
def sidecarImage = docker.image(config.sidecarImage)
sidecarImage.pull()
sidecarImage.withRun(getDockerOptions(config.sidecarEnvVars, config.sidecarVolumeBind, config.sidecarOptions)) { c ->
config.dockerOptions = config.dockerOptions?:[]
config.dockerOptions.add("--link ${c.id}:${config.sidecarName}")
image.inside(getDockerOptions(config.dockerEnvVars, config.dockerVolumeBind, config.dockerOptions)) {
echo "[INFO][${STEP_NAME}] Running with sidecar container."
body()
def networkName = "sidecar-${UUID.randomUUID()}"
sh "docker network create ${networkName}"
try{
def sidecarImage = docker.image(config.sidecarImage)
sidecarImage.pull()
config.sidecarOptions = config.sidecarOptions?:[]
if(config.sidecarName)
config.sidecarOptions.add("--network-alias ${config.sidecarName}")
config.sidecarOptions.add("--network ${networkName}")
sidecarImage.withRun(getDockerOptions(config.sidecarEnvVars, config.sidecarVolumeBind, config.sidecarOptions)) { c ->
config.dockerOptions = config.dockerOptions?:[]
if(config.dockerName)
config.dockerOptions.add("--network-alias ${config.dockerName}")
config.dockerOptions.add("--network ${networkName}")
image.inside(getDockerOptions(config.dockerEnvVars, config.dockerVolumeBind, config.dockerOptions)) {
echo "[INFO][${STEP_NAME}] Running with sidecar container."
body()
}
}
}finally{
sh "docker network remove ${networkName}"
}
}
} else {

View File

@ -29,8 +29,8 @@ void call(Map parameters = [:], body) {
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
ConfigurationHelper configHelper = 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)

View File

@ -0,0 +1,94 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import com.sap.piper.GitUtils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'gaugeExecuteTests'
@Field Set STEP_CONFIG_KEYS = [
'buildTool',
'dockerEnvVars',
'dockerImage',
'dockerName',
'dockerWorkspace',
'failOnError',
'gitBranch',
'gitSshKeyCredentialsId',
'installCommand',
'languageRunner',
'runCommand',
'stashContent',
'testOptions',
'testRepository',
'testServerUrl'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters.juStabUtils ?: new Utils()
script.commonPipelineEnvironment.setInfluxStepData('gauge', false)
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.dependingOn('buildTool').mixin('dockerImage')
.dependingOn('buildTool').mixin('dockerName')
.dependingOn('buildTool').mixin('dockerWorkspace')
.dependingOn('buildTool').mixin('languageRunner')
.dependingOn('buildTool').mixin('runCommand')
.dependingOn('buildTool').mixin('testOptions')
.use()
utils.pushToSWA([step: STEP_NAME, stepParam1: config.buildTool, stepParam2: config.dockerName], config)
if(!config.dockerEnvVars.TARGET_SERVER_URL && config.testServerUrl)
config.dockerEnvVars.TARGET_SERVER_URL = config.testServerUrl
if (config.testRepository) {
// handle separate test repository
config.stashContent = [GitUtils.handleTestRepository(this, config)]
} else {
config.stashContent = utils.unstashAll(config.stashContent)
}
seleniumExecuteTests (
script: script,
buildTool: config.buildTool,
dockerEnvVars: config.dockerEnvVars,
dockerImage: config.dockerImage,
dockerName: config.dockerName,
dockerWorkspace: config.dockerWorkspace,
stashContent: config.stashContent
) {
String gaugeScript = ''
if (config.installCommand) {
gaugeScript = '''export HOME=${HOME:-$(pwd)}
if [ "$HOME" = "/" ]; then export HOME=$(pwd); fi
export PATH=$HOME/bin/gauge:$PATH
mkdir -p $HOME/bin/gauge
''' + config.installCommand + '''
gauge telemetry off
gauge install ''' + config.languageRunner + '''
gauge install html-report
gauge install xml-report
'''
}
gaugeScript += config.runCommand
try {
sh "${gaugeScript} ${config.testOptions}"
script.commonPipelineEnvironment.setInfluxStepData('gauge', true)
} catch (err) {
echo "[${STEP_NAME}] One or more tests failed"
script.currentBuild.result = 'UNSTABLE'
if (config.failOnError) throw err
}
}
}
}

View File

@ -0,0 +1,134 @@
import com.sap.piper.Utils
import com.sap.piper.ConfigurationHelper
import groovy.transform.Field
@Field String STEP_NAME = 'githubPublishRelease'
@Field Set GENERAL_CONFIG_KEYS = ['githubApiUrl', 'githubTokenCredentialsId', 'githubServerUrl']
@Field Set STEP_CONFIG_KEYS = [
'addClosedIssues',
'addDeltaToLastRelease',
'customFilterExtension',
'excludeLabels',
'githubApiUrl',
'githubTokenCredentialsId',
'githubOrg',
'githubRepo',
'githubServerUrl',
'releaseBodyHeader',
'version'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
// 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)
.addIfEmpty('githubOrg', script.commonPipelineEnvironment.getGithubOrg())
.addIfEmpty('githubRepo', script.commonPipelineEnvironment.getGithubRepo())
.addIfEmpty('version', script.commonPipelineEnvironment.getArtifactVersion())
.withMandatoryProperty('githubOrg')
.withMandatoryProperty('githubRepo')
.withMandatoryProperty('githubTokenCredentialsId')
.withMandatoryProperty('version')
.use()
new Utils().pushToSWA([step: STEP_NAME], config)
withCredentials([string(credentialsId: config.githubTokenCredentialsId, variable: 'TOKEN')]) {
def releaseBody = config.releaseBodyHeader?"${config.releaseBodyHeader}<br />":''
def content = getLastRelease(config, TOKEN)
if (config.addClosedIssues)
releaseBody += addClosedIssue(config, TOKEN, content.published_at)
if (config.addDeltaToLastRelease)
releaseBody += addDeltaToLastRelease(config, content.tag_name)
postNewRelease(config, TOKEN, releaseBody)
}
}
}
Map getLastRelease(config, TOKEN){
def result = [:]
def response = httpRequest "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/releases/latest?access_token=${TOKEN}"
if (response.status == 200) {
result = readJSON text: response.content
} else {
echo "[${STEP_NAME}] This is the first release - no previous releases available"
config.addDeltaToLastRelease = false
}
return result
}
String addClosedIssue(config, TOKEN, publishedAt){
if (config.customFilterExtension) {
config.customFilterExtension = "&${config.customFilterExtension}"
}
def publishedAtFilter = publishedAt ? "&since=${publishedAt}": ''
def response = httpRequest "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/issues?access_token=${TOKEN}&per_page=100&state=closed&direction=asc${publishedAtFilter}${config.customFilterExtension}"
def result = ''
content = readJSON text: response.content
//list closed pull-requests
result += '<br />**List of closed pull-requests since last release**<br />'
for (def item : content) {
if (item.pull_request && !isExcluded(item, config.excludeLabels)) {
result += "[#${item.number}](${item.html_url}): ${item.title}<br />"
}
}
//list closed issues
result += '<br />**List of closed issues since last release**<br />'
for (def item : content) {
if (!item.pull_request && !isExcluded(item, config.excludeLabels)) {
result += "[#${item.number}](${item.html_url}): ${item.title}<br />"
}
}
return result
}
String addDeltaToLastRelease(config, latestTag){
def result = ''
//add delta link to previous release
result += '<br />**Changes**<br />'
result += "[${latestTag}...${config.version}](${config.githubServerUrl}/${config.githubOrg}/${config.githubRepo}/compare/${latestTag}...${config.version}) <br />"
return result
}
void postNewRelease(config, TOKEN, releaseBody){
releaseBody = releaseBody.replace('"', '\\"')
//write release information
def data = "{\"tag_name\": \"${config.version}\",\"target_commitish\": \"master\",\"name\": \"${config.version}\",\"body\": \"${releaseBody}\",\"draft\": false,\"prerelease\": false}"
try {
httpRequest httpMode: 'POST', requestBody: data, url: "${config.githubApiUrl}/repos/${config.githubOrg}/${config.githubRepo}/releases?access_token=${TOKEN}"
} catch (e) {
echo """[${STEP_NAME}] Error occured when writing release information
---------------------------------------------------------------------
Request body was:
---------------------------------------------------------------------
${data}
---------------------------------------------------------------------"""
throw e
}
}
boolean isExcluded(item, excludeLabels){
def result = false
excludeLabels.each {labelName ->
item.labels.each { label ->
if (label.name == labelName) {
result = true
}
}
}
return result
}

View File

@ -1,50 +1,34 @@
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field STEP_NAME = 'handlePipelineStepErrors'
def call(Map parameters = [:], body) {
void call(Map parameters = [:], body) {
def stepParameters = parameters.stepParameters //mandatory
def stepName = parameters.stepName //mandatory
def echoDetails = parameters.get('echoDetails', true)
def verbose = parameters.get('echoDetails', true)
def message = ''
try {
if (stepParameters == null && stepName == null)
error "step handlePipelineStepErrors requires following mandatory parameters: stepParameters, stepName"
if (echoDetails)
echo "--- BEGIN LIBRARY STEP: ${stepName}.groovy ---"
if (verbose)
echo "--- BEGIN LIBRARY STEP: ${stepName} ---"
body()
} catch (Throwable err) {
if (echoDetails)
echo """----------------------------------------------------------
--- ERROR OCCURED IN LIBRARY STEP: ${stepName}
----------------------------------------------------------
FOLLOWING PARAMETERS WERE AVAILABLE TO THIS STEP:
***
${stepParameters}
***
ERROR WAS:
***
${err}
***
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/
* Library repository: https://github.com/SAP/jenkins-library
----------------------------------------------------------"""
if (verbose)
message += SimpleTemplateEngine.newInstance()
.createTemplate(libraryResource('com.sap.piper/templates/error.log'))
.make([
stepName: stepName,
stepParameters: stepParameters?.toString(),
error: err
]).toString()
throw err
} finally {
if (echoDetails)
echo "--- END LIBRARY STEP: ${stepName}.groovy ---"
if (verbose)
message += "--- END LIBRARY STEP: ${stepName} ---"
echo message
}
}

View File

@ -1,27 +1,33 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.transform.Field
@Field String STEP_NAME = 'healthExecuteCheck'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'healthEndpoint',
'healthEndpoint',
'testServerUrl'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
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)
.withMandatoryProperty('testServerUrl')
.use()
new Utils().pushToSWA([step: STEP_NAME], config)
def checkUrl = config.testServerUrl
if(config.healthEndpoint){
if(!checkUrl.endsWith('/'))

View File

@ -17,15 +17,15 @@ import groovy.transform.Field
'artifactVersion'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, allowBuildFailure: true) {
def script = parameters.script
if (script == null)
script = [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
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)

View File

@ -0,0 +1,194 @@
import com.sap.piper.ConfigurationHelper
import com.sap.piper.Utils
import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'mailSendNotification'
@Field Set GENERAL_CONFIG_KEYS = ['gitSshKeyCredentialsId']
@Field Set STEP_CONFIG_KEYS = [
'projectName',
'buildResult',
'gitUrl',
'gitCommitId',
'gitSshKeyCredentialsId',
'wrapInNode',
'notifyCulprits',
'notificationAttachment',
'notificationRecipients',
'numLogLinesInBody'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, allowBuildFailure: true) {
def script = parameters.script?:[commonPipelineEnvironment: commonPipelineEnvironment]
// 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(
projectName: script.currentBuild.fullProjectName,
displayName: script.currentBuild.displayName,
buildResult: script.currentBuild.result,
gitUrl: script.commonPipelineEnvironment.getGitSshUrl(),
gitCommitId: script.commonPipelineEnvironment.getGitCommitId()
)
.mixin(parameters, PARAMETER_KEYS)
.use()
new Utils().pushToSWA([step: STEP_NAME], config)
//this takes care that terminated builds due to milestone-locking do not cause an error
if (script.commonPipelineEnvironment.getBuildResult() == 'ABORTED') return
def subject = "${config.buildResult}: Build ${config.projectName} ${config.displayName}"
def log = ''
def mailTemplate
if (config.buildResult == 'UNSTABLE' || config.buildResult == 'FAILURE'){
mailTemplate = 'com.sap.piper/templates/mailFailure.html'
log = script.currentBuild.rawBuild.getLog(config.numLogLinesInBody).join('\n')
}else if(hasRecovered(config.buildResult, script.currentBuild)){
mailTemplate = 'com.sap.piper/templates/mailRecover.html'
subject += ' is back to normal'
}
if(mailTemplate){
def mailContent = SimpleTemplateEngine.newInstance().createTemplate(libraryResource(mailTemplate)).make([env: env, log: log]).toString()
def recipientList = ''
if(config.notifyCulprits){
if (!config.gitUrl) {
echo "[${STEP_NAME}] no gitUrl available, -> exiting without sending mails"
return
}
recipientList += getCulpritCommitters(config, script.currentBuild)
}
if(config.notificationRecipients)
recipientList += " ${config.notificationRecipients}"
emailext(
mimeType: 'text/html',
subject: subject.trim(),
body: mailContent,
to: recipientList.trim(),
recipientProviders: [requestor()],
attachLog: config.notificationAttachment
)
}
}
}
def getNumberOfCommits(buildList){
def numCommits = 0
if(buildList != null)
for(actBuild in buildList) {
def changeLogSets = actBuild.getChangeSets()
if(changeLogSets != null)
for(changeLogSet in changeLogSets)
for(change in changeLogSet)
numCommits++
}
return numCommits
}
def getCulpritCommitters(config, currentBuild) {
def recipients
def buildList = []
def build = currentBuild
if (build != null) {
// At least add the current build
buildList.add(build)
// Now collect FAILED or ABORTED ones
build = build.getPreviousBuild()
while (build != null) {
if (build.getResult() != 'SUCCESS') {
buildList.add(build)
} else {
break
}
build = build.getPreviousBuild()
}
}
def numberOfCommits = getNumberOfCommits(buildList)
if(config.wrapInNode){
node(){
try{
recipients = getCulprits(config, env.BRANCH_NAME, numberOfCommits)
}finally{
deleteDir()
}
}
}else{
try{
recipients = getCulprits(config, env.BRANCH_NAME, numberOfCommits)
}finally{
deleteDir()
}
}
echo "[${STEP_NAME}] last ${numberOfCommits} commits revealed following responsibles ${recipients}"
return recipients
}
def getCulprits(config, branch, numberOfCommits) {
if (branch?.startsWith('PR-')) {
//special GitHub Pull Request handling
deleteDir()
sshagent(
credentials: [config.gitSshKeyCredentialsId],
ignoreMissing: true
) {
def pullRequestID = branch.replaceAll('PR-', '')
def localBranchName = "pr" + pullRequestID;
sh """git init
git fetch ${config.gitUrl} pull/${pullRequestID}/head:${localBranchName} > /dev/null 2>&1
git checkout -f ${localBranchName} > /dev/null 2>&1
"""
}
} else {
//standard git/GitHub handling
if (config.gitCommitId) {
deleteDir()
sshagent(
credentials: [config.gitSshKeyCredentialsId],
ignoreMissing: true
) {
sh """git clone ${config.gitUrl} .
git checkout ${config.gitCommitId} > /dev/null 2>&1"""
}
} else {
def retCode = sh(returnStatus: true, script: 'git log > /dev/null 2>&1')
if (retCode != 0) {
echo "[${STEP_NAME}] No git context available to retrieve culprits"
return ''
}
}
}
def recipients = sh(returnStdout: true, script: "git log -${numberOfCommits} --pretty=format:'%ae %ce'")
return getDistinctRecipients(recipients)
}
def getDistinctRecipients(recipients){
def result
def recipientAddresses = recipients.split()
def knownAddresses = new HashSet<String>()
if(recipientAddresses != null) {
for(address in recipientAddresses) {
address = address.trim()
if(address
&& address.contains("@")
&& !address.startsWith("noreply")
&& !knownAddresses.contains(address)) {
knownAddresses.add(address)
}
}
result = knownAddresses.join(" ")
}
return result
}
def hasRecovered(buildResult, currentBuild){
return buildResult == 'SUCCESS' && currentBuild.getPreviousBuild()?.result != 'SUCCESS'
}

View File

@ -21,13 +21,13 @@ import groovy.transform.Field
'logSuccessfulMavenTransfers'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = parameters.script
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
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)

View File

@ -20,13 +20,13 @@ import groovy.transform.Field
'dockerOptions'
])
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
final script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
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)

View File

@ -29,7 +29,7 @@ import groovy.transform.Field
'warAction'
])
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
@ -70,10 +70,10 @@ def call(parameters = [:]) {
parameters.put('neoCredentialsId', credId)
}
// Backward compatibility end
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
Map configuration = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.mixin(stepCompatibilityConfiguration)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
@ -81,9 +81,9 @@ def call(parameters = [:]) {
.addIfEmpty('archivePath', script.commonPipelineEnvironment.getMtarFilePath())
.mixin(parameters, PARAMETER_KEYS)
.use()
utils.pushToSWA([
step: STEP_NAME,
step: STEP_NAME,
stepParam1: configuration.deployMode == 'mta'?'mta':'war', // ['mta', 'warParams', 'warPropertiesFile']
stepParam2: configuration.warAction == 'rolling-update'?'blue-green':'standard' // ['deploy', 'deploy-mta', 'rolling-update']
], configuration)

View File

@ -5,6 +5,9 @@ import groovy.text.SimpleTemplateEngine
import groovy.transform.Field
@Field String STEP_NAME = 'newmanExecute'
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'dockerImage',
'failOnError',
@ -17,17 +20,18 @@ import groovy.transform.Field
'stashContent',
'testRepository'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters?.juStabUtils ?: new Utils()
// load default & individual configuration
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
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)

View File

@ -11,7 +11,7 @@ import groovy.transform.Field
* Load and executes a pipeline from another git repository.
*
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
node() {

View File

@ -0,0 +1,52 @@
import com.sap.piper.JenkinsUtils
import com.sap.piper.ConfigurationHelper
import groovy.transform.Field
@Field String STEP_NAME = 'pipelineRestartSteps'
@Field Set STEP_CONFIG_KEYS = [
'sendMail',
'timeoutInSeconds'
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
void call(Map parameters = [:], body) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def jenkinsUtils = parameters.jenkinsUtilsStub ?: new JenkinsUtils()
// load default & individual configuration
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)
.mixin(parameters, PARAMETER_KEYS)
.use()
def restart = true
while (restart) {
try {
body()
restart = false
} catch (Throwable err) {
echo "ERROR occured: ${err}"
if (config.sendMail)
if (jenkinsUtils.nodeAvailable()) {
mailSendNotification script: script, buildResult: 'UNSTABLE'
} else {
node {
mailSendNotification script: script, buildResult: 'UNSTABLE'
}
}
try {
timeout(time: config.timeoutInSeconds, unit: 'SECONDS') {
input message: 'Do you want to restart?', ok: 'Restart'
}
} catch(e) {
restart = false
throw err
}
}
}
}
}

View File

@ -2,7 +2,7 @@ import groovy.transform.Field
@Field STEP_NAME = 'pipelineStashFiles'
def call(Map parameters = [:], body) {
void call(Map parameters = [:], body) {
handlePipelineStepErrors (stepName: 'pipelineStashFiles', stepParameters: parameters) {
pipelineStashFilesBeforeBuild(parameters)
body() //execute build

View File

@ -6,7 +6,7 @@ import groovy.transform.Field
@Field Set STEP_CONFIG_KEYS = ['runCheckmarx', 'stashIncludes', 'stashExcludes']
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
def utils = parameters.juStabUtils
@ -20,8 +20,8 @@ def call(Map parameters = [:]) {
//additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
//additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -6,7 +6,7 @@ import groovy.transform.Field
@Field Set STEP_CONFIG_KEYS = ['runOpaTests', 'stashIncludes', 'stashExcludes']
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters, stepNameDoc: 'stashFiles') {
@ -22,8 +22,8 @@ def call(Map parameters = [:]) {
//additional includes via passing e.g. stashIncludes: [opa5: '**/*.include']
//additional excludes via passing e.g. stashExcludes: [opa5: '**/*.exclude']
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, STEP_CONFIG_KEYS)

View File

@ -5,7 +5,7 @@ import groovy.transform.Field
@Field STEP_NAME = 'prepareDefaultValues'
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'prepareDefaultValues', stepParameters: parameters) {
if(!DefaultValueCache.getInstance() || parameters.customDefaults) {
def defaultValues = [:]

View File

@ -6,8 +6,13 @@ import groovy.transform.Field
import groovy.text.SimpleTemplateEngine
@Field String STEP_NAME = 'seleniumExecuteTests'
@Field GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'buildTool', //defines the tool which is used for executing the tests
'containerPortMappings', //port mappings required for containers. This will only take effect inside a Kubernetes pod, format [[containerPort: 1111, hostPort: 1111]]
'dockerEnvVars', //envVars to be set in the execution container if required
'dockerImage', //Docker image for code execution
'dockerName', //name of the Docker container. This will only take effect inside a Kubernetes pod.
'dockerWorkspace', //user home directory for Docker execution. This will only take effect inside a Kubernetes pod.
@ -21,17 +26,18 @@ import groovy.text.SimpleTemplateEngine
'stashContent', //list of stash names which are required to be unstashed before test run
'testRepository' //if tests are in a separate repository, git url can be defined. For protected repositories the git ssh url is required
]
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:], Closure body) {
void call(Map parameters = [:], Closure body) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
def utils = parameters?.juStabUtils ?: new Utils()
// load default & individual configuration
Map config = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
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)
@ -45,6 +51,7 @@ def call(Map parameters = [:], Closure body) {
dockerExecute(
script: script,
containerPortMappings: config.containerPortMappings,
dockerEnvVars: config.dockerEnvVars,
dockerImage: config.dockerImage,
dockerName: config.dockerName,
dockerWorkspace: config.dockerWorkspace,

View File

@ -5,7 +5,7 @@ import groovy.transform.Field
@Field String STEP_NAME = 'setupCommonPipelineEnvironment'
@Field Set GENERAL_CONFIG_KEYS = ['collectTelemetryData']
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -17,8 +17,8 @@ def call(Map parameters = [:]) {
loadConfigurationFromFile(script, configFile)
Map config = ConfigurationHelper
.loadStepDefaults(this)
Map config = ConfigurationHelper.newInstance(this)
.loadStepDefaults()
.mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
.use()

View File

@ -19,13 +19,13 @@ import groovy.transform.Field
])
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors(stepName: STEP_NAME, stepParameters: parameters) {
def utils = parameters.juStabUtils ?: new Utils()
def script = parameters.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
Map config = ConfigurationHelper
.loadStepDefaults(this)
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)

View File

@ -10,6 +10,7 @@ import groovy.transform.Field
]
@Field def STEP_NAME = 'testsPublishResults'
@Field Set GENERAL_CONFIG_KEYS = TOOLS
@Field Set STEP_CONFIG_KEYS = TOOLS
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
@ -19,7 +20,7 @@ import groovy.transform.Field
* @param script global script environment of the Jenkinsfile run
* @param others document all parameters
*/
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters.script
if (script == null)
@ -27,9 +28,9 @@ def call(Map parameters = [:]) {
prepare(parameters)
// load default & individual configuration
Map configuration = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
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)

View File

@ -9,7 +9,7 @@ import hudson.AbortException
@Field STEP_NAME = 'toolValidate'
def call(Map parameters = [:]) {
void call(Map parameters = [:]) {
handlePipelineStepErrors (stepName: 'toolValidate', stepParameters: parameters) {

View File

@ -2,96 +2,134 @@ import com.sap.piper.Utils
import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
import hudson.AbortException
import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrationDisabled
import hudson.AbortException
@Field def STEP_NAME = 'transportRequestCreate'
@Field Set stepConfigurationKeys = [
@Field GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'changeManagement',
'developmentSystemId'
'description', // CTS
'developmentSystemId', // SOLMAN
'targetSystem', // CTS
'transportType', // CTS
]
@Field Set parameterKeys = stepConfigurationKeys.plus(['changeDocumentId'])
@Field generalConfigurationKeys = stepConfigurationKeys
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus(['changeDocumentId'])
def call(parameters = [:]) {
def transportRequestId
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
def script = parameters?.script ?: [commonPipelineEnvironment: commonPipelineEnvironment]
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)
.mixin(parameters, parameterKeys)
ConfigurationHelper configHelper = 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)
Map configuration = configHelper.use()
BackendType backendType = getBackendTypeAndLogInfoIfCMIntegrationDisabled(this, configuration)
if(backendType == BackendType.NONE) return
new Utils().pushToSWA([step: STEP_NAME], configuration)
configHelper
.withMandatoryProperty('changeManagement/clientOpts')
.withMandatoryProperty('changeManagement/credentialsId')
.withMandatoryProperty('changeManagement/endpoint')
.withMandatoryProperty('changeManagement/git/from')
.withMandatoryProperty('changeManagement/git/to')
.withMandatoryProperty('changeManagement/git/format')
.withMandatoryProperty('developmentSystemId')
.withMandatoryProperty('transportType', null, { backendType == BackendType.CTS})
.withMandatoryProperty('targetSystem', null, { backendType == BackendType.CTS})
.withMandatoryProperty('description', null, { backendType == BackendType.CTS})
Map configuration = configHelper.use()
def changeDocumentId = null
new Utils().pushToSWA([step: STEP_NAME], configuration)
if(backendType == BackendType.SOLMAN) {
def changeDocumentId = configuration.changeDocumentId
changeDocumentId = configuration.changeDocumentId
if(changeDocumentId?.trim()) {
if(changeDocumentId?.trim()) {
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
} else {
} else {
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
try {
try {
changeDocumentId = cm.getChangeDocumentId(
changeDocumentId = cm.getChangeDocumentId(
configuration.changeManagement.git.from,
configuration.changeManagement.git.to,
configuration.changeManagement.changeDocumentLabel,
configuration.changeManagement.git.format
)
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
}
}
configHelper.mixin([changeDocumentId: changeDocumentId?.trim() ?: null], ['changeDocumentId'] as Set)
.withMandatoryProperty('developmentSystemId')
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
}
configuration = configHelper.mixin([changeDocumentId: changeDocumentId?.trim() ?: null], ['changeDocumentId'] as Set)
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
.use()
configuration = configHelper.use()
def transportRequestId
echo "[INFO] Creating transport request for change document '${configuration.changeDocumentId}' and development system '${configuration.developmentSystemId}'."
def creatingMessage = ["[INFO] Creating transport request"]
if(backendType == BackendType.SOLMAN) {
creatingMessage << " for change document '${configuration.changeDocumentId}' and development system '${configuration.developmentSystemId}'"
}
creatingMessage << '.'
echo creatingMessage.join()
try {
transportRequestId = cm.createTransportRequest(configuration.changeDocumentId,
if(backendType == BackendType.SOLMAN) {
transportRequestId = cm.createTransportRequestSOLMAN(
configuration.changeDocumentId,
configuration.developmentSystemId,
configuration.changeManagement.endpoint,
configuration.changeManagement.credentialsId,
configuration.changeManagement.clientOpts)
} else if(backendType == BackendType.CTS) {
transportRequestId = cm.createTransportRequestCTS(
configuration.transportType,
configuration.targetSystem,
configuration.description,
configuration.changeManagement.endpoint,
configuration.changeManagement.credentialsId,
configuration.changeManagement.clientOpts)
} else {
throw new IllegalArgumentException("Invalid backend type: '${backendType}'.")
}
} catch(ChangeManagementException ex) {
throw new AbortException(ex.getMessage())
}
echo "[INFO] Transport Request '$transportRequestId' has been successfully created."
return transportRequestId
}
return transportRequestId
}

View File

@ -2,26 +2,28 @@ import com.sap.piper.Utils
import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.ChangeManagementException
import hudson.AbortException
import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrationDisabled
@Field def STEP_NAME = 'transportRequestRelease'
@Field Set stepConfigurationKeys = [
@Field Set GENERAL_CONFIG_KEYS = STEP_CONFIG_KEYS
@Field Set STEP_CONFIG_KEYS = [
'changeManagement'
]
@Field Set parameterKeys = stepConfigurationKeys.plus([
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([
'changeDocumentId',
'transportRequestId',
])
@Field Set generalConfigurationKeys = stepConfigurationKeys
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -29,12 +31,20 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)
.mixin(parameters, parameterKeys)
ConfigurationHelper configHelper = 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)
Map configuration = configHelper.use()
BackendType backendType = getBackendTypeAndLogInfoIfCMIntegrationDisabled(this, configuration)
if(backendType == BackendType.NONE) return
configHelper
.withMandatoryProperty('changeManagement/clientOpts')
.withMandatoryProperty('changeManagement/credentialsId')
.withMandatoryProperty('changeManagement/endpoint')
@ -42,8 +52,6 @@ def call(parameters = [:]) {
.withMandatoryProperty('changeManagement/git/from')
.withMandatoryProperty('changeManagement/git/format')
Map configuration = configHelper.use()
new Utils().pushToSWA([step: STEP_NAME], configuration)
def transportRequestId = configuration.transportRequestId
@ -72,45 +80,56 @@ def call(parameters = [:]) {
}
}
def changeDocumentId = configuration.changeDocumentId
def changeDocumentId = null
if(changeDocumentId?.trim()) {
if(backendType == BackendType.SOLMAN) {
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
changeDocumentId = configuration.changeDocumentId
} else {
if(changeDocumentId?.trim()) {
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
try {
changeDocumentId = cm.getChangeDocumentId(
configuration.changeManagement.git.from,
configuration.changeManagement.git.to,
configuration.changeManagement.changeDocumentLabel,
configuration.changeManagement.gitformat
)
} else {
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
try {
changeDocumentId = cm.getChangeDocumentId(
configuration.changeManagement.git.from,
configuration.changeManagement.git.to,
configuration.changeManagement.changeDocumentLabel,
configuration.changeManagement.gitformat
)
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
}
}
configHelper.mixin([changeDocumentId: changeDocumentId?.trim() ?: null], ['changeDocumentId'] as Set)
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
}
configuration = configHelper
.mixin([transportRequestId: transportRequestId?.trim() ?: null,
changeDocumentId: changeDocumentId?.trim() ?: null], ['transportRequestId', 'changeDocumentId'] as Set)
.mixin([transportRequestId: transportRequestId?.trim() ?: null], ['transportRequestId'] as Set)
.withMandatoryProperty('transportRequestId',
"Transport request id not provided (parameter: \'transportRequestId\' or via commit history).")
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
.use()
echo "[INFO] Closing transport request '${configuration.transportRequestId}' for change document '${configuration.changeDocumentId}'."
def closingMessage = ["[INFO] Closing transport request '${configuration.transportRequestId}'"]
if(backendType == BackendType.SOLMAN) closingMessage << " for change document '${configuration.changeDocumentId}'"
closingMessage << '.'
echo closingMessage.join()
try {
cm.releaseTransportRequest(configuration.changeDocumentId,
cm.releaseTransportRequest(backendType,
configuration.changeDocumentId,
configuration.transportRequestId,
configuration.changeManagement.endpoint,
configuration.changeManagement.credentialsId,

View File

@ -3,27 +3,29 @@ import groovy.transform.Field
import com.sap.piper.ConfigurationHelper
import com.sap.piper.cm.ChangeManagement
import com.sap.piper.cm.BackendType
import com.sap.piper.cm.ChangeManagementException
import hudson.AbortException
import static com.sap.piper.cm.StepHelpers.getBackendTypeAndLogInfoIfCMIntegrationDisabled
@Field def STEP_NAME = 'transportRequestUploadFile'
@Field Set generalConfigurationKeys = [
@Field Set GENERAL_CONFIG_KEYS = [
'changeManagement'
]
@Field Set stepConfigurationKeys = generalConfigurationKeys.plus([
@Field Set STEP_CONFIG_KEYS = GENERAL_CONFIG_KEYS.plus([
'applicationId'
])
@Field Set parameterKeys = stepConfigurationKeys.plus([
@Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS.plus([
'changeDocumentId',
'filePath',
'transportRequestId'])
def call(parameters = [:]) {
void call(parameters = [:]) {
handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
@ -31,50 +33,60 @@ def call(parameters = [:]) {
ChangeManagement cm = parameters.cmUtils ?: new ChangeManagement(script)
ConfigurationHelper configHelper = ConfigurationHelper
.loadStepDefaults(this)
.mixinGeneralConfig(script.commonPipelineEnvironment, generalConfigurationKeys)
.mixinStepConfig(script.commonPipelineEnvironment, stepConfigurationKeys)
.mixinStageConfig(script.commonPipelineEnvironment, parameters.stageName?:env.STAGE_NAME, stepConfigurationKeys)
.mixin(parameters, parameterKeys)
ConfigurationHelper configHelper = 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)
.addIfEmpty('filePath', script.commonPipelineEnvironment.getMtarFilePath())
.withMandatoryProperty('applicationId')
Map configuration = configHelper.use()
BackendType backendType = getBackendTypeAndLogInfoIfCMIntegrationDisabled(this, configuration)
if(backendType == BackendType.NONE) return
configHelper
.withMandatoryProperty('changeManagement/changeDocumentLabel')
.withMandatoryProperty('changeManagement/clientOpts')
.withMandatoryProperty('changeManagement/credentialsId')
.withMandatoryProperty('changeManagement/endpoint')
.withMandatoryProperty('changeManagement/type')
.withMandatoryProperty('changeManagement/git/from')
.withMandatoryProperty('changeManagement/git/to')
.withMandatoryProperty('changeManagement/git/format')
.withMandatoryProperty('filePath')
Map configuration = configHelper.use()
new Utils().pushToSWA([step: STEP_NAME, stepParam1: configuration.changeManagement.type], configuration)
new Utils().pushToSWA([step: STEP_NAME], configuration)
def changeDocumentId = null
def changeDocumentId = configuration.changeDocumentId
if(backendType == BackendType.SOLMAN) {
if(changeDocumentId?.trim()) {
changeDocumentId = configuration.changeDocumentId
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
if(changeDocumentId?.trim()) {
} else {
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from parameters."
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeManagement.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
} else {
try {
changeDocumentId = cm.getChangeDocumentId(
configuration.changeManagement.git.from,
configuration.changeManagement.git.to,
configuration.changeManagement.changeDocumentLabel,
configuration.changeManagement.git.format
)
echo "[INFO] Retrieving ChangeDocumentId from commit history [from: ${configuration.changeManagement.git.from}, to: ${configuration.changeManagement.git.to}]." +
"Searching for pattern '${configuration.changeManagement.changeDocumentLabel}'. Searching with format '${configuration.changeManagement.git.format}'."
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
try {
changeDocumentId = cm.getChangeDocumentId(
configuration.changeManagement.git.from,
configuration.changeManagement.git.to,
configuration.changeManagement.changeDocumentLabel,
configuration.changeManagement.git.format
)
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
echo "[INFO] ChangeDocumentId '${changeDocumentId}' retrieved from commit history"
} catch(ChangeManagementException ex) {
echo "[WARN] Cannot retrieve changeDocumentId from commit history: ${ex.getMessage()}."
}
}
}
@ -104,21 +116,33 @@ def call(parameters = [:]) {
}
}
configHelper
.mixin([changeDocumentId: changeDocumentId?.trim() ?: null,
transportRequestId: transportRequestId?.trim() ?: null], ['changeDocumentId', 'transportRequestId'] as Set)
if(backendType == BackendType.SOLMAN) {
configHelper
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
.withMandatoryProperty('applicationId')
}
configuration = configHelper
.mixin([changeDocumentId: changeDocumentId?.trim() ?: null,
transportRequestId: transportRequestId?.trim() ?: null], ['changeDocumentId', 'transportRequestId'] as Set)
.withMandatoryProperty('changeDocumentId',
"Change document id not provided (parameter: \'changeDocumentId\' or via commit history).")
.withMandatoryProperty('transportRequestId',
.withMandatoryProperty('transportRequestId',
"Transport request id not provided (parameter: \'transportRequestId\' or via commit history).")
.use()
echo "[INFO] Uploading file '${configuration.filePath}' to transport request '${configuration.transportRequestId}' of change document '${configuration.changeDocumentId}'."
def uploadingMessage = ["[INFO] Uploading file '${configuration.filePath}' to transport request '${configuration.transportRequestId}'"]
if(backendType == BackendType.SOLMAN)
uploadingMessage << " of change document '${configuration.changeDocumentId}'"
uploadingMessage << '.'
echo uploadingMessage.join()
try {
cm.uploadFileToTransportRequest(configuration.changeDocumentId,
cm.uploadFileToTransportRequest(backendType,
configuration.changeDocumentId,
configuration.transportRequestId,
configuration.applicationId,
configuration.filePath,
@ -131,6 +155,10 @@ def call(parameters = [:]) {
}
echo "[INFO] File '${configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}' of change document '${configuration.changeDocumentId}'."
def uploadedMessage = ["[INFO] File '${configuration.filePath}' has been successfully uploaded to transport request '${configuration.transportRequestId}'"]
if(backendType == BackendType.SOLMAN)
uploadedMessage << " of change document '${configuration.changeDocumentId}'"
uploadedMessage << '.'
echo uploadedMessage.join()
}
}