Skip to content

Instantly share code, notes, and snippets.

@JD10NN3
Last active November 11, 2019 13:57
Show Gist options
  • Save JD10NN3/7796639e4d427d474c172d5e6b0fa49a to your computer and use it in GitHub Desktop.
Save JD10NN3/7796639e4d427d474c172d5e6b0fa49a to your computer and use it in GitHub Desktop.
Basic Jenkinsfile that use a external configuration as pipeline mapping
#!groovy
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import hudson.model.Result
import hudson.model.Run
import jenkins.model.CauseOfInterruption.UserInterruption
/*
Jenkins version: 2.150.2
Github API version: v3
Depend on: https://gist.github.com/JD10NN3/cb44137a94111cf9693c7815d26fa919
Part of: https://medium.com/faun/externalize-the-configuration-of-the-jenkins-pipeline-flow-8f326c7cc9c4
*/
// Our Github endpoints and repositories
// *Change the domain name to make it work with Github Enterprise.
GITHUB_DOMAIN_NAME = "github.com"
GITHUB_ENDPOINT_API = "${GITHUB_DOMAIN_NAME}/api/v3"
GITHUB_MY_PROJECT_REPO = "https://${GITHUB_ENDPOINT_API}/repos/JD10NN3/MyProject"
// This is where we host our external configuration files.
// Note: You must adjust this variable to point an existing repository...
GITHUB_SCRIPTS_AND_CONFIGURATION_CONTENTS_URL = "https://${GITHUB_ENDPOINT_API}/repos/JD10NN3/Scripts-and-Configurations/contents"
// The location of the mapping configuration
// Refer to that Gist to see the content of mapping.groovy:
// https://gist.github.com/JD10NN3/cb44137a94111cf9693c7815d26fa919
// Note: You must adjust this variable to point your mapping configuration...
GITHUB_MAPPING_URL = "${GITHUB_SCRIPTS_AND_CONFIGURATION_CONTENTS_URL}/pipeline/src/mapping.groovy"
// The location of the triggers configuration
// Refer to that Gist to see the content of mapping.groovy:
// https://gist.github.com/JD10NN3/5de45a5c9b75c25c81da107acdda34c8
// Note: You must adjust this variable to point your triggers configuration...
GITHUB_TRIGGERS_URL = "${GITHUB_SCRIPTS_AND_CONFIGURATION_CONTENTS_URL}/pipeline/src/triggers.groovy"
// This is where we fetch our external configuration (mapping and triggers) used to
// determine the steps that are required to run inside the pipeline. We are hosting
// the configuration inside a separate repository to allow us to apply different
// rights for devops purposes.
def mapping = null
def triggers = null
node {
// Wrap the call inside the withCredentials to avoid showing in clear text sensible datas.
// eg.: passwords, tokens... etc
withCredentials([string(credentialsId: 'github-token', variable: 'GITHUB_ACCESS_TOKEN')]) {
// We fetch the pipeline mapping and assign it to our mapping variable.
// This is going to be used later to determine the steps to perform and with which options.
def response = GITHUB_MAPPING_URL.toURL().getText(requestProperties: ['Authorization': "token ${GITHUB_ACCESS_TOKEN}", 'Accept': "application/vnd.github.v3.raw"])
mapping = evaluate(response)
// We fetch the pipeline triggers.
// The triggers fetched are going to be used to launch actions at specific moment by the pipeline.
// eg.: on_commit, on_approval
response = GITHUB_TRIGGERS_URL.toURL().getText(requestProperties: ['Authorization': "token ${GITHUB_ACCESS_TOKEN}", 'Accept': "application/vnd.github.v3.raw"])
triggers = evaluate(response)
}
}
// Global variables
def workspaceDirectory = ""
def pipeline = ["branch": env.BRANCH_NAME, "targets": mapping.getFor(env.BRANCH_NAME)]
boolean isMultiTargets = pipeline.targets.size() > 1
boolean checkoutDone = false
//
def handleCheckout = {
checkout([
$class : 'GitSCM',
branches : scm.branches,
extensions : [
[$class: 'PruneStaleBranch'],
[$class: 'CleanCheckout']
],
userRemoteConfigs: scm.userRemoteConfigs
])
}
// This must be adjusted to meet the specific project criterias
properties([[$class: 'ThrottleJobProperty', categories: [], limitOneJobWithMatchingParams: false, maxConcurrentPerNode: 0,
maxConcurrentTotal: 0, paramsToUseForLimit: '', throttleEnabled: false, throttleOption: 'project'],
[$class: 'BuildDiscarderProperty', strategy: [$class: 'LogRotator', artifactDaysToKeepStr: '',
artifactNumToKeepStr: '1', daysToKeepStr: '', numToKeepStr: '3']],
[$class: 'GithubProjectProperty', displayName: 'IdentityIQ',
projectUrlStr: 'https://github.com/JD10NN3/MyProject/'], pipelineTriggers([])])
try {
// We iterate through each targeted environment (intg, uat, prep, prod...)
// provided by our pipeline configuration.
// Take a look at the mapping.groovy for a better understanding
pipeline.targets.each { target ->
//
def stageSuffix = isMultiTargets ? ' (' + target.env + ')' : ''
// Does the "checkout" step must be executed ?
// If true, only checkout if not done previously
// (we don't want to checkout a branch two times in the same pipeline for no reason)
if (target.stages.containsKey('checkout') && !checkoutDone) {
stage('Checkout') {
node {
workspaceDirectory = env.WORKSPACE
// time to checkout the sources
handleCheckout()
checkoutDone = true
// Let's do a call to the "on_commit" trigger for the current branch.
withCredentials([string(credentialsId: 'github-token', variable: 'GITHUB_ACCESS_TOKEN')]) {
triggers.callTriggerOnBranch("on_commit", env.BRANCH_NAME)
}
}
}
}
// Does the "build" step must be executed ?
if (target.stages.containsKey('build')) {
// Options provided from the pipeline mapping
def buildOptions = target.stages.build
stage('Build' + stageSuffix) {
milestone()
node {
dir(workspaceDirectory) {
/*
This is where you should provide the commands
needed as part of the Build step.
eg.: Clean, Build, Archiving...
*/
// Example for the build command
// target.env : contains the name of the environment for which
// we are currently building the package
def command = buildOptions.getOrDefault("command", "war")
sh "\$GRADLE_HOME/bin/gradle --no-daemon ${command} -DtargetedEnv=${target.env}"
// Example for the archive command
// Are we archiving the deployment package ?
if (buildOptions.getOrDefault("archive", true)) {
zip archive: true, dir: 'build/deployment', glob: '', zipFile: target.env + '_deployment_package.zip'
}
// delete file to save space and avoid concurrent build to
// complain because the file already exist
if (fileExists(target.env + '_deployment_package.zip')) {
sh 'rm -f ' + target.env + '_deployment_package.zip'
}
}
}
}
}
// Does the "tests" step must be executed ?
if (target.stages.containsKey('tests')) {
stage('Tests' + stageSuffix) {
milestone()
node {
dir(workspaceDirectory) {
/*
This is where you should provide the commands
needed as part of the Test step.
eg.: Run tests, Report results...
*/
}
}
}
}
// Does the "deploy" step must be executed ?
if (target.stages.containsKey('deploy')) {
// Options provided from the pipeline mapping
def deployAttributes = target.stages.deploy
// Does an approval is required to deploy to the targeted environment ?
// This approval allow us to confirm with other developers or testers
// whether or not the environment is currently used before launching the deployment.
if (deployAttributes.getOrDefault("approval", false) || currentBuild.result == "UNSTABLE") {
stage('Approval' + stageSuffix) {
milestone()
// If an approval is required before deployment, we ask for.
if (deployAttributes.getOrDefault("approval", false)) {
input "Do you approve deployment in ${target.env}?"
}
// Let's do a call to the "on_approval" trigger for the current branch.
// Note: we can provide arguments... (eg.: target)
withCredentials([string(credentialsId: 'github-token', variable: 'GITHUB_ACCESS_TOKEN')]) {
triggers.callTriggerOnBranch("on_approval", env.BRANCH_NAME, target)
}
// Second approval in case the build is unstable...
if (currentBuild.result == "UNSTABLE") {
input "Based on the previous steps, this version seems UNSTABLE, do you still want to deploy on '${target.env}'?"
}
milestone()
}
}
// Lock the target environment to avoid doing multiple deployment at the same time
lock(resource: target.env + '-server', inversePrecedence: true) {
stage('Deploy' + stageSuffix) {
milestone()
node {
// Change the folder path to meet your specific project criterias.
dir(workspaceDirectory + '/build/deploy') {
/*
This is where you should provide the commands
needed as part of the Deployment step.
*/
}
}
}
}
}
}
} catch (error) {
// Due to an error, we set the current build result to the proper state.
currentBuild.result = 'FAILURE'
throw error
} finally {
// Do nothing and let Jenkins manage the current build result.
// This would be a nice place to add Github check statuses updates.
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment