Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion JenkinsJobs/Releng/prepareNextDevCycle.jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pipeline {
utilities.setDryRun(params.DRY_RUN)
githubAPI = load "JenkinsJobs/shared/githubAPI.groovy"
githubAPI.setDryRun(params.DRY_RUN)
calendarAPI = load "JenkinsJobs/shared/calendarAPI.groovy"
calendarAPI.setDryRun(params.DRY_RUN)

echo "NEXT_RELEASE_VERSION: ${NEXT_RELEASE_VERSION}"
def nextVersion = utilities.matchPattern('NEXT_RELEASE_VERSION', "${NEXT_RELEASE_VERSION}", [ /(?<major>\d+)\.(?<minor>\d+)/ ])
Expand Down Expand Up @@ -360,7 +362,6 @@ pipeline {
- [ ] Review and submit the new N&N pages created for the [eclipse website](https://github.com/eclipse-platform/www.eclipse.org-eclipse/pulls) repository.
- [ ] Submit this PR.
Before the preparation PRs in all submodules have been submitted the build of this change cannot complete (this can still submit it before).
- [ ] Update build calendar for platform ${NEXT_RELEASE_VERSION} release cycle.

After this and all submodule PRs are submitted:
- [ ] Run the [`Create Jobs`](https://ci.eclipse.org/releng/job/CreateJobs/) seed job to create the Jenkins jobs of the new stream
Expand Down Expand Up @@ -411,13 +412,44 @@ pipeline {
}
}
}
stage('Create build calendar') {
environment {
GCLOULD_KEY_FILE = credentials('gclould-service-account-key') // Secret file
}
steps {
script {
calendarAPI.initialize(utilities)
def rc2 = utilities.parseDate("${RC2_DATE}")
def ga = utilities.parseDate("${GA_DATE}")
def eclipsePrefix = "Eclipse ${NEXT_RELEASE_VERSION}" //TODO: Or keep Eclipse Platform
// I-build events
def iBuildStart = java.time.LocalDate.now().plusDays(1).atTime(18, 0).atZone(java.time.ZoneId.of('America/Toronto'))
def iBuildUntil = rc2.minusDays(1) // exclusive
calendarAPI.createEvent("Eclipse ${NEXT_RELEASE_VERSION} I-build", iBuildStart, iBuildStart.plusHours(1),
[ calendarAPI.createRecurrenceRRule('DAILY', iBuildUntil) ])
// Release events
utilities.releaseEvents().each{ name, description ->
def date = utilities.parseDate(env["${name}_DATE"])
if (name.startsWith('RC')) {
calendarAPI.createEvent("${eclipsePrefix} ${name} Stabilization", date.minusDays(3), date.minusDays(1))
calendarAPI.createEvent("${eclipsePrefix} ${name} Sign-off", date.minusDays(1))
}
def title = eclipsePrefix + (name != 'GA' ? " ${name} Promotion" : " (${NEXT_SIMREL_NAME}) GA")
calendarAPI.createEvent(title, date, date)
}
calendarAPI.createEvent("${eclipsePrefix} Quiet period", rc2.plusDays(1), ga)
}
}
}
}
}

@groovy.transform.Field
def utilities = null
@groovy.transform.Field
def githubAPI = null
@groovy.transform.Field
def calendarAPI = null

// --- utility methods

Expand Down
115 changes: 115 additions & 0 deletions JenkinsJobs/shared/calendarAPI.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (c) 2026 Hannes Wellmann and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Hannes Wellmann - initial API and implementation
*******************************************************************************/

import java.time.LocalDate
import java.time.ZonedDateTime

@groovy.transform.Field
def boolean _CALENDAR_API_IS_DRY_RUN = true

def setDryRun(boolean isDryRun) {
_CALENDAR_API_IS_DRY_RUN = isDryRun
}

def initialize(Object utilities) {
def gcloudInstall = utilities.installDownloadableTool('gcloud', 'https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-cli-linux-x86_64.tar.gz')
env.GCLOUD = "${gcloudInstall}/bin/gcloud"
// https://docs.cloud.google.com/sdk/docs/configurations
env.CLOUDSDK_CONFIG = "${gcloudInstall}/../config"
// https://docs.cloud.google.com/sdk/docs/initializing
sh '''#!/bin/bash -xe
# Skip default initialization since this is a non-interactive session
$GCLOUD config set disable_prompts true
$GCLOUD auth activate-service-account --key-file=${GCLOULD_KEY_FILE}
'''
}

// Calendar events: https://developers.google.com/workspace/calendar/api/v3/reference/events#resource

def createEvent(String title, LocalDate start, LocalDate end = null) {
if (end == null) {
end = start.plusDays(1) // Assume one day event
}
def parameters = [summary: title, start: [date: formatDate(start)], end: [date: formatDate(end)] ]
insertEvent(parameters)
}

// Recurrence Rules: https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10

def createRecurrenceRRule(String frequency, LocalDate until) {
// See https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.5.3
return "RRULE:FREQ=${frequency.toUpperCase()};UNTIL=${java.time.format.DateTimeFormatter.BASIC_ISO_DATE.format(until)}"
}
// createRecurrenceExRule(), createRecurrenceRDate() and createRecurrenceExDate() currently not needed

def createEvent(String title, ZonedDateTime start, ZonedDateTime end, recurrenceRules = null) {
def parameters = [summary: title]
parameters.start = [dateTime: formatDateTime(start), timeZone: start.zone.id]
parameters.end = [dateTime: formatDateTime(end), timeZone: end.zone.id]
parameters.recurrence = recurrenceRules
insertEvent(parameters)
}

private String formatDate(LocalDate date) {
return java.time.format.DateTimeFormatter.ISO_LOCAL_DATE.format(date)
}

private String formatDateTime(ZonedDateTime dateTime) {
return java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dateTime)
}

// https://developers.google.com/workspace/calendar/api/v3/reference/events/insert
private void insertEvent(Map<String, Object> parameters) {
def response = queryGoogleCalendarAPI('-X POST', 'events', parameters)
if (response != null && !isConfirmedEvent(response)) {
error "Response contains errors:\n${response}"
}
}

private boolean isConfirmedEvent(Map response) {
return response.kind == 'calendar#event' && response.status == 'confirmed'
}

// See documentation of the Google Calendar API
// - https://developers.google.com/workspace/calendar/api/guides/overview

private Map<String, String> queryGoogleCalendarAPI(String method, String endpoint, Map<String, Object> parameters = null) {
def calendarId = 'prfk26fdmpru1mptlb06p0jh4s@group.calendar.google.com'
def query = """\
GC_API_TOKEN=\$(\$GCLOUD auth print-access-token --scopes=https://www.googleapis.com/auth/calendar)
curl -L ${method} \
'https://www.googleapis.com/calendar/v3/calendars/${calendarId}/${endpoint}' \
-H "Authorization: Bearer \${GC_API_TOKEN}" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json'
""".replace('\t','').trim()
if (parameters) {
def data = writeJSON(json: parameters, returnText: true)
data = data.replace("'", "'\\''") // Escape single quotes by '\''
query += " --data '${data}'"
}
if (_CALENDAR_API_IS_DRY_RUN && !method.isEmpty()) {
echo "Query (not send):\n${query}"
return null
}
echo "Execute:\n${query}"
// Ensure the commands are not printed to prevent exposure of (temporary credentials)
def response = sh(script: "#!/bin/bash -e\n${query}", returnStdout: true)
if (response == null || response.isEmpty()) {
error 'Response is null or empty. This commonly indicates: HTTP/1.1 500 Internal Server Error'
}
return readJSON(text: response)
}

return this
10 changes: 1 addition & 9 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ The actual steps to release
- The final release repo has to be contributed to SimRel before the currently active I-build repo is deleted (is done automatically after the release is published)

**Wednesday**
The release is scheduled for 15:00 UTC.
The release is scheduled for 12:00 UTC.

- #### **Make the Release Visible**
- Run the [Publish Promoted Build](https://ci.eclipse.org/releng/job/Releng/job/publishPromotedBuild/) job in Releng jenkins to make the promoted build visible on the download page.
Expand All @@ -118,14 +118,6 @@ The release is scheduled for 15:00 UTC.
- After RC2 is promoted/published run the [`Prepare Next Development Cycle`](https://ci.eclipse.org/releng/job/Releng/job/prepareNextDevCycle/) job
- Review the Pull-Requests created by it in this `eclipse.platform.releng.aggregator` repository and all its submodules and complete the listed tasks.

#### **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 [page](https://github.com/eclipse-simrel/.github/blob/main/wiki/SimRel/2026-06.md) with a more detailed schedule.
- List of people who can edit the calendar
- Alexander Kurtakov(@akurtakov)
- Gireesh Punathil
- Rahul Mohanan

#### **Update Splash Screen:**
- Future spash screens are kept in a subfolder of [eclipse.platform/platform/org.eclipse.platform](https://github.com/eclipse-platform/eclipse.platform/tree/master/platform/org.eclipse.platform/futureSplashScreens).
- NOTE: Splash screens are created 4 at a time, for 4 consequtive quarterly releases, so they need to be requested once a year before the 20XX-06 release (the cycle is 2022-06 -> 2023-03, etc). Create an issue in the [Eclipse Help Desk](https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/issues) similar to [Bug 575781](https://bugs.eclipse.org/bugs/show_bug.cgi?id=575781). It is customary to do this by the previous -09 (September) release so that there's plenty of time for discussion before the -06 (June) release is opened.
Expand Down
Loading