-
-
Notifications
You must be signed in to change notification settings - Fork 138
/
infra.groovy
332 lines (303 loc) · 12.8 KB
/
infra.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
#!/usr/bin/env groovy
import io.jenkins.infra.InfraConfig
// Method kept for backward compatibility (as the method is available on the InfraConfig stateful object)
Boolean isRunningOnJenkinsInfra() {
return new InfraConfig(env).isRunningOnJenkinsInfra()
}
// Method kept for backward compatibility (as the method is available on the InfraConfig stateful object)
Boolean isTrusted() {
return new InfraConfig(env).isTrusted()
}
// Method kept for backward compatibility (as the method is available on the InfraConfig stateful object)
Boolean isInfra() {
return new InfraConfig(env).isInfra()
}
Object withDockerCredentials(Closure body) {
if (new InfraConfig(env).isRunningOnJenkinsInfra()) {
env.DOCKERHUB_ORGANISATION = ( isTrusted() ? 'jenkins' : 'jenkins4eval')
withCredentials([[$class: 'ZipFileBinding', credentialsId: 'jenkins-dockerhub', variable: 'DOCKER_CONFIG']]) {
return body.call()
}
}
else {
echo 'Cannot use Docker credentials outside of jenkins infra environment'
}
}
Object checkout(String repo = null) {
checkoutSCM(repo);
}
Object checkoutSCM(String repo = null) {
if (env.BRANCH_NAME) {
checkout scm
} else if ((env.BRANCH_NAME == null) && (repo)) {
git repo
} else {
error 'buildPlugin must be used as part of a Multibranch Pipeline *or* a `repo` argument must be provided'
}
}
/**
* Retrieves Settings file to be used with Maven.
* If {@code MAVEN_SETTINGS_FILE_ID} variable is defined, the file will be retrieved from the specified
* configuration ID provided by Config File Provider Plugin.
* Otherwise it will fallback to some unspecified file for Jenkins infrastructure (currently empty).
* @param settingsXml Absolute path to the destination settings file
* @param jdk Version of JDK to be used (no longer used)
* @return {@code true} if the file has been defined
*/
boolean retrieveMavenSettingsFile(String settingsXml, String jdk = 8) {
if (env.MAVEN_SETTINGS_FILE_ID != null) {
configFileProvider([configFile(fileId: env.MAVEN_SETTINGS_FILE_ID, variable: 'mvnSettingsFile')]) {
if (isUnix()) {
sh "cp ${mvnSettingsFile} ${settingsXml}"
} else {
bat "copy ${mvnSettingsFile} ${settingsXml}"
}
}
return true
} else if (new InfraConfig(env).isRunningOnJenkinsInfra()) {
echo 'NOTE: infra.retrieveMavenSettingsFile currently writes an empty settings file.'
writeFile file: settingsXml, text: libraryResource('settings.xml')
return true
}
return false
}
/**
* Runs Maven for the specified options in the current workspace.
* Maven settings will be added by default if needed.
* @param jdk JDK to be used
* @param options Options to be passed to the Maven command
* @param extraEnv Extra environment variables to be passed when invoking the command
* @return nothing
* @see #retrieveMavenSettingsFile(String)
*/
Object runMaven(List<String> options, String jdk = "8", List<String> extraEnv = null, String settingsFile = null, Boolean addToolEnv = true) {
List<String> mvnOptions = [
'--batch-mode',
'--show-version',
'--errors',
'--no-transfer-progress',
]
if (settingsFile != null) {
mvnOptions += "-s $settingsFile"
}
mvnOptions.addAll(options)
mvnOptions.unique()
String command = "mvn ${mvnOptions.join(' ')}"
runWithMaven(command, jdk, extraEnv, addToolEnv)
}
/**
* Runs Maven for the specified options in the current workspace.
* Maven settings will be added by default if needed.
* @param Major version of JDK to be used (integer)
* @param options Options to be passed to the Maven command
* @param extraEnv Extra environment variables to be passed when invoking the command
* @return nothing
* @see #retrieveMavenSettingsFile(String)
*/
Object runMaven(List<String> options, Integer jdk, List<String> extraEnv = null, String settingsFile = null, Boolean addToolEnv = true) {
runMaven(options, jdk.toString(), extraEnv, settingsFile, addToolEnv)
}
/**
* Runs the command with Java and Maven environments.
* The command may be either Batch or Shell depending on the OS.
* @param command Command to be executed
* @param jdk JDK version to be used
* @param extraEnv Extra environment variables to be passed
* @return nothing
*/
Object runWithMaven(String command, String jdk = 8, List<String> extraEnv = null, Boolean addToolEnv = true) {
List<String> env = [];
if(addToolEnv) {
env = [
"PATH+MAVEN=${tool 'mvn'}/bin"
]
}
if (extraEnv != null) {
env.addAll(extraEnv)
}
runWithJava(command, jdk, env, addToolEnv)
}
/**
* Runs the command with Java environment.
* {@code PATH} and {@code JAVA_HOME} will be set.
* The command may be either Batch or Shell depending on the OS.
* @param command Command to be executed
* @param jdk JDK version to be used
* @param extraEnv Extra environment variables to be passed
* @return nothing
*/
Object runWithJava(String command, String jdk = 8, List<String> extraEnv = null, Boolean addToolEnv = true) {
List<String> env = [];
if(addToolEnv) {
// Collection of well-known JDK locations on our agent templates (VMs and containers)
def agentJavaHomes = [
"linux": [
'/opt/java/openjdk', // Adoptium (Eclipse Temurin) for Linux - // https://github.com/adoptium/containers
"/opt/jdk-${jdk}", // Our own custom VMs/containers - https://github.com/jenkins-infra/packer-images
],
"windows": [
"C:/openjdk-${jdk}", // Adoptium (Eclipse Temurin) for Windows - // https://github.com/adoptium/containers
"C:/tools/jdk-${jdk}", // Our own custom VMs/containers - https://github.com/jenkins-infra/packer-images
],
];
// Prepare the list of JDK locations to search for on the agent
List<String> javaHomesToTry = agentJavaHomes[isUnix() ? 'linux' : 'windows']
// Define the java home based on the found JDK (or fallback to the Jenkins tool)
String javaHome
for(javaHomeToTry in javaHomesToTry) {
String javaBinToTry = "${javaHomeToTry}/bin/java"
if (!isUnix()) {
javaBinToTry += '.exe' // On windows, binaries have an extension
}
if (fileExists(javaBinToTry)) {
javaHome = javaHomeToTry
break
}
}
if (!javaHome) {
echo "WARNING: switching to the Jenkins tool named ${jdkTool} to set the environment variables JAVA_HOME and PATH, because no java installation found in any of the following locations: ${javaHomesToTry.join(", ")}"
String jdkTool = "jdk${jdk}"
javaHome = tool jdkTool
}
// Define the environment to ensure that the correct JDK is used
env = [
"JAVA_HOME=${javaHome}",
'PATH+JAVA=${JAVA_HOME}/bin',
]
echo "INFO: Using JAVA_HOME=${javaHome} as default JDK home."
}
if (extraEnv != null) {
env.addAll(extraEnv)
}
withEnv(env) {
if (isUnix()) { // TODO JENKINS-44231 candidate for simplification
sh command
} else {
bat command
}
}
}
/**
* Gets a specification for a jenkins version or war and downloads and stash it under the name provided
* @param Specification for a jenkins war, can be a jenkins URI to the jenkins.war, a Jenkins version or one of "latest", "latest-rc", "lts" and "lts-rc". Defaults to "latest". For local war files use the file:// protocol
* @param stashName The name used to stash the jenkins war file, defaults to "jenkinsWar"
*/
void stashJenkinsWar(String jenkins, String stashName = "jenkinsWar") {
def isVersionNumber = (jenkins =~ /^\d+([.]\d+)*(-rc[0-9]+[.][0-9a-f]{12})?$/).matches()
def isLocalJenkins = jenkins.startsWith("file://")
def mirror = "http://mirrors.jenkins.io/"
def jenkinsURL
if (jenkins == "latest") {
jenkinsURL = mirror + "war/latest/jenkins.war"
} else if (jenkins == "latest-rc") {
jenkinsURL = mirror + "/war-rc/latest/jenkins.war"
} else if (jenkins == "lts") {
jenkinsURL = mirror + "war-stable/latest/jenkins.war"
} else if (jenkins == "lts-rc") {
jenkinsURL = mirror + "war-stable-rc/latest/jenkins.war"
}
if (isLocalJenkins) {
if (!fileExists(jenkins - "file://")) {
error "Specified Jenkins file does not exists"
}
}
if (!isVersionNumber && !isLocalJenkins) {
if (jenkinsURL == null) {
error "Not sure how to interpret $jenkins as a version, alias, or URL"
}
echo 'Checking whether Jenkins WAR is available…'
sh "curl -ILf ${jenkinsURL}"
}
if (isVersionNumber) {
List<String> downloadCommand = [
"dependency:copy",
"-Dartifact=org.jenkins-ci.main:jenkins-war:${jenkins}:war",
"-DoutputDirectory=.",
"-Dmdep.stripVersion=true"
]
dir("deps") {
runMaven(downloadCommand)
sh "cp jenkins-war.war jenkins.war"
stash includes: 'jenkins.war', name: stashName
}
} else if (isLocalJenkins) {
dir(pwd(tmp: true)) {
sh "cp ${jenkins - 'file://'} jenkins.war"
stash includes: "*.war", name: "jenkinsWar"
}
} else {
sh("curl -o jenkins.war -L ${jenkinsURL}")
stash includes: '*.war', name: 'jenkinsWar'
}
}
/**
* Make sure the code block is run in a node with the all the specified nodeLabels as labels, if already running in that
* it simply executes the code block, if not allocates the desired node and runs the code inside it
* Node labels must be specified as String formed by a comma separated list of labels
* Please note that this step is not able to manage complex labels and checks for them literally, so do not try to use labels like 'docker,(lowmemory&&linux)' as it will result in
* the step launching a new node as is unable to find the label '(lowmemory&&linux)' in the list of labels for the current node
*
* @param env The run environment, used to access the current node labels
* @param nodeLabels The node labels, a string containing the comma separated labels
* @param body The code to run in the desired node
*/
void ensureInNode(env, nodeLabels, body) {
def inCorrectNode = true
def splitted = nodeLabels.split(",")
if (env.NODE_LABELS == null) {
inCorrectNode = false
} else {
for (label in splitted) {
if (!env.NODE_LABELS.contains(label)) {
inCorrectNode = false
break
}
}
}
if (inCorrectNode) {
body()
} else {
node(splitted.join("&&")) {
body()
}
}
}
/**
* Record artifacts created by this build which could be published via Incrementals (JEP-305).
* Call at most once per build, on a Linux node, after running mvn -Dset.changelist install.
* Follow up with #maybePublishIncrementals.
*/
void prepareToPublishIncrementals() {
// MINSTALL-126 would make this easier by letting us publish to a different directory to begin with:
String m2repo = sh script: 'mvn -Dset.changelist -Dexpression=settings.localRepository -q -DforceStdout help:evaluate', returnStdout: true
// No easy way to load both of these in one command: https://stackoverflow.com/q/23521889/12916
String version = sh script: 'mvn -Dset.changelist -Dexpression=project.version -q -DforceStdout help:evaluate', returnStdout: true
echo "Collecting $version from $m2repo for possible Incrementals publishing"
dir(m2repo) {
fingerprint '**/*-rc*.*/*-rc*.*' // includes any incrementals consumed
archiveArtifacts "**/$version/*$version*"
}
}
/**
* When appropriate, publish artifacts from the current build to the Incrementals repository.
* Call at the end of the build, outside any node, when #prepareToPublishIncrementals may have been called previously.
* See INFRA-1571 and JEP-305.
*/
void maybePublishIncrementals() {
if (new InfraConfig(env).isRunningOnJenkinsInfra() && currentBuild.currentResult == 'SUCCESS') {
stage('Deploy') {
withCredentials([string(credentialsId: 'incrementals-publisher-token', variable: 'FUNCTION_TOKEN')]) {
httpRequest url: 'https://incrementals.jenkins.io/',
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
validResponseCodes: '100:599',
timeout: 300,
requestBody: /{"build_url":"$BUILD_URL"}/,
customHeaders: [[name: 'Authorization', value: 'Bearer ' + FUNCTION_TOKEN]],
consoleLogResponseBody: true
}
}
} else {
echo 'Skipping deployment to Incrementals'
}
}