skip to content

Shared Libraries

Factoring pipeline logic into a shared library: vars for the steps teams call, src for the classes behind them, loading explicitly or implicitly, and versioning by branch or tag. Asked because it is how an organisation stops copy-pasting the same 200-line Jenkinsfile into forty repositories.

part ofJenkinsoverview, primer and where to startread it →
on this pageshow

explore

questions

6

In a Jenkinsfile, what does the line `@Library('[email protected]') _` do, and why is there a bare underscore after it?

level: juniorimportance: must knowfreq 62%

answer

  1. names a registered library, not a URL
  2. annotations need something to attach to
  3. after the @ is a revision
  4. branch, tag or commit — one is reproducible

basics

~20 s

It loads the Jenkins shared library registered as utils at version v1.4 — a branch, tag or commit in the library's repository — so the library's steps become callable in that Jenkinsfile. The underscore is a throwaway statement the annotation must attach to.

solid answer

~50 s

The `@Library` annotation, from the Pipeline: Shared Groovy Libraries plugin, tells Jenkins to fetch a library that an administrator has already registered under the name `utils` and make its code available to this build. The part after `@` selects the version: with a Git-backed library that can be a branch, a tag, or a full commit SHA, and it overrides the default version configured for the library — but only if that library was configured to allow overriding, otherwise the build fails at load time. Once loaded, every file in the library's `vars/` directory is available as a pipeline step, and classes under `src/` can be imported. The trailing `_` exists only because a Groovy annotation must be attached to something; it is a no-op statement standing in for the import you are not writing. In a Declarative pipeline the annotation goes at the very top of the file, above the `pipeline` block.

code

groovy · 12 lines
groovy
@Library('[email protected]') _

pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                deployApp app: 'billing', env: 'staging'
            }
        }
    }
}

go deeper

for a junior

Be able to read the line out loud: it loads a named shared library at a chosen version, and the underscore is just something for the annotation to sit on. Know that the name is registered by an administrator, not a repository URL.

for a middle

Explain when resolution happens — at build start, before any stage — and what becomes available afterwards: vars files as steps, src classes for import. Mention that a tag or SHA is reproducible while a branch is not.

for a senior

Show that you treat the version reference as a blast-radius decision, and that you know an override can be forbidden by configuration. Be ready to say what a failed library load looks like and how you diagnose it.

for a principal

Own the policy: which libraries exist, who may register them, whether Jenkinsfiles may pin versions, and how you keep the global var namespace from colliding as more teams publish libraries.

## What a shared library is A Jenkins shared library is an ordinary SCM repository — nearly always Git — containing Groovy code that pipelines load at build time. It is not a plugin and nothing about it is automatic. A Jenkins administrator first registers it: a **name** (`utils`), a **retrieval method** (the repo URL and credentials), and a **default version**. The name in the annotation is that registered name, not a URL and not a package. ## What the annotation actually triggers When a build starts, before any stage runs, Jenkins resolves each requested library, checks out the requested version into a workspace on the controller, compiles the Groovy, and puts it on the pipeline's classpath. Two things become reachable: - every file in the library's `vars/` directory becomes a global variable, and if it defines a `call` method it can be invoked as if it were a built-in step; - classes under `src/` can be imported or referenced by fully-qualified name. Because resolution happens up front, a typo in the library name or a version that does not exist fails the build immediately — you get no stages at all, which is a useful diagnostic: the failure is in loading, not in your pipeline logic. ## The part after the @ ```groovy @Library('[email protected]') _ // a tag @Library('utils@main') _ // a branch @Library('utils@0f3a1b2c9d') _ // a commit SHA @Library('utils') _ // the configured default version ``` For a Git-backed library, anything the SCM can resolve to a revision works. A tag or a SHA is immutable and therefore reproducible; a branch means the build gets whatever that branch pointed at the moment the build started, which is how a library change reaches many pipelines at once. A library configuration has a checkbox controlling whether Jenkinsfiles are permitted to name a version at all. If overriding the default version is not allowed, `@Library('[email protected]')` fails with an error saying the override is not permitted — a very common first encounter with shared libraries. ## Why the underscore Groovy annotations cannot float on their own; they must be attached to a declaration or statement. The idiomatic target is an import: ```groovy @Library('utils') import com.acme.Deployer ``` When you only want the library's `vars/` steps, there is no import to annotate, so the convention is to attach the annotation to `_`, a bare no-op statement that does nothing else. It is not special syntax — any statement would do; `_` is simply the community convention and what the Jenkins documentation uses. ## Loading more than one, and loading at runtime The annotation accepts a list: ```groovy @Library(['[email protected]', 'security-scans']) _ ``` There is also a `library` step that loads a library while the pipeline is already running: ```groovy def lib = library '[email protected]' ``` That form is useful when the version is computed at runtime, but it cannot be combined with `import` statements, and classes are reached through the returned object rather than by import. For the overwhelmingly common case — a fixed library at a fixed version — the annotation is the right tool. ## Placement in Declarative pipelines Declarative pipelines forbid arbitrary code outside the `pipeline` block, but the library annotation is explicitly allowed at the top of the file. It must come before `pipeline {`; put it lower and the file will not even parse as Declarative. ## Practical notes Library names live in a flat namespace per controller, and so do the global variables the libraries provide — two libraries that each define `vars/deploy.groovy` collide, so prefix var names when several teams publish libraries. And because loading a library means running someone else's Groovy inside your build, which library you load and who can merge to it is a security decision, not just a convenience.

  • How would you load two shared libraries in the same Jenkinsfile, and pin only one of them?
    Pass a list to the annotation: `@Library(['[email protected]', 'security-scans']) _`. Each entry is resolved independently, so one can carry an explicit version while the other falls back to its configured default. Both libraries' vars steps become available in the same build, which is also why var names should be prefixed per library to avoid collisions.
  • The build fails immediately with an error about the library version not being allowed. What is wrong?
    The library's configuration does not permit Jenkinsfiles to override its default version — the checkbox for allowing an override is off. Either an administrator enables it, or you drop the `@version` suffix and accept the configured default. It is a policy setting, not a problem with your branch or tag.
  • When would you use the library step instead of the @Library annotation?
    When the version is not known until the pipeline is running — for example derived from a parameter or a file in the checkout. The `library` step loads at that point and returns an object through which classes are reached. The tradeoff: no import statements, so class references get verbose, and the load happens mid-build rather than up front.

saying these in an interview costs you the question

  • Thinks the underscore is required Groovy import syntax
  • Says @Library installs or downloads a Jenkins plugin
  • Believes the version after @ must be a branch
  • Assumes any Jenkinsfile may pick any library version
  • Puts the annotation below the pipeline block in Declarative

context

open as a page

In a Jenkins shared library repository, what do the vars/, src/ and resources/ directories each hold, and how does a Jenkinsfile reach each one?

level: middleimportance: must knowfreq 70%

basics

~20 s

vars/ holds one file per callable pipeline step, named after the step. src/ holds a normal Groovy class hierarchy on the pipeline classpath, imported by package and class name. resources/ holds non-Groovy files read with the libraryResource step.

open as a page

What changes for Jenkins pipelines when a shared library is configured to load implicitly, and what can an individual Jenkinsfile still control about it?

level: middleimportance: should knowfreq 42%

basics

~20 s

Implicit loading makes the library available to every pipeline in its scope with no @Library line, always at the library's configured default version. A Jenkinsfile can still name a different version — but only if the configuration permits overriding the default.

open as a page

In Jenkins, how does a shared library configured globally differ in trust from one configured on a folder, and what does that mean for who may merge to the library repository?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Globally configured Jenkins shared libraries are trusted: their Groovy runs outside the script sandbox with full access to Jenkins internals. Folder-scoped libraries are untrusted and run sandboxed. So merge rights on a trusted library repository are effectively administrator rights on the controller.

open as a page

Forty Jenkins pipelines load a shared library implicitly at its default version, the branch main. A merge to main breaks all forty builds at once. How would you change the setup?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Stop using a moving branch as the default version. Pin the default to an immutable tag, make sure Jenkinsfiles are allowed to override it so individual repos can canary or roll back, and treat library changes as releases with tests rather than as merges.

open as a page

Your organisation has forty repositories with near-identical Jenkinsfiles. How do you decide what moves into a Jenkins shared library and what stays in each repository?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Extract logic that is genuinely identical, stable, and organisationally mandated — publishing, notification, scanning, credential wiring. Leave anything repository-specific or fast-changing in the Jenkinsfile, and keep enough visible there that an engineer can still read what their build does.

open as a page