From 2f8a87398a2b9cee201f1d3114b6ecacf995097c Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Wed, 3 Sep 2025 22:55:17 +0200 Subject: [PATCH] [RelEng] Update build configurations with release in release promotion This automates the update of build configurations with the final GA release versions on the master and corresponding maintenance branch, by updating all versions or URLs that were pointing to a RC or specific I-build to point to the final GA release. On the maintenance branch also set a fixed version for ECJ. --- .../Releng/prepareNextDevCycle.jenkinsfile | 32 ++--- JenkinsJobs/Releng/promoteBuild.jenkinsfile | 113 +++++++++++++++++- JenkinsJobs/shared/githubAPI.groovy | 3 + JenkinsJobs/shared/utilities.groovy | 29 +++++ RELEASE.md | 20 +--- 5 files changed, 154 insertions(+), 43 deletions(-) create mode 100644 JenkinsJobs/shared/utilities.groovy diff --git a/JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile b/JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile index 44946a21b21..d4a517f913d 100644 --- a/JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile +++ b/JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile @@ -76,6 +76,7 @@ pipeline { steps { checkout scm script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency) + utilities = load "JenkinsJobs/shared/utilities.groovy" githubAPI = load "JenkinsJobs/shared/githubAPI.groovy" githubAPI.setDryRun(params.DRY_RUN) } @@ -154,7 +155,7 @@ pipeline { "(?# Schedule:.*\\R)(?s).*(?\\R'''\\))" : "\${prefix}${I_BUILD_SCHEDULE}\${suffix}", ]) - commitAllChangesExcludingSubmodules("Update versions to ${NEXT_RELEASE_VERSION} in build scripts") + utilities.commitAllChangesExcludingSubmodules("Update versions to ${NEXT_RELEASE_VERSION} in build scripts") } } stage('Move previous version to current RC') { @@ -182,7 +183,7 @@ pipeline { 'previousReleaseVersion=.*' : "previousReleaseVersion=${PREVIOUS_RELEASE_CANDIDATE_TAG}", 'previousReleaseVersionRepo=.*' : "previousReleaseVersionRepo=${PREVIOUS_RELEASE_VERSION}-I-builds", ]) - commitAllChangesExcludingSubmodules("Move previous version to ${PREVIOUS_RELEASE_CANDIDATE_TAG} in build scripts") + utilities.commitAllChangesExcludingSubmodules("Move previous version to ${PREVIOUS_RELEASE_CANDIDATE_TAG} in build scripts") } } stage('Clear Qualifier-update files') { @@ -242,7 +243,7 @@ pipeline { ]) sh "mv cje-production/streams/repositories_master.txt cje-production/streams/repositories_${MAINTENANCE_BRANCH}.txt" - commitAllChangesExcludingSubmodules("Move ${PREVIOUS_RELEASE_VERSION}-I builds to ${MAINTENANCE_BRANCH} branch") + utilities.commitAllChangesExcludingSubmodules("Move ${PREVIOUS_RELEASE_VERSION}-I builds to ${MAINTENANCE_BRANCH} branch") // Switch back to master for subsequent parts of this pipeline sh 'git checkout master' @@ -441,6 +442,8 @@ pipeline { } } +@groovy.transform.Field +def utilities = null @groovy.transform.Field def githubAPI = null @@ -466,28 +469,9 @@ def parseDate(String dateString) { } def replaceInFile(String filePath, Map replacements) { - replaceAllInFile(filePath, replacements.collectEntries{ k, v -> [java.util.regex.Pattern.quote(k), v] }); + utilities.replaceInFile(filePath, replacements) } def replaceAllInFile(String filePath, Map replacements) { - def content = readFile(filePath) - for (entry in replacements) { - def newContent = content.replaceAll(entry.key, entry.value) - if (newContent == content && !(content =~ entry.key)) { // pattern matches, but the replacement is equal to the current content - error("Pattern not found in file '${filePath}': ${entry.key}") - } - content = newContent - } - writeFile(file:filePath, text: content) -} - -def commitAllChangesExcludingSubmodules(String commitMessage) { - withEnv(["COMMIT_MESSAGE=${commitMessage}"]) { - sh ''' - #Commit all changes, except for the updated sub-modules here - git add --all - git restore --staged $(git submodule foreach --quiet 'echo $sm_path') - git commit --message "${COMMIT_MESSAGE}" - ''' - } + utilities.replaceAllInFile(filePath, replacements) } diff --git a/JenkinsJobs/Releng/promoteBuild.jenkinsfile b/JenkinsJobs/Releng/promoteBuild.jenkinsfile index eb04934ef42..2431e449168 100644 --- a/JenkinsJobs/Releng/promoteBuild.jenkinsfile +++ b/JenkinsJobs/Releng/promoteBuild.jenkinsfile @@ -67,6 +67,7 @@ pipeline { ) // This is DL_DROP_ID for Eclipse and Equinox assignEnvVariable('DL_DROP_ID', "${DL_TYPE}-${DL_LABEL}-${buildTimestamp}") + assignEnvVariable('MAINTENANCE_BRANCH', "R${BUILD_MAJOR}_${BUILD_MINOR}_maintenance") if (!env.SIGNOFF_BUG) { echo '''\ @@ -79,14 +80,20 @@ pipeline { } } } - stage('Checkout Git') { + stage('Checkout SCM') { steps { dir("${WORKSPACE}/repository") { checkout scm script { // Always load the script from the very same state this pipeline is loaded (to ensure consistency) + utilities = load "JenkinsJobs/shared/utilities.groovy" githubAPI = load "JenkinsJobs/shared/githubAPI.groovy" } sh '''#!/bin/bash -xe + git branch --force master HEAD + git fetch origin "refs/heads/${MAINTENANCE_BRANCH}" + git reflog show master + git reflog show "origin/${MAINTENANCE_BRANCH}" + git fetch origin tag "${REPO_ID}" git checkout "${REPO_ID}" # Check out all submodules at the specified REPO_ID @@ -187,6 +194,108 @@ pipeline { ] } } + stage('Update build configurations') { + when { + environment name: 'DL_TYPE', value: 'R' + } + environment { + RELEASE_P2_REPOSITORY = "https://download.eclipse.org/eclipse/updates/${BUILD_MAJOR}.${BUILD_MINOR}/${DL_DROP_ID}/" + } + steps { + dir("${WORKSPACE}/repository") { + script { // Update the master branch + sh 'git checkout master' + sh ''' + mvn -f eclipse-platform-parent/pom.xml org.eclipse.tycho:tycho-versions-plugin:set-property \ + -Dproperties=previous-release.baseline \ + -DnewPrevious-release.baseline="${RELEASE_P2_REPOSITORY}" + ''' + utilities.replaceAllInFile('cje-production/buildproperties.txt', [ + 'BASEBUILDER_TAG=".*?"' : "BASEBUILDER_TAG=\"${BUILD_MAJOR}.${BUILD_MINOR}\"", + 'API_PREV_REF_LABEL=".*?"' : "API_PREV_REF_LABEL=\"${BUILD_MAJOR}.${BUILD_MINOR}\"", + 'PREVIOUS_RELEASE_VER=".*?"' : "PREVIOUS_RELEASE_VER=\"${BUILD_MAJOR}.${BUILD_MINOR}\"", + 'PREVIOUS_RELEASE_REPO_ID=".*?"' : "PREVIOUS_RELEASE_REPO_ID=\"${BUILD_MAJOR}.${BUILD_MINOR}\"", + 'BASEBUILD_ID=".*?"' : "BASEBUILD_ID=\"${DL_DROP_ID}\"", + 'PREVIOUS_RELEASE_ID=".*?"' : "PREVIOUS_RELEASE_ID=\"${DL_DROP_ID}\"", + ]) + utilities.replaceAllInFile('eclipse.platform.releng.tychoeclipsebuilder/eclipse-junit-tests/src/main/resources/equinoxp2tests.properties', [ + 'eclipse-platform-\\d+.\\d+[^-]*?-' : "eclipse-platform-${BUILD_MAJOR}.${BUILD_MINOR}-", + 'org.eclipse.equinox.p2.tests.last.release.build.repo=.*' : "org.eclipse.equinox.p2.tests.last.release.build.repo=https://download.eclipse.org/equinox/drops/${DL_DROP_ID}/", + ]) + utilities.replaceAllInFile('eclipse.platform.releng.tychoeclipsebuilder/eclipse-junit-tests/src/main/resources/label.properties', [ + 'previousReleaseVersion=.*' : "previousReleaseVersion=${BUILD_MAJOR}.${BUILD_MINOR}", + ]) + utilities.replaceAllInFile('eclipse.platform.releng.tychoeclipsebuilder/eclipse-junit-tests/src/main/scripts/getPreviousRelease.sh', [ + 'R\\/R-\\d+.\\d+-\\d{12}' : "R/${DL_DROP_ID}", + '-\\d+.\\d+-linux-gtk-' : "-${BUILD_MAJOR}.${BUILD_MINOR}-linux-gtk-", + ]) + utilities.replaceAllInFile('production/testScripts/configuration/streamSpecific.properties', [ + 'previousReleaseLocation=.*' : 'previousReleaseLocation=https://\\${DOWNLOAD_HOST}/eclipse/downloads/drops4/' + DL_DROP_ID + '/', + 'previousReleaseVersion=.*' : "previousReleaseVersion=${BUILD_MAJOR}.${BUILD_MINOR}", + 'previousReleaseVersionRepo=.*' : "previousReleaseVersionRepo=${BUILD_MAJOR}.${BUILD_MINOR}", + ]) + + utilities.commitAllChangesExcludingSubmodules("Update previous release version to ${BUILD_MAJOR}.${BUILD_MINOR} GA across build scripts") + } + script { // Update the maintenance branch + sh 'git checkout -b updateMaintenance "origin/${MAINTENANCE_BRANCH}"' + def ecjManifest = sh(script: "curl https://raw.githubusercontent.com/eclipse-jdt/eclipse.jdt.core/refs/tags/${REPO_ID}/org.eclipse.jdt.core.compiler.batch/META-INF/MANIFEST.MF", returnStdout: true).trim() + def bundleVersion = readManifest(text: ecjManifest).main['Bundle-Version'] + def ecjVersion = bundleVersion.substring(0, bundleVersion.indexOf('.qualifier')) + sh """ + mvn -f eclipse-platform-parent/pom.xml org.eclipse.tycho:tycho-versions-plugin:set-property \ + -Dproperties=eclipse-sdk-repo,previous-release.baseline,cbi-ecj-version \ + -DnewEclipse-sdk-repo="${RELEASE_P2_REPOSITORY}" \ + -DnewPrevious-release.baseline="${RELEASE_P2_REPOSITORY}" \ + -DnewCbi-ecj-version="${ecjVersion}" + """ + utilities.replaceAllInFile('cje-production/buildproperties.txt', [ + 'ECLIPSE_RUN_REPO="https://download.eclipse.org/eclipse/updates/.*"' : "ECLIPSE_RUN_REPO=\"${RELEASE_P2_REPOSITORY}\"", + ]) + + utilities.commitAllChangesExcludingSubmodules("Update ${MAINTENANCE_BRANCH} branch with release version for ${BUILD_MAJOR}_${BUILD_MINOR}+ changes") + } + // Switch back to master for subsequent parts of this pipeline + sh 'git checkout master' + + // Display created commits + sh '''#!/bin/bash -xe + git log origin/master..master --patch-with-stat --summary + git log origin/${MAINTENANCE_BRANCH}..updateMaintenance --patch-with-stat --summary + ''' + sshagent(['github-bot-ssh']) { + sh '''#!/bin/bash -xe + pushURL=$(git config remote.origin.url) + # Switch to SSH, if the configured URL uses HTTPS (we can only push with SSH) + if [[ "$pushURL" == http* ]]; then + pushURL=$(echo $pushURL|sed --expression 's|https://github.com/|git@github.com:|') + fi + + git push ${pushURL} "master:refs/heads/update-build-to-R${BUILD_MAJOR}.${BUILD_MINOR}" + git push ${pushURL} "updateMaintenance:refs/heads/update-${MAINTENANCE_BRANCH}" + ''' + } + // Create PRs agains the master and maintenance branch + script { withEnv(['GITHUB_BOT_TOKEN=' + credentials('github-bot-token')]) { + def masterPR = githubAPI.createPullRequest('eclipse-platform/eclipse.platform.releng.aggregator', + "Update previous release version to ${BUILD_MAJOR}.${BUILD_MINOR} GA across build scripts", """\ + Update the the `${MAINTENANCE_BRANCH}` branch with final ${BUILD_MAJOR}.${BUILD_MINOR} release version. + + **This should not be submitted before ${BUILD_MAJOR}.${BUILD_MINOR} is finally released.** + """.stripIndent(),"update-build-to-R${BUILD_MAJOR}.${BUILD_MINOR}", 'master') + + githubAPI.createPullRequest('eclipse-platform/eclipse.platform.releng.aggregator', + "Update ${MAINTENANCE_BRANCH} branch with release version for ${BUILD_MAJOR}_${BUILD_MINOR}+ changes", """\ + Update the the `${MAINTENANCE_BRANCH}` branch with final ${BUILD_MAJOR}.${BUILD_MINOR} release version. + This complements: + - ${masterPR} + + **This should not be submitted before ${BUILD_MAJOR}.${BUILD_MINOR} is finally released.** + """.stripIndent(),"update-${MAINTENANCE_BRANCH}", "${MAINTENANCE_BRANCH}") + }} + } + } + } stage('Update acknowledgements') { environment { GITHUB_BOT_TOKEN = credentials('github-bot-token') @@ -292,6 +401,8 @@ EOF """ } +@groovy.transform.Field +def utilities = null @groovy.transform.Field def githubAPI = null diff --git a/JenkinsJobs/shared/githubAPI.groovy b/JenkinsJobs/shared/githubAPI.groovy index 591e824e65b..1246c4d608e 100644 --- a/JenkinsJobs/shared/githubAPI.groovy +++ b/JenkinsJobs/shared/githubAPI.groovy @@ -69,6 +69,9 @@ def queryGithubAPI(String method, String endpoint, Map queryPara query += "-d '" + params + "'" } if (IS_DRY_RUN && !method.isEmpty()) { + if (!env.GITHUB_BOT_TOKEN) { + error 'Required GITHUB_BOT_TOKEN not set' + } echo "Query (not send): ${query}" return null } diff --git a/JenkinsJobs/shared/utilities.groovy b/JenkinsJobs/shared/utilities.groovy new file mode 100644 index 00000000000..90c8f0109ad --- /dev/null +++ b/JenkinsJobs/shared/utilities.groovy @@ -0,0 +1,29 @@ + +def replaceInFile(String filePath, Map replacements) { + replaceAllInFile(filePath, replacements.collectEntries{ k, v -> [java.util.regex.Pattern.quote(k), v] }); +} + +def replaceAllInFile(String filePath, Map replacements) { + def content = readFile(filePath) + for (entry in replacements) { + def newContent = content.replaceAll(entry.key, entry.value) + if (newContent == content && !(content =~ entry.key)) { // pattern matches, but the replacement is equal to the current content + error("Pattern not found in file '${filePath}': ${entry.key}") + } + content = newContent + } + writeFile(file:filePath, text: content) +} + +def commitAllChangesExcludingSubmodules(String commitMessage) { + withEnv(["COMMIT_MESSAGE=${commitMessage}"]) { + sh ''' + #Commit all changes, except for the updated sub-modules here + git add --all + git restore --staged $(git submodule foreach --quiet 'echo $sm_path') + git commit --message "${COMMIT_MESSAGE}" + ''' + } +} + +return this diff --git a/RELEASE.md b/RELEASE.md index 9f9d5b392c6..1e040e7b29c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -93,6 +93,8 @@ The actual steps to release * #### **Promote to GA** - After Simrel declares RC2 (usually the Friday before release) run the [Promote Build](https://ci.eclipse.org/releng/job/Releng/job/promoteBuild/) job to promote RC2 (or RC2a). - `DROP_ID`: Final delease candidate's ID, e.g.: `S-4.36RC2-202505281830/` + - This will create pull requests to update the build configuration on the master and corresponding maintenance branch to the promoted release. + - Only submit them AFTER the release was finally published. - You can subscribe to [cross-project-issues](https://accounts.eclipse.org/mailing-list/cross-project-issues-dev) to get the notifications on Simrel releases. * **Contribute to SimRel** - If SimRel is not updated before the I-builds are cleaned up (specifically the build for RC2/GA) it will break. @@ -120,29 +122,11 @@ The release is scheduled for 10AM EST. Typically the jobs are scheduled beforeha - For the Y and P build parameters it's important to know whether or not Y and P builds were run during the release. Since they correspond to java releases on a 6 month cycle, typically they are built in odd-numbered releases. The existing builds are kept for one release, then cleaned up before the next stream that will have Y and P builds. it's convoluted and I dont want to type it out. Remove Y builds on even releases. - If something doesn't get cleaned up properly you can use Use the [list artifacts](https://ci.eclipse.org/releng/view/Cleanup/job/list_artifacts_from_download_server/) job to generate ta list of what's on the download server and either create a new job to clean it up or update and rerun the cleanup job as appropriate. - * **Set Previous Release to GA** - - Everything that was updated to RC2 (see below) should now use the released build. ### **Preparation for the next Release** After RC2 create an issue to track preparation work for the next stream (see [Preparation work for 4.25 (2022-09)](https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/284)). - A script to create this issue exists [here](scripts/newReleasePrep.sh) for those who have the hub cli tool installed. The process has been in flux recently so please update the script if necessary, but it provides a helpful template since most tasks in the previous release's issue become links. -#### **Maintenance Branches:** - * **Update maintenance branch with release version** - - Once the I-build repo is removed for the previous release the maintenance branch will have to use the release location, i.e. any references to `https://download.eclipse.org/eclipse/updates/4.25-I-builds/` will need to be updated to `https://download.eclipse.org/eclipse/updates/4.26/R-4.26-202211231800/` - - Functionally this means: - - Update the ECLIPSE_RUN_REPO in the [cje-production](cje-production) buildproperties.txt files - - Update `eclipse-sdk-repo` in [eclipse-platform-parent/pom.xml](eclipse-platform-parent/pom.xml) - - This step can be prepared ahead of time but can't be merged until the release build has been promoted and the update site exists. - * **Update ECJ compiler** in the platform build (if it needs to be updated). - * To find the new *unqualified* compiler version: - - Go to the update site for the release candidate - - Click `plugins` - - Find the *unqualified* version of the artifact `org.eclipse.jdt.core.complier.batch_${ecjversion}.jar`, i.e. the version consisting only of the _major_._minor_._service_ part, but without qualifier. - It's the version of the `org.eclipse.jdt:ecj` artifact at Maven-Central, about to be relased. - * Update the `cbi-ecj-version` in [eclipse.platform.releng.aggregator/eclipse-platform-parent/pom.xml](https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/blob/master/eclipse-platform-parent/pom.xml) - to the exact version of the to be released`org.eclipse.jdt.core.complier.batch` bundle, e.g.: `3.40.0` - #### **Update the Build Calendar:** - Create an [issue](https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/289) and update the [build calendar](https://calendar.google.com/calendar/u/0?cid=cHJmazI2ZmRtcHJ1MW1wdGxiMDZwMGpoNHNAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ) for the next GA release based on the [Simultaneous Release schedule](https://wiki.eclipse.org/Simultaneous_Release). - Each stream has its own [wiki](https://wiki.eclipse.org/Category:SimRel-2022-06) page with a more detailed schedule.