Jenkinsfile — The Assembly Line Recipe
A Jenkinsfile is a text file committed to your Git repository that defines the entire CI/CD pipeline. Think of it as the assembly line recipe — it tells Jenkins exactly what to do, in what order, and where.
Anatomy of a Jenkinsfile
pipeline {
agent {
docker {
image 'node:20-alpine'
}
}
environment {
APP_NAME = 'campus-library'
DOCKER_IMAGE = "riya/${APP_NAME}:${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Build Docker Image') {
steps {
sh "docker build -t ${DOCKER_IMAGE} ."
}
}
stage('Push to Registry') {
steps {
withCredentials([usernamePassword(
credentialsId: 'dockerhub',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh "echo \$DOCKER_PASS | docker login -u \$DOCKER_USER --password-stdin"
sh "docker push ${DOCKER_IMAGE}"
}
}
}
stage('Deploy') {
steps {
sh "kubectl set image deployment/${APP_NAME} web=${DOCKER_IMAGE}"
}
}
}
post {
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed!'
}
}
}
Each section explained
| Section | Purpose | Analogy |
|---|---|---|
agent | Where the pipeline runs | Which workstation |
environment | Variables available everywhere | The factory's shared tools |
stages | The sequence of work | Assembly line stations |
steps | Commands within a stage | Actions at each station |
post | Actions after the pipeline finishes | Quality report after shipping |
Environment Variables
environment {
APP_PORT = '3000'
BUILD_NUMBER_TAG = "${env.BUILD_NUMBER}"
}
Jenkins provides built-in variables:
| Variable | Meaning |
|---|---|
env.BUILD_NUMBER | Unique build number (1, 2, 3...) |
env.BUILD_URL | URL of this build |
env.JOB_NAME | Name of the job |
env.GIT_COMMIT | Current Git commit hash |
env.GIT_BRANCH | Current Git branch |
env.BUILD_NUMBER is your best friend for tagging images. Every build gets a unique, traceable tag.
Credentials
Never hardcode passwords. Use Jenkins credentials:
withCredentials([usernamePassword(
credentialsId: 'dockerhub',
usernameVariable: 'USER',
passwordVariable: 'PASS'
)]) {
sh 'echo $PASS | docker login -u $USER --password-stdin'
}
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
sh 'kubectl get pods'
}
Never print credentials in logs. Jenkins masks $PASS in output, but be careful with other variables.
Parallel Execution — Multiple Workstations
Run independent stages simultaneously:
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
}
}
Parallel stages cut build time in half. Use them for independent tasks (unit tests + integration tests, linting + security scans).
When Conditions — Skip Stations
Control when a stage runs:
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'kubectl apply -f k8s/'
}
}
stage('Notify') {
when {
success()
}
steps {
slackSend message: "Build ${env.BUILD_NUMBER} succeeded!"
}
}
when prevents unnecessary work. Don't deploy feature branches to production. Don't send failure notifications when the build succeeds.
Post Actions — What Happens After
The post block runs regardless of success or failure:
post {
always {
sh 'docker system prune -f'
}
success {
slackSend message: "Build ${env.BUILD_NUMBER} succeeded!"
}
failure {
mail to: 'team@campuslibrary.dev',
subject: "Build ${env.BUILD_NUMBER} failed",
body: "Check: ${env.BUILD_URL}"
}
}
| Condition | When it runs |
|---|---|
always | Always, regardless of result |
success | Only on success |
failure | Only on failure |
cleanup | Always, after all other post conditions |
always is the place for cleanup (prune Docker images, remove temp files). failure is the place for alerts (email, Slack).
Input — Manual Approval Gate
stage('Deploy to Production') {
input {
message 'Deploy to production?'
ok 'Yes, deploy it!'
}
steps {
sh 'kubectl apply -f k8s/prod/'
}
}
Manual gates are the "quality inspector's signature." They prevent accidental production deployments.