We use Jenkins shared libraries and need to support the ability of individual repositories to override the default JDK. We have added OpenJDK-20
and OpenJDK-11
in Jenkins > Manage Jenkins as available JDKs alongside the default JDK 8. Now, how can I express this conditional within the tools
block?
Current pipeline definition (debug_pipeline.groovy
):
// Jenkins declarative pipeline for debugging Jenkins agent
def call (body) {
def pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent any
options {
// Prepend all console output generated by the
// Pipeline run with the time at which the line was emitted
timestamps()
}
tools {
// Define tools to auto-install and put on the PATH
// The tool name must be pre-configured in Jenkins under Manage Jenkins → Tools.
maven 'apache-maven-3.9.3'
jdk 'OpenJDK-20'
}
stages {
stage('Initialize') {
steps {
hello(pipelineParams.greeting)
}
}
stage('Debug') {
steps {
sh 'command -v java && java -version'
sh 'command -v mvn && mvn -version'
}
}
}
}
}
Now, I would like for the use of JDK 11 or 20 to be optional by having projects use pipeline parameters to specify them. I have tried:
tools {
if (pipelineParams.maven != 'system') {
maven pipelineParams.maven
}
if (pipelineParams.jdk != 'system') {
jdk pipelineParams.jdk
}
}
And the invoking repository's Jenkinsfile
:
@Library('pipeline-library') _
debug_pipeline {
Greeting = 'Goodbye'
jdk = 'OpenJDK-20'
maven = 'apache-maven-3.9.3'
}
However, this errors with debug_pipeline.groovy: 15: Expected to find ‘someTool "someVersion"’
. It also seems that I cannot use when
within tools
.
So, how can I effectively allow projects to override the JDK using pipeline parameters?
You are right in stating that tool {}
does not allow you to make tool inclusions conditional (within the curly brackets).
The best way to fix/solve this is to add a default directly inside of def pipelineParams = [:]
; these defaults will be overridden by the input of your Jenkinsfile (debug_pipeline {...}
). This is done because you specified: body.resolveStrategy = Closure.DELEGATE_FIRST
, body.delegate = pipelineParams
and body()
inside of def call (body) {}
. Example:
def call (body) {
def pipelineParams = [
'GREETINGS' : 'Hi!'
'JDK' : 'OpenJDK-20',
'MAVEN' : 'apache-maven-3.9.3'
]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
...
}
@Library('pipeline-library') _
debug_pipeline {
JDK = 'OpenJDK-11'
MAVEN = 'apache-maven-3.8.8'
CHEESE = 'GOUDA'
}
Would Result in:
pipelineParams == [
'GREETINGS' : 'Hi!'
'JDK' : 'OpenJDK-11',
'MAVEN' : 'apache-maven-3.8.8'
'CHEESE' : 'GOUDA'
]
Considering you are using a pipeline library, you might even create a "helper" class (inside of src) and centralize tool versions. This is really helpful if you have multiple pipelines or many versions of a tool; So only the helper class has to change and not all pipelines and Jenkinsfiles. Example:
package com.corp.lib
class EnvironmentHelper implements Serializable {
public static final String JDK_11 = 'OpenJDK-11'
public static final String JDK_20 = 'OpenJDK-20'
public static final String DEFAULT_JDK = JDK_11
}
This allows you to use:
import com.corp.lib.EnvironmentHelper
def call (body) {
def pipelineParams = [
'JDK' : EnvironmentHelper.DEFAULT_JDK,
...
]
...
}
@Library('pipeline-library') _
import com.corp.lib.EnvironmentHelper
debug_pipeline {
JDK = EnvironmentHelper.JDK_20
...
}