Skip to content

Commit 553a2cd

Browse files
committed
[RelEng] Unify identifier matching and input preparation in pipelines
1 parent 92acef8 commit 553a2cd

File tree

3 files changed

+72
-70
lines changed

3 files changed

+72
-70
lines changed

JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@ pipeline {
99
timestamps()
1010
timeout(time: 120, unit: 'MINUTES')
1111
buildDiscarder(logRotator(numToKeepStr:'5'))
12-
skipDefaultCheckout()
1312
}
1413
parameters {
15-
booleanParam(name: 'DRY_RUN', defaultValue: true, description: '''\
16-
If enabled, the final publication of all changes is skipped respectivly all changes at the download-server happen in a 'try-out' area at https://download.eclipse.org/eclipse/try-outs/.
17-
This area is cleaned on each dry-run and should be cleaned after a try-out session is completed.
18-
Useful for debugging and to very that the pipeline behaves as intended.
19-
''')
14+
booleanParam(name: 'DRY_RUN', defaultValue: true,
15+
description: 'If enabled, the final publication of all changes is skipped. Useful for debugging and to very that the pipeline behaves as intended.')
2016
string(name: 'NEXT_RELEASE_VERSION', trim: true, description: 'Version of the release to prepare, for example: 4.37')
2117
string(name: 'PREVIOUS_RELEASE_CANDIDATE_ID', trim: true, description: 'Id of the current release-candiate for the previous release. For example: S-4.36RC2-202505281830')
2218
string(name: 'NEXT_SIMREL_NAME', trim: true, description: 'The name of the Simultanious Release that is targeted by the prepared release. Value is in the format yyyy-mm, for example: 2026-03')
@@ -39,51 +35,40 @@ pipeline {
3935
steps {
4036
script {
4137
echo "DRY_RUN: ${DRY_RUN}"
38+
// Always load the script from the very same state this pipeline is loaded (to ensure consistency)
39+
utilities = load "JenkinsJobs/shared/utilities.groovy"
40+
utilities.setDryRun(params.DRY_RUN)
41+
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
42+
githubAPI.setDryRun(params.DRY_RUN)
43+
4244
echo "NEXT_RELEASE_VERSION: ${NEXT_RELEASE_VERSION}"
43-
def nextVersionMatcher = "${NEXT_RELEASE_VERSION}" =~ /(?<major>\d+)\.(?<minor>\d+)/
44-
if (!nextVersionMatcher.matches()) {
45-
error "Unexpected format for NEXT_RELEASE_VERSION: ${NEXT_RELEASE_VERSION}"
46-
}
47-
assignEnvVariable('NEXT_RELEASE_VERSION_MAJOR', nextVersionMatcher.group('major'))
48-
assignEnvVariable('NEXT_RELEASE_VERSION_MINOR', nextVersionMatcher.group('minor'))
49-
nextVersionMatcher = null // release matcher as it's not serializable
45+
def nextVersion = utilities.matchPattern('NEXT_RELEASE_VERSION', "${NEXT_RELEASE_VERSION}", [ /(?<major>\d+)\.(?<minor>\d+)/ ])
46+
assignEnvVariable('NEXT_RELEASE_VERSION_MAJOR', nextVersion.major)
47+
assignEnvVariable('NEXT_RELEASE_VERSION_MINOR', nextVersion.minor)
5048

5149
echo "PREVIOUS_RELEASE_CANDIDATE_ID: ${PREVIOUS_RELEASE_CANDIDATE_ID}"
5250
def buildPropertiesTxt = sh(script: "curl --fail https://download.eclipse.org/eclipse/downloads/drops4/${PREVIOUS_RELEASE_CANDIDATE_ID}/buildproperties.txt", returnStdout: true)
5351
def buildProperties = readProperties(text: buildPropertiesTxt)
5452
assignEnvVariable('IS_JAVA_RELEASE_IMMINENT', !buildProperties.NEXT_JAVA_RELEASE_DATE.replace('"','').isEmpty()) // Remove surrounding quotes
5553

56-
def previousIdMatcher = null
57-
if ((previousIdMatcher = "${PREVIOUS_RELEASE_CANDIDATE_ID}" =~ /(?<type>[SR])-(?<major>\d+)\.(?<minor>\d+)(\.(?<service>\d+))?(?<checkpoint>(M|RC)\d+[a-z]?)?-(?<date>\d{8})(?<time>\d{4})/).matches()) {
58-
def checkpoint = previousIdMatcher.group('checkpoint')
59-
assignEnvVariable('PREVIOUS_RELEASE_VERSION_MAJOR', previousIdMatcher.group('major'))
60-
assignEnvVariable('PREVIOUS_RELEASE_VERSION_MINOR', previousIdMatcher.group('minor'))
61-
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_TAG', "${PREVIOUS_RELEASE_VERSION_MAJOR}.${PREVIOUS_RELEASE_VERSION_MINOR}${checkpoint}")
62-
def previousServiceVersion = previousIdMatcher.group('service') ?: '0'
63-
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_GIT_TAG', "${previousIdMatcher.group('type')}${PREVIOUS_RELEASE_VERSION_MAJOR}_${PREVIOUS_RELEASE_VERSION_MINOR}${(checkpoint || previousServiceVersion != '0') ? ('_' + previousServiceVersion) : ''}${checkpoint ? ('_' + checkpoint) : ''}")
64-
65-
} else if ((previousIdMatcher = "${PREVIOUS_RELEASE_CANDIDATE_ID}" =~ /I(?<date>\d{8})-(?<time>\d{4})/).matches()) {
66-
assignEnvVariable('PREVIOUS_RELEASE_VERSION_MAJOR', buildProperties.STREAMMajor.replace('"','')) // Remove surrounding quotes
67-
assignEnvVariable('PREVIOUS_RELEASE_VERSION_MINOR', buildProperties.STREAMMinor.replace('"','')) // Remove surrounding quotes
54+
def previousRC = utilities.matchBuildIdentifier("${PREVIOUS_RELEASE_CANDIDATE_ID}", { iBuild ->
55+
iBuild.major = buildProperties.STREAMMajor.replace('"','') // Remove surrounding quotes
56+
iBuild.minor = buildProperties.STREAMMinor.replace('"','') // Remove surrounding quotes
6857
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_TAG', "${PREVIOUS_RELEASE_CANDIDATE_ID}")
6958
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_GIT_TAG', "${PREVIOUS_RELEASE_CANDIDATE_ID}")
70-
71-
} else {
72-
error "Unexpected format for PREVIOUS_RELEASE_CANDIDATE_ID: ${PREVIOUS_RELEASE_CANDIDATE_ID}"
73-
}
74-
assignEnvVariable('PREVIOUS_RELEASE_VERSION', "${PREVIOUS_RELEASE_VERSION_MAJOR}.${PREVIOUS_RELEASE_VERSION_MINOR}")
75-
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_I_BUILD', "I${previousIdMatcher.group('date')}-${previousIdMatcher.group('time')}")
76-
previousIdMatcher = null // release matcher as it's not serializable
59+
}, { sBuild ->
60+
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_TAG', "${sBuild.major}.${sBuild.minor}${sBuild.checkpoint}")
61+
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_GIT_TAG', utilities.stableBuildGitTag(sBuild))
62+
})
63+
assignEnvVariable('PREVIOUS_RELEASE_VERSION', "${previousRC.major}.${previousRC.minor}")
64+
assignEnvVariable('PREVIOUS_RELEASE_CANDIDATE_I_BUILD', "I${previousRC.date}-${previousRC.time}")
65+
assignEnvVariable('MAINTENANCE_BRANCH', "R${previousRC.major}_${previousRC.minor}_maintenance")
7766

7867
echo "NEXT_SIMREL_NAME: ${NEXT_SIMREL_NAME}"
79-
def simRelMatcher = "${NEXT_SIMREL_NAME}" =~ /(?<year>\d{4})-(?<month>\d{2})/
80-
if (!simRelMatcher.matches()) {
81-
error "Unexpected format for NEXT_SIMREL_NAME: ${NEXT_SIMREL_NAME}"
82-
}
83-
assignEnvVariable('NEXT_RELEASE_YEAR', simRelMatcher.group('year'))
84-
assignEnvVariable('NEXT_RELEASE_MONTH', simRelMatcher.group('month'))
68+
def simRel = utilities.matchPattern('NEXT_SIMREL_NAME', "${NEXT_SIMREL_NAME}", [ /(?<year>\d{4})-(?<month>\d{2})/ ])
69+
assignEnvVariable('NEXT_RELEASE_YEAR', simRel.year)
70+
assignEnvVariable('NEXT_RELEASE_MONTH', simRel.month)
8571
assignEnvVariable('NEXT_RELEASE_NAME', "${NEXT_SIMREL_NAME}")
86-
simRelMatcher = null // release matcher as it's not serializable
8772

8873
def simRelDatesRaw = sh(script: "curl --fail https://raw.githubusercontent.com/eclipse-simrel/.github/refs/heads/main/wiki/SimRel/${NEXT_SIMREL_NAME}_dates.json", returnStdout: true)
8974
def simRelDates = readJSON(text: simRelDatesRaw)
@@ -92,7 +77,6 @@ pipeline {
9277
assignEnvVariable("${name}_DATE", date)
9378
return [name, date]
9479
}
95-
assignEnvVariable('MAINTENANCE_BRANCH', "R${PREVIOUS_RELEASE_VERSION_MAJOR}_${PREVIOUS_RELEASE_VERSION_MINOR}_maintenance")
9680

9781
// Compute new build schedule
9882
def now = java.time.LocalDate.now()
@@ -120,15 +104,8 @@ pipeline {
120104
}
121105
}
122106
}
123-
stage('Checkout SCM') {
107+
stage('Checkout Submodules') {
124108
steps {
125-
checkout scm
126-
script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency)
127-
utilities = load "JenkinsJobs/shared/utilities.groovy"
128-
utilities.setDryRun(params.DRY_RUN)
129-
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
130-
githubAPI.setDryRun(params.DRY_RUN)
131-
}
132109
sh '''#!/bin/bash -xe
133110
git submodule update --init --recursive --remote
134111
git config --global user.email 'releng-bot@eclipse.org'

JenkinsJobs/Releng/promoteBuild.jenkinsfile

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ The second (deferred) step that makes things visible works, in part, based on so
99

1010
pipeline {
1111
options {
12-
skipDefaultCheckout()
12+
checkoutToSubdirectory('repository')
1313
timestamps()
1414
timeout(time: 120, unit: 'MINUTES')
1515
buildDiscarder(logRotator(numToKeepStr:'10'))
1616
}
1717
parameters {
18-
booleanParam(name: 'DRY_RUN', defaultValue: true,
19-
description: 'If enabled, the final publication of all changes is skipped. Useful for debugging and to very that the pipeline behaves as intended.')
18+
booleanParam(name: 'DRY_RUN', defaultValue: true, description: '''\
19+
If enabled, the final publication of all changes is skipped respectivly all changes at the download-server happen in a 'try-out' area at https://download.eclipse.org/eclipse/try-outs/.
20+
This area is cleaned on each dry-run and should be cleaned after a try-out session is completed.
21+
Useful for debugging and to very that the pipeline behaves as intended.
22+
''')
2023
string(name: 'DROP_ID', trim: true, description: '''\
2124
The name (or, build id) of the build to promote. Typically would be a value such as 'I20250714-1800'.
2225
It must match the name of the build on the build machine.
@@ -47,23 +50,28 @@ pipeline {
4750
steps {
4851
script {
4952
echo "DRY_RUN: ${DRY_RUN}"
53+
// Always load the script from the very same state this pipeline is loaded (to ensure consistency)
54+
dir('repository') {
55+
utilities = load "JenkinsJobs/shared/utilities.groovy"
56+
utilities.setDryRun(params.DRY_RUN)
57+
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
58+
githubAPI.setDryRun(params.DRY_RUN)
59+
}
5060
echo "DROP_ID: ${DROP_ID}"
5161
echo "CHECKPOINT: ${CHECKPOINT}"
5262
echo "SIGNOFF_ISSUE: ${SIGNOFF_ISSUE}"
53-
def idMatcher = null
54-
if ((idMatcher = "${DROP_ID}" =~ /I(?<date>\d{8})-(?<time>\d{4})/).matches()) {
63+
def build = utilities.matchBuildIdentifier("${DROP_ID}", { iBuild ->
5564
assignEnvVariable('BUILD_LABEL', "${DROP_ID}")
56-
} else if ((idMatcher = "${DROP_ID}" =~ /S-(?<label>\d+\.\d+(\.\d+)?(M|RC)\d+[a-z]?)-(?<date>\d{8})(?<time>\d{4})/).matches()) {
57-
assignEnvVariable('BUILD_LABEL', idMatcher.group('label'))
65+
}, { sBuild ->
66+
if (sBuild.type == 'R') {
67+
error "Releases cannot be promoted further: ${DROP_ID}"
68+
}
69+
assignEnvVariable('BUILD_LABEL', sBuild.label)
5870
if ("${CHECKPOINT}") {
5971
error "Stable build DROP_ID=${DROP_ID} may only be promoted to release CHECKPOINT, which therefore must be empty: ${CHECKPOINT}"
6072
}
61-
} else {
62-
error "DROP_ID, ${DROP_ID}, did not match any expected pattern."
63-
}
64-
def buildTimestamp = idMatcher.group('date') + idMatcher.group('time')
65-
assignEnvVariable('REPO_ID', "I${idMatcher.group('date')}-${idMatcher.group('time')}")
66-
idMatcher = null // release matcher as it's not serializable
73+
})
74+
assignEnvVariable('REPO_ID', "I${build.date}-${build.time}")
6775

6876
def buildPropertiesTxt = sh(script: "curl --fail https://download.eclipse.org/eclipse/downloads/drops4/${DROP_ID}/buildproperties.txt", returnStdout: true)
6977
def buildProperties = readProperties(text: buildPropertiesTxt)
@@ -84,7 +92,7 @@ pipeline {
8492
: "${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_SERVICE}${CHECKPOINT}"
8593
)
8694
// This is DL_DROP_ID for Eclipse and Equinox
87-
assignEnvVariable('DL_DROP_ID', "${DL_TYPE}-${DL_LABEL}-${buildTimestamp}")
95+
assignEnvVariable('DL_DROP_ID', "${DL_TYPE}-${DL_LABEL}-${build.date}${build.time}")
8896
assignEnvVariable('MAINTENANCE_BRANCH', "R${BUILD_MAJOR}_${BUILD_MINOR}_maintenance")
8997

9098
if ("${SIGNOFF_ISSUE}".isEmpty()) {
@@ -101,16 +109,9 @@ pipeline {
101109
}
102110
}
103111
}
104-
stage('Checkout SCM') {
112+
stage('Checkout Submodules') {
105113
steps {
106114
dir("${WORKSPACE}/repository") {
107-
checkout scm
108-
script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency)
109-
utilities = load "JenkinsJobs/shared/utilities.groovy"
110-
utilities.setDryRun(params.DRY_RUN)
111-
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
112-
githubAPI.setDryRun(params.DRY_RUN)
113-
}
114115
sh '''#!/bin/bash -xe
115116
git branch --force master HEAD
116117
if [[ "${DL_TYPE}" == 'R' ]]; then

JenkinsJobs/shared/utilities.groovy

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,30 @@ def setDryRun(boolean isDryRun) {
77
IS_DRY_RUN = isDryRun
88
}
99

10+
def Map<String, String> matchPattern(String stringName, String string, List<String> patterns, List<Closure> handlers = null) {
11+
for (int i = 0; i < patterns.size(); i++) {
12+
def matcher = (string =~ patterns[i])
13+
if (matcher.matches()) {
14+
def groups = matcher.pattern().namedGroups().keySet().collectEntries{ name -> [name, matcher.group(name)]}
15+
if (handlers != null) {
16+
handlers[i].call(groups)
17+
}
18+
return groups
19+
}
20+
}
21+
throw new Exception("${stringName}, ${string}, did not match expected pattern(s).")
22+
}
23+
24+
def matchBuildIdentifier(String dropID, Closure iBuildHandler, Closure sBuildHandler) {
25+
return matchPattern('dropID', dropID, [
26+
/(?<type>[I])(?<date>\d{8})-(?<time>\d{4})/,
27+
/(?<type>[SR])-(?<label>(?<major>\d+)\.(?<minor>\d+)(\.(?<service>\d+))?(?<checkpoint>(M|RC)\d+[a-z]?)?)-(?<date>\d{8})(?<time>\d{4})/,
28+
], [
29+
{ iBuild -> Objects.requireNonNull(iBuildHandler, "No handler for I-build id match: ${dropID}").call(iBuild)},
30+
{ sBuild -> Objects.requireNonNull(sBuildHandler, "No handler for S-build id match: ${dropID}").call(sBuild)},
31+
])
32+
}
33+
1034
// --- local file modifications ---
1135

1236
def replaceAllInFile(String filePath, Map<String,String> replacements) {

0 commit comments

Comments
 (0)