What is a Jenkinsfile?
A Jenkinsfile defines your pipeline as code, stored alongside your application in version control. This enables:
- Version control — Pipeline changes are tracked in Git
- Code review — Pipeline changes go through PR review
- Reproducibility — Same pipeline runs everywhere
- Self-documenting — Pipeline logic is visible to the team
Declarative vs Scripted
| Feature | Declarative | Scripted |
|---|---|---|
| Syntax | Structured, opinionated | Flexible Groovy |
| Learning curve | Lower | Higher |
| Error handling | Built-in post blocks | Try/catch |
| Best for | Most pipelines | Complex logic |
Declarative Pipeline Structure
pipeline {
agent any
environment {
APP_NAME = 'nextgen-app'
VERSION = '1.0.0'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/org/repo.git'
}
}
stage('Build') {
steps {
sh 'npm install'
sh 'npm run build'
}
}
stage('Test') {
steps {
sh 'npm run test'
}
}
stage('Deploy') {
steps {
sh 'npm run deploy'
}
}
}
post {
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed!'
}
always {
cleanWs()
}
}
}Agent Options
// Run on any available agent
agent any
// Run on a specific label
agent { label 'linux' }
// Run in a Docker container
agent {
docker {
image 'node:20-alpine'
args '-p 3000:3000'
}
}
// Run on a Kubernetes pod
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: node
image: node:20
command: ['sleep', 'infinity']
'''
}
}
// No agent at top level (define per stage)
agent noneEnvironment Variables
pipeline {
agent any
environment {
// Static variables
APP_NAME = 'nextgen-app'
DEPLOY_ENV = 'staging'
// From credentials
DOCKER_CREDS = credentials('docker-hub')
AWS_KEY = credentials('aws-access-key')
// From shell commands
GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
BUILD_DATE = sh(script: 'date +%Y%m%d', returnStdout: true).trim()
}
stages {
stage('Info') {
steps {
echo "Building ${APP_NAME} version ${GIT_COMMIT_SHORT}"
echo "Deploy target: ${DEPLOY_ENV}"
}
}
}
}Parameters
pipeline {
agent any
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: 'Branch to build')
choice(name: 'ENVIRONMENT', choices: ['dev', 'staging', 'production'], description: 'Deploy target')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Run test suite')
password(name: 'DEPLOY_KEY', description: 'Deployment API key')
}
stages {
stage('Build') {
steps {
echo "Building branch: ${params.BRANCH}"
echo "Target: ${params.ENVIRONMENT}"
}
}
stage('Test') {
when {
expression { params.RUN_TESTS == true }
}
steps {
sh 'npm run test'
}
}
stage('Deploy') {
when {
expression { params.ENVIRONMENT == 'production' }
}
steps {
input message: 'Deploy to production?', ok: 'Deploy'
sh "deploy.sh ${params.ENVIRONMENT}"
}
}
}
}Parallel Execution
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm install'
sh 'npm run build'
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
}
}
stage('Deploy') {
steps {
sh 'npm run deploy'
}
}
}
}Conditional Stages
stages {
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
sh 'deploy.sh staging'
}
}
stage('Deploy to Production') {
when {
allOf {
branch 'main'
tag pattern: "v\\d+\\.\\d+\\.\\d+", comparator: "REGEXP"
}
}
steps {
sh 'deploy.sh production'
}
}
stage('PR Checks') {
when {
changeRequest()
}
steps {
sh 'npm run lint'
sh 'npm run test'
}
}
}Post-Build Actions
post {
always {
// Always runs — cleanup
cleanWs()
junit '**/test-results/*.xml'
}
success {
// Only on success
archiveArtifacts artifacts: 'dist/**/*'
slackSend channel: '#deploys', message: "✅ Build ${BUILD_NUMBER} passed"
}
failure {
// Only on failure
slackSend channel: '#deploys', message: "❌ Build ${BUILD_NUMBER} failed"
emailext subject: "Build Failed: ${JOB_NAME}",
body: "Check: ${BUILD_URL}",
to: 'team@nextgenplayground.org'
}
unstable {
// Test failures but build succeeded
echo 'Some tests failed!'
}
}Complete Real-World Pipeline
pipeline {
agent { docker { image 'node:20' } }
environment {
CI = 'true'
DOCKER_IMAGE = "nextgenplayground/app"
DOCKER_TAG = "${BUILD_NUMBER}-${GIT_COMMIT[0..6]}"
}
stages {
stage('Install') {
steps {
sh 'npm ci'
}
}
stage('Quality') {
parallel {
stage('Lint') { steps { sh 'npm run lint' } }
stage('Type Check') { steps { sh 'npm run typecheck' } }
stage('Unit Tests') {
steps {
sh 'npm run test -- --coverage'
}
post {
always {
publishHTML([reportDir: 'coverage', reportFiles: 'index.html', reportName: 'Coverage'])
}
}
}
}
}
stage('Build') {
steps {
sh 'npm run build'
archiveArtifacts artifacts: 'dist/**/*'
}
}
stage('Docker') {
steps {
sh "docker build -t ${DOCKER_IMAGE}:${DOCKER_TAG} ."
withCredentials([usernamePassword(credentialsId: 'docker-hub', usernameVariable: 'USER', passwordVariable: 'PASS')]) {
sh "echo $PASS | docker login -u $USER --password-stdin"
sh "docker push ${DOCKER_IMAGE}:${DOCKER_TAG}"
}
}
}
stage('Deploy') {
when { branch 'main' }
steps {
sh "kubectl set image deployment/app app=${DOCKER_IMAGE}:${DOCKER_TAG}"
}
}
}
}Summary
You've learned:
- Declarative pipeline structure and syntax
- Agent configuration options (Docker, Kubernetes, labels)
- Environment variables, parameters, and credentials
- Parallel execution and conditional stages
- Post-build actions and notifications
- Building a complete real-world pipeline
Next Steps
Next, we'll deploy applications to Kubernetes using Jenkins pipelines.