Lesson 02 of 8 · Level 1 — Foundations
Declarative pipelines
Write pipelines as code in a Jenkinsfile: the declarative structure (agent, stages, steps, post), conditions with when, parameters and environment variables, credentials without leaking them, timeouts, parallel stages and manual approval.
Pipelines belong in the repository
A Jenkinsfile at the root of the repository defines the pipeline. It's versioned, reviewed in pull requests, and every branch can carry its own changes to the build. Jenkins has two syntaxes: declarative (structured, validated, recommended) and scripted (free-form Groovy). Use declarative; drop into script { } only where you must.
A Jenkinsfile is a recipe card stuck to the ingredients box. Whoever opens the box gets the right recipe for exactly those ingredients, and changing the recipe goes through the same checks as changing the food.
The structure
pipeline {
agent { label 'linux' } // where it runs (lesson 06: Kubernetes pods)
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '30'))
disableConcurrentBuilds()
timestamps()
}
parameters {
booleanParam(name: 'RUN_E2E', defaultValue: false, description: 'Run end-to-end tests')
}
environment {
APP = 'orders-api'
REGISTRY = 'registry.example.com/shop'
}
stages {
stage('Test') {
parallel {
stage('Unit') { steps { sh 'make test' } }
stage('Lint') { steps { sh 'make lint' } }
}
}
stage('E2E') {
when { expression { params.RUN_E2E } }
steps { sh 'make e2e' }
}
stage('Publish') {
when { branch 'main' }
steps { sh 'make image push' } // lesson 03 does this properly
}
}
post {
always { junit allowEmptyResults: true, testResults: 'reports/**/*.xml' }
failure { echo "Build failed: ${env.BUILD_URL}" }
cleanup { cleanWs() }
}
}
| Section | Purpose |
|---|---|
agent |
Where the pipeline (or a stage) runs |
options |
Timeouts, retention, concurrency, timestamps |
parameters |
Inputs when triggering manually |
environment |
Variables for all stages (or per stage) |
stages / stage / steps |
The work, in order; parallel for side-by-side |
when |
Conditions: branch, tag, changeset, expression |
post |
always, success, failure, unstable, cleanup handlers |
Credentials without leaks
Store secrets in Jenkins credentials (or an external store through a plugin), never in the Jenkinsfile:
environment {
REG = credentials('registry-creds') // username/password: REG_USR and REG_PSW
}
steps {
sh 'echo "$REG_PSW" | docker login -u "$REG_USR" --password-stdin registry.example.com'
withCredentials([string(credentialsId: 'slack-token', variable: 'SLACK')]) {
sh 'curl -s -H "Authorization: Bearer $SLACK" https://slack.com/api/auth.test'
}
}
- Use single quotes so the shell, not Groovy, expands the secret.
- Jenkins masks known secret values in logs, but masking is best-effort: never
echoa secret or write it to a file in the workspace. - Scope credentials to folders so a team's jobs only see their own.
Manual approval
stage('Approve prod') {
when { branch 'main' }
steps {
timeout(time: 1, unit: 'HOURS') {
input message: 'Promote to production?', submitter: 'release-managers'
}
}
}
Keep input steps outside an agent allocation (use agent none at the top level and per-stage agents), otherwise an executor sits idle while waiting for a human. In GitOps setups, the approval is usually a reviewed pull request instead (lesson 03).
Try it: a pipeline from a repository
- Put the Jenkinsfile above (with simple
echoormakesteps) in a Git repository. - Create a Pipeline job with "Pipeline script from SCM" pointing at it, and run it.
- Add a username/password credential and use it with
credentials(); check the console shows****where the password would be. - Break the syntax on purpose and validate it with the
pipeline-model-converter/validateendpoint before running.
Recap
- Jenkinsfile in the repo, declarative syntax.
agent,options,parameters,environment,stages/steps,when,post.- Secrets via credentials() / withCredentials, single-quoted shell steps, folder-scoped credentials.
- Timeouts, build retention and
post { always }for reports;inputoutside agent allocation.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.