Writing a Global Pipeline Step

The first step replaces the grep one-liner of BookNest's Jenkinsfile (Jenkinsfile in the Repo) with a named check. A call method with a Map parameter gives it named, optional arguments, as built-in steps have:

vars/seedCheck.groovyGroovy
// seedCheck(): fail the build unless db/seed.json holds the expected number of books
def call(Map args = [:]) {
  int expected = args.get('expected', 6)
  String file = args.get('file', 'db/seed.json')
  int count = sh(returnStdout: true, script: "grep -c '\"title\"' ${file}").trim() as int
  if (count != expected) {
    error "seedCheck: ${file} has ${count} books, expected ${expected}"
  }
  echo "seedCheck: ${file} OK, ${count} books"
  return count
}

Inside vars/, sh, echo, error and env work as in a Jenkinsfile: the script runs inside the pipeline, under the CPS interpreter (Surviving Restarts). The second step, added in version 1.1.0, computes the Docker 514 tag that Image Tagging uses, with its rules in a src/ class:

src/org/booknest/ImageTag.groovyGroovy
package org.booknest
// Docker tags allow [A-Za-z0-9_.-], up to 128 characters, and must not start with . or -
class ImageTag implements Serializable {
  static String forBuild(String branch, String commit, def buildNumber) {
    String safe = (branch ?: 'detached').toLowerCase()
        .replaceAll(/[^a-z0-9_.-]+/, '-').replaceAll(/^[.-]+/, '')
    return "${safe}-${commit.take(7)}-${buildNumber}".take(128)
  }
}

vars/imageTag.groovy imports the class and returns ImageTag.forBuild(env.BRANCH_NAME, commit, env.BUILD_NUMBER), taking the commit from env.GIT_COMMIT or, failing that, git rev-parse HEAD. The class is Serializable because whatever a pipeline holds between steps is written to disk (Serializing State).