Complete jenkinsfile toolkit with generation and validation capabilities
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Risky
Do not use without reviewing
Comprehensive guide based on official Jenkins documentation and community best practices.
Bad:
sh 'echo "Starting build"'
sh 'mkdir build'
sh 'cd build'
sh 'cmake ..'
sh 'make'
sh 'echo "Build complete"'Good:
sh '''
echo "Starting build"
mkdir build
cd build
cmake ..
make
echo "Build complete"
'''Why: Each sh step has start-up and tear-down overhead. Combining commands reduces this overhead and improves performance.
Bad (runs on controller):
@NonCPS
def parseJson(String jsonString) {
def jsonSlurper = new groovy.json.JsonSlurper()
return jsonSlurper.parseText(jsonString)
}
def data = readFile('data.json')
def parsed = parseJson(data)Good (runs on agent):
def result = sh(script: 'jq ".field" data.json', returnStdout: true).trim()Why: Controller resources are shared across all builds. Heavy operations should run on agents to prevent controller bottlenecks.
Bad:
def logFile = readFile('huge-log.txt') // Loads entire file into controller memory
def lines = logFile.split('\n')Good:
def errorCount = sh(script: 'grep ERROR huge-log.txt | wc -l', returnStdout: true).trim()Why: Reduces memory usage on controller and network transfer time.
Bad:
sh 'docker login -u admin -p password123'
sh 'curl -H "Authorization: Bearer abc123xyz" https://api.example.com'Good:
withCredentials([usernamePassword(
credentialsId: 'docker-hub',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh 'docker login -u $DOCKER_USER -p $DOCKER_PASS'
}
withCredentials([string(credentialsId: 'api-token', variable: 'API_TOKEN')]) {
sh 'curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com'
}Why: Credentials stored in Jenkins Credentials Manager are encrypted and access-controlled.
Good:
environment {
AWS_CREDENTIALS = credentials('aws-credentials-id')
// Creates AWS_CREDENTIALS_USR and AWS_CREDENTIALS_PSW
}Bad:
parameters {
string(name: 'BRANCH', defaultValue: '', description: 'Branch to build')
}
sh "git checkout ${params.BRANCH}" // Injection risk!Good:
parameters {
choice(name: 'BRANCH', choices: ['main', 'develop', 'release'], description: 'Branch to build')
}
// Or validate input
def branch = params.BRANCH
if (!branch.matches(/^[a-zA-Z0-9_\-\/]+$/)) {
error "Invalid branch name: ${branch}"
}Good:
// Declarative
options {
timeout(time: 1, unit: 'HOURS')
}
// Scripted
timeout(time: 30, unit: 'MINUTES') {
node {
// steps
}
}Why: Prevents builds from hanging indefinitely and consuming resources.
Declarative:
post {
always {
cleanWs()
}
success {
slackSend color: 'good', message: "Build succeeded"
}
failure {
mail to: 'team@example.com',
subject: "Build Failed: ${currentBuild.fullDisplayName}",
body: "Check ${env.BUILD_URL}"
}
}Scripted:
node {
try {
stage('Build') {
sh 'make build'
}
stage('Test') {
sh 'make test'
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
mail to: 'team@example.com',
subject: "Build Failed",
body: "Error: ${e.message}"
throw e
} finally {
cleanWs()
}
}Good:
post {
always {
cleanWs()
}
}
// Or for specific cleanup
post {
cleanup {
deleteDir()
}
}Why: Ensures consistent build environment and prevents disk space issues.
Good:
retry(3) {
sh 'curl -f https://flaky-api.example.com/data'
}
// Or with exponential backoff
script {
def attempts = 0
retry(3) {
attempts++
if (attempts > 1) {
sleep time: attempts * 10, unit: 'SECONDS'
}
sh 'flaky-command'
}
}Bad: Copy-pasting common code across Jenkinsfiles
Good:
@Library('my-shared-library@master') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildMavenProject() // From shared library
}
}
stage('Deploy') {
steps {
deployToKubernetes(env: 'production') // From shared library
}
}
}
}Bad:
stage('Step 1') { }
stage('Step 2') { }Good:
stage('Build Application') { }
stage('Run Unit Tests') { }
stage('Build Docker Image') { }
stage('Deploy to Staging') { }Good:
script {
// Calculate next version based on git tags
def lastTag = sh(script: 'git describe --tags --abbrev=0', returnStdout: true).trim()
def (major, minor, patch) = lastTag.tokenize('.')
// Increment patch version for feature branches
if (env.BRANCH_NAME.startsWith('feature/')) {
patch = patch.toInteger() + 1
}
def nextVersion = "${major}.${minor}.${patch}"
echo "Next version: ${nextVersion}"
}Good:
pipeline {
stages {
stage('Preparation') {
stages {
stage('Checkout') { }
stage('Setup Environment') { }
}
}
stage('Build') {
stages {
stage('Compile') { }
stage('Package') { }
}
}
stage('Quality Checks') {
parallel {
stage('Unit Tests') { }
stage('Integration Tests') { }
stage('Code Analysis') { }
}
}
}
}Good:
stage('Tests') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify'
}
}
stage('E2E Tests') {
steps {
sh 'npm run e2e'
}
}
}
}Good:
stage('Deploy') {
failFast true
parallel {
stage('Region 1') { }
stage('Region 2') { }
stage('Region 3') { }
}
}Why: Stops remaining parallel tasks immediately if one fails, saving time and resources.
Good:
node('build-agent') {
stage('Build') {
sh 'mvn package'
stash name: 'app-jar', includes: 'target/*.jar'
}
}
node('test-agent') {
stage('Test') {
unstash 'app-jar'
sh 'java -jar target/*.jar --test'
}
}Good:
options {
skipDefaultCheckout() // Don't checkout automatically
}
stages {
stage('Build') {
steps {
checkout scm // Checkout only when needed
}
}
}Good:
agent {
docker {
image 'maven:3.8.1-adoptopenjdk-11'
args '-v $HOME/.m2:/root/.m2'
}
}Bad:
sh 'docker run maven:3.8.1 mvn clean'
sh 'docker run maven:3.8.1 mvn compile'
sh 'docker run maven:3.8.1 mvn package'Good:
docker.image('maven:3.8.1').inside {
sh 'mvn clean compile package'
}Good:
stage('Build') {
steps {
script {
dockerImage = docker.build("myapp:${env.BUILD_NUMBER}")
}
}
}
stage('Test') {
steps {
script {
dockerImage.inside {
sh 'run-tests.sh'
}
}
}
}
stage('Deploy to Staging') {
steps {
script {
dockerImage.push('staging')
}
}
}
stage('Deploy to Production') {
steps {
script {
dockerImage.push('production')
dockerImage.push('latest')
}
}
}Good:
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.8.1
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
'''
}
}Good:
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
serviceAccountName: jenkins-agent
containers:
- name: kubectl
image: bitnami/kubectl:latest
'''
}
}Good:
post {
always {
junit '**/target/test-results/*.xml'
publishHTML([
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}Good:
post {
success {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}Good:
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/test-results/*.xml'
}
}
}
}Bad:
triggers {
pollSCM('H/5 * * * *') // Polls every 5 minutes
}Good: Configure webhooks in your repository to trigger builds on push/PR
Why: Webhooks are more efficient and provide faster feedback than polling.
Good:
triggers {
cron('H 2 * * *') // Daily at ~2 AM (H for hash-based distribution)
cron('H H(0-7) * * *') // Once between midnight and 7 AM
}Good:
post {
failure {
slackSend (
color: 'danger',
message: "Build FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
)
}
fixed {
slackSend (
color: 'good',
message: "Build FIXED: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
}Good:
post {
failure {
mail to: 'team@example.com',
subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Build: ${env.BUILD_URL}
Branch: ${env.BRANCH_NAME}
Commit: ${env.GIT_COMMIT}
Author: ${env.CHANGE_AUTHOR}
Please check the build logs for details.
"""
}
}Good:
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'deploy-production.sh'
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
sh 'deploy-staging.sh'
}
}Good:
stage('PR Validation') {
when {
changeRequest()
}
steps {
sh 'run-pr-checks.sh'
}
}Organize credentials by domain (global, project-specific, etc.)
Good:
withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
wrap([$class: 'MaskPasswordsBuildWrapper']) {
sh 'echo "Using API key: $API_KEY"' // Will be masked in logs
}
}Good:
options {
buildDiscarder(logRotator(
numToKeepStr: '10', // Keep last 10 builds
daysToKeepStr: '30', // Keep builds from last 30 days
artifactNumToKeepStr: '5', // Keep artifacts from last 5 builds
artifactDaysToKeepStr: '14' // Keep artifacts from last 14 days
))
}Good:
options {
disableConcurrentBuilds()
}Good:
options {
timestamps()
}