Jenkinsfile & Pipeline Development

30 minLesson 3 of 8

Learning Objectives

  • Write declarative Jenkins pipelines
  • Use stages, steps, and parallel execution
  • Implement environment variables and parameters
  • Handle errors and post-build actions

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

FeatureDeclarativeScripted
SyntaxStructured, opinionatedFlexible Groovy
Learning curveLowerHigher
Error handlingBuilt-in post blocksTry/catch
Best forMost pipelinesComplex 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 none

Environment 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.