How to Use Jenkins Declarative Pipeline: Architecture and Code Implementation
Jenkins Declarative Pipeline compiles high-level pipeline {} syntax into an executable Scripted Pipeline model at runtime through the pipeline-model-definition plugin's parser and CPS conversion layer.
The jenkinsci/jenkins repository contains the pipeline-model-definition plugin that transforms user-friendly declarative configurations into robust Jenkins executions. Understanding how to use Jenkins Declarative Pipeline effectively requires familiarity with its parsing architecture, the DeclarativePipeline entry point, and the CPS (Continuation-Passing-Style) engine that ultimately runs your build logic.
Core Architecture and Entry Points
The Declarative Pipeline system functions as a transpiler: it accepts the restricted pipeline {} DSL, validates it through a dedicated parser, and generates a standard Scripted Pipeline model that executes within the existing Jenkins workflow engine.
The DeclarativePipeline Class
At the center of this architecture is [DeclarativePipeline.java](https://github.com/jenkinsci/jenkins/blob/master/plugins/pipeline-model-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/DeclarativePipeline.java), which extends CpsScmFlowDefinition—the same base class used for traditional Scripted Pipelines. This inheritance ensures that declarative blocks ultimately resolve to executable CPS code stored in the backing WorkflowJob.
The class stores three critical components populated via its @DataBoundConstructor and @DataBoundSetter annotations:
pipelineScript(line 23): The raw string content of yourJenkinsfileagent(line 24): The execution environment configuration injected throughsetAgent()options(line 25): Pipeline-level configurations like timeouts injected throughsetOptions()
When Jenkins instantiates the job, the createFlowDefinition() method transforms these stored values into a runnable flow definition attached to a WorkflowJob. The plugin descriptor at line 48 registers this functionality under the display name "Declarative Pipeline".
The Parser and Error Handling
Before execution begins, the [DeclarativeParser.java](https://github.com/jenkinsci/jenkins/blob/master/plugins/pipeline-model-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/parser/DeclarativeParser.java) class handles syntax validation. Extending the base Parser from the workflow-cps plugin, this component accepts the script content through its @DataBoundConstructor and validates declarative sections (agent, stages, environment, options, post).
The parser's parse() method returns a fully configured DeclarativePipeline object, effectively converting static DSL text into an executable model. Syntax errors are wrapped using DeclarativeErrorHandler to provide line-specific feedback in the Jenkins UI.
Runtime Execution Model
Once parsed, the declarative model executes through the standard CPS engine, but with structural guarantees enforced by the model definition layer.
Agent Resolution
The agent directive maps to specific implementations of AgentImpl referenced in lines 9-11 of the main class:
LabelAgent: Allocates a node matching a label expressionDockerAgentImpl: Provisions a container environment
These agents are instantiated via @DataBoundSetter calls during pipeline configuration and are resolved when the CPS flow begins execution.
Stage Execution and Steps
Individual steps within steps { ... } blocks resolve to classes in the org.jenkinsci.plugins.pipeline.modeldefinition.steps package. Each step extends Jenkins' standard Step class and can be registered via the @Extension annotation, allowing plugins to contribute new declarative capabilities without modifying core parser logic.
Practical Jenkinsfile Implementation
Below is a complete Jenkinsfile demonstrating declarative syntax that maps directly to the Java classes discussed above:
pipeline {
agent any // Maps to LabelAgent implementation via AgentImpl
options {
timeout(time: 30, unit: 'MINUTES') // Stored in DeclarativePipeline.options
retry(2)
}
environment {
BUILD_VERSION = "1.0.${BUILD_NUMBER}"
}
stages {
stage('Compile') {
steps {
// Executes via pipeline-model-definition steps infrastructure
sh 'mvn clean compile'
}
}
stage('Parallel Testing') {
parallel {
unit: {
sh 'mvn test'
}
integration: {
sh 'mvn verify -Pintegration'
}
}
}
stage('Deploy') {
when {
branch 'main' // Conditional logic enforced by DeclarativeParser
}
steps {
// Custom steps from the modeldefinition steps package
kubernetesDeploy(
configs: 'k8s/*.yaml',
kubeConfig: [path: '$KUBECONFIG']
)
}
}
}
post {
always {
archiveArtifacts artifacts: '**/target/*.jar'
}
failure {
// Error output integrated with DeclarativeErrorHandler
notifySlack(message: "Build failed: ${env.JOB_NAME}")
}
}
}
This configuration demonstrates how the pipeline {} block structure is parsed by DeclarativeParser, stored in the DeclarativePipeline model, and executed through the CPS engine using the specified AgentImpl.
Extending Declarative Pipelines
To add custom functionality, developers can create new classes in the org.jenkinsci.plugins.pipeline.modeldefinition.steps directory extending Step and register them with @Extension. These steps become available immediately within any declarative steps block without requiring changes to [DeclarativeParser.java](https://github.com/jenkinsci/jenkins/blob/master/plugins/pipeline-model-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/parser/DeclarativeParser.java), maintaining the separation between syntax definition and execution logic.
Summary
- Entry Point: The
DeclarativePipelineclass extendsCpsScmFlowDefinitionand stores yourpipelineScript,agent, andoptionsvia its data-bound constructor and setters. - Parsing:
DeclarativeParservalidates the DSL and converts it into a scripted model, usingDeclarativeErrorHandlerfor error reporting. - Execution: The model transforms into a CPS flow that executes on
WorkflowJobinstances using specializedAgentImplimplementations likeLabelAgentorDockerAgentImpl. - Extensibility: Custom steps reside in the
stepspackage and integrate via standard Jenkins step extensions.
Frequently Asked Questions
What is the difference between Declarative and Scripted Pipeline?
Declarative Pipeline provides a structured, opinionated syntax through the pipeline {} block that enforces organizational standards and simplifies maintenance, while Scripted Pipeline offers unrestricted Groovy code execution. Internally, Declarative Pipeline compiles into a Scripted Pipeline model via the DeclarativeParser before CPS execution, meaning both ultimately run on the same engine but Declarative adds validation and abstraction layers.
How does the agent directive work at the source code level?
When you specify agent any or agent { docker 'node:18' }, the DeclarativePipeline class stores this configuration via setAgent() using @DataBoundSetter at line 33. At runtime, this resolves to concrete implementations like LabelAgent or DockerAgentImpl (both extending AgentImpl), which handle node allocation or container provisioning before your stages execute.
Can I inject Scripted Pipeline code inside a Declarative Pipeline?
Yes, via the script step available in the steps package. This step provides an escape hatch to run arbitrary Groovy within a steps block. The DeclarativeParser allows this specific step to contain unvalidated scripted logic, which executes directly in the CPS engine alongside the structured declarative elements.
Where does Jenkins handle syntax errors in Declarative Pipelines?
Syntax validation occurs in [DeclarativeParser.java](https://github.com/jenkinsci/jenkins/blob/master/plugins/pipeline-model-definition/src/main/java/org/jenkinsci/plugins/pipeline/modeldefinition/parser/DeclarativeParser.java), which invokes DeclarativeErrorHandler to wrap exceptions. This handler provides line-specific error messages in the Jenkins UI during the initial parsing phase before any CPS execution begins, distinguishing declarative syntax errors from runtime failures.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →