George V. Reilly

Computed Parallel Branches in Jenkins Pipeline

I’ve been using Jenkins lately, setting up Pipeline builds. I have mixed feelings about that, but I’m quite liking Groovy.

Here’s an example of a Closure called ac­cep­tance_in­te­gra­tion_tests, where the re­lease_lev­el parameter is a String which must be either "dev" or "prod".

def acceptance_integration_tests = { String release_level ->
    assert release_level =~ /^(dev|prod)$/
    String arg = "--${release_level}"

    def branches = [
        "${release_level}_acceptance_tests": {
            run_tests("ci_acceptance_test", arg, '**/*nosetests.xml')
        "${release_level}_integration_tests": {
            run_tests("ci_integration_test", arg, '**/*nosetests.xml')
    parallel branches

We create a Map called branches with dy­nam­i­cal­ly named keys, such as "prod_in­te­gra­tion_tests", thanks to GString in­ter­po­la­tion. The values in the branches map are themselves closures, where arg is bound to "--dev" or "--prod".

The branches map is passed to Pipeline’s parallel command, which causes the two run_tests closures to be executed on two different ex­ecu­tors—even­tu­al­ly.

I figured out the above syntax myself, then found a similar example.

stage("Deploy to Dev") { deploy "dev" }
stage("Dev Tests") { acceptance_integration_tests "dev" }

stage("Deploy to Prod") { deploy "prod" }
stage("Prod Tests") { acceptance_integration_tests "prod" }

The ac­cep­tance_in­te­gra­tion_tests closure is used in two different stages. Each stage is passed an anonymous closure, which invokes the parallel tests at a suitable time.

The use of closures and Groovy’s DSL facilities is powerful, terse, and expressive.

blog comments powered by Disqus
Review: Death of a Red Heroine » « Review: Boiling Point