-
Notifications
You must be signed in to change notification settings - Fork 99
/
util2.groovy
executable file
·637 lines (596 loc) · 28.9 KB
/
util2.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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
import groovy.transform.Field
@Field String CSV_VERSION_F = ""
// Requires installYq()
def String getCSVVersion(String MIDSTM_BRANCH) {
if (CSV_VERSION_F.equals("")) {
CSV_VERSION_F = sh(script: '''#!/bin/bash -xe
curl -sSLo- https://raw.githubusercontent.com/redhat-developer/devspaces-images/''' + MIDSTM_BRANCH + '''/devspaces-operator-bundle/manifests/devspaces.csv.yaml | yq -r .spec.version''', returnStdout: true).trim()
}
// CRW-2039 check that CSV version is aligned to DS version, and throw warning w/ call to action to avoid surprises
if (DS_VERSION_F.equals("")) {
DS_VERSION_F = getDsVersion(MIDSTM_BRANCH)
}
CSV_VERSION_BASE=CSV_VERSION_F.replaceAll("([0-9]+\\.[0-9]+)\\.[0-9]+","\$1"); // extract 3.yy from 3.yy.z
if (!CSV_VERSION_BASE.equals(DS_VERSION_F)) {
println "[WARNING] CSV version (from getCSVVersion() -> csv.yaml = " + CSV_VERSION_F +
") does not match DS version (from getDsVersion() -> VERSION = " + DS_VERSION_F + ") !"
println "This could mean that your VERSION file or CSV file update processes have not run correctly."
println "Check these jobs:"
println "* https://main-jenkins-csb-crwqe.apps.ocp-c1.prod.psi.redhat.com/job/DS_CI/job/Releng/job/update-version-and-registry-tags/ "
println "* https://main-jenkins-csb-crwqe.apps.ocp-c1.prod.psi.redhat.com/job/DS_CI/job/operator-bundle_" + getJobBranch(MIDSTM_BRANCH)
println "Check these files:"
println "https://raw.githubusercontent.com/redhat-developer/devspaces/" + MIDSTM_BRANCH + "/dependencies/VERSION"
println "https://github.com/redhat-developer/devspaces-images/blob/" + MIDSTM_BRANCH + "/devspaces-operator-bundle/manifests/devspaces.csv.yaml"
println "https://github.com/redhat-developer/devspaces-images/blob/" + MIDSTM_BRANCH + "/devspaces-operator-bundle-generated/manifests/devspaces.csv.yaml"
}
return CSV_VERSION_F
}
@Field String DS_VERSION_F = ""
@Field String DS_BRANCH_F = ""
def String getDsVersion(String MIDSTM_BRANCH) {
if (DS_VERSION_F.equals("")) {
DS_BRANCH_F = MIDSTM_BRANCH
DS_VERSION_F = sh(script: '''#!/bin/bash -xe
curl -sSLo- https://raw.githubusercontent.com/redhat-developer/devspaces/''' + MIDSTM_BRANCH + '''/dependencies/VERSION''', returnStdout: true).trim()
}
return DS_VERSION_F
}
@Field String JOB_BRANCH
// JOB_BRANCH defines which set of jobs to run, eg., dashboard_ + JOB_BRANCH
def String getJobBranch(String MIDSTM_BRANCH) {
if (JOB_BRANCH.equals("") || JOB_BRANCH == null) {
if (MIDSTM_BRANCH.equals("devspaces-3-rhel-8") || MIDSTM_BRANCH.equals("main")) {
JOB_BRANCH="3.x"
} else {
// for 3.y (and 2.y)
JOB_BRANCH=MIDSTM_BRANCH.replaceAll("devspaces-","").replaceAll("crw-","").replaceAll("-rhel-8","")
}
}
return JOB_BRANCH
}
// method to check for global var or job param; if not set, return nullstring and throw no MissingPropertyException
// thanks to https://stackoverflow.com/questions/42465028/how-can-i-determine-if-a-variable-exists-from-within-the-groovy-code-running-in
// Usage: globalVar({nodeVersion}) <-- braces are required
def globalVar(varNameExpr) {
try {
varNameExpr() // return value of the string if defined by global var or job param
} catch (exc) {
"" // return nullstring if not defined
}
}
def cloneRepo(String URL, String REPO_PATH, String BRANCH, boolean withPolling=false, String excludeRegions='', String includeRegions='*') {
if (URL.indexOf("pkgs.devel.redhat.com") == -1) {
// remove http(s) prefix, then trim any token@ prefix too
URL=URL - ~/http(s*):\/\// - ~/.*@/
def AUTH_URL_SHELL='https://\$GITHUB_TOKEN:x-oauth-basic@' + URL
def AUTH_URL_GROOVY='https://$GITHUB_TOKEN:x-oauth-basic@' + URL
if (!fileExists(REPO_PATH) || withPolling) {
// clean before checkout
sh('''rm -fr ${WORKSPACE}/''' + REPO_PATH)
checkout(
poll: withPolling,
changelog: withPolling,
scm: [
$class: 'GitSCM',
branches: [[name: BRANCH]],
clean: true,
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'RelativeTargetDirectory', relativeTargetDir: REPO_PATH],
[$class: 'PathRestriction', excludedRegions: excludeRegions, includedRegions: includeRegions]
],
submoduleCfg: [],
userRemoteConfigs: [[url: AUTH_URL_GROOVY]]
]
)
}
sh('''#!/bin/bash -xe
cd ''' + REPO_PATH + '''
git checkout --track origin/''' + BRANCH + ''' || true
export GITHUB_TOKEN=''' + GITHUB_TOKEN + ''' # echo "''' + GITHUB_TOKEN + '''"
git config user.email "[email protected]"
git config user.name "devstudio-release"
git config --global push.default matching
# fix for warning: Pulling without specifying how to reconcile divergent branches is discouraged
git config --global pull.rebase true
# Fix for Could not read Username / No such device or address :: https://github.com/github/hub/issues/1644
git config --global hub.protocol https
git remote set-url origin ''' + AUTH_URL_SHELL + '''
'''
)
} else {
if (!fileExists(REPO_PATH)) {
sh('''#!/bin/bash -xe
export KRB5CCNAME=/var/tmp/devspaces-build_ccache
kinit -k -t /home/hudson/devspaces-build-keytab [email protected] || true; klist
git clone ''' + URL + ''' ''' + REPO_PATH
)
}
sh('''#!/bin/bash -xe
export KRB5CCNAME=/var/tmp/devspaces-build_ccache
kinit -k -t /home/hudson/devspaces-build-keytab [email protected] || true; klist
cd ''' + REPO_PATH + '''
git checkout --track origin/''' + BRANCH + ''' || true
git config user.email [email protected]
git config user.name "Dev Spaces Build"
git config --global push.default matching
# fix for warning: Pulling without specifying how to reconcile divergent branches is discouraged
git config --global pull.rebase true
'''
)
}
}
// Requires getDsVersion() to set DS_BRANCH_F in order to install correct version of the script; or, if JOB_BRANCH is defined by .groovy param or in .jenkinsfile, will use that version
def updateBaseImages(String REPO_PATH, String SOURCES_BRANCH, String FLAGS="", String SCRIPTS_BRANCH="") {
def String updateBaseImages_bin="${WORKSPACE}/updateBaseImages.sh"
if (SOURCES_BRANCH?.trim() && !DS_BRANCH_F?.trim()) {
getDsVersion(SOURCES_BRANCH)
}
if (!SCRIPTS_BRANCH?.trim() && DS_BRANCH_F?.trim()) {
SCRIPTS_BRANCH = DS_BRANCH_F // this should work for midstream/downstream branches like devspaces-3.1-rhel-8
} else if (!SCRIPTS_BRANCH?.trim() && MIDSTM_BRANCH?.trim()) {
SCRIPTS_BRANCH = MIDSTM_BRANCH // this should work for midstream/downstream branches like devspaces-3.1-rhel-8
} else if (!SCRIPTS_BRANCH?.trim() && JOB_BRANCH?.trim()) {
SCRIPTS_BRANCH = JOB_BRANCH // this might fail if the JOB_BRANCH is 2.6 and there's no such branch
}
// fail build if not true
assert (DS_BRANCH_F?.trim()) : "ERROR: execute getDsVersion() before calling updateBaseImages()"
if (!fileExists(updateBaseImages_bin)) {
// otherwise continue
sh('''#!/bin/bash -xe
URL="https://raw.githubusercontent.com/redhat-developer/devspaces/''' + SCRIPTS_BRANCH + '''/product/updateBaseImages.sh"
# check for 404 and fail if can't load the file
header404="$(curl -sSLI ${URL} | grep -E -v "id: |^x-" | grep -v "content-length" | grep -E "404|Not Found" || true)"
if [[ $header404 ]]; then
echo "[ERROR] Can not resolved $URL : $header404 "
echo "[ERROR] Please check the value of SCRIPTS_BRANCH = ''' + SCRIPTS_BRANCH + ''' to confirm it's a valid branch."
exit 1
else
curl -sSL ${URL} -o ''' + updateBaseImages_bin + ''' && chmod +x ''' + updateBaseImages_bin + '''
fi
''')
}
// NOTE: b = sources branch, sb = scripts branch
// TODO - https://issues.redhat.com/browse/CRW-3153 connection on x86_64-rhel8-3640 OK, fails on cpt-ppc-006, so enable -v (verbose) flag so we can see what's happening more clearly
updateBaseImages_cmd='''
echo "[INFO] util.groovy :: updateBaseImages :: SOURCES_BRANCH = ''' + SOURCES_BRANCH + '''"
echo "[INFO] util.groovy :: updateBaseImages :: SCRIPTS_BRANCH = ''' + SCRIPTS_BRANCH + '''"
cd ''' + REPO_PATH + '''
''' + updateBaseImages_bin + ''' --sources-branch ''' + SOURCES_BRANCH + ''' --scripts-branch ''' + SCRIPTS_BRANCH + ''' ''' + FLAGS + ''' || true
'''
is_pkgsdevel = sh(script: '''#!/bin/bash -xe
cd ''' + REPO_PATH + '''; git remote -v | grep pkgs.devel.redhat.com || true''', returnStdout: true).trim()
if (is_pkgsdevel?.trim()) {
sh('''#!/bin/bash -xe
export KRB5CCNAME=/var/tmp/devspaces-build_ccache
kinit -k -t /home/hudson/devspaces-build-keytab [email protected] || true; klist
''' + updateBaseImages_cmd
)
} else {
assert (GITHUB_TOKEN?.trim()) : "ERROR: GITHUB_TOKEN is not set; must be defined in order to manipulate github repos"
sh('''#!/bin/bash -xe
export GITHUB_TOKEN="''' + GITHUB_TOKEN + '''"
''' + updateBaseImages_cmd
)
}
}
// return a short SHA by default (4-char if possible, longer if required for uniqueness); or use num_digits=40 for a full length SHA
def getLastCommitSHA(String REPO_PATH, int num_digits=4) {
return sh(script: '''#!/bin/bash -xe
cd ''' + REPO_PATH + '''
git rev-parse --short=''' + num_digits + ''' HEAD''', returnStdout: true).trim()
}
def getDSLongName(String SHORT_NAME) {
return "devspaces-" + SHORT_NAME
}
def getDSShortName(String LONG_NAME) {
return LONG_NAME.minus("devspaces-")
}
// see https://hdn.corp.redhat.com/rhel7-csb-stage/repoview/redhat-internal-cert-install.html
// and https://hdn.corp.redhat.com/rhel7-csb-stage/RPMS/noarch/?C=M;O=D
def installRedHatInternalCerts() {
sh('''#!/bin/bash -xe
if [[ ! $(rpm -qa | grep redhat-internal-cert-install || true) ]]; then
cd /tmp
# 403 access on https://hdn.corp.redhat.com/rhel7-csb-stage/RPMS/noarch/redhat-internal-cert-install-0.1-24.el7.noarch.rpm
# so use latest csb.noarch file instead
rpm=$(curl --insecure -sSLo- "https://hdn.corp.redhat.com/rhel7-csb-stage/RPMS/noarch/?C=M;O=D" | grep cert-install | grep "csb.noarch" | head -1 | sed -r -e 's#.+>(redhat-internal-cert-install-.+[^<])</a.+#\\1#')
curl --insecure -sSLO https://hdn.corp.redhat.com/rhel7-csb-stage/RPMS/noarch/${rpm}
sudo yum -y install ${rpm}
rm -fr /tmp/${rpm}
fi
''')
}
// CRW-3598: @since 3.5
// note that this URL is also in devspaces-chectl/build/scripts/build.sh and devspaces/product/manifest/get-3rd-party-sources.sh
// also mentioned in dsc.groovy and get-3rd-party-sources.groovy
def getStagingHost() {
return "[email protected]"
}
@Field String defaultFailedEmailRecipients='[email protected]'
def notifyBuildFailed(String details="", String toRecipients=defaultFailedEmailRecipients) {
emailext (
subject: "Build failed in Jenkins: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: details + """
Build failed in Jenkins: ${env.JOB_NAME} #${env.BUILD_NUMBER}
Build: ${env.BUILD_URL}
Steps: ${env.BUILD_URL}/flowGraphTable
Params: ${env.BUILD_URL}/parameters
Console: ${env.BUILD_URL}/console
Rebuild: ${env.BUILD_URL}/rebuild
""",
// [$class: 'CulpritsRecipientProvider'],[$class: 'DevelopersRecipientProvider']]
recipientProviders: [culprits(), developers(), requestor()],
to: toRecipients
)
}
// commit all changed files in dir with message to branch
def commitChanges(String dir, String message, String branch) {
sh('''#!/bin/bash -xe
cd ''' + dir + ''' || exit 1
if [[ \$(git diff --name-only) ]]; then # file changed
git add --all -f . || true
git commit -s -m "''' + message + '''" || true
git push origin ''' + branch + ''' || true
fi
''')
}
// ensure static Dockerfiles have the correct version encoded in them, then commit changes
def updateDockerfileVersions(String dir="${WORKSPACE}/sources", String branch=MIDSTM_BRANCH, String DS_VERSION=DS_VERSION_F) {
sh('''#!/bin/bash -e
echo "[INFO] Run util.updateDockerfileVersions('''+dir+''', '''+branch+''', '''+DS_VERSION+''')"
cd ''' + dir + ''' || exit 1
for d in $(find . -name "*ockerfile*" -type f); do
sed -i $d -r -e 's#version="[0-9.]+"#version="''' + DS_VERSION + '''"#g' || true
done
''')
commitChanges(dir, "[sync] Update Dockerfiles to latest version = " + DS_VERSION, branch)
}
// call getLatestRPM.sh -s SOURCE_DIR -r RPM_PATTERN -u BASE_URL -a 'ARCH1 ... ARCHN' -q
// will also update content_sets.* files too, if ocp version has changed
def updateRpms(String RPM_PATTERN, String BASE_URL, String dir="${WORKSPACE}/sources", String branch=MIDSTM_BRANCH, String ARCHES="x86_64 s390x ppc64le") {
return sh(returnStdout: true, script: '''#!/bin/bash -xe
if [[ ! -x getLatestRPM.sh ]]; then
curl -sSLO https://raw.githubusercontent.com/redhat-developer/devspaces/''' + branch + '''/product/getLatestRPM.sh && chmod +x getLatestRPM.sh
fi
./getLatestRPM.sh -r "''' + RPM_PATTERN + '''" -u "''' + BASE_URL + '''" -s "''' + dir + '''" -a "''' + ARCHES + '''" -q
''').trim()
}
// URL from which to get internal RPM installations
@Field String pulpRepoURL = "https://rhsm-pulp.corp.redhat.com"
// ./getLatestRPM.sh -r "openshift-clients-4" -u https://rhsm-pulp.corp.redhat.com/content/dist/layered/rhel8/basearch/rhocp/4.12 -s ...
def updateOCRpms(String rpmRepoVersion="4.11", String dir="${WORKSPACE}/sources", String branch=MIDSTM_BRANCH, String ARCHES="x86_64 s390x ppc64le") {
updatedVersion=updateRpms("openshift-clients-4", pulpRepoURL + "/content/dist/layered/rhel8/basearch/rhocp/" + rpmRepoVersion, dir, branch, ARCHES)
commitChanges(dir, "[rpms] Update to " + updatedVersion, branch)
}
// ./getLatestRPM.sh -r "odo-3" -u https://rhsm-pulp.corp.redhat.com/content/dist/layered/rhel8/basearch/ocp-tools/4.11 -s ...
def updateOdoRpms(String rpmRepoVersion="4.11", String dir="${WORKSPACE}/sources", String branch=MIDSTM_BRANCH, String ARCHES="x86_64 s390x ppc64le") {
updatedVersion=updateRpms("odo-3", pulpRepoURL + "/content/dist/layered/rhel8/basearch/ocp-tools/" + rpmRepoVersion, dir, branch, ARCHES)
commitChanges(dir, "[rpms] Update to " + updatedVersion, branch)
}
// ./getLatestRPM.sh -r "helm-3" -u https://rhsm-pulp.corp.redhat.com/content/dist/layered/rhel8/basearch/ocp-tools/4.11 -s ...
def updateHelmRpms(String rpmRepoVersion="4.11", String dir="${WORKSPACE}/sources", String branch=MIDSTM_BRANCH, String ARCHES="x86_64 s390x ppc64le") {
updatedVersion=updateRpms("helm-3", pulpRepoURL + "/content/dist/layered/rhel8/basearch/ocp-tools/" + rpmRepoVersion, dir, branch, ARCHES)
commitChanges(dir, "[rpms] Update to " + updatedVersion, branch)
}
// run a job with default token, FORCE_BUILD=true, and SCRATCH=false
// use jobPath = /job/folder/job/jobname so we can both invoke a job, and then use json API in getLastSuccessfulBuildId()
def runJob(String jobPath, boolean doWait=false, boolean doPropagateStatus=true, String jenkinsURL=JENKINS_URL, String TIMEOUT="180") {
def int prevSuccessBuildId = getLastSuccessfulBuildId(jenkinsURL + jobPath) // eg., #5
println ("runJob(" + jobPath + ") :: prevSuccessBuildId = " + prevSuccessBuildId)
final jobResult = build(
// convert jobPath /job/folder/job/jobname (used in json API in getLastSuccessfulBuildId() to /folder/jobname (used in build())
job: jobPath.replaceAll("/job/","/"),
wait: doWait,
propagate: doPropagateStatus,
quietPeriod: 0,
parameters: [
[
$class: 'StringParameterValue',
name: 'TIMEOUT',
value: TIMEOUT
],
[
$class: 'BooleanParameterValue',
name: 'FORCE_BUILD',
value: true
],
[
$class: 'BooleanParameterValue',
name: 'SCRATCH',
value: false
]
]
)
// wait until #5 -> #6
jobLink=jobPath + "/" + (prevSuccessBuildId + 1).toString()
triggerDesc="<br/>=?> Job <a href=${JENKINS_URL}${jobLink}/>${jobPath} #" + (prevSuccessBuildId + 1).toString() + " triggered</a>"
if (doWait) {
println("waiting for runJob(" + jobPath + ") :: prevSuccessBuildId = " + prevSuccessBuildId)
currentBuild.description+=triggerDesc + " [waiting]"
if (!waitForNewBuild(jenkinsURL + jobPath, prevSuccessBuildId)) {
jobLink=jobPath + "/" + jobResult?.number?.toString()
println("--x Job ${JENKINS_URL}${jobLink}/console failed!")
currentBuild.description+="<br/>* <b style='color:red'>FAILED: <a href=${jobLink}/console>" + (jobLink.replaceAll("/job/","/")) + "</a></b>"
currentBuild.result = 'FAILED'
notifyBuildFailed(currentBuild.description,defaultFailedEmailRecipients)
}
println("++> Job ${JENKINS_URL}${jobLink}/console completed.")
} else {
println("=?> Job ${JENKINS_URL}${jobLink} triggered.")
currentBuild.description+=triggerDesc + " [no wait]"
}
return getLastSuccessfulBuildId(jenkinsURL + jobPath)
}
/*
lastBuild: build in progress -- if running, .result = null; else "FAILURE", "SUCCESS", etc
lastSuccessfulBuild
lastFailedBuild
*/
def getBuildJSON(String url, String buildType, String field) {
return sh(returnStdout: true, script: '''
URL="''' + url + '''/''' + buildType + '''/api/json"
# check for 404 and return 0 if can't load, or the actual value if loaded
header404="$(curl --insecure -sSLI ${URL} | grep -E -v "id: |^x-" | grep -v "content-length" | grep -E "404|Not Found" || true)"
if [[ $header404 ]]; then # echo "[WARNING] Can not resolve ${URL} : $header404 "
echo 0
else
curl --insecure -sSLo- ${URL} | jq -r "''' + field + '''"
fi
''').trim()
}
def getLastBuildId(String url) {
return (getBuildJSON(url, "lastBuild", ".number") as int)
}
def getLastBuildResult(String url) {
return getBuildJSON(url, "lastBuild", ".result")
}
def getLastSuccessfulBuildId(String url) {
return (getBuildJSON(url, "lastSuccessfulBuild", ".number") as int)
}
def getLastFailedBuildId(String url) {
return (getBuildJSON(url, "lastFailedBuild", ".number") as int)
}
def getLastUnsuccessfulBuildId(String url) {
return (getBuildJSON(url, "lastUnsuccessfulBuild", ".number") as int)
}
// default timeout = 7200s = 2h
def waitForNewBuild(String jobURL, int oldId, int checkInterval=120, int timeout=7200) {
echo "Id baseline for " + jobURL + "/lastBuild :: " + oldId
elapsed=0
nextId=oldId+1
while (true) {
newId=getLastSuccessfulBuildId(jobURL)
if (newId > oldId && getLastBuildResult(jobURL).equals("SUCCESS")) {
println "Id rebuilt in " + elapsed + "s (SUCCESS): " + jobURL + "/" + newId
return true
break
} else {
newId=getLastBuildId(jobURL)
if (newId > oldId && getLastFailedBuildId(jobURL).equals(newId)) {
println "Id rebuilt in " + elapsed + "s (FAILURE): " + jobURL + "/" + newId
return false
break
} else if (newId > oldId && getLastUnsuccessfulBuildId(jobURL).equals(newId)) {
println "Id rebuilt in " + elapsed + "s (ABORTED): " + jobURL + "/" + newId
return false
break
}
if (newId > oldId && getLastBuildResult(jobURL).equals("FAILURE")) {
println "Id rebuilt in " + elapsed + "s (FAILURE): " + jobURL + "/" + newId
return false
break
} else if (newId > oldId && getLastBuildResult(jobURL).equals("ABORTED")) {
println "Id rebuilt in " + elapsed + "s (ABORTED): " + jobURL + "/" + newId
return false
break
}
}
sleep(time:checkInterval,unit:"SECONDS")
elapsed += checkInterval
if (elapsed >= timeout) {
println "ERROR: No new build #" + newId + " > #" + oldId + " found after " + elapsed + " elapsed seconds!"
return false
break
} else {
println "Waiting " + checkInterval + "s for " + jobURL + "/" + nextId + " to complete"
}
}
return true
}
// requires brew, skopeo, jq, yq
// for a given image, return latest image tag in quay
def getLatestImageAndTag(String orgAndImage, String repo="quay", String tag=DS_VERSION_F) {
sh '''#!/bin/bash -xe
if [[ ! -x getLatestImageTags.sh ]]; then
curl -sSLO https://raw.githubusercontent.com/redhat-developer/devspaces/''' + MIDSTM_BRANCH + '''/product/getLatestImageTags.sh && chmod +x getLatestImageTags.sh
fi
'''
return sh(
returnStdout: true,
// -b devspaces-3.0-rhel-8 -c devspaces/server-rhel8 --tag "3.0-" --quay
script: './getLatestImageTags.sh -b ' + MIDSTM_BRANCH + ' -c "' + orgAndImage + '" --tag "' + tag + '" --' + repo
).trim()
}
// requires brew, skopeo, jq, yq
// check for latest image tags in quay for a given image
// default timeout = 7200s = 2h
def waitForNewQuayImage(String orgAndImage, String oldImage, int checkInterval=120, int timeout=7200) {
echo "Image baseline: " + oldImage
elapsed=0
while (true) {
def newImage = getLatestImageAndTag(orgAndImage, "quay")
// use bash version sort to put largest imageAndTag on top, then select that imageAndTag
def newestImage = sh(script: 'echo -e "' + oldImage + '\n' + newImage + '" | grep -v "???" | sort -uVr | head -1', returnStdout: true).trim()
// if the new image is different from the old one, and the newest image is not the old one, then we have a newer image
if (!newImage.equals(oldImage) && !newestImage.equals(oldImage)) {
echo "Image rebuilt in " + elapsed + "s (SUCCESS): " + newImage
return true
break
}
sleep(time:checkInterval,unit:"SECONDS")
elapsed += checkInterval
if (elapsed >= timeout) {
println "ERROR: No new build #" + newImage + " > #" + oldImage + " found after " + elapsed + " elapsed seconds!"
return false
break
} else {
println "Waiting " + checkInterval + "s for newer build than " + oldImage
}
}
return true
}
// depends on rpm perl-Digest-SHA for 'shasum -a ZZZ', or rpm coreutils for 'shaZZZsum'
// createSums("${DS_path}/*/target/", "*.tar.*")
def createSums(String filePath, String filePattern, String algorithm=512) {
sh '''#!/bin/bash -xe
suffix=".sha''' + algorithm + '''"
# delete any existing .shaZZZ files so we don't accidentally use them as shasum input if filePattern is too aggressive
for d in $(find ''' + filePath + ''' -name "''' + filePattern + '''${suffix}"); do
rm -f $d
done
# create new .shaZZZ files
prefix="SHA''' + algorithm + '''"
for d in $(find ''' + filePath + ''' -name "''' + filePattern + '''"); do
sum=""
if [[ -x /usr/bin/sha''' + algorithm + '''sum ]]; then
sum="$(/usr/bin/sha''' + algorithm + '''sum $d)"
elif [[ -x /usr/bin/shasum ]]; then
sum="$(/usr/bin/shasum -a ''' + algorithm + ''' $d)"
else
echo "[ERROR] Could not find /usr/bin/shasum or /usr/bin/sha''' + algorithm + '''sum!"
echo "[ERROR] Install rpm package perl-Digest-SHA for shasum, or coreutils for shaZZZsum to proceed."
exit 1
fi
if [[ ${sum} != "" ]]; then
echo "${sum}" | sed -r -e "s# ${d}##" -e "s#^#${prefix} (${d##*/}) = #" > ${d}${suffix}
else
echo "[ERROR] No ${prefix} sum calculated for ${d} !"
exit 1
fi
done
'''
}
// return false if URL is 404'd
def checkURL(String URL) {
def statusCode = sh(script: '''#!/bin/bash -xe
# check for 404 and fail if can't load the file
URL="''' + URL + '''"
header404="$(curl --insecure -sSLI "${URL}" | grep -E -v "id: |^x-" | grep -v "content-length" | grep -E "404|Not Found" || true)"
if [[ $header404 ]]; then
echo "[ERROR] Can not resolve $URL : $header404 "
exit 1
fi
exit 0
''', returnStatus: true)
return statusCode > 1 ? false : true
}
boolean hasSuccessfullyBuiltAllArches(String containerYamlPath, String jobOutput) {
int containerBuildCount = sh(script: '''#!/bin/bash -xe
yq -r ".platforms.only | length" ''' + containerYamlPath, returnStdout: true).trim()
echo "Expected number of container builds (arches in container.yaml): "+containerBuildCount
int containerSuccessCount = jobOutput.count("Build finished successfully! Pushing image to ")
echo "Successful builds detected: "+containerSuccessCount
// should get 1 per arch + 1 overall success, which is 1 more than list in container.yaml
if (containerSuccessCount > containerBuildCount) {
return true
} else {
return false
}
}
String prepareHTMLStringForJSON(String input) {
return input.replaceAll("<([a-z]+)/>","<\$1 />").replaceAll("\n","").replaceAll("/>","\\/>")
}
String buildResultBadge() {
def currentBuildResult = currentBuild.result?.trim().toString()
println("currentBuildResult = " + currentBuildResult)
def badge = currentBuildResult
switch(currentBuildResult) {
case ["SUCCESS", "", "null", null]:
badge = "![SUCCESS](https://img.shields.io/badge/Build-SUCCESS-brightgreen?style=plastic&logo=redhat)"
break
case "UNSTABLE":
badge = "![UNSTABLE](https://img.shields.io/badge/Build-UNSTABLE-yellow?style=plastic&logo=redhat)"
break
case "ABORTED":
badge = "![ABORTED](https://img.shields.io/badge/Build-ABORTED-lightgrey?style=plastic&logo=redhat)"
break
case "FAILURE":
badge = "![FAILURE](https://img.shields.io/badge/Build-FAILURE-red?style=plastic&logo=redhat)"
break
default:
badge = "![UNKNOWN](https://img.shields.io/badge/Build-UNKNOWN-blue?style=plastic&logo=redhat)"
break
}
return "[" + badge + "](" + currentBuild.absoluteUrl+")"
}
String defaultPullRequestCommentBuildDescription(String MIDSTM_BRANCH) {
return prepareHTMLStringForJSON(defaultPullRequestCommentHeader(MIDSTM_BRANCH) + \
buildResultBadge() + "<br /><blockquote>" + currentBuild.description + "</blockquote>")
}
String defaultPullRequestCommentHeader(String MIDSTM_BRANCH) {
return "Build [" + getDsVersion(MIDSTM_BRANCH) + "](https://github.com/redhat-developer/devspaces-images/tree/" + MIDSTM_BRANCH + "/) :: [" + \
currentBuild.absoluteUrl.replaceAll(".+/([^/]+/[0-9]+)/","\$1") + "](" + currentBuild.absoluteUrl+"): "
}
// formatted for submission via JSON
String defaultPullRequestComment (String MIDSTM_BRANCH) {
def comment = \
defaultPullRequestCommentHeader(MIDSTM_BRANCH) + \
"[Console](" + currentBuild.absoluteUrl + "console), " + \
"[Changes](" + currentBuild.absoluteUrl + "changes), " + \
"[Git Data](" + currentBuild.absoluteUrl + "git)"
return comment
}
// convenience methods to comment with build URL or description
String commentOnPullRequestBuildLinks(String ownerRepo, String SHA) {
return commentOnPullRequest(ownerRepo, SHA, defaultPullRequestComment(MIDSTM_BRANCH))
}
String commentOnPullRequestBuildLinks(String comments_url) {
return commentOnPullRequest(comments_url, defaultPullRequestComment(MIDSTM_BRANCH))
}
String commentOnPullRequestBuildDescription(String ownerRepo, String SHA) {
return commentOnPullRequest(ownerRepo, SHA, defaultPullRequestCommentBuildDescription(MIDSTM_BRANCH))
}
String commentOnPullRequestBuildDescription(String comments_url) {
return commentOnPullRequest(comments_url, defaultPullRequestCommentBuildDescription(MIDSTM_BRANCH))
}
// given a repo and commit SHA, compute PR comments_url like https://api.github.com/repos/redhat-developer/devspaces/issues/848/comments
// then publish a message to that URL
// return comments_url (with comment hash) so we can pass that to downstream jobs
String commentOnPullRequest(String ownerRepo, String SHA, String message) {
def comments_url = sh(script: '''#!/bin/bash -xe
export GITHUB_TOKEN=''' + GITHUB_TOKEN + ''' # echo "''' + GITHUB_TOKEN + '''"
ownerRepo="''' + ownerRepo + '''"
SHA="''' + SHA + '''"
# use gh to query a given repo for closed pulls for a given commitSHA; return the PR URL
curl -sSL -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" \\
"https://api.github.com/repos/${ownerRepo}/pulls?state=closed" | yq -r --arg SHA "$SHA" '.[]|select(.merge_commit_sha == $SHA or .head.sha == $SHA)|.comments_url'
''', returnStdout: true).trim()
return commentOnPullRequest(comments_url, message)
}
// given a PR comments_url like https://api.github.com/repos/redhat-developer/devspaces/issues/848/comments
// publish a message containing links to build console/changes, or the currentBuild.description
// return comments_url (with comment hash) so we can pass that to downstream jobs
String commentOnPullRequest(String comments_url, String message) {
// if url and message are set; otherwise return nullstring
if (comments_url?.trim() && message?.trim()) {
// convert html PR URL to API comment URL
comments_url = comments_url.replaceAll("https://github.com/","https://api.github.com/repos/")
comments_url = comments_url.replaceAll("/pull/([0-9]+)","/issues/\$1/comments")
// fix relative job/build URLs to be absolute
// TODO do we have to add suport for href="" and href='' ?
message=message.replaceAll("<a href=/","<a href=${JENKINS_URL}")
message=message.replaceAll("<a href=../","<a href="+currentBuild.absoluteUrl.replaceAll(".+/([^/]+/[0-9]+/*)",""))
return sh(script: '''#!/bin/bash -xe
export GITHUB_TOKEN=''' + GITHUB_TOKEN + ''' # echo "''' + GITHUB_TOKEN + '''"
message="''' + message + '''"
comments_url="''' + comments_url + '''"
# comment on the PR by URL: https://api.github.com/repos/redhat-developer/devspaces/issues/848/comments
curl -sSL -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" -X POST \\
-d '{"body": "'"${message}"'"}' "${comments_url}" | yq -r '.html_url'
''', returnStdout: true).trim()
}
return ""
}
// return this file's contents when loaded
return this