Skip to content

Commit b2e2717

Browse files
committed
[RelEng] Automate more tasks in promotion/publication jobs
- Trigger Maven-Central staging for release promotions - Update acknowledgements on each promotion (to have the list up-to-date even for Milestones, etc.) - Trigger publication immediatly for Milestone- and RC-promotions - Only defer for release promotion - Send mail on milestone/RC/release publication - And remove mail template generation from promotion job Extract code to interact with the GitHub-API into a dedicated Groovy-file, which is shared among all using jobs.
1 parent 304a9b7 commit b2e2717

File tree

7 files changed

+242
-159
lines changed

7 files changed

+242
-159
lines changed

JenkinsJobs/Releng/FOLDER.groovy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ It must match the name of the build on the build machine.
112112
''')
113113
stringParam('CHECKPOINT', null, 'M1, M3, RC1, RC2, RC3 etc (blank for final releases).')
114114
stringParam('SIGNOFF_BUG', null, 'The issue that was used to "signoff" the checkpoint. If there are no unit test failures, this can be left blank. Otherwise a link is added to test page explaining that "failing unit tests have been investigated".')
115-
stringParam('TRAIN_NAME', null, 'The name of the release stream, typically yyyy-mm. For example: 2022-09')
116115
}
117116
definition {
118117
cpsScm {

JenkinsJobs/Releng/deployToMaven.jenkinsfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,6 @@ pipeline {
226226

227227
def determineDeploymentType() {
228228
if ("${sourceRepository}".trim() =~ /\/(?<version>\d+\.\d+)\/R-\k<version>(\.\d+)?-\d{12}(\/)?$/) {
229-
input message: 'The specified P2 repository is a release and will be deployed to Maven-Central.', ok : 'Proceed deploying to Maven-Central'
230229
return 'release'
231230
} else {
232231
return 'snapshot'

JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ pipeline {
8181
stage('Checkout SCM') {
8282
steps {
8383
checkout scm
84+
script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency)
85+
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
86+
githubAPI.setDryRun(params.DRY_RUN)
87+
}
8488
sh '''
8589
git submodule update --init --recursive
8690
git config --global user.email '[email protected]'
@@ -240,7 +244,7 @@ pipeline {
240244
script {
241245
def prHeadline = "Prepare ${NEXT_RELEASE_VERSION} development"
242246
def prBranch = "prepare_R${NEXT_RELEASE_VERSION}"
243-
def aggregatorPreparationPR = createPullRequest('eclipse-platform/eclipse.platform.releng.aggregator', prHeadline, """\
247+
def aggregatorPreparationPR = githubAPI.createPullRequest('eclipse-platform/eclipse.platform.releng.aggregator', prHeadline, """\
244248
Prepare development of Eclipse ${NEXT_RELEASE_VERSION}.
245249
This includes:
246250
- Updating the version of the Maven parent, all references to it and the Eclipse products to `${NEXT_RELEASE_VERSION}`
@@ -263,7 +267,7 @@ pipeline {
263267
error "Unexpected of submodule URL: ${submoduleURL}"
264268
}
265269
def repoName = submoduleURL.substring(expectedPrefix.length(), submoduleURL.length() - expectedSuffix.length())
266-
createPullRequest(repoName, prHeadline, """\
270+
githubAPI.createPullRequest(repoName, prHeadline, """\
267271
Prepare development of Eclipse ${NEXT_RELEASE_VERSION}.
268272
This complements:
269273
- ${aggregatorPreparationPR}
@@ -280,7 +284,7 @@ pipeline {
280284
script {
281285
def organisations = [ 'eclipse-platform', 'eclipse-jdt', 'eclipse-pde', 'eclipse-equinox' ]
282286
for (organisation in organisations) {
283-
def repositories = listReposOfOrganization(organisation)
287+
def repositories = githubAPI.listReposOfOrganization(organisation)
284288
echo "${organisation} repositories: ${repositories.name}"
285289
for (repositoryData in repositories) {
286290
def repository = repositoryData.name
@@ -291,12 +295,12 @@ pipeline {
291295
echo "Skipping .eclipsefdn repository of : ${organisation}"
292296
continue
293297
}
294-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M1", "${NEXT_RELEASE_VERSION} Milestone 1", "${M1_DATE}")
295-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M2", "${NEXT_RELEASE_VERSION} Milestone 2", "${M2_DATE}")
296-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M3", "${NEXT_RELEASE_VERSION} Milestone 3", "${M3_DATE}")
297-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} RC1", "${NEXT_RELEASE_VERSION} Release Candidate 1", "${RC1_DATE}")
298-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} RC2", "${NEXT_RELEASE_VERSION} Release Candidate 2", "${RC2_DATE}")
299-
createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION}", "${NEXT_RELEASE_VERSION} Release", "${GA_DATE}")
298+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M1", "${NEXT_RELEASE_VERSION} Milestone 1", "${M1_DATE}")
299+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M2", "${NEXT_RELEASE_VERSION} Milestone 2", "${M2_DATE}")
300+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} M3", "${NEXT_RELEASE_VERSION} Milestone 3", "${M3_DATE}")
301+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} RC1", "${NEXT_RELEASE_VERSION} Release Candidate 1", "${RC1_DATE}")
302+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION} RC2", "${NEXT_RELEASE_VERSION} Release Candidate 2", "${RC2_DATE}")
303+
githubAPI.createMilestone(organisation, repository, "${NEXT_RELEASE_VERSION}", "${NEXT_RELEASE_VERSION} Release", "${GA_DATE}")
300304
}
301305
}
302306
}
@@ -305,6 +309,9 @@ pipeline {
305309
}
306310
}
307311

312+
@groovy.transform.Field
313+
def githubAPI = null
314+
308315
// --- utility methods
309316

310317
@NonCPS
@@ -334,68 +341,3 @@ def commitAllChangesExcludingSubmodules(String commitMessage) {
334341
'''
335342
}
336343
}
337-
338-
// Github API interactions
339-
340-
def listReposOfOrganization(String orga) {
341-
def response = queryGithubAPI('', "orgs/${orga}/repos", null)
342-
if (!(response instanceof List) && (response.errors || (response.status && response.status != 201))) {
343-
error "Response contains errors:\n${response}"
344-
}
345-
return response
346-
}
347-
348-
/**
349-
* Create a new milestone.
350-
* @param msDueDay the milestone's due-date, format: YYYY-MM-DD
351-
*/
352-
def createMilestone(String orga, String repo, String msTitle, String msDescription, String msDueDay) {
353-
echo "In ${orga}/${repo} create milestone: ${msTitle} due on ${msDueDay}"
354-
def params = [title: msTitle, description: msDescription, due_on: "${msDueDay}T23:59:59Z"]
355-
def response = queryGithubAPI('-X POST', "repos/${orga}/${repo}/milestones", params)
356-
if (response?.errors || (response?.status && response.status != 201)) {
357-
if (response.errors && response.errors[0]?.code == 'already_exists') {
358-
echo 'Milestone already exists and is not modified'
359-
// TODO: update milestone in this case: https://docs.github.com/en/rest/issues/milestones?apiVersion=2022-11-28#update-a-milestone
360-
// Usefull if e.g. the dates are wrongly read from the calendar
361-
} else {
362-
error "Response contains errors:\n${response}"
363-
}
364-
}
365-
}
366-
367-
/**
368-
* Create a PR in the specified repo, from a branch that is expected to reside in the same repository.
369-
*/
370-
def createPullRequest(String orgaSlashRepo, String prTitle, String prBody, String headBranch, String baseBranch = 'master') {
371-
echo "In ${orgaSlashRepo} create PR: '${prTitle}' on branch ${headBranch}"
372-
def params = [title: prTitle, body: prBody, head: headBranch, base: baseBranch]
373-
def response = queryGithubAPI('-X POST',"repos/${orgaSlashRepo}/pulls", params)
374-
if (response?.errors || (response?.status && response.status != 201)) {
375-
error "Response contains errors:\n${response}"
376-
}
377-
return response?.html_url
378-
}
379-
380-
def queryGithubAPI(String method, String endpoint, Map<String, String> queryParameters) {
381-
def query = """\
382-
curl -L ${method} \
383-
-H "Accept: application/vnd.github+json" \
384-
-H "Authorization: Bearer \${GITHUB_BOT_TOKEN}" \
385-
-H "X-GitHub-Api-Version: 2022-11-28" \
386-
https://api.github.com/${endpoint} \
387-
""".stripIndent()
388-
if (queryParameters != null) {
389-
def params = writeJSON(json: queryParameters, returnText: true)
390-
query += "-d '" + params + "'"
391-
}
392-
if (params.DRY_RUN && !method.isEmpty()) {
393-
echo "Query (not send): ${query}"
394-
return null
395-
}
396-
def response = sh(script: query, returnStdout: true)
397-
if (response == null || response.isEmpty()) {
398-
error 'Response is null or empty. This commonly indicates: HTTP/1.1 500 Internal Server Error'
399-
}
400-
return readJSON(text: response)
401-
}

JenkinsJobs/Releng/promoteBuild.jenkinsfile

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ pipeline {
2727
}
2828
env.CHECKPOINT = readParameter('CHECKPOINT')
2929
env.SIGNOFF_BUG = readParameter('SIGNOFF_BUG')
30-
env.TRAIN_NAME = readParameter('TRAIN_NAME')
31-
//TODO: record the train in buildproperties.shsource and read it here
32-
if (!"${TRAIN_NAME}") {
33-
error("Parameter 'TRAIN_NAME' is not specified")
34-
}
3530
def idMatcher = null
3631
if ((idMatcher = env.DROP_ID =~ /I(?<date>\d{8})-(?<time>\d{4})/).matches()) {
3732
assignEnvVariable('BUILD_LABEL', "${DROP_ID}")
@@ -58,14 +53,10 @@ pipeline {
5853
assignEnvVariable('BUILD_SERVICE', versionMatcher.group('service'))
5954
versionMatcher = null // release matcher as it's not serializable
6055

61-
assignEnvVariable('BUILD_REPO_ORIGINAL', "${BUILD_MAJOR}.${BUILD_MINOR}-I-builds")
62-
6356
if ("${CHECKPOINT}" ==~ /M\d+([a-z])?/ || "${CHECKPOINT}" ==~ /RC\d+([a-z])?/) { // milestone or RC promotion
6457
assignEnvVariable('DL_TYPE', 'S')
65-
// REPO_SITE_SEGMENT variale not used in this case
6658
} else if (!"${CHECKPOINT}") { // release promotion
6759
assignEnvVariable('DL_TYPE', 'R')
68-
assignEnvVariable('REPO_SITE_SEGMENT', "${BUILD_MAJOR}.${BUILD_MINOR}")
6960
} else {
7061
error "CHECKPOINT, ${CHECKPOINT}, did not match any expected pattern."
7162
}
@@ -93,6 +84,9 @@ pipeline {
9384
steps {
9485
dir("${WORKSPACE}/repository") {
9586
checkout scm
87+
script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency)
88+
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
89+
}
9690
sh '''#!/bin/bash -xe
9791
git fetch origin tag "${REPO_ID}"
9892
git checkout "${REPO_ID}"
@@ -107,26 +101,6 @@ pipeline {
107101
}
108102
stage('Move and rename Pages') {
109103
steps {
110-
writeFile(file: "${WORKSPACE}/stage2output${TRAIN_NAME}${CHECKPOINT}/mailtemplate.txt", text: """\
111-
We are pleased to announce that ${TRAIN_NAME} ${CHECKPOINT} is available for download and updates.
112-
113-
Eclipse downloads:
114-
https://download.eclipse.org/eclipse/downloads/drops4/${DL_DROP_ID}/
115-
116-
New and Noteworthy:
117-
https://www.eclipse.org/eclipse/news/${BUILD_MAJOR}.${BUILD_MINOR}/
118-
119-
Update existing (non-production) installs:
120-
https://download.eclipse.org/eclipse/updates/${DL_TYPE == 'R' ? REPO_SITE_SEGMENT : BUILD_REPO_ORIGINAL}/
121-
122-
Specific repository good for building against:
123-
https://download.eclipse.org/eclipse/updates/${DL_TYPE == 'R' ? (REPO_SITE_SEGMENT + '/' + DL_DROP_ID) : (BUILD_REPO_ORIGINAL + '/' + DROP_ID)}/
124-
125-
Equinox specific downloads:
126-
https://download.eclipse.org/equinox/drops/${DL_DROP_ID}/
127-
128-
Thank you to everyone who made this checkpoint possible.
129-
""".stripIndent())
130104
sshagent(['projects-storage.eclipse.org-bot-ssh']) {
131105

132106
echo 'Promote Equinox'
@@ -186,8 +160,8 @@ pipeline {
186160
dir("${WORKSPACE}/updates") {
187161
sshagent(['projects-storage.eclipse.org-bot-ssh']) {
188162
sh '''#!/bin/bash -xe
189-
sourceRepo="eclipse/updates/${BUILD_REPO_ORIGINAL}/${REPO_ID}"
190-
targetRepo="eclipse/updates/${REPO_SITE_SEGMENT}/${DL_DROP_ID}"
163+
sourceRepo="eclipse/updates/${BUILD_MAJOR}.${BUILD_MINOR}-I-builds/${REPO_ID}"
164+
targetRepo="eclipse/updates/${BUILD_MAJOR}.${BUILD_MINOR}/${DL_DROP_ID}"
191165
ssh [email protected] cp -r "${EP_ROOT}/${sourceRepo}/." "${EP_ROOT}/${targetRepo}"
192166

193167
MIRRORS_URL="https://www.eclipse.org/downloads/download.php?file=/${targetRepo}"
@@ -204,6 +178,38 @@ pipeline {
204178
}
205179
}
206180
}
181+
stage('Stage for Maven-Central') {
182+
when {
183+
environment name: 'DL_TYPE', value: 'R'
184+
}
185+
steps {
186+
build job: 'Releng/deployToMaven', wait: true, propagate: true, parameters: [
187+
string(name: 'sourceRepository', value: "https://download.eclipse.org/eclipse/updates/${BUILD_MAJOR}.${BUILD_MINOR}/${DL_DROP_ID}/")
188+
]
189+
}
190+
}
191+
stage('Update acknowledgements') {
192+
environment {
193+
GITHUB_BOT_TOKEN = credentials('github-bot-token')
194+
}
195+
steps {
196+
script {
197+
githubAPI.triggerWorkflow('eclipse-platform/www.eclipse.org-eclipse', 'generateAcknowledgements.yml', [ 'eclipse-version': "${BUILD_MAJOR}.${BUILD_MINOR}" ])
198+
}
199+
}
200+
}
201+
stage('Trigger publication') {
202+
when {
203+
not {
204+
environment name: 'DL_TYPE', value: 'R'
205+
}
206+
}
207+
steps {
208+
build job: 'Releng/publishPromotedBuild', wait: true, propagate: true, parameters: [
209+
string(name: 'releaseBuildID', value: "${DL_DROP_ID}")
210+
]
211+
}
212+
}
207213
}
208214
post {
209215
always {
@@ -212,22 +218,6 @@ pipeline {
212218
}
213219
}
214220

215-
@NonCPS
216-
def assignEnvVariable(String name, String value) {
217-
env."${name}" = value
218-
println("${name}=${value}")
219-
}
220-
221-
@NonCPS
222-
def readParameter(String name) {
223-
//TODO: let jenkins trim the parameters
224-
def value = (params[name] ?: '').trim()
225-
if (value) {
226-
println("${name}: ${value}")
227-
}
228-
return value
229-
}
230-
231221
def renameBuildDrop(String baseDropPath, String oldDropID, String oldBuildLabel, String newDropID, String newBuildLabel, Closure extraTasks=null) {
232222
def sourcePath="${EP_ROOT}/${baseDropPath}/${oldDropID}"
233223
def targetPath="${EP_ROOT}/${baseDropPath}/${newDropID}"
@@ -302,3 +292,20 @@ EOF
302292
fi
303293
"""
304294
}
295+
296+
@groovy.transform.Field
297+
def githubAPI = null
298+
299+
@NonCPS
300+
def readParameter(String name) {
301+
//TODO: let jenkins trim the parameters
302+
def value = (params[name] ?: '').trim()
303+
println("${name}: ${value}")
304+
return value
305+
}
306+
307+
@NonCPS
308+
def assignEnvVariable(String name, String value) {
309+
env."${name}" = value
310+
println("${name}=${value}")
311+
}

0 commit comments

Comments
 (0)