diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..c2bdaecb0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,2 @@ +language: groovy +sudo: false diff --git a/documentation/docs/steps/pipelineExecute.md b/documentation/docs/steps/pipelineExecute.md new file mode 100644 index 000000000..7e6067c9a --- /dev/null +++ b/documentation/docs/steps/pipelineExecute.md @@ -0,0 +1,45 @@ +# pipelineExecute + +## Description +Loads a pipeline from a git repository. The idea is to set up a pipeline job in Jenkins that loads a minimal pipeline, which in turn loads the shared library and then uses this step to load the actual pipeline. + +A centrally maintained pipeline script (Jenkinsfile) can be re-used by +several projects using `pipelineExecute` as outlined in the example +below. + +## Prerequisites + +none + +## Parameters + +| parameter | mandatory | default | possible values | +| -------------------|-----------|-----------------|-----------------| +| `repoUrl` | yes | | | +| `branch` | no | 'master' | | +| `path` | no | 'Jenkinsfile' | | +| `credentialsId` | no | An empty String | | + +* `repoUrl` The url to the git repository of the pipeline to be loaded. +* `branch` The branch of the git repository from which the pipeline should be checked out. +* `path` The path to the Jenkinsfile, inside the repository, to be loaded. +* `credentialsId` The Jenkins credentials containing user and password needed to access a private git repository. + +## Return value + +none + +## Side effects + +none + +## Exceptions + +* `Exception` + * If `repoUrl` is not provided. + +## Example + +```groovy +pipelineExecute repoUrl: "https://github.com/MyOrg/MyPipelineRepo.git", branch: 'feature1', path: 'path/to/Jenkinsfile', credentialsId: 'MY_REPO_CREDENTIALS' +``` diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index aeafed2de..f94ac36a9 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -4,6 +4,7 @@ pages: - 'Library steps': - commonPipelineEnvironment: steps/commonPipelineEnvironment.md - handlePipelineStepErrors: steps/handlePipelineStepErrors.md + - pipelineExecute: steps/pipelineExecute.md - toolValidate: steps/toolValidate.md - mtaBuild: steps/mtaBuild.md - neoDeploy: steps/neoDeploy.md diff --git a/test/groovy/PipelineExecuteTest.groovy b/test/groovy/PipelineExecuteTest.groovy new file mode 100644 index 000000000..4452c9373 --- /dev/null +++ b/test/groovy/PipelineExecuteTest.groovy @@ -0,0 +1,120 @@ +import hudson.AbortException +import org.junit.rules.TemporaryFolder +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.ExpectedException + +class PipelineExecuteTest extends PiperTestBase { + + @Rule + public ExpectedException thrown = new ExpectedException().none() + + def pipelinePath + def checkoutParameters = [:] + def load + + @Before + void setUp() { + + super.setUp() + + pipelinePath = null + checkoutParameters.clear() + load = null + + helper.registerAllowedMethod('deleteDir', [], null) + helper.registerAllowedMethod('checkout', [Map], { m -> + checkoutParameters.branch = m.branches[0].name + checkoutParameters.repoUrl = m.userRemoteConfigs[0].url + checkoutParameters.credentialsId = m.userRemoteConfigs[0].credentialsId + checkoutParameters.path = m.extensions[0].sparseCheckoutPaths[0].path + }) + helper.registerAllowedMethod('load', [String], { s -> load = s }) + + } + + + @Test + void straightForwardTest() { + + withPipeline(defaultPipeline()).execute() + assert load == "Jenkinsfile" + assert checkoutParameters.branch == 'master' + assert checkoutParameters.repoUrl == "https://test.com/myRepo.git" + assert checkoutParameters.credentialsId == '' + assert checkoutParameters.path == 'Jenkinsfile' + + } + + @Test + void parameterizeTest() { + + withPipeline(parameterizePipeline()).execute() + assert load == "path/to/Jenkinsfile" + assert checkoutParameters.branch == 'feature' + assert checkoutParameters.repoUrl == "https://test.com/anotherRepo.git" + assert checkoutParameters.credentialsId == 'abcd1234' + assert checkoutParameters.path == 'path/to/Jenkinsfile' + + } + + @Test + void noRepoUrlTest() { + + thrown.expect(Exception) + thrown.expectMessage("ERROR - NO VALUE AVAILABLE FOR repoUrl") + + withPipeline(noRepoUrlPipeline()).execute() + + } + + + private defaultPipeline() { + return """ + @Library('piper-library-os') + + execute() { + + node() { + pipelineExecute repoUrl: "https://test.com/myRepo.git" + } + + } + + return this + """ + } + + private parameterizePipeline() { + return """ + @Library('piper-library-os') + + execute() { + + node() { + pipelineExecute repoUrl: "https://test.com/anotherRepo.git", branch: 'feature', path: 'path/to/Jenkinsfile', credentialsId: 'abcd1234' + } + + } + + return this + """ + } + + private noRepoUrlPipeline() { + return """ + @Library('piper-library-os') + + execute() { + + node() { + pipelineExecute() + } + + } + + return this + """ + } +} diff --git a/vars/pipelineExecute.groovy b/vars/pipelineExecute.groovy new file mode 100644 index 000000000..f0209bd32 --- /dev/null +++ b/vars/pipelineExecute.groovy @@ -0,0 +1,46 @@ +import com.sap.piper.Utils + +/** + * pipelineExecute + * Load and executes a pipeline from another git repository. + * + */ +def call(Map parameters = [:]) { + + node() { + + def path + + handlePipelineStepErrors (stepName: 'pipelineExecute', stepParameters: parameters) { + + def utils = new Utils() + + // The coordinates of the pipeline script + def repo = utils.getMandatoryParameter(parameters, 'repoUrl', null) + def branch = utils.getMandatoryParameter(parameters, 'branch', 'master') + + path = utils.getMandatoryParameter(parameters, 'path', 'Jenkinsfile') + + // In case access to the repository containing the pipeline + // script is restricted the credentialsId of the credentials used for + // accessing the repository needs to be provided below. The corresponding + // credentials needs to be configured in Jenkins accordingly. + def credentialsId = utils.getMandatoryParameter(parameters, 'credentialsId', '') + + deleteDir() + + checkout([$class: 'GitSCM', branches: [[name: branch]], + doGenerateSubmoduleConfigurations: false, + extensions: [[$class: 'SparseCheckoutPaths', + sparseCheckoutPaths: [[path: path]] + ]], + submoduleCfg: [], + userRemoteConfigs: [[credentialsId: credentialsId, + url: repo + ]] + ]) + + } + load path + } +}