pipeline {
agent none
options { timeout(time: 1, unit: 'HOURS') }
triggers { cron('H 2 * * *') }
parameters { choice(name: 'TARGET', choices: ['dev', 'prod']) }
stages {
stage('Test') {
parallel {
stage('Unit') {
agent { docker { image 'node:22-alpine' } }
steps { sh 'npm test' }
}
stage('Lint') {
agent { label 'linux' }
steps { sh 'npm run lint' }
}
}
}
stage('Deploy prod') {
agent any
when {
allOf {
branch 'main'
expression { params.TARGET == 'prod' }
}
}
steps {
input message: 'Deploy to prod?'
sh './deploy.sh prod'
}
}
}
post {
failure { echo 'Build failed' }
always { junit 'reports/*.xml' }
}
}
| Block | Meaning |
|---|---|
Per-stage agent, docker { image ... } |
Each stage runs on its own agent/container (agent none at the top) |
options { timeout(...) } |
Time limit for the whole pipeline or one stage |
triggers |
cron('H 2 * * *'), pollSCM(...) — webhooks beat polling |
when |
Stage conditions: branch, expression, environment, allOf/anyOf |
parallel / matrix |
Run in parallel; matrix runs every axes combination (OS × browser) |
input |
Pause for a human approval |
script { } |
Embed Scripted-style Groovy for complex logic |
flowchart LR
R["Build result"] --> S{"success?"}
S -- "yes" --> SU["success"]
S -- "no" --> F["failure / unstable / aborted"]
F --> UN["unsuccessful"]
SU --> CH{"changed since last run?"}
F --> CH
CH -- "fail → success" --> FX["fixed"]
CH -- "success → fail" --> RG["regression"]
SU --> AL["always → cleanup (last)"]
F --> AL
post: always, success, failure, unstable (tests failed but the build completed), aborted, changed, fixed, regression, unsuccessful (any result that is not success), cleanup (runs last).
Traps: confusing failure with unsuccessful; thinking cleanup runs before always; forgetting a stage agent when the top is agent none.
| Command | Meaning |
|---|---|
java -jar jenkins-cli.jar -s http://localhost:8080/ -auth @/home/fresher/.jenkins-cli-auth build my-app-ci -p TARGET=prod -s |
Run with a parameter |
java -jar jenkins-cli.jar -s http://localhost:8080/ -auth @/home/fresher/.jenkins-cli-auth console my-app-ci -f |
Follow the last build log |
A matrix build
stage('Test matrix') {
matrix {
axes {
axis { name 'NODE'; values '20', '22' }
axis { name 'BROWSER'; values 'chrome', 'firefox' }
}
excludes {
exclude {
axis { name 'NODE'; values '20' }
axis { name 'BROWSER'; values 'firefox' }
}
}
agent { docker { image "node:${NODE}-alpine" } }
stages { stage('Run') { steps { sh 'npm ci && npm test -- --browser=$BROWSER' } } }
}
}
Each combination runs as its own branch in parallel; excludes removes combinations you do not need.
More options
| Option | Effect |
|---|---|
buildDiscarder(logRotator(numToKeepStr: '20')) |
Keep only the last 20 builds |
disableConcurrentBuilds() |
Never run two builds of this job at once |
timestamps() |
Timestamp every log line |
retry(2) |
Retry the whole pipeline or stage on failure |
skipDefaultCheckout() |
Do not check out automatically; call checkout scm yourself |
Approvals with timeouts
stage('Deploy prod') {
when { branch 'main' }
options { timeout(time: 1, unit: 'HOURS') }
input { message 'Deploy to production?'; ok 'Deploy'; submitter 'release-managers' }
steps { sh './deploy.sh prod' }
}
Without a timeout, an unanswered input holds an executor forever. Put input at stage level (as here) so no agent is allocated while waiting.
More when conditions
changeset 'frontend/**' runs a stage only when those files changed, tag 'v*' only for release tags, changeRequest() only for pull requests, and beforeAgent true evaluates the condition before allocating an agent, saving time on skipped stages.
Shared libraries
A shared library is a Git repository with reusable Groovy steps under vars/, for example vars/buildImage.groovy defining a buildImage() step. After configuring it under Manage Jenkins, any Jenkinsfile uses it with @Library('ci-lib') _ and calls buildImage(name: 'my-app'). This keeps dozens of repositories on one standard pipeline that a platform team can improve centrally.