#!groovy import groovy.json.JsonOutput import groovy.json.JsonSlurper node { // pull request or feature branch if (env.BRANCH_NAME != 'master') { checkout() build() unitTest() // test whether this is a regular branch build or a merged PR build if (!isPRMergeBuild()) { // maybe disabled for ChatOps // preview() } } // master branch / production else { checkout() build() allTests() preProduction() manualPromotion() production() } } def isPRMergeBuild() { return (env.BRANCH_NAME ==~ /^PR-\d+$/) } def checkout () { stage 'Checkout code' context="continuous-integration/jenkins/" context += isPRMergeBuild()?"branch/checkout":"pr-merge/checkout" setBuildStatus ("${context}", 'Checking out...', 'PENDING') checkout scm setBuildStatus ("${context}", 'Checking out completed', 'SUCCESS') } def build () { stage 'Build' // cache maven artifacts shareM2 '/tmp/m2repo' mvn 'clean install -DskipTests=true -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -B -V' } def unitTest() { stage 'Unit tests' mvn 'test -B -Dmaven.javadoc.skip=true -Dcheckstyle.skip=true' step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml']) if (currentBuild.result == "UNSTABLE") { sh "exit 1" } } def allTests() { stage 'All tests' // don't skip anything mvn 'test -B' step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml']) if (currentBuild.result == "UNSTABLE") { // input "Unit tests are failing, proceed?" sh "exit 1" } } def preview() { herokuApp = "${env.HEROKU_PREVIEW}" deployToStage("preview", herokuApp) } def preProduction() { herokuApp = "${env.HEROKU_PREPRODUCTION}" deployToStage("preproduction", herokuApp) } def manualPromotion() { stage 'Manual Promotion' // we need a first milestone step so that all jobs entering this stage are tracked an can be aborted if needed milestone 1 // time out manual approval after ten minutes timeout(time: 10, unit: 'MINUTES') { input message: "Does Pre-Production look good?" } // this will kill any job which is still in the input step milestone 2 } def production() { herokuApp = "${env.HEROKU_PRODUCTION}" deployToStage("production", herokuApp) step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true]) } def deployToStage(stageName, herokuApp) { stage name: "Deploy to ${stageName}", concurrency: 1 id = createDeployment(getBranch(), "${stageName}", "Deploying branch to ${stageName}") echo "Deployment ID for ${stageName}: ${id}" if (id != null) { setDeploymentStatus(id, "pending", "https://${herokuApp}.herokuapp.com/", "Pending deployment to ${stageName}"); herokuDeploy "${herokuApp}" setDeploymentStatus(id, "success", "https://${herokuApp}.herokuapp.com/", "Successfully deployed to ${stageName}"); } } def herokuDeploy (herokuApp) { withCredentials([[$class: 'StringBinding', credentialsId: 'HEROKU_API_KEY', variable: 'HEROKU_API_KEY']]) { mvn "heroku:deploy -DskipTests=true -Dmaven.javadoc.skip=true -B -V -D heroku.appName=${herokuApp}" } } def mvn(args) { // point to settings.xml with cached .m2 directory and proceed in case of test failures sh "${tool 'Maven 3.x'}/bin/mvn -s settings.xml ${args} -Dmaven.test.failure.ignore" } def shareM2(file) { // Set up a shared Maven repo so we don't need to download all dependencies on every build. writeFile file: 'settings.xml', text: "${file}" } def getRepoSlug() { tokens = "${env.JOB_NAME}".tokenize('/') org = tokens[tokens.size()-3] repo = tokens[tokens.size()-2] return "${org}/${repo}" } def getBranch() { tokens = "${env.JOB_NAME}".tokenize('/') branch = tokens[tokens.size()-1] return "${branch}" } def createDeployment(ref, environment, description) { withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) { def payload = JsonOutput.toJson(["ref": "${ref}", "description": "${description}", "environment": "${environment}", "required_contexts": []]) def apiUrl = "https://octodemo.com/api/v3/repos/${getRepoSlug()}/deployments" def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Token ${env.GITHUB_TOKEN}\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST -d '${payload}' ${apiUrl}").trim() def jsonSlurper = new JsonSlurper() def data = jsonSlurper.parseText("${response}") return data.id } } void setDeploymentStatus(deploymentId, state, targetUrl, description) { withCredentials([[$class: 'StringBinding', credentialsId: 'GITHUB_TOKEN', variable: 'GITHUB_TOKEN']]) { def payload = JsonOutput.toJson(["state": "${state}", "target_url": "${targetUrl}", "description": "${description}"]) def apiUrl = "https://octodemo.com/api/v3/repos/${getRepoSlug()}/deployments/${deploymentId}/statuses" def response = sh(returnStdout: true, script: "curl -s -H \"Authorization: Token ${env.GITHUB_TOKEN}\" -H \"Accept: application/json\" -H \"Content-type: application/json\" -X POST -d '${payload}' ${apiUrl}").trim() } } void setBuildStatus(context, message, state) { // partially hard coded URL because of https://issues.jenkins-ci.org/browse/JENKINS-36961, adjust to your own GitHub instance step([ $class: "GitHubCommitStatusSetter", contextSource: [$class: "ManuallyEnteredCommitContextSource", context: context], errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]], reposSource: [$class: "ManuallyEnteredRepositorySource", url: "https://github.com/${getRepoSlug()}"], statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ] ]); }