Is a Jenkinsfile in pure Groovy? What Groovy language construct is used in `steps` in a Jenkinsfile?

Viewed 2839

What Groovy language construct, syntax or control structure is used in the following code fragment in a Jenkinsfile?

stage('Stage 1') {
    steps {
        // One or more steps
    }
}

i.e. What are blocks in Jenkinsfile, in terms of pure Groovy language?

what is 'steps'? or stage? Is it calling a function? or definition? or a function call with anonymous (lambda) argument?

Inherent in this question is another question:

Question 2:

Is a Jenkinsfile, a code fragment in groovy language?

In other words, 1. Does a Jenkinsfile follow all syntax and control structures of pure Groovy? (perhaps by an implicit library import-ed or #include d silently in beginning),

As opposed to being a DSL: 2. Jenkinsfile being almost a groovy source file augmented with new Jenkins-specific constructs not originally in Groovy, e.g. Jenkins using a preprocessing.

Which of the above two hold?

Related:

2 Answers

In Jenkins (or Gradle) 2 main features are used:

  1. Groovy (java) idiomatic structures like loops, switches, chain of command etc
  2. DSLBuilder facility based on Closures to allow for nesting and invoking of domain-specific methods as they were a part of Groovy itself.

So, if you write something like

stage('Stage 1') {
    steps {
        // One or more steps
    }
}

it translates internaly to roughly:

jenkinsContext.stage('Stage 1') {
    jenkinsContext.steps {
        // One or more steps
    }
}

so it is way clearer to write and read. Here the Closures - the {...} blocks - represent nesting or grouping of your code.

In this block you can also see Groovy's way of calling methods where the last argument is a Closure. The code above could be rewritten as:

jenkinsContext.stage 'Stage 1', { // here no brackets around args
    jenkinsContext.steps( { // here with normal java-style brackets
        // One or more steps
    } )
}

In jenkins you can mix and match DSL calls with Groovy structures:

[ 'Stage 1', 'Stage 2' ].each{
  stage( it ) {}
}

or even have your DSL names generated dynamically:

[ 'Stage 1':'stage', 'step 2':'steps' ].each{ value, name ->
  "$name"( value ) {}
}

would create the DSL (as example ONLY!):

  stage( 'Stage 1') {}
  steps( 'Step 2' ) {}

So, Jenkins pipeline syntax is Groovy + Jenkins DSL

Looks like it's primarily Groovy, so if you're looking for simply syntax highligting adding something like the following does the job.

<!-- language: lang-groovy -->

There are caveats however documented in the online docs, so maybe this implies it's not written in pure groovy but some specialized/constrained form of it?

The basic statements and expressions which are valid in Declarative Pipeline follow the same rules as Groovy’s syntax with the following exceptions:

  • The top-level of the Pipeline must be a block, specifically: pipeline { }.

  • No semicolons as statement separators. Each statement has to be on its own line.

  • Blocks must only consist of Sections, Directives, Steps, or assignment statements.

  • A property reference statement is treated as a no-argument method invocation. So, for example, input is treated as input().

https://www.jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline

Related