Build Your First Jenkins Declarative Pipeline for CI/CD
Build a first Jenkins Declarative Pipeline with stages, agents, checkout, tests, deployment gates, credentials, and post actions.
Build Your First Jenkins Declarative Pipeline for CI/CD
Your first Jenkins Declarative Pipeline should answer a practical question: can Jenkins check out your code, build it, test it, and run deployment steps only when the right branch passes? A Jenkinsfile gives you that workflow as code in the same repository as your app.
Declarative syntax is a good starting point because it gives you a predictable structure: pipeline, agent, stages, steps, and post.
Understanding the Declarative Pipeline Structure
The Declarative Pipeline defines the entire workflow in a file named Jenkinsfile, which is stored alongside your application code in your SCM repository (e.g., Git). This practice is known as Pipeline as Code.
Core Elements of a Declarative Pipeline
A Declarative Pipeline must contain the following top-level blocks:
pipeline: The mandatory outermost block defining the pipeline content.agent: Specifies where the pipeline or a specific stage will execute (e.g.,any,none, or specific labels).stages: Contains one or more sequentialstageblocks.stage: Defines a conceptual block of work (e.g., Build, Test, Deploy).steps: Contains one or more commands or functions executed within astage.
pipeline {
agent any
environment { // Optional: Defines environment variables
APP_VERSION = '1.0.0'
}
stages {
stage('Build') {
steps {
// Commands go here
}
}
}
post { // Optional: Actions run after the pipeline completes
always {
echo 'Pipeline finished.'
}
}
}
Step 1: Setting Up the Jenkins Job
To run a Pipeline from code, you need to configure a Jenkins job to read the Jenkinsfile from your repository.
- Navigate to your Jenkins dashboard and select New Item.
- Enter a name for your pipeline (e.g.,
my-first-ci-pipeline). - Select the Pipeline item type and click OK.
- In the configuration page, scroll down to the Pipeline section.
- Change the definition from Pipeline script to Pipeline script from SCM.
- Select your SCM (e.g., Git).
- Enter the Repository URL and configure your credentials if necessary.
- Ensure the Script Path is set to
Jenkinsfile(the default).
Step 2: Defining the Jenkinsfile for CI/CD
We will create a comprehensive Jenkinsfile that simulates a simple CI/CD workflow, integrating standard stages for a successful deployment.
Assume you have a repository structure containing your application source code (e.g., a simple Python or Java project) and the Jenkinsfile at the root.
Complete Declarative Pipeline Example
This pipeline uses the node agent (if configured) and utilizes basic shell steps (sh) to simulate real work. The deployment stage is conditional, running only upon successful completion of the previous stages.
// Jenkinsfile
pipeline {
// 1. Agent Definition: Specifies where the entire pipeline runs
agent {
label 'my-build-agent' // Use a specific label if available, or 'any'
}
// 2. Environment Variables: Define variables usable throughout the pipeline
environment {
CONTAINER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
}
stages {
// Stage 1: Checkout (Source Code Management)
stage('Checkout Source') {
steps {
// The 'checkout scm' step automatically checks out the code
// based on the configuration defined in the Jenkins job settings.
checkout scm
sh 'echo "Source code successfully checked out."'
}
}
// Stage 2: Build Artifacts
stage('Build Artifact') {
steps {
sh 'echo "Starting build for $IMAGE_NAME:$BUILD_ID"'
// Simulation: Build a Docker image or compile a jar/war file
sh "docker build --label build_id=${BUILD_ID} -t ${CONTAINER_REGISTRY}/${IMAGE_NAME}:${BUILD_ID} ."
}
}
// Stage 3: Testing and Quality Gates
stage('Run Tests') {
steps {
sh './run_unit_tests.sh'
sh 'echo "Running integration tests..."'
// Example of collecting test results (requires appropriate plugins)
// junit '**/target/surefire-reports/*.xml'
}
}
// Stage 4: Deployment (Conditional)
stage('Deploy to Staging') {
// The 'when' directive ensures the stage only runs under specific conditions
when {
branch 'main'
}
steps {
sh "docker push ${CONTAINER_REGISTRY}/${IMAGE_NAME}:${BUILD_ID}"
sh 'kubectl apply -f k8s/deployment-staging.yaml'
script {
// Script blocks allow traditional Groovy logic within Declarative steps
echo "Deployment successful to Staging environment."
}
}
}
}
// 3. Post Actions: Define actions based on the final pipeline status
post {
success {
echo 'Pipeline completed successfully. Notifying team via Slack...'
// slackSend channel: '#devops-alerts', message: 'CI/CD Success!'
}
failure {
echo 'Pipeline failed. Review logs for errors.'
}
cleanup {
sh 'docker image prune -f --filter "label=build_id=$BUILD_ID"'
}
}
}
Step 3: Running and Monitoring the Pipeline
Once the Jenkinsfile is committed and pushed to the branch configured in your Jenkins job:
- On the Jenkins job page, click Build Now (or wait for the SCM polling/webhook trigger).
- Monitor the build in the Build History panel.
- Click the running build number and select Console Output to view the execution details step-by-step.
- You can also use the Stage View visualization (if the plugin is installed) to see the progress of the
Checkout,Build,Test, andDeploystages graphically.
Best Practices and Advanced Features
Utilizing Libraries and Shared Code
For complex or highly repetitive steps, avoid writing verbose Groovy directly in the Jenkinsfile. Instead, use Shared Libraries. These external libraries allow you to define common functions (like standard deployment routines or notification functions) that can be called from multiple pipelines, making your Jenkinsfile cleaner.
// Calling a shared library step
stage('Custom Setup') {
steps {
customLibrary.initializeEnvironment(env: 'prod')
}
}
Working with Credentials
Never hardcode sensitive information (passwords, tokens, API keys) directly into the Jenkinsfile. Use Jenkins' built-in Credentials Management system.
The withCredentials step allows you to securely access stored secrets:
stage('Authenticate Registry') {
steps {
withCredentials([usernamePassword(credentialsId: 'docker-registry-creds',
passwordVariable: 'PASS',
usernameVariable: 'USER')]) {
sh '''
printf '%s' "$PASS" | docker login \
--username "$USER" \
--password-stdin "$CONTAINER_REGISTRY"
'''
}
}
}
Agent Selection
Tip: Always define your
agentspecifically using alabelrather thanagent any. Usingagent anymeans the pipeline could run on the Jenkins controller node, which is a security risk and can impact controller performance. Ensure you have properly configured Jenkins agents (nodes) with necessary tools (Docker, Maven, Node.js) installed.
Takeaway
Keep the first pipeline boring and reliable: choose a real agent label, check out code, build, test, gate deployment by branch, and keep secrets in Jenkins credentials. Once that works, add artifact publishing, security scanning, and environment promotion one step at a time.