Making your own DSL with plugins, written in Pipeline script

    In this post I will show how you can make your own DSL extensions and distribute them as a plugin, using Pipeline Script.

    A quick refresher

    Pipeline has a well kept secret: the ability to add your own DSL elements. Pipeline is itself a DSL, but you can extend it.

    There are 2 main reasons I can think you may want to do this:

    1. You want to reduce boilerplate by encapsulating common snippets/things you do in one DSL statement.

    2. You want to provide a DSL that provides a prescriptive way that your builds work - uniform across your organisations Jenkinsfiles.

    A DSL could look as simple as

    acmeBuild {
        script = "./bin/ci"
        environment = "nginx"
        team = "evil-devs"
        deployBranch = "production"
    }

    This could be the entirety of your Jenkinsfile!

    In this "simple" example, it could actually be doing a multi stage build with retries, in a specified docker container, that deploys only from the production branch. Detailed notifications are sent to the right team on important events (as defined by your org).

    Traditionally this is done via the global library. You take a snippet of DSL you want to want to make into a DSL, and drop it in the git repo that is baked into Jenkins.

    A great trivial example is this:

    jenkinsPlugin {
        name = 'git'
    }

    Which is enabled by git pushing the following into vars/jenkinsPlugin.groovy

    The name of the file is the name of the DSL expression you use in the Jenkinsfile
    def call(body) {
        def config = [:]
        body.resolveStrategy = Closure.DELEGATE_FIRST
        body.delegate = config
        body()
    
        // This is where the magic happens - put your pipeline snippets in here, get variables from config.
        node {
            git url: "https://github.com/jenkinsci/${config.name}-plugin.git"
            sh "mvn install"
            mail to: "...", subject: "${config.name} plugin build", body: "..."
        }
    }

    You can imagine many more pipelines, or even archetypes/templates of pipelines you could do in this way, providing a really easy Jenkinsfile syntax for your users.

    Making it a plugin

    Using the global DSL library is a handy thing if you have a single Jenkins, or want to keep the DSLs local to a Jenkins instance. But what if you want to distribute it around your org, or, perhaps it is general purpose enough you want to share it with the world?

    Well this is possible, by wrapping it in a plugin. You use the same pipeline snippet tricks you use in the global lib, but put it in the dsl directory of a plugin.

    My simple build plugin shows how it is done. To make your own plugin:

    1. Create a new plugin project, either fork the simple build one, or add a dependency to it in your pom.xml / build.gradle file

    2. Put your dsl in the resources directory in a similar fashion to this (note the "package dsl" declaration at the top)

    3. Create the equivalent extension that just points to the DSL by name like this This is mostly "boiler plate" but it tells Jenkins there is a GlobalVariable extension available when Pipelines run.

    4. Deploy it to an Jenkins Update Center to share with your org, or everyone!

    The advantage of delivering this DSL as a plugin is that it has a version (you can also put tests in there), and distributable just like any other plugin.

    For the more advanced, Andrew Bayer has a Simple Travis Runner plugin that interprets and runs travis.yml files which is also implemented in pipeline.

    So, approximately, you can build plugins for pipeline that extend pipeline, in pipeline script (with a teeny bit of boiler plate).

    Enjoy!

    About the Author
    Michael Neale
    Michael Neale

    Michael is a CD enthusiast with a interest in User Experience. He is a co-founder of CloudBees and a long time OSS developer, and can often be found lurking around the jenkins-dev mailing list or #jenkins on irc (same nick as twitter name). Before CloudBees he worked at Red Hat.