From 11a81d1df16cc55c26739051ee137c03bab3dc8b Mon Sep 17 00:00:00 2001 From: Robert Lin Date: Wed, 20 Jan 2021 20:59:50 +0800 Subject: [PATCH] release: remove xDaysBeforeRelease, unify issue and timelines (#17401) * Remove `xDaysBeforeRelease` and associated features (calendar events, template args, etc) * Unify generation of issues and calendar events, as well as things like release naming. Previously this was rather brittle to change and had a lot of code duplication * Other improvements: * `tracking:issues` now handles _all_ tracking issue generation * Calendar dry run * Backlinks to "parent" issue for child issues related to a release * Update patch request issue --- .../ISSUE_TEMPLATE/request_patch_release.md | 4 +- dev/release/release-config.jsonc | 8 +- dev/release/src/config.ts | 6 +- dev/release/src/github.ts | 305 +++++++++--------- dev/release/src/google-calendar.ts | 8 + dev/release/src/release.ts | 194 +++-------- dev/release/src/slack.ts | 4 + 7 files changed, 214 insertions(+), 315 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/request_patch_release.md b/.github/ISSUE_TEMPLATE/request_patch_release.md index 39846f17a3dd7..661572e771440 100644 --- a/.github/ISSUE_TEMPLATE/request_patch_release.md +++ b/.github/ISSUE_TEMPLATE/request_patch_release.md @@ -43,8 +43,8 @@ I have read [when and why we perform patch releases](https://about.sourcegraph.c - If there is [already an upcoming patch release](https://github.com/sourcegraph/sourcegraph/issues?q=is%3Aissue+label%3Arelease-tracking+), add the listed commits alongside a link to this issue - If there is no upcoming patch release, create a new one: - - Update [`dev/release/release-config.jsonc`](https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/dev/release/release-config.jsonc) with the patch release in `upcomingRelease` (and open a PR to `main` to update it) - - `cd dev/release && yarn build && yarn run release tracking:patch-issue` + - Update [`dev/release/release-config.jsonc`](https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/dev/release/release-config.jsonc) with the patch release in `upcomingRelease` and `releaseDate` (and open a PR to `main` to update it) + - `yarn release tracking:issues` - Add the listed commits alongside a link to this issue to the generated [release tracking issue](https://github.com/sourcegraph/sourcegraph/issues?q=is%3Aissue+label%3Arelease-tracking+) Comment and close this issue once the relevant commit(s) have been cherry-picked into the release branch. diff --git a/dev/release/release-config.jsonc b/dev/release/release-config.jsonc index a382424fa9fc5..c5cee44e752b9 100644 --- a/dev/release/release-config.jsonc +++ b/dev/release/release-config.jsonc @@ -18,11 +18,8 @@ "upcomingRelease": "3.24.0", // Release dates + "releaseDate": "20 January 2021 10:00 PST", "oneWorkingDayAfterRelease": "21 January 2021 10:00 PST", - "releaseDateTime": "20 January 2021 10:00 PST", - "oneWorkingDayBeforeRelease": "19 January 2021 10:00 PST", - "fourWorkingDaysBeforeRelease": "15 January 2021 10:00 PST", - "fiveWorkingDaysBeforeRelease": "14 January 2021 10:00 PST", // Channel where messages from the tooling are posted "slackAnnounceChannel": "dev-announce", @@ -37,6 +34,7 @@ "dryRun": { "tags": false, "changesets": false, - "trackingIssues": false + "trackingIssues": false, + "calendar": false } } diff --git a/dev/release/src/config.ts b/dev/release/src/config.ts index 50e760a50ead0..a747f32e410dc 100644 --- a/dev/release/src/config.ts +++ b/dev/release/src/config.ts @@ -15,10 +15,7 @@ export interface Config { previousRelease: string upcomingRelease: string - releaseDateTime: string - oneWorkingDayBeforeRelease: string - fourWorkingDaysBeforeRelease: string - fiveWorkingDaysBeforeRelease: string + releaseDate: string oneWorkingDayAfterRelease: string slackAnnounceChannel: string @@ -27,6 +24,7 @@ export interface Config { tags?: boolean changesets?: boolean trackingIssues?: boolean + calendar?: boolean } } diff --git a/dev/release/src/github.ts b/dev/release/src/github.ts index ccdf382290dea..fb98ddb8167f8 100644 --- a/dev/release/src/github.ts +++ b/dev/release/src/github.ts @@ -18,163 +18,142 @@ export async function getAuthenticatedGitHubClient(): Promise { return new Octokit({ auth: trimmedGithubPAT }) } -function dateMarkdown(date: Date, name: string): string { - return `[${formatDate(date)}](${timezoneLink(date, name)})` +/** + * releaseName generates a standardized format for referring to releases. + */ +export function releaseName(release: semver.SemVer): string { + return `${release.major}.${release.minor}${release.patch !== 0 ? `.${release.patch}` : ''}` } -// Ensure these templates are up to date with the state of the tooling and release processes. -const templates = { - releaseIssue: { - owner: 'sourcegraph', - repo: 'about', - path: 'handbook/engineering/releases/release_issue_template.md', - }, - patchReleaseIssue: { - owner: 'sourcegraph', - repo: 'about', - path: 'handbook/engineering/releases/patch_release_issue_template.md', - }, - upgradeMangedInstanceIssue: { - owner: 'sourcegraph', - repo: 'about', - path: 'handbook/engineering/releases/upgrade_managed_issue_template.md', - }, +// https://github.com/sourcegraph/sourcegraph/labels/release-tracking +const labelReleaseTracking = 'release-tracking' + +/** + * Template used to generate tracking issue + */ +interface IssueTemplate { + owner: string + repo: string + /** + * Relative path to markdown file containing template body. + * + * Template bodies can leverage arguments as described in `IssueTemplateArguments` docstrings. + */ + path: string + /** + * Title for issue. + */ + title: (v: semver.SemVer) => string + /** + * Labels to apply on issues. + */ + labels: string[] } /** - * Ensures a release ($MAJOR.$MINOR) tracking issue has been created with the given - * parameters using `templates.releaseIssue`. + * Arguments available for rendering IssueTemplate */ -export async function ensureReleaseTrackingIssue({ - version, - assignees, - releaseDateTime, - oneWorkingDayBeforeRelease, - fourWorkingDaysBeforeRelease, - fiveWorkingDaysBeforeRelease, - dryRun, -}: { +interface IssueTemplateArguments { + /** + * Available as `$MAJOR`, `$MINOR`, and `$PATCH` + */ version: semver.SemVer - assignees: string[] - releaseDateTime: Date - oneWorkingDayBeforeRelease: Date + /** + * Available as `$RELEASE_DATE` + */ + releaseDate: Date + /** + * Available as `$ONE_WORKING_DAY_AFTER_RELEASE` + */ oneWorkingDayAfterRelease: Date - fourWorkingDaysBeforeRelease: Date - fiveWorkingDaysBeforeRelease: Date - dryRun: boolean -}): Promise<{ url: string; created: boolean }> { - const octokit = await getAuthenticatedGitHubClient() - console.log(`Preparing issue from ${JSON.stringify(templates.releaseIssue)}`) - const releaseIssueTemplate = await getContent(octokit, templates.releaseIssue) - const majorMinor = `${version.major}.${version.minor}` - const releaseIssueBody = releaseIssueTemplate +} + +async function execTemplate( + octokit: Octokit, + template: IssueTemplate, + { version, releaseDate, oneWorkingDayAfterRelease }: IssueTemplateArguments +): Promise { + console.log(`Preparing issue from ${JSON.stringify(template)}`) + const name = releaseName(version) + const content = await getContent(octokit, template) + return content .replace(/\$MAJOR/g, version.major.toString()) .replace(/\$MINOR/g, version.minor.toString()) .replace(/\$PATCH/g, version.patch.toString()) - .replace(/\$RELEASE_DATE/g, dateMarkdown(releaseDateTime, `${majorMinor} release date`)) - .replace( - /\$FIVE_WORKING_DAYS_BEFORE_RELEASE/g, - dateMarkdown(fiveWorkingDaysBeforeRelease, `Five working days before ${majorMinor} release`) - ) + .replace(/\$RELEASE_DATE/g, dateMarkdown(releaseDate, `${name} release date`)) .replace( - /\$FOUR_WORKING_DAYS_BEFORE_RELEASE/g, - dateMarkdown(fourWorkingDaysBeforeRelease, `Four working days before ${majorMinor} release`) - ) - .replace( - /\$ONE_WORKING_DAY_BEFORE_RELEASE/g, - dateMarkdown(oneWorkingDayBeforeRelease, `One working day before ${majorMinor} release`) - ) - - // Release milestones are not as emphasised now as they used to be, since most teams - // use sprints shorter than releases to track their work. For reference, if one is - // available we apply it to this tracking issue, otherwise just leave it without a - // milestone. - let milestoneNumber: number | undefined - const milestone = await getReleaseMilestone(octokit, version) - if (!milestone) { - console.log( - `Milestone ${JSON.stringify(releaseMilestoneName(version))} is closed or not found — omitting from issue.` + /\$ONE_WORKING_DAY_AFTER_RELEASE/g, + dateMarkdown(oneWorkingDayAfterRelease, `One working day after ${name} release`) ) - } else { - milestoneNumber = milestone ? milestone.number : undefined - } - - return ensureIssue( - octokit, - { - title: trackingIssueTitle(version), - owner: 'sourcegraph', - repo: 'sourcegraph', - assignees, - body: releaseIssueBody, - milestone: milestoneNumber, - labels: ['release-tracking'], - }, - dryRun - ) } /** - * Ensures a patch release ($MAJOR.$MINOR.PATCH) tracking issue has been created with the - * given parameters using `templates.releaseIssue`. + * Configure templates for the release tool to generate issues with. + * + * Ensure these templates are up to date with the state of the tooling and release processes. */ -export async function ensurePatchReleaseIssue({ - version, - assignees, - dryRun, -}: { - version: semver.SemVer - assignees: string[] - dryRun: boolean -}): Promise<{ url: string; created: boolean }> { - const octokit = await getAuthenticatedGitHubClient() - console.log(`Preparing issue from ${JSON.stringify(templates.patchReleaseIssue)}`) - const issueTemplate = await getContent(octokit, templates.patchReleaseIssue) - const issueBody = issueTemplate - .replace(/\$MAJOR/g, version.major.toString()) - .replace(/\$MINOR/g, version.minor.toString()) - .replace(/\$PATCH/g, version.patch.toString()) - return ensureIssue( - octokit, - { - title: trackingIssueTitle(version), - owner: 'sourcegraph', - repo: 'sourcegraph', - assignees, - body: issueBody, - labels: ['release-tracking'], - }, - dryRun - ) +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +const getTemplates = () => { + const releaseIssue: IssueTemplate = { + owner: 'sourcegraph', + repo: 'about', + path: 'handbook/engineering/releases/release_issue_template.md', + title: trackingIssueTitle, + labels: [labelReleaseTracking], + } + const patchReleaseIssue: IssueTemplate = { + owner: 'sourcegraph', + repo: 'about', + path: 'handbook/engineering/releases/patch_release_issue_template.md', + title: trackingIssueTitle, + labels: [labelReleaseTracking], + } + const upgradeManagedInstanceIssue: IssueTemplate = { + owner: 'sourcegraph', + repo: 'about', + path: 'handbook/engineering/releases/upgrade_managed_issue_template.md', + title: (version: semver.SemVer) => `${version.version} upgrade managed instances tracking issue`, + labels: [labelReleaseTracking, 'managed-instances'], + } + return { releaseIssue, patchReleaseIssue, upgradeManagedInstanceIssue } +} + +interface MaybeIssue { + title: string + url: string + created: boolean } /** - * Ensures a upgrade managed instances to ($MAJOR.$MINOR) tracking issue has been created with the given - * parameters using `templates.upgradeManagedInstanceIssue`. + * Ensures tracking issues for the given release. + * + * The first returned issue is considered the parent issue. */ -export async function ensureUpgradeManagedTrackingIssue({ +export async function ensureTrackingIssues({ version, assignees, + releaseDate, oneWorkingDayAfterRelease, dryRun, }: { version: semver.SemVer assignees: string[] + releaseDate: Date oneWorkingDayAfterRelease: Date dryRun: boolean -}): Promise<{ url: string; created: boolean }> { +}): Promise { const octokit = await getAuthenticatedGitHubClient() - console.log(`Preparing issue from ${JSON.stringify(templates.upgradeMangedInstanceIssue)}`) - const issueTemplate = await getContent(octokit, templates.upgradeMangedInstanceIssue) - const majorMinor = `${version.major}.${version.minor}` - const issueBody = issueTemplate - .replace(/\$MAJOR/g, version.major.toString()) - .replace(/\$MINOR/g, version.minor.toString()) - .replace(/\$PATCH/g, version.patch.toString()) - .replace( - /\$ONE_WORKING_DAY_AFTER_RELEASE/g, - dateMarkdown(oneWorkingDayAfterRelease, `One working day before ${majorMinor} release`) - ) + const templates = getTemplates() + const release = releaseName(version) + + // Determine what issues to generate. The first issue is considered the "main" + // tracking issue, and subsequent issues will contain references to it. + let issueTemplates: IssueTemplate[] + if (version.patch === 0) { + issueTemplates = [templates.releaseIssue, templates.upgradeManagedInstanceIssue] + } else { + issueTemplates = [templates.patchReleaseIssue, templates.upgradeManagedInstanceIssue] + } // Release milestones are not as emphasised now as they used to be, since most teams // use sprints shorter than releases to track their work. For reference, if one is @@ -183,26 +162,40 @@ export async function ensureUpgradeManagedTrackingIssue({ let milestoneNumber: number | undefined const milestone = await getReleaseMilestone(octokit, version) if (!milestone) { - console.log( - `Milestone ${JSON.stringify(releaseMilestoneName(version))} is closed or not found — omitting from issue.` - ) + console.log(`Milestone ${release} is closed or not found — omitting from issue.`) } else { milestoneNumber = milestone ? milestone.number : undefined } - return ensureIssue( - octokit, - { - title: managedIssueTrackingTitle(version), - owner: 'sourcegraph', - repo: 'sourcegraph', - assignees, - body: issueBody, - milestone: milestoneNumber, - labels: ['release-tracking'], - }, - dryRun - ) + // Create issues + let parentIssue: MaybeIssue | undefined + const created: MaybeIssue[] = [] + for (const template of issueTemplates) { + const body = await execTemplate(octokit, template, { + version, + releaseDate, + oneWorkingDayAfterRelease, + }) + const issue = await ensureIssue( + octokit, + { + title: template.title(version), + labels: template.labels, + body: parentIssue ? `${body}\n\n---\n\nAlso see [${parentIssue.title}](${parentIssue.url})` : body, + assignees, + owner: 'sourcegraph', + repo: 'sourcegraph', + milestone: milestoneNumber, + }, + dryRun + ) + // if this is the first issue, we treat it as the parent issue + if (!parentIssue) { + parentIssue = { ...issue } + } + created.push({ ...issue }) + } + return created } async function getContent( @@ -240,7 +233,7 @@ async function ensureIssue( labels?: string[] }, dryRun: boolean -): Promise<{ url: string; created: boolean }> { +): Promise { const issueData = { title, owner, @@ -249,18 +242,18 @@ async function ensureIssue( milestone, labels, } + const issue = await getIssueByTitle(octokit, title) + if (issue) { + return { title, url: issue.url, created: false } + } if (dryRun) { console.log('Dry run enabled, skipping issue creation') console.log(`Issue that would have been created:\n${JSON.stringify(issueData, null, 1)}`) console.log(`With body: ${body}`) - return { url: '', created: false } - } - const issue = await getIssueByTitle(octokit, title) - if (issue) { - return { url: issue.url, created: false } + return { title, url: '', created: false } } const createdIssue = await octokit.issues.create({ body, ...issueData }) - return { url: createdIssue.data.html_url, created: true } + return { title, url: createdIssue.data.html_url, created: true } } export async function listIssues( @@ -283,10 +276,6 @@ export async function getTrackingIssue(client: Octokit, release: semver.SemVer): return getIssueByTitle(client, trackingIssueTitle(release)) } -function releaseMilestoneName(release: semver.SemVer): string { - return `${release.major}.${release.minor}${release.patch !== 0 ? `.${release.patch}` : ''}` -} - interface Milestone { number: number url: string @@ -299,7 +288,7 @@ interface Milestone { async function getReleaseMilestone(client: Octokit, release: semver.SemVer): Promise { const owner = 'sourcegraph' const repo = 'sourcegraph' - const milestoneTitle = releaseMilestoneName(release) + const milestoneTitle = releaseName(release) const milestones = await client.issues.listMilestonesForRepo({ owner, repo, @@ -324,10 +313,6 @@ function trackingIssueTitle(version: semver.SemVer): string { return `${version.version} patch release tracking issue` } -function managedIssueTrackingTitle(version: semver.SemVer): string { - return `${version.version} upgrade managed instances tracking issue` -} - async function getIssueByTitle(octokit: Octokit, title: string): Promise { const owner = 'sourcegraph' const repo = 'sourcegraph' @@ -569,3 +554,7 @@ export async function createTag( ) await execa('bash', ['-c', `git tag -a ${tag} -m ${tag} && ${finalizeTag}`], { stdio: 'inherit', cwd: workdir }) } + +function dateMarkdown(date: Date, name: string): string { + return `[${formatDate(date)}](${timezoneLink(date, name)})` +} diff --git a/dev/release/src/google-calendar.ts b/dev/release/src/google-calendar.ts index 312a90c2995cc..a8921a698ee53 100644 --- a/dev/release/src/google-calendar.ts +++ b/dev/release/src/google-calendar.ts @@ -4,6 +4,7 @@ import open from 'open' import { Credentials } from 'google-auth-library' import { readLine, cacheFolder } from './util' import { readFile, writeFile } from 'mz/fs' +import { addMinutes } from 'date-fns' const SCOPES = ['https://www.googleapis.com/auth/calendar.events'] const TOKEN_PATH = `${cacheFolder}/google-calendar-token.json` @@ -99,3 +100,10 @@ async function listEvents(auth: OAuth2Client): Promise { - const googleCalendar = await getClient() const { upcoming: release } = await releaseVersions(config) + const name = releaseName(release) const events: EventOptions[] = [ { - title: 'Release captain: prepare for branch cut (5 working days until release)', - description: 'See the release tracking issue for TODOs', - startDateTime: new Date(config.fiveWorkingDaysBeforeRelease).toISOString(), - endDateTime: addMinutes(new Date(config.fiveWorkingDaysBeforeRelease), 1).toISOString(), - }, - { - title: 'Release captain: branch cut (4 working days until release)', - description: 'See the release tracking issue for TODOs', - startDateTime: new Date(config.fourWorkingDaysBeforeRelease).toISOString(), - endDateTime: addMinutes(new Date(config.fourWorkingDaysBeforeRelease), 1).toISOString(), - }, - ...eachDayOfInterval({ - start: addDays(new Date(config.fourWorkingDaysBeforeRelease), 1), - end: subDays(new Date(config.oneWorkingDayBeforeRelease), 1), - }) - .filter(date => !isWeekend(date)) - .map(date => ({ - title: 'Release captain: cut new release candidate', - description: 'See release tracking issue for TODOs', - startDateTime: date.toISOString(), - endDateTime: addMinutes(date, 1).toISOString(), - })), - { - title: 'Release captain: tag final release (1 working day before release)', - description: 'See the release tracking issue for TODOs', - startDateTime: new Date(config.oneWorkingDayBeforeRelease).toISOString(), - endDateTime: addMinutes(new Date(config.oneWorkingDayBeforeRelease), 1).toISOString(), - }, - { - title: `Cut release branch ${release.major}.${release.minor}`, + title: `Cut and release Sourcegraph ${name}`, description: '(This is not an actual event to attend, just a calendar marker.)', anyoneCanAddSelf: true, attendees: [config.teamEmail], - startDateTime: new Date(config.fourWorkingDaysBeforeRelease).toISOString(), - endDateTime: addMinutes(new Date(config.fourWorkingDaysBeforeRelease), 1).toISOString(), + ...calendarTime(config.releaseDate), }, { - title: `Release Sourcegraph ${release.major}.${release.minor}`, + title: `Deploy Sourcegraph ${name} to managed instances`, description: '(This is not an actual event to attend, just a calendar marker.)', anyoneCanAddSelf: true, attendees: [config.teamEmail], - startDateTime: new Date(config.releaseDateTime).toISOString(), - endDateTime: addMinutes(new Date(config.releaseDateTime), 1).toISOString(), - }, - { - title: `Deploy Sourcegraph ${release.major}.${release.minor} to managed instances`, - description: '(This is not an actual event to attend, just a calendar marker.)', - anyoneCanAddSelf: true, - attendees: [config.teamEmail], - startDateTime: new Date(config.oneWorkingDayAfterRelease).toISOString(), - endDateTime: addMinutes(new Date(config.oneWorkingDayAfterRelease), 1).toISOString(), + ...calendarTime(config.oneWorkingDayAfterRelease), }, ] - for (const event of events) { - console.log(`Create calendar event: ${event.title}: ${event.startDateTime || 'undefined'}`) - await ensureEvent(event, googleCalendar) + if (!config.dryRun.calendar) { + const googleCalendar = await getClient() + for (const event of events) { + console.log(`Create calendar event: ${event.title}: ${event.startDateTime || 'undefined'}`) + await ensureEvent(event, googleCalendar) + } + } else { + console.log('dryRun.calendar=true, skipping calendar event creation', events) } }, }, { - id: 'tracking:release-issue', - description: 'Generate a GitHub tracking issue for a MAJOR.MINOR release', + id: 'tracking:issues', + description: 'Generate GitHub tracking issue for the configured release', run: async (config: Config) => { const { - releaseDateTime, + releaseDate, captainGitHubUsername, oneWorkingDayAfterRelease, - oneWorkingDayBeforeRelease, - fourWorkingDaysBeforeRelease, - fiveWorkingDaysBeforeRelease, captainSlackUsername, slackAnnounceChannel, dryRun, } = config const { upcoming: release } = await releaseVersions(config) + const date = new Date(releaseDate) // Create issue - const { url, created } = await ensureReleaseTrackingIssue({ + const trackingIssues = await ensureTrackingIssues({ version: release, assignees: [captainGitHubUsername], - releaseDateTime: new Date(releaseDateTime), + releaseDate: date, oneWorkingDayAfterRelease: new Date(oneWorkingDayAfterRelease), - oneWorkingDayBeforeRelease: new Date(oneWorkingDayBeforeRelease), - fourWorkingDaysBeforeRelease: new Date(fourWorkingDaysBeforeRelease), - fiveWorkingDaysBeforeRelease: new Date(fiveWorkingDaysBeforeRelease), dryRun: dryRun.trackingIssues || false, }) - if (url) { - console.log(created ? `Created tracking issue ${url}` : `Tracking issue already exists: ${url}`) - } + console.log('Rendered tracking issues', trackingIssues) - // Announce issue if issue does not already exist - if (created) { - // Slack markdown links - const majorMinor = `${release.major}.${release.minor}` - const branchCutDate = new Date(fourWorkingDaysBeforeRelease) - const branchCutDateString = `<${timezoneLink(branchCutDate, `${majorMinor} branch cut`)}|${formatDate( - branchCutDate - )}>` - const releaseDate = new Date(releaseDateTime) - const releaseDateString = `<${timezoneLink(releaseDate, `${majorMinor} release`)}|${formatDate( - releaseDate - )}>` - await postMessage( - `:mega: *${majorMinor} Release* + // If at least one issue was created, post to Slack + if (trackingIssues.find(({ created }) => created)) { + const name = releaseName(release) + const releaseDateString = slackURL(formatDate(date), timezoneLink(date, `${name} release`)) + let annoncement = `:mega: *${name} release* :captain: Release captain: @${captainSlackUsername} -:pencil: Tracking issue: ${url} -:spiral_calendar_pad: Key dates: -* Branch cut: ${branchCutDateString} -* Release: ${releaseDateString}`, - slackAnnounceChannel - ) - console.log(`Posted to Slack channel ${slackAnnounceChannel}`) - } - }, - }, - { - id: 'tracking:patch-issue', - description: 'Generate a GitHub tracking issue for a MAJOR.MINOR.PATCH release', - run: async config => { - const { captainGitHubUsername, captainSlackUsername, slackAnnounceChannel, dryRun } = config - const { upcoming: release } = await releaseVersions(config) - - // Create issue - const { url, created } = await ensurePatchReleaseIssue({ - version: release, - assignees: [captainGitHubUsername], - dryRun: dryRun.trackingIssues || false, - }) - if (url) { - console.log(created ? `Created tracking issue ${url}` : `Tracking issue already exists: ${url}`) - } - - // Announce issue if issue does not already exist - if (created) { - const patchRequestTemplate = `https://github.com/sourcegraph/sourcegraph/issues/new?assignees=&labels=team%2Fdistribution&template=request_patch_release.md&title=${release.version}%3A+` - await postMessage( - `:mega: *${release.version} Patch Release* - -:captain: Release captain: @${captainSlackUsername} -:pencil: Tracking issue: ${url} - -If you have changes that should go into this patch release, <${patchRequestTemplate}|please *file a patch request issue*>, or it will not be included.`, - slackAnnounceChannel - ) +:spiral_calendar_pad: Scheduled for: ${releaseDateString} +:pencil: Tracking issues: +${trackingIssues.map(index => `- ${slackURL(index.title, index.url)}`).join('\n')}` + if (release.patch !== 0) { + const patchRequestTemplate = `https://github.com/sourcegraph/sourcegraph/issues/new?assignees=&labels=team%2Fdistribution&template=request_patch_release.md&title=${release.version}%3A+` + annoncement += `\n\nIf you have changes that should go into this patch release, ${slackURL( + 'please *file a patch request issue*', + patchRequestTemplate + )}, or it will not be included.` + } + await postMessage(annoncement, slackAnnounceChannel) console.log(`Posted to Slack channel ${slackAnnounceChannel}`) - } - }, - }, - { - id: 'tracking:release-managed-instances', - description: 'Create a Github issue to track upgrade of managed instances MAJOR.MINOR release', - run: async config => { - const { captainGitHubUsername, oneWorkingDayAfterRelease, dryRun } = config - const { upcoming: release } = await releaseVersions(config) - - // Create issue - const { url, created } = await ensureUpgradeManagedTrackingIssue({ - version: release, - assignees: [captainGitHubUsername], - oneWorkingDayAfterRelease: new Date(oneWorkingDayAfterRelease), - dryRun: dryRun.trackingIssues || false, - }) - if (url) { - console.log(created ? `Created tracking issue ${url}` : `Tracking issue already exists: ${url}`) + } else { + console.log('No tracking issues were created, skipping Slack announcement') } }, }, @@ -675,8 +577,8 @@ Campaign: ${campaignURL}`, await ensureEvent( { title: 'TEST EVENT', - startDateTime: new Date(config.releaseDateTime).toISOString(), - endDateTime: addMinutes(new Date(config.releaseDateTime), 1).toISOString(), + startDateTime: new Date(config.releaseDate).toISOString(), + endDateTime: addMinutes(new Date(config.releaseDate), 1).toISOString(), }, googleCalendar ) diff --git a/dev/release/src/slack.ts b/dev/release/src/slack.ts index 52e20030c7082..8147c615dd6c7 100644 --- a/dev/release/src/slack.ts +++ b/dev/release/src/slack.ts @@ -10,3 +10,7 @@ export async function postMessage(message: string, channel: string): Promise` +}