Jenkins pipeline shared library example

May 26, 2026 · View on GitHub

Build

An example Jenkins Pipeline Shared Library built with the Shared Library Gradle plugin.

Project layout

PathContents
src/Groovy shared library classes
vars/Pipeline step scripts
resources/Files accessible via libraryResource()
test/unit/JenkinsPipelineUnit — fast, no Jenkins runtime
test/integration/Plugin-provided integration suite — JUnit Jupiter via @WithJenkins

Library contents

src/ — shared classes

ClassPurpose
PipelineLoggerInterface with numeric log levels (1=DEBUG, 2=INFO, 3=WARN, 4=ERROR) read from PIPELINE_LOG_LEVEL env var
BasicScriptStepsLoggerPipelineLogger implementation — plain echo output
AnsiColorScriptStepsLoggerPipelineLogger implementation — ANSI-colored output (requires AnsiColor plugin)
BuildContextReads job metadata from env eagerly; holds no script reference after construction
BranchPolicyClassifies a branch name into environment (production, staging, development); @CompileStatic
ConventionalCommitParses and classifies a Conventional Commit message
SemVerParses, compares, and bumps semantic version strings

vars/ — pipeline steps

StepSignaturePurpose
captureBuildInfocall(PipelineLogger log = null)Logs job metadata; demonstrates optional logger injection
eventuallycall(Map opts = [:], Closure condition)Polls a boolean closure with exponential backoff
nextReleaseVersioncall(String fallback = 'v0.0.0')Computes next version from git tags and conventional commits
notifyBuildcall(String status)Emits a build notification message with job metadata
requireConventionalPrTitlecall()Fails the build if the PR title is not a conventional commit
requireEnvcall(String... names)Fails the build if any named env vars are absent
tagBuildcall(String label)Sets currentBuild.displayName and description
withParallelMatrixcall(Map axes, Closure body)Runs a closure for every combination of axis values in parallel

Design patterns demonstrated

Dependency injection via optional parametercaptureBuildInfo accepts a PipelineLogger defaulting to null, resolved with an Elvis operator:

def call(PipelineLogger log = null) {
    def effectiveLog = log ?: new BasicScriptStepsLogger(this, 'captureBuildInfo')
    ...
}

Callers can inject AnsiColorScriptStepsLogger, a test mock, or omit the argument entirely.

CPS safety — Groovy GDK methods such as padRight and collection methods with closures are CPS-unsafe in Jenkins pipelines. Methods that use them are annotated @NonCPS. Plain for loops and .collectMany/.collectEntries without closures are CPS-safe.

Serialization pattern — Three distinct patterns across the src classes:

ClassHolds script reftransientWhen serialized
BasicScriptStepsLoggerYesYes (field not serialized)Logger reconstructed on resume
BuildContextNoN/AFully safe; all primitives
BranchPolicyNoN/AFully safe; all primitives

@CompileStatic on value classesBranchPolicy uses @CompileStatic because all its methods are @NonCPS and the class holds only plain primitives.

Running tests

TaskRuns
./gradlew testUnit tests (fast, no Jenkins runtime)
./gradlew integrationTestIntegration suite (starts embedded Jenkins)
./gradlew checkAll suites + CodeNarc + Spotless

Using this as a template

  1. Replace src/com/mkobit/libraryexample/ with your package structure and update vars/.
  2. Set rootProject.name in settings.gradle.kts to your Jenkins library name.
  3. To target a different Jenkins LTS line, set sharedLibrary { jenkins { version = "..." } } in build.gradle.kts (default: 2.479.1).
  4. Pin to a released plugin version on the Gradle Plugin Portal.
  5. Drop the composite-build wiring used during plugin development:
    • Remove includeBuild("../jenkins-pipeline-shared-libraries-gradle-plugin") from settings.gradle.kts.
    • Remove the // TEMPLATE FORK blocks in .github/workflows/build.yml.