Back
atom.xml
blog
Parsing Jenkins Pipeline without Jenkins 25 Dec 2020
jenkins rust otto pipeline

Writing and locally verifying a CI/CD pipeline is a challenge thousands of developers face, which I’m hoping to make a little bit easier with a new tool named: Jenkins Declarative Parser (jdp). Jenkins Pipeline is one of the most important advancements made in the last 10 years for Jenkins, it can however behave like a frustrating black box for many new and experienced Jenkins users. The goal with jdp is to provide a lightweight and easy to run utility and library for validating declarative Jenkinsfiles.


If you have cargo installed, you can get jdp right now by running cargo install jdp, or you can download a Linux/amd64-based binary from the releases.


Within the Jenkins advocacy group, I think the biggest marketing coup we have accomplished to date is making people think that “Declarative Pipeline” is actually declarative. For better or worse, it’s really not. There is no first-party grammar or parser for a declarative Jenkinsfile. The dirty little secret is that Declarative Pipeline is just a Groovy domain specific language, one which tries to hide Groovy from the user, but in many cases fails to do so. Personally, I still think Declarative is a much better option than Scripted, with far more guardrails which help keep users working with best practices. As I have gotten deep into the supported syntax of Declarative however, I have started to see numerous holes in the thin veneer over Groovy.

As of now, jdp is at v0.2.2 and can support lots of Declarative Pipeline syntax.

This parser is not a Groovy syntax parser and as such any advanced or wacky groovy that is littered around a Jenkinsfile will be ignored. This includes the script step which is basically checked to make sure that there is a script { } block, but anything within it is explicitly ignored.

Here’s an example of a non-trivial pipeline pulled from the Jenkins documentation:

pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                branch 'production'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

When jdp check is executed against this file, it let’s me know that it’s correct!

❯ jdp check data/valid/when-branch/Jenkinsfile
Looks valid! Great work!

The jdp check tries to be as helpful as possible with bad syntax or semantics in an invalid Jenkinsfile. This is an area that I want to improve more, with rustc as a good inspiration for how helpful a “compiler” or “linter” should behave:

pipeline {
    agent any
    stages {
        stage('Build') {
        }
    }
}

The above pipeline doesn’t have syntax errors, but does lack some important semantics required by a Declarative Jenkinsfile:

jdp check data/invalid/no-steps-in-stage/Jenkinsfile 

data/invalid/no-steps-in-stage/Jenkinsfile
------------------------------------------
0: pipeline {
1:     agent any
2:     stages {
3:         stage('Build') {
  --------^

Fail: A stage must have either steps{}, parallel{}, or nested stages {}

There are still tons of things I want to do with jdp, which is written in Rust. If you’re keen on building a first-class Jenkinsfile grammar or development tooling: check out the issues page for some of the ideas I have in mind for jdp.

As we enter 2021, Jenkins Pipeline will definitely continue to play a major role in many of our CI/CD pipelines. With jdp, I’m hoping it becomes a bit easier for us all to write high quality and correct Jenkinsfiles!