Skip to content

Instantly share code, notes, and snippets.

@rakeyshkande
Forked from brianoflan/Jenkinsfile
Created February 4, 2018 05:27
Show Gist options
  • Select an option

  • Save rakeyshkande/23705083a63464d7fb655144bf3f6af5 to your computer and use it in GitHub Desktop.

Select an option

Save rakeyshkande/23705083a63464d7fb655144bf3f6af5 to your computer and use it in GitHub Desktop.
Exhaustive Jenkinsfile
#!/usr/bin/env groovy
CLEAN = true
DEBUG = 2
OVERALL_TIMEOUT = 1 // minutes
email_to = 'person2@someOrg.net, person3@gmail.com, '
git_creds = 'general-deploy-key-1'
git_url = 'git@gitlab.some.domain.tld:someNamespace/someProject.git'
branch = ''
default_branch = 'master' // Ignored if Jenkinsfile is pulled from SCM - in favor of that Jenkinsfile's SCM branch.
build_node = '' // Blank for default build server / slave node.
build_archive = "git/logs/**"
build_creds = [
// AWS access key and secret
[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'aws-s3-creds-1', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY'],
// Secret file
file(credentialsId: 'a-secret-file-creds-id', variable: 'secret_file2'),
// Secret text
string(credentialsId: 'a-secret-text-creds-id', variable: 'secret_text3'),
// Username and password (conjoined)
usernameColonPassword(credentialsId: 'un-pw-creds-id-4', variable: 'conjoined_un_pw'),
// Username and password (separated)
usernamePassword(credentialsId: 'un-pw-creds-id-5', passwordVariable: 'pw5', usernameVariable: 'un5'),
]
git_timeout = 30 // seconds
git_sleep = 1
git_patience1 = 2
git_patience2 = 2
disable_email = false
def prebuild() {
true
}
properties([
disableConcurrentBuilds(),
])
stage('overall') { timeout(time: OVERALL_TIMEOUT, unit: 'MINUTES') { node(build_node) {
currentBuild.result = "SUCCESS"
try {
if ( CLEAN ) { stage('clean') { clean() } }
stage('checkout') {
sh 'pwd ; hostname'
checkout()
} // END: stage('checkout')
stage('checkout on another node') { // This works.
node() {
sh 'pwd ; hostname'
checkout()
} // END: node
} // END: stage('checkout on another node')
stage('build') {
withCredentials(build_creds) {
try { timeout(time: 10, unit: 'SECONDS') {
prebuild()
} } catch(e) {
echo "Caught prebuild() error: '${e}'. Carrying on."
}
sh '[[ ! -e build.sh ]] || bash build.sh'
archive includes: build_archive
}
}
} catch (err) {
echo "Overall: Caught exception {${err}}."
well_known = catchy(err)
if ( ! isStringBlankOrNull(well_known) ) {
error = well_known
betterError("Caught well-known error: ${well_known} (${err})")
}
currentBuild.result = "FAILURE"
if ( !disable_email ) { notifyFailed("${err}") }
// throw err
betterError("Caught overall exception/error: {${err}}.")
} // END: try catch
} } } // END: node, timeout, and stage('overall')
// // //
def betterError(String s) {
def msg = "ERROR: ${s}."
echo msg
error msg
}
def catchy(e) {
result = ''
echo "catchy( e = '${e}')"
if ( e instanceof java.lang.NullPointerException && "${e}" =~ /\QCannot invoke method call() on null object\E/ ) {
echo """Caught an exception but it may be that we caught an explicit call to the 'error' keyword.
In this case the String argument to 'error' is lost and replaced with a NPE exception
(this exception, '${e}',
matches that usual vague pattern).
There is no way to recover or print that String argument. Sorry.
Consider preceding all 'error' calls with 'echo' to make sure the error is displayed
even if it gets caught in a 'try'/'catch' block.
Or define an alternate function like so:
def betterError(String s) {
def msg = "ERROR: \${s}."
echo msg
error msg
}
Such a custom function seems to be immune to the problem of try/catch block
gobbling up an 'error' keyword.
(That may actually print it twice - once with 'echo' and again when the caught
exception is printed, depending on what the 'catch' clause does with it.)
If you don't want to litter your build console output with the 'echo', you can
wrap it in an 'if':
if ( ! "\${env.VERBOSE}" =~ /^\$/ ) { echo msg }
"""
result = "Maybe caught _error_ keyword NPE."
} else if ( e instanceof java.lang.InterruptedException ||
( e instanceof hudson.AbortException && "${e}" ==~ /\Qhudson.AbortException: script returned exit code 143\E/ )
) {
result = "Caught timeout or user interrupt. Stopping."
betterError(result)
}
return result
}
def checkout() {
sh '[[ -d git ]] || mkdir git'
dir('git') {
def _branch = getBranch()
for (int i = 0; i <= git_patience1 + git_patience2; i++) { // NOTE: Unusual but proper '<=' logic.
def error = null
//
if ( i < git_patience1 + git_patience2) {
try { timeout(time: git_timeout, unit: 'SECONDS') {
if ( i < git_patience1 ) {
checkout scm // Works when Jenkinsfile is fetched immediately from source control.
} else {
// Works when Jenkinsfile is just pasted into a Jenkins pipeline job's Jenkinsfile text block.
git poll:true, url: git_url, branch: _branch, credentialsId: git_creds
echo "Using git branch '${_branch}'."
}
if ( DEBUG > 1 ) { debug_git() }
} } catch(e) {
well_known = catchy(e)
error = e
if ( ! isStringBlankOrNull(well_known) ) {
error = well_known
betterError("Caught well-known error: ${well_known} (${e})")
}
echo "Caught error, {${e}}. Trying again."
}
} else {
betterError("Caught too many errors trying to fetch from source control. Giving up.")
}
if ( ! error ) { break }
sh "sleep ${git_sleep}"
//
} // END: for(int i...)
}
}
def clean() {
sh 'pwd'
sh 'ls -A | while read x ; do rm -rf "$x" ; done'
sh 'ls -lart ; pwd'
}
def debug_git() {
echo "Using git commit:"
sh 'git rev-parse HEAD'
echo "Using git branch: '${getBranch()}'."
sh 'git rev-parse --abbrev-ref HEAD'
sh 'env | grep -i git || true'
sh 'env | grep -i branch || true'
}
def getBranch() {
def result = '' ;
// Variables 'branch' and 'default_branch' should already be defined as script globals.
try {
if (isStringBlankOrNull(branch)) {
// Might work if this is a multi-branch pipeline job kind.
timeout(time: 1, unit: 'SECONDS') {
branch = BRANCH_NAME
}
}
} catch ( e ) {
echo "Caught exception/error trying to use variable BRANCH_NAME: {${e}}. Trying other ways to decide branch name."
}
if (isStringBlankOrNull(branch)) {
// Works if this is a multi-branch pipeline job kind.
branch = env.BRANCH_NAME
}
try {
// if (branch == null || ( 0 == branch.compareTo(""))) {
if (isStringBlankOrNull(branch)) {
if (fileExists('.git')) {
// Works if this workspace has been used before and remembers its branch
branch = sh(returnStdout: true, script: "git rev-parse --abbrev-ref HEAD | egrep -v '^HEAD\$'").trim()
echo "Obtained branch from 'git rev-parse --abbrev-ref HEAD': '${branch}'."
}
}
} catch ( e ) {
echo "Caught exception/error trying to 'git rev-parse --abbrev-ref HEAD': {${e}}. Trying other ways to decide branch name."
}
result = (branch != null && ! ( 0 == branch.compareTo(""))) ? branch : default_branch
return result
}
def isStringBlankOrNull(s) {
if ( s == null ) {
return true
} else if ( 0 == s.compareTo("") ) {
return true
}
return false
}
def notifyFailed(report) {
// step([$class: 'Mailer', recipients: 'person4@someGovernment.countryCode.tld'])
emailext(
// to: 'person1@someCompany.com, ',
to: email_to,
recipientProviders: [[$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider']],
subject: "[${env.JOB_NAME}] build #${env.BUILD_NUMBER} failed",
body: """
There was an error.
Full report:
${report}
See ${env.BUILD_URL} or ${env.BUILD_URL}console for more information.
""",
)
}
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment