From 8e0d8ca3441fc6fb76542a028a4cc75948334e66 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 12:52:01 +0200 Subject: [PATCH 01/11] add step to sync to main on prelease promotions --- .github/workflows/publish.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d319a540c376..3b461334b15f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -173,13 +173,25 @@ jobs: git commit -m "Update versions/next.json for v${{ steps.version.outputs.current-version }}" git push origin main - # Force push from next to main if it is not a prerelease, and this release is from next-release + # Sync next-release to main if it is not a prerelease, and this release is from next-release # This happens when eg. next has been tracking 7.1.0-alpha.X, and now we want to release 7.1.0 - # This will keep release-next, next and main all tracking v7.1.0 - # - name: Force push ${{ steps.target.outputs.target }} to main - # if: steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease - # run: | - # git push --force origin ${{ steps.target.outputs.target }}:main + # This will keep next-release, next and main all tracking v7.1.0 + # See "Alternative merge strategies" in https://stackoverflow.com/a/36321787 + - name: Sync next-release to main + if: steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease + working-directory: . + run: | + git fetch origin next-release + git checkout next-release + git pull + git fetch origin main + git checkout main + git pull + git merge --no-commit -s ours next-release + git rm -rf . + git checkout next-release -- . + git commit -m "Sync next-release to main" + git push origin main - name: Report job failure to Discord if: failure() From d27c2f7adbb3d354d1037e7e282eee358fe39433 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 13:00:59 +0200 Subject: [PATCH 02/11] sync correct version json file --- .github/workflows/publish.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3b461334b15f..feb623f6e764 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -161,16 +161,17 @@ jobs: git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" git push origin next - - name: Sync versions/next.json from `next` to `main` + - name: Sync version JSONs from `next-release` to `main` if: github.ref_name == 'next-release' working-directory: . run: | + VERSION_FILE="./docs/versions/${{ steps.is-prerelease.outputs.prerelease == 'true' && 'next' || 'latest' }}.json" git fetch origin main git checkout main git pull - git checkout origin/next ./docs/versions/next.json - git add ./docs/versions/next.json - git commit -m "Update versions/next.json for v${{ steps.version.outputs.current-version }}" + git checkout origin/next-release $VERSION_FILE + git add $VERSION_FILE + git commit -m "Update $VERSION_FILE for v${{ steps.version.outputs.current-version }}" git push origin main # Sync next-release to main if it is not a prerelease, and this release is from next-release From 95cff3712c2e72e60530299169459053bdfde782 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 13:21:19 +0200 Subject: [PATCH 03/11] note about manual changelog edits --- CONTRIBUTING/RELEASING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index 19822e849d74..e3f715a19efb 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -315,6 +315,9 @@ It's possible and valid to push manual changes directly on the release branch wh It's recommended to use the automated process as much as possible to ensure that the information in GitHub is the single source of truth, and that pull requests and changelogs are in sync. +> **Warning** +> If you make manual changes to the changelog, you also need to make those changes in either [`./docs/versions/latest.json`](../docs/versions/latest.json) or [`./docs/versions/next.json`](../docs/versions/next.json). The `"plain"` property should match the changelog entry, **without the heading** and with all new lines replaces with `\n`. + ### 6. Merge When the pull request was frozen, a CI run was triggered on the branch. If it's green, it's time to merge the pull request. If CI is failing for some reason, consult with the rest of the core team. These release pull requests are almost exact copies of `next|main` so CI should only fail if they fail too. From 50ba033f8eecee62a7eaf3c546c07b87a4747713 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 13:24:18 +0200 Subject: [PATCH 04/11] more to note about manual changelogs --- CONTRIBUTING/RELEASING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index e3f715a19efb..b9eb8f3ee786 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -317,6 +317,7 @@ It's recommended to use the automated process as much as possible to ensure that > **Warning** > If you make manual changes to the changelog, you also need to make those changes in either [`./docs/versions/latest.json`](../docs/versions/latest.json) or [`./docs/versions/next.json`](../docs/versions/next.json). The `"plain"` property should match the changelog entry, **without the heading** and with all new lines replaces with `\n`. +> This is common for custom release notes when releasing majors and minors. ### 6. Merge From b216afafbccc168cbf71c5719a23f22d2f218614 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 21:49:49 +0200 Subject: [PATCH 05/11] add text about manual changes to PR description --- scripts/release/__tests__/generate-pr-description.test.ts | 8 ++++++++ scripts/release/generate-pr-description.ts | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/scripts/release/__tests__/generate-pr-description.test.ts b/scripts/release/__tests__/generate-pr-description.test.ts index 4a9dc04bcd4c..b0f1bbe89db5 100644 --- a/scripts/release/__tests__/generate-pr-description.test.ts +++ b/scripts/release/__tests__/generate-pr-description.test.ts @@ -215,6 +215,8 @@ For each pull request below, you need to either manually cherry pick it, or disc If you\\'ve made any changes doing the above QA (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](https://github.com/storybookjs/storybook/actions/workflows/prepare-prerelease.yml) and wait for it to finish. It will wipe your progress in this to do, which is expected. + Feel free to manually commit any changes necessary to this branch **after** you\\'ve done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs, *especially* if you\\'re making changes to the changelog. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml) @@ -273,6 +275,8 @@ For each pull request below, you need to either manually cherry pick it, or disc If you\\'ve made any changes (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) and wait for it to finish. + Feel free to manually commit any changes necessary to this branch **after** you\\'ve done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml)" @@ -338,6 +342,8 @@ For each pull request below, you need to either manually cherry pick it, or disc If you\\'ve made any changes doing the above QA (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](https://github.com/storybookjs/storybook/actions/workflows/prepare-prerelease.yml) and wait for it to finish. It will wipe your progress in this to do, which is expected. + Feel free to manually commit any changes necessary to this branch **after** you\\'ve done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs, *especially* if you\\'re making changes to the changelog. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml) @@ -391,6 +397,8 @@ For each pull request below, you need to either manually cherry pick it, or disc If you\\'ve made any changes (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) and wait for it to finish. + Feel free to manually commit any changes necessary to this branch **after** you\\'ve done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml)" diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index 1a10ca89197f..16a6928e994f 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -175,6 +175,8 @@ export const generateReleaseDescription = ({ If you've made any changes doing the above QA (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](${workflowUrl}) and wait for it to finish. It will wipe your progress in this to do, which is expected. + Feel free to manually commit any changes necessary to this branch **after** you've done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs, *especially* if you're making changes to the changelog. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml) @@ -215,6 +217,8 @@ export const generateNonReleaseDescription = ( If you've made any changes (change PR titles, revert PRs), manually trigger a re-generation of this PR with [this workflow](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) and wait for it to finish. + Feel free to manually commit any changes necessary to this branch **after** you've done the last re-generation, following the [Make Manual Changes](https://github.com/storybookjs/storybook/blob/next/CONTRIBUTING/RELEASING.md#5-make-manual-changes) section in the docs. + When everything above is done: - Merge this PR - [Follow the run of the publish action](https://github.com/storybookjs/storybook/actions/workflows/publish.yml)` From 9da9ae506752fa87f48f0c36208f43f2d0dfddff Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 21:53:21 +0200 Subject: [PATCH 06/11] cleanup unused labelling code --- scripts/release/pick-patches.ts | 61 --------------------------------- 1 file changed, 61 deletions(-) diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index 8182ee3fad88..633532bc22c8 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -2,13 +2,10 @@ /* eslint-disable no-await-in-loop */ import program from 'commander'; import chalk from 'chalk'; -import { v4 as uuidv4 } from 'uuid'; -import type { GraphQlQueryResponseData } from '@octokit/graphql'; import ora from 'ora'; import { simpleGit } from 'simple-git'; import { setOutput } from '@actions/core'; import { getUnpickedPRs } from './utils/get-unpicked-prs'; -import { githubGraphQlClient } from './utils/github-client'; program.name('pick-patches').description('Cherry pick patch PRs back to main'); @@ -28,70 +25,12 @@ interface PR { mergeCommit: string; } -const LABEL = { - PATCH: 'patch:yes', - PICKED: 'patch:done', - DOCUMENTATION: 'documentation', -} as const; - function formatPR(pr: PR): string { return `https://github.com/${OWNER}/${REPO}/pull/${pr.number} "${pr.title}" ${chalk.yellow( pr.mergeCommit )}`; } -// @ts-expect-error not used atm -async function getLabelIds(labelNames: string[]) { - const query = labelNames.join('+'); - const result = await githubGraphQlClient( - ` - query ($owner: String!, $repo: String!, $q: String!) { - repository(owner: $owner, name: $repo) { - labels(query: $q, first: 10) { - nodes { - id - name - description - } - } - } - } - `, - { - owner: OWNER, - repo: REPO, - q: query, - } - ); - - const { labels } = result.repository; - const labelToId = {} as Record; - labels.nodes.forEach((label: { name: string; id: string }) => { - labelToId[label.name] = label.id; - }); - return labelToId; -} - -// @ts-expect-error not used atm -async function labelPR(id: string, labelToId: Record) { - await githubGraphQlClient( - ` - mutation ($input: AddLabelsToLabelableInput!) { - addLabelsToLabelable(input: $input) { - clientMutationId - } - } - `, - { - input: { - labelIds: [labelToId[LABEL.PICKED]], - labelableId: id, - clientMutationId: uuidv4(), - }, - } - ); -} - export const run = async (_: unknown) => { if (!process.env.GH_TOKEN) { logger.error('GH_TOKEN environment variable must be set, exiting.'); From e2444f826ea3ddf1fa44e76a5d626a2e22994fe9 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 22:16:42 +0200 Subject: [PATCH 07/11] move getUnpickedPRs --- scripts/release/pick-patches.ts | 3 +- scripts/release/utils/get-changes.ts | 2 +- scripts/release/utils/get-unpicked-prs.ts | 72 ---------------------- scripts/release/utils/github-client.ts | 73 +++++++++++++++++++++++ 4 files changed, 75 insertions(+), 75 deletions(-) delete mode 100644 scripts/release/utils/get-unpicked-prs.ts diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index 633532bc22c8..3740b1c98edf 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -5,7 +5,7 @@ import chalk from 'chalk'; import ora from 'ora'; import { simpleGit } from 'simple-git'; import { setOutput } from '@actions/core'; -import { getUnpickedPRs } from './utils/get-unpicked-prs'; +import { getUnpickedPRs } from './utils/github-client'; program.name('pick-patches').description('Cherry pick patch PRs back to main'); @@ -41,7 +41,6 @@ export const run = async (_: unknown) => { const spinner = ora('Searching for patch PRs to cherry-pick').start(); - // const labelToId = await getLabelIds(Object.values(LABEL)); const patchPRs = await getUnpickedPRs(sourceBranch); if (patchPRs.length > 0) { diff --git a/scripts/release/utils/get-changes.ts b/scripts/release/utils/get-changes.ts index 34586dfee2d0..e3e414e766e3 100644 --- a/scripts/release/utils/get-changes.ts +++ b/scripts/release/utils/get-changes.ts @@ -3,8 +3,8 @@ import chalk from 'chalk'; import semver from 'semver'; import type { PullRequestInfo } from './get-github-info'; import { getPullInfoFromCommit } from './get-github-info'; -import { getUnpickedPRs } from './get-unpicked-prs'; import { git } from './git-client'; +import { getUnpickedPRs } from './github-client'; export const RELEASED_LABELS = { 'BREAKING CHANGE': '❗ Breaking Change', diff --git a/scripts/release/utils/get-unpicked-prs.ts b/scripts/release/utils/get-unpicked-prs.ts deleted file mode 100644 index ade20ff68af3..000000000000 --- a/scripts/release/utils/get-unpicked-prs.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* eslint-disable no-console */ -import type { GraphQlQueryResponseData } from '@octokit/graphql'; -import { githubGraphQlClient } from './github-client'; - -export interface PR { - number: number; - id: string; - branch: string; - title: string; - mergeCommit: string; -} - -export async function getUnpickedPRs(baseBranch: string, verbose?: boolean): Promise> { - console.log(`💬 Getting unpicked patch pull requests...`); - const result = await githubGraphQlClient( - ` - query ($owner: String!, $repo: String!, $state: PullRequestState!, $order: IssueOrder!) { - repository(owner: $owner, name: $repo) { - pullRequests(states: [$state], labels: ["patch:yes"], orderBy: $order, first: 50, baseRefName: "next") { - nodes { - id - number - title - baseRefName - mergeCommit { - oid - } - labels(first: 20) { - nodes { - name - } - } - } - } - } - } - `, - { - owner: 'storybookjs', - repo: 'storybook', - order: { - field: 'UPDATED_AT', - direction: 'DESC', - }, - state: 'MERGED', - } - ); - - const { - pullRequests: { nodes }, - } = result.repository; - - const prs = nodes.map((node: any) => ({ - number: node.number, - id: node.id, - branch: node.baseRefName, - title: node.title, - mergeCommit: node.mergeCommit.oid, - labels: node.labels.nodes.map((l: any) => l.name), - })); - - const unpickedPRs = prs - .filter((pr: any) => !pr.labels.includes('patch:done')) - .filter((pr: any) => pr.branch === baseBranch) - .reverse(); - - if (verbose) { - console.log(`🔍 Found unpicked patch pull requests: - ${JSON.stringify(unpickedPRs, null, 2)}`); - } - return unpickedPRs; -} diff --git a/scripts/release/utils/github-client.ts b/scripts/release/utils/github-client.ts index 3c6a2355e0dc..646ba1003986 100644 --- a/scripts/release/utils/github-client.ts +++ b/scripts/release/utils/github-client.ts @@ -1,10 +1,83 @@ +/* eslint-disable no-console */ import type { GraphQlQueryResponseData } from '@octokit/graphql'; import { graphql } from '@octokit/graphql'; +export interface PullRequest { + number: number; + id: string; + branch: string; + title: string; + mergeCommit: string; +} + export const githubGraphQlClient = graphql.defaults({ headers: { authorization: `token ${process.env.GH_TOKEN}` }, }); +export async function getUnpickedPRs( + baseBranch: string, + verbose?: boolean +): Promise> { + console.log(`💬 Getting unpicked patch pull requests...`); + const result = await githubGraphQlClient( + ` + query ($owner: String!, $repo: String!, $state: PullRequestState!, $order: IssueOrder!) { + repository(owner: $owner, name: $repo) { + pullRequests(states: [$state], labels: ["patch:yes"], orderBy: $order, first: 50, baseRefName: "next") { + nodes { + id + number + title + baseRefName + mergeCommit { + oid + } + labels(first: 20) { + nodes { + name + } + } + } + } + } + } + `, + { + owner: 'storybookjs', + repo: 'storybook', + order: { + field: 'UPDATED_AT', + direction: 'DESC', + }, + state: 'MERGED', + } + ); + + const { + pullRequests: { nodes }, + } = result.repository; + + const prs = nodes.map((node: any) => ({ + number: node.number, + id: node.id, + branch: node.baseRefName, + title: node.title, + mergeCommit: node.mergeCommit.oid, + labels: node.labels.nodes.map((l: any) => l.name), + })); + + const unpickedPRs = prs + .filter((pr: any) => !pr.labels.includes('patch:done')) + .filter((pr: any) => pr.branch === baseBranch) + .reverse(); + + if (verbose) { + console.log(`🔍 Found unpicked patch pull requests: + ${JSON.stringify(unpickedPRs, null, 2)}`); + } + return unpickedPRs; +} + export async function getLabelIds({ repo: fullRepo, labelNames, From baad25ac3c871e33a92ba56af80e9140c74e59e1 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 22:38:34 +0200 Subject: [PATCH 08/11] add --all flag to label-patches --- .../release/__tests__/label-patches.test.ts | 93 ++++++++++++++++++- scripts/release/label-patches.ts | 47 +++++++--- 2 files changed, 125 insertions(+), 15 deletions(-) diff --git a/scripts/release/__tests__/label-patches.test.ts b/scripts/release/__tests__/label-patches.test.ts index ddfc0be15c50..d98abc7eb763 100644 --- a/scripts/release/__tests__/label-patches.test.ts +++ b/scripts/release/__tests__/label-patches.test.ts @@ -74,6 +74,22 @@ beforeEach(() => { gitClient.git.getRemotes.mockResolvedValue(remoteMock); githubInfo.getPullInfoFromCommit.mockResolvedValue(pullInfoMock); github.getLabelIds.mockResolvedValue({ 'patch:done': 'pick-id' }); + github.getUnpickedPRs.mockResolvedValue([ + { + number: 42, + id: 'some-id', + branch: 'some-patching-branch', + title: 'Fix: Patch this PR', + mergeCommit: 'abcd1234', + }, + { + number: 44, + id: 'other-id', + branch: 'other-patching-branch', + title: 'Fix: Also patch this PR', + mergeCommit: 'abcd1234', + }, + ]); }); test('it should fail early when no GH_TOKEN is set', async () => { @@ -130,8 +146,83 @@ test('it should label the PR associated with cheery picks in the current branch' "Found latest tag: v7.2.1", "Looking at cherry pick commits since v7.2.1", "Found the following picks : Commit: 930b47f011f750c44a1782267d698ccdd3c04da3 PR: [#55](https://github.com/storybookjs/storybook/pull/55)", - "Labeling the PRs with the patch:done label...", + "Labeling 1 PRs with the patch:done label...", + "Successfully labeled all PRs with the patch:done label.", + ] + `); +}); + +test('it should label all PRs when the --all flag is passed', async () => { + process.env.GH_TOKEN = 'MY_SECRET'; + + // clear the git log, it shouldn't depend on it in --all mode + gitClient.git.log.mockResolvedValue({ + all: [], + latest: null!, + total: 0, + }); + + const writeStderr = jest.spyOn(process.stderr, 'write').mockImplementation(); + + await run({ all: true }); + expect(github.githubGraphQlClient.mock.calls).toMatchInlineSnapshot(` + [ + [ + " + mutation ($input: AddLabelsToLabelableInput!) { + addLabelsToLabelable(input: $input) { + clientMutationId + } + } + ", + { + "input": { + "clientMutationId": "39cffd21-7933-56e4-9d9c-1afeda9d5906", + "labelIds": [ + "pick-id", + ], + "labelableId": "some-id", + }, + }, + ], + [ + " + mutation ($input: AddLabelsToLabelableInput!) { + addLabelsToLabelable(input: $input) { + clientMutationId + } + } + ", + { + "input": { + "clientMutationId": "cc31033b-5da7-5c9e-adf2-80a2963e19a8", + "labelIds": [ + "pick-id", + ], + "labelableId": "other-id", + }, + }, + ], + ] + `); + + const stderrCalls = writeStderr.mock.calls + .map(([text]) => + typeof text === 'string' + ? text + .replace(ansiRegex(), '') + .replace(/[^\x20-\x7E]/g, '') + .replaceAll('-', '') + .trim() + : text + ) + .filter((it) => it !== ''); + + expect(stderrCalls).toMatchInlineSnapshot(` + [ + "Labeling 2 PRs with the patch:done label...", "Successfully labeled all PRs with the patch:done label.", ] `); + expect(github.getUnpickedPRs).toHaveBeenCalledTimes(1); }); diff --git a/scripts/release/label-patches.ts b/scripts/release/label-patches.ts index dea8e62e469e..1e9305b9806c 100644 --- a/scripts/release/label-patches.ts +++ b/scripts/release/label-patches.ts @@ -1,13 +1,18 @@ import program from 'commander'; import { v4 as uuidv4 } from 'uuid'; import ora from 'ora'; -import { getLabelIds, githubGraphQlClient } from './utils/github-client'; +import { getLabelIds, githubGraphQlClient, getUnpickedPRs } from './utils/github-client'; import { getPullInfoFromCommits, getRepo } from './utils/get-changes'; import { getLatestTag, git } from './utils/git-client'; program .name('label-patches') - .description('Label all patches applied in current branch up to the latest release tag.'); + .description('Label all patches applied in current branch up to the latest release tag.') + .option( + '-A, --all', + 'Label all pull requests pending patches, iregardless if they are in the git log or not', + false + ); async function labelPR(id: string, labelId: string) { await githubGraphQlClient( @@ -22,11 +27,7 @@ async function labelPR(id: string, labelId: string) { ); } -export const run = async (_: unknown) => { - if (!process.env.GH_TOKEN) { - throw new Error('GH_TOKEN environment variable must be set, exiting.'); - } - +async function getPullRequestsFromLog({ repo }: { repo: string }) { const spinner = ora('Looking for latest tag').start(); const latestTag = await getLatestTag(); spinner.succeed(`Found latest tag: ${latestTag}`); @@ -41,10 +42,8 @@ export const run = async (_: unknown) => { if (cherryPicked.length === 0) { spinner2.fail('No cherry pick commits found to label.'); - return; + return []; } - - const repo = await getRepo(); const pullRequests = ( await getPullInfoFromCommits({ repo, @@ -56,17 +55,37 @@ export const run = async (_: unknown) => { spinner2.fail( `Found picks: ${cherryPicked.join(', ')}, but no associated pull request found to label.` ); - return; + return pullRequests; } const commitWithPr = pullRequests.map((pr) => `Commit: ${pr.commit}\n PR: ${pr.links.pull}`); spinner2.succeed(`Found the following picks 🍒:\n ${commitWithPr.join('\n')}`); - const spinner3 = ora(`Labeling the PRs with the patch:done label...`).start(); + return pullRequests; +} + +export const run = async (options: unknown) => { + if (!process.env.GH_TOKEN) { + throw new Error('GH_TOKEN environment variable must be set, exiting.'); + } + + const repo = await getRepo(); + const labelAll = typeof options === 'object' && 'all' in options && Boolean(options.all); + + const pullRequestsToLabel = labelAll + ? await getUnpickedPRs('next') + : await getPullRequestsFromLog({ repo }); + if (pullRequestsToLabel.length === 0) { + return; + } + + const spinner3 = ora( + `Labeling ${pullRequestsToLabel.length} PRs with the patch:done label...` + ).start(); try { const labelToId = await getLabelIds({ repo, labelNames: ['patch:done'] }); - await Promise.all(pullRequests.map((pr) => labelPR(pr.id, labelToId['patch:done']))); + await Promise.all(pullRequestsToLabel.map((pr) => labelPR(pr.id, labelToId['patch:done']))); spinner3.succeed(`Successfully labeled all PRs with the patch:done label.`); } catch (e) { spinner3.fail(`Something went wrong when labelling the PRs.`); @@ -75,7 +94,7 @@ export const run = async (_: unknown) => { }; if (require.main === module) { - const options = program.parse(process.argv); + const options = program.parse().opts(); run(options).catch((err) => { console.error(err); process.exit(1); From d23691485e6f6ca0c07a3937a4f3e2773e0436c2 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 22:40:54 +0200 Subject: [PATCH 09/11] label all patches when doing stable release from next --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index feb623f6e764..cbb898dcbd02 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -122,10 +122,10 @@ jobs: run: git fetch --tags origin - name: Label patch PRs as picked - if: github.ref_name == 'latest-release' + if: github.ref_name == 'latest-release' || (steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease) env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release:label-patches + run: yarn release:label-patches ${{ steps.target.outputs.target == 'next' && '--all' || '' }} - name: Create GitHub Release if: steps.publish-needed.outputs.published == 'false' From f9c4846447bc3ea16428f5c1a354dc318c004e7f Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 18 Jul 2023 22:52:59 +0200 Subject: [PATCH 10/11] explain patching all --- .github/workflows/publish.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cbb898dcbd02..ba00b20cc13a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -121,6 +121,8 @@ jobs: if: github.ref_name == 'latest-release' run: git fetch --tags origin + # when this is a patch release from main, label any patch PRs included in the release + # when this is a stable release from next, label ALL patch PRs found, as they will per definition be "patched" now - name: Label patch PRs as picked if: github.ref_name == 'latest-release' || (steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease) env: From 7ef3f2cf7c2bca7f467fee8b6b981bc470a046aa Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 24 Jul 2023 09:50:32 +0200 Subject: [PATCH 11/11] disable merging next to main --- .github/workflows/publish.yml | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ba00b20cc13a..3a6eeebd8248 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -176,25 +176,30 @@ jobs: git commit -m "Update $VERSION_FILE for v${{ steps.version.outputs.current-version }}" git push origin main + # TODO: this is currently disabled, because we may have a better strategy that we want to try out manually first before comitting to it: + # - create a branch "release-" from HEAD of main + # - git push --force origin ${{ steps.target.outputs.target }}:main + # - ... this will keep the "main" history in the new release branch, and then overwrite main's history with next's + # Sync next-release to main if it is not a prerelease, and this release is from next-release # This happens when eg. next has been tracking 7.1.0-alpha.X, and now we want to release 7.1.0 # This will keep next-release, next and main all tracking v7.1.0 # See "Alternative merge strategies" in https://stackoverflow.com/a/36321787 - - name: Sync next-release to main - if: steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease - working-directory: . - run: | - git fetch origin next-release - git checkout next-release - git pull - git fetch origin main - git checkout main - git pull - git merge --no-commit -s ours next-release - git rm -rf . - git checkout next-release -- . - git commit -m "Sync next-release to main" - git push origin main + # - name: Sync next-release to main + # if: steps.publish-needed.outputs.published == 'false' && steps.target.outputs.target == 'next' && !steps.is-prerelease.outputs.prerelease + # working-directory: . + # run: | + # git fetch origin next-release + # git checkout next-release + # git pull + # git fetch origin main + # git checkout main + # git pull + # git merge --no-commit -s ours next-release + # git rm -rf . + # git checkout next-release -- . + # git commit -m "Sync next-release to main" + # git push origin main - name: Report job failure to Discord if: failure()