From f5430d37636a711c7dcc368a0bf36dc36829ab7d Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 28 Aug 2023 15:37:37 +0200 Subject: [PATCH 01/27] rename release preparement workflows --- ...release.yml => prepare-hotfix-release.yml} | 4 +- ...rerelease.yml => prepare-next-release.yml} | 2 +- CONTRIBUTING/RELEASING.md | 70 ++++++++++--------- scripts/release/generate-pr-description.ts | 6 +- 4 files changed, 44 insertions(+), 38 deletions(-) rename .github/workflows/{prepare-patch-release.yml => prepare-hotfix-release.yml} (99%) rename .github/workflows/{prepare-prerelease.yml => prepare-next-release.yml} (99%) diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-hotfix-release.yml similarity index 99% rename from .github/workflows/prepare-patch-release.yml rename to .github/workflows/prepare-hotfix-release.yml index c88022c7ea0..6ee8d3091c1 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-hotfix-release.yml @@ -16,8 +16,8 @@ concurrency: cancel-in-progress: true jobs: - prepare-patch-pull-request: - name: Prepare patch pull request + prepare-hotfix-pull-request: + name: Prepare hotfix pull request runs-on: ubuntu-latest environment: release defaults: diff --git a/.github/workflows/prepare-prerelease.yml b/.github/workflows/prepare-next-release.yml similarity index 99% rename from .github/workflows/prepare-prerelease.yml rename to .github/workflows/prepare-next-release.yml index 1250aedcfaa..42695e823cc 100644 --- a/.github/workflows/prepare-prerelease.yml +++ b/.github/workflows/prepare-next-release.yml @@ -34,7 +34,7 @@ concurrency: cancel-in-progress: true jobs: - prepare-prerelease-pull-request: + prepare-next-pull-request: name: Prepare prerelease pull request runs-on: ubuntu-latest environment: release diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index 0997b757b6e..e81029088f8 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -8,8 +8,8 @@ - [Introduction](#introduction) - [Branches](#branches) - [Release Pull Requests](#release-pull-requests) - - [Prereleases](#prereleases) - - [Patch Releases](#patch-releases) + - [`next`-releases](#next-releases) + - [Hotfix Releases](#hotfix-releases) - [Publishing](#publishing) - [👉 How to Release](#-how-to-release) - [1. Find the Prepared Pull Request](#1-find-the-prepared-pull-request) @@ -21,6 +21,8 @@ - [7. See the "Publish" Workflow Finish](#7-see-the-publish-workflow-finish) - [Releasing Locally in an Emergency 🚨](#releasing-locally-in-an-emergency-) - [Canary Releases](#canary-releases) + - [With GitHub UI](#with-github-ui) + - [With the CLI](#with-the-cli) - [Versioning Scenarios](#versioning-scenarios) - [Prereleases - `7.1.0-alpha.12` -\> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13) - [Prerelease promotions - `7.1.0-alpha.13` -\> `7.1.0-beta.0`](#prerelease-promotions---710-alpha13---710-beta0) @@ -31,7 +33,7 @@ - [Prerelease of upcoming patch release - `7.0.20` -\> `7.0.21-alpha.0`](#prerelease-of-upcoming-patch-release---7020---7021-alpha0) - [Merges to `main` without versioning](#merges-to-main-without-versioning) - [FAQ](#faq) - - [When should I use the "patch" label?](#when-should-i-use-the-patch-label) + - [When should I use the "patch:yes" label?](#when-should-i-use-the-patchyes-label) - [How do I make changes to the release tooling/process?](#how-do-i-make-changes-to-the-release-toolingprocess) - [Why do I need to re-trigger workflows to update the changelog?](#why-do-i-need-to-re-trigger-workflows-to-update-the-changelog) - [Which combination of inputs creates the version bump I need?](#which-combination-of-inputs-creates-the-version-bump-i-need) @@ -50,8 +52,8 @@ The release process is based on automatically created "Release Pull Requests", t A designated Releaser -- which may rotate between core team members -- will go through the release process in the current Release PR. This process is implemented with NodeJS scripts in [`scripts/release`](../scripts/release/) and three GitHub Actions workflows: -- [Prepare Prerelease PR](../.github/workflows/prepare-prerelease.yml) -- [Prepare Patch PR](../.github/workflows/prepare-patch-release.yml) +- [Prepare `next` PR](../.github/workflows/prepare-next-release.yml) +- [Prepare hotfix PR](../.github/workflows/prepare-hotfix-release.yml) - [Publish](../.github/workflows/publish.yml) > **Note** @@ -115,18 +117,18 @@ A few key points to note in this flow: - The changelogs are committed during the preparation, but the packages are not version bumped and not published until later. - The release pull requests don't target their working branches (`next` and `main`), but rather `next-release` and `latest-release`. -### Prereleases +### `next`-releases > **Note** -> Workflow: [`prepare-prerelease.yml`](../.github/workflows/prepare-prerelease.yml) +> Workflow: [`prepare-next-release.yml`](../.github/workflows/prepare-next-release.yml) -Prereleases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. +`next`-releases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. The default versioning strategy is to increase the current prerelease number, as described in [Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13). If there is no prerelease number (i.e., we just released a new stable minor/major version), it will add one to a patch bump, so it would go from `7.2.0` to `7.2.1-0` by default. Prerelease PRs are only created if there are actual changes to release. Content labeled with "build" or "documentation" is [not considered "releasable"](#which-changes-are-considered-releasable-and-what-does-it-mean) and is not user-facing, so it doesn't make sense to create a release. This is explained in more detail in [Why are no release PRs being prepared?](#why-are-no-release-prs-being-prepared). -The preparation workflow will create a new branch from `next`, called `version-prerelease-from-`, and open a pull request targeting `next-release`. When the Releaser merges it, the [publish workflow](#publishing) will merge `next-release` into `next`. +The preparation workflow will create a new branch from `next`, called `version-next-from-`, and open a pull request targeting `next-release`. When the Releaser merges it, the [publish workflow](#publishing) will merge `next-release` into `next`. Here's an example of a workflow where a feature and a bugfix have been created and then released to a new `7.1.0-alpha.29` version. All the commits highlighted with square dots are the ones that will be considered when generating the changelog. @@ -157,20 +159,20 @@ gitGraph merge next-release ``` -### Patch Releases +### Hotfix Releases > **Note** -> Workflow: [`prepare-patch-release.yml`](../.github/workflows/prepare-patch-release.yml) +> Workflow: [`prepare-hotfix-release.yml`](../.github/workflows/prepare-hotfix-release.yml) -Patch releases are created by [cherry-picking](https://www.atlassian.com/git/tutorials/cherry-pick) any merged, unreleased pull requests that have the "**patch**" label applied to the `next` branch. The merge commit of said pull requests are cherry-picked. +Hotfix releases are created by [cherry-picking](https://www.atlassian.com/git/tutorials/cherry-pick) any merged, unreleased pull requests that have the "**patch:yes**" label applied to the `next` branch. The merge commit of said pull requests are cherry-picked. -Sometimes it is desired to pick pull requests back to `main` even if they are not considered "releasable". Unlike prerelease preparation, patch releases will not be canceled if the content is not releasable. It might not make sense to create a new patch release if the changes are only for documentation and/or internal build systems. However, getting the changes back to `main` is the only way to deploy the documentation to the production docs site. You may also want to cherry-pick changes to internal CI to fix issues. These are valid scenarios where you want to cherry-pick the changes without being blocked on "releasable" content. In these cases, where all cherry picks are non-releasable, the preparation workflow creates a "merging" pull request instead of a "releasing" pull request. This pull request does not bump versions or update changelogs; it just cherry-picks the changes and allows you to merge them into `latest-release` -> `main`. +Sometimes it is desired to pick pull requests back to `main` even if they are not considered "releasable". Unlike `next`-release preparation, hotfix releases will not be canceled if the content is not releasable. It might not make sense to create a new hotfix release if the changes are only for documentation and/or internal build systems. However, getting the changes back to `main` is the only way to deploy the documentation to the production docs site. You may also want to cherry-pick changes to internal CI to fix issues. These are valid scenarios where you want to cherry-pick the changes without being blocked on "releasable" content. In these cases, where all cherry picks are non-releasable, the preparation workflow creates a "merging" pull request instead of a "releasing" pull request. This pull request does not bump versions or update changelogs; it just cherry-picks the changes and allows you to merge them into `latest-release` -> `main`. The preparation workflow sequentially cherry-picks each patch pull request to its branch. If this cherry-picking fails due to conflicts or other reasons, it is ignored and the next pull request is processed. All failing cherry-picks are listed in the release pull request's description, for the Releaser to manually cherry-pick during the release process. This problem occurs more often when `main` and `next` diverge, i.e. the longer it has been since a stable major/minor release. -Similar to the prerelease flow, the preparation workflow for patches will create a new branch from `main` called `version-patch-from-`, and open a pull request that targets `latest-release`. When the pull request is merged by the Releaser, the [publish workflow](#publishing) will eventually merge `latest-release` into `main`. +Similar to the `next`-release flow, the preparation workflow for patches will create a new branch from `main` called `version-hotfix-from-`, and open a pull request that targets `latest-release`. When the pull request is merged by the Releaser, the [publish workflow](#publishing) will eventually merge `latest-release` into `main`. -Here is an example of a workflow where a feature and two bug fixes have been merged to `next`. Only the bug fixes have the "**patch**" label, so only those two go into the new `7.0.19` release. Note that it is the merge commits to `next` that are cherry-picked, not the commits on the bugfix branches. +Here is an example of a workflow where a feature and two bug fixes have been merged to `next`. Only the bug fixes have the "**patch:yes**" label, so only those two go into the new `7.0.19` release. Note that it is the merge commits to `next` that are cherry-picked, not the commits on the bugfix branches. ```mermaid gitGraph @@ -213,16 +215,15 @@ gitGraph > **Note** > Workflow: [`publish.yml`](../.github/workflows/publish.yml) -When either a prerelease or a patch release branch is merged into `main` or `next-release`, the publishing workflow is triggered. This workflow performs the following tasks: +When either a `next`-release or a hotfix release branch is merged into `latest-release` or `next-release`, the publishing workflow is triggered. This workflow performs the following tasks: 1. Bump versions of all packages according to the plan from the prepared PRs 2. Install dependencies and build all packages. 3. Publish packages to npm. -4. (If this is a patch release, add the "**picked**" label to all relevant pull requests.) +4. (If this is a hotfix release, add the "**patch:done**" label to all relevant pull requests.) 5. Create a new GitHub Release, including a version tag in the release branch (`latest-release` or `next-release`). 6. Merge the release branch into the core branch (`main` or `next`). -7. (If this is a patch release, copy the `CHANGELOG.md` changes from `main` to `next`.) -8. (If this is [a promotion from a prerelease to a stable release](#minormajor-releases---710-rc2---710-or-800-rc3---800), force push `next` to `main`.) +7. (If this is a hotfix release, copy the `CHANGELOG.md` changes from `main` to `next`.) The publish workflow runs in the "release" GitHub environment, which has the npm token required to publish packages to the `@storybook` npm organization. For security reasons, this environment can only be accessed from the four "core" branches: `main`, `next`, `latest-release` and `next-release`. @@ -300,10 +301,10 @@ When triggering the workflows, always choose the `next` branch as the base, unle The workflows can be triggered here: -- [Prepare prerelease PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-prerelease.yml) -- [Prepare patch PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) +- [Prepare prerelease PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) +- [Prepare patch PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml) -Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - Prereleases](#prereleases). When triggering the prerelease workflow manually, you can optionally add inputs: +Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - `next`-releases](#next-releases). When triggering the prerelease workflow manually, you can optionally add inputs: ![Screenshot of triggering the prerelease workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) @@ -434,7 +435,7 @@ There are multiple types of releases that use the same principles, but are done ### Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13` -This is the default strategy for prereleases, there's nothing special needed to trigger this scenario. +This is the default strategy for `next`-releases, there's nothing special needed to trigger this scenario. ### Prerelease promotions - `7.1.0-alpha.13` -> `7.1.0-beta.0` @@ -445,14 +446,14 @@ To promote a prerelease to a new prerelease ID, during the [Re-trigger the Workf ### Minor/major releases - `7.1.0-rc.2` -> `7.1.0` or `8.0.0-rc.3` -> `8.0.0` -To promote a prerelease to a new prerelease ID, during the [Re-trigger the Workflow](#4-re-trigger-the-workflow) step, choose: +To promote a prerelease to a stable reelase, during the [Re-trigger the Workflow](#4-re-trigger-the-workflow) step, choose: - Release type: Patch - Prerelease ID: Leave empty The "Patch" release type ensures the current prerelease version gets promoted to a stable version without any major/minor/patch bumps. -This scenario is special as it turns the `next` branch into a stable branch (until the next prerelease). Therefore, this will also force push `next` to `main`, to ensure that `main` contains the latest stable release. Consequently, the history for `main` is lost. +This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-from- `latest-release` -> `main`. When this is done, the Releaser will need to trigger a new release on `next` that bumps the version to a new prerelease minor as described [just below](#first-prerelease-of-new-majorminor---710---720-alpha0-or-800-alpha0). ### First prerelease of new major/minor - `7.1.0` -> `7.2.0-alpha.0` or `8.0.0-alpha.0` @@ -463,7 +464,7 @@ This is the first prerelease after a stable major/minor has been released. The d ### Patch releases to stable - subset of `7.1.0-alpha.13` -> `7.0.14` -This is the default patch release scenario, which cherry picks patches to `main`. +This is the default hotfix release scenario, which cherry picks patches to `main`. ### Patch releases to earlier versions - subset of `7.1.0-alpha.13` -> `6.5.14` @@ -477,17 +478,17 @@ No process is defined for this. ### Merges to `main` without versioning -As described in more details in [the Patch Releases section](#patch-releases), there are scenarios where you want to patch [unreleasable](#which-changes-are-considered-releasable-and-what-does-it-mean) content back to `main` without bumping versions or publishing a new release. This happens automatically as long as all the unpicked patch pull requests have unreleasable labels. In that case the prepared patch pull request will change form slighty, to just cherry-picking the patches without bumping the versions. +As described in more details in [the Hotfix Releases section](#hotfix-releases), there are scenarios where you want to patch [unreleasable](#which-changes-are-considered-releasable-and-what-does-it-mean) content back to `main` without bumping versions or publishing a new release. This happens automatically as long as all the unpicked patch pull requests have unreleasable labels. In that case the prepared patch pull request will change form slighty, to just cherry-picking the patches without bumping the versions. ## FAQ -### When should I use the "patch" label? +### When should I use the "patch:yes" label? -Not all pull requests need to be patched back to the stable release, which is why only those with the **"patch"** label gets that treatment. But how do you decide whether or not a give pull requests should have that label? +Not all pull requests need to be patched back to the stable release, which is why only those with the **"patch:yes"** label gets that treatment. But how do you decide whether or not a give pull requests should have that label? -First of all, patches are only for fixes and minor improvements, and not completely new features. A pull request that introduces a new feature shouldn't be patched back to the stable release. +First of all, patches are only for important and time-sensitive fixes, and not minor improvements or completely new features. A pull request that introduces a new feature shouldn't be patched back to the stable release. -Second, any destabilizing changes shouldn't be patched back either. Breaking changes are reserved for major releases, but changes can be destabilizing without being strictly breaking, and those shouldn't be patched back either. An example is moving the settings panel in the manager to a completely different place, but with the same functionality. Many wouldn't consider this breaking because no usage will stop working because of this, but it can be considered a destabilizing change because user behavior have to change as a result of this. +Second, PRs that changes the code in a big architectural way should ideally not be patched back either, because that makes merge conflicts more likely in the future. When in doubt ask the core team for their input. @@ -497,12 +498,15 @@ The whole process is based on [GitHub Action workflows](../.github/workflows/) a The short answer to "how", is to make changes as a regular pull request that is also patched back to `main`. -There's a longer answer too, but it's pretty confusing: +
+ There's a longer answer too, but it's pretty confusing The scripts run from either `main` or `next`, so if you're changing a release script, you must patch it back to `main` for it to have an effect on patch releases. If you need the change to take effect immediately, you must manually cherry pick it to `main`. For workflow file changes, they usually run from `next`, but patching them back is recommended for consistency. The "publish" workflow runs from `latest-release` and `next-release`, so you should always patch changes back for _that_. 🙃 +
+ ### Why do I need to re-trigger workflows to update the changelog? Changes to pull requests' titles, labels or even reverts won't be reflected in the release pull request. This is because the workflow only triggers on pushes to `next`, not when pull request meta data is changed. @@ -536,7 +540,7 @@ If a pull request does not have any of the above labels at the time of release, This is most likely because `next` only contains [unreleasable changes](#which-changes-are-considered-releasable-and-what-does-it-mean), which causes the preparation workflow to cancel itself. That's because it doesn't make sense to prepare a new release if all the changes are unreleasable, as that wouldn't bump the version nor write a new changelog entry, so "releasing" it would just merge it back to `next` without any differences. -You can always see the workflows and if they have been cancelled [here for prereleases](https://github.com/storybookjs/storybook/actions/workflows/prepare-prerelease.yml) and [here for patch releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml). +You can always see the workflows and if they have been cancelled [here for `next`-releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) and [here for hotfix releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml). ### Why do we need separate release branches? diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index 16a6928e994..f912c588522 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -141,7 +141,9 @@ export const generateReleaseDescription = ({ changelogText: string; manualCherryPicks?: string; }): string => { - const workflow = semver.prerelease(nextVersion) ? 'prepare-prerelease' : 'prepare-patch-release'; + const workflow = semver.prerelease(nextVersion) + ? 'prepare-next-release' + : 'prepare-hotfix-release'; const workflowUrl = `https://github.com/storybookjs/storybook/actions/workflows/${workflow}.yml`; return ( @@ -215,7 +217,7 @@ export const generateNonReleaseDescription = ( ${manualCherryPicks || ''} - 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. + 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-hotfix-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. From 8d582d7e4dd66b0b1c85044afcf8228240eb1f8e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 29 Aug 2023 14:28:23 +0200 Subject: [PATCH 02/27] merge stable to latest-release instead of next-release, resolving merge conflicts --- .github/workflows/prepare-hotfix-release.yml | 4 +-- .github/workflows/prepare-next-release.yml | 27 +++++++++++++++----- CONTRIBUTING/RELEASING.md | 16 ++++++------ scripts/release/generate-pr-description.ts | 1 + scripts/release/is-pr-frozen.ts | 6 ++--- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-hotfix-release.yml index 6ee8d3091c1..2a331fb5f3e 100644 --- a/.github/workflows/prepare-hotfix-release.yml +++ b/.github/workflows/prepare-hotfix-release.yml @@ -56,7 +56,7 @@ jobs: id: check-frozen env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release:is-pr-frozen --patch + run: yarn release:is-pr-frozen --hotfix - name: Cancel when frozen if: steps.check-frozen.outputs.frozen == 'true' && github.event_name != 'workflow_dispatch' @@ -121,7 +121,7 @@ jobs: git config --global user.email '32066757+storybook-bot@users.noreply.github.com' git checkout -b version-patch-from-${{ steps.versions.outputs.current }} git add . - git commit -m "Write changelog for ${{ steps.versions.outputs.next }}" || true + git commit -m "Write changelog for ${{ steps.versions.outputs.next }} [skip ci]" || true git push --force origin version-patch-from-${{ steps.versions.outputs.current }} - name: Generate PR description diff --git a/.github/workflows/prepare-next-release.yml b/.github/workflows/prepare-next-release.yml index 42695e823cc..ad79b5f1b3f 100644 --- a/.github/workflows/prepare-next-release.yml +++ b/.github/workflows/prepare-next-release.yml @@ -112,21 +112,35 @@ jobs: run: | yarn release:version --deferred --release-type ${{ inputs.release-type || 'prerelease' }} ${{ inputs.pre-id && format('{0} {1}', '--pre-id', inputs.pre-id) || '' }} --verbose + - name: Check release vs prerelease + id: is-prerelease + run: yarn release:is-prerelease ${{ steps.bump-version.outputs.next-version }} + - name: Write changelog env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | yarn release:write-changelog ${{ steps.bump-version.outputs.next-version }} --verbose - - name: 'Commit changes to branch: version-prerelease-from-${{ steps.bump-version.outputs.current-version }}' + - name: 'Commit changes to branch: version-next-from-${{ steps.bump-version.outputs.current-version }}' working-directory: . run: | git config --global user.name 'storybook-bot' git config --global user.email '32066757+storybook-bot@users.noreply.github.com' - git checkout -b version-prerelease-from-${{ steps.bump-version.outputs.current-version }} + git checkout -b version-next-from-${{ steps.bump-version.outputs.current-version }} + git add . + git commit -m "Write changelog for ${{ steps.bump-version.outputs.next-version }} [skip ci]" || true + git push --force origin version-next-from-${{ steps.bump-version.outputs.current-version }} + + - name: Resolve merge-conflicts with base branch + if: steps.is-prerelease.outputs.prerelease == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git pull origin latest-release + git checkout --ours . git add . - git commit -m "Write changelog for ${{ steps.bump-version.outputs.next-version }}" || true - git push --force origin version-prerelease-from-${{ steps.bump-version.outputs.current-version }} + git commit -m "Merge latest-release into version-1 with conflicts resolved to ours [skip ci]" - name: Generate PR description id: description @@ -144,14 +158,15 @@ jobs: gh pr edit \ --repo "${{github.repository }}" \ --title "Release: $CAPITALIZED_RELEASE_TYPE ${{ inputs.pre-id && format('{0} ', inputs.pre-id) }}${{ steps.bump-version.outputs.next-version }}" \ + --base ${{ steps.is-prerelease.outputs.prerelease == 'true' && 'next-release' || 'latest-release' }} \ --body "${{ steps.description.outputs.description }}" else gh pr create \ --repo "${{github.repository }}"\ --title "Release: $CAPITALIZED_RELEASE_TYPE ${{ inputs.pre-id && format('{0} ', inputs.pre-id) }}${{ steps.bump-version.outputs.next-version }}" \ --label "release" \ - --base next-release \ - --head version-prerelease-from-${{ steps.bump-version.outputs.current-version }} \ + --base ${{ steps.is-prerelease.outputs.prerelease == 'true' && 'next-release' || 'latest-release' }} \ + --head version-next-from-${{ steps.bump-version.outputs.current-version }} \ --body "${{ steps.description.outputs.description }}" fi diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index e81029088f8..ab71e8fe6b4 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -150,10 +150,10 @@ gitGraph commit checkout next merge some-bugfix type: HIGHLIGHT - branch version-prerelease-from-7.1.0-alpha.28 + branch version-next-from-7.1.0-alpha.28 commit id: "write changelog" checkout next-release - merge version-prerelease-from-7.1.0-alpha.28 + merge version-next-from-7.1.0-alpha.28 commit id: "bump versions" tag: "7.1.0-alpha.29" checkout next merge next-release @@ -562,11 +562,11 @@ gitGraph branch some-simultaneous-bugfix commit checkout next - branch version-prerelease-from-7.1.0-alpha.28 + branch version-next-from-7.1.0-alpha.28 commit id checkout next merge some-simultaneous-bugfix type: HIGHLIGHT id: "whoops!" - merge version-prerelease-from-7.1.0-alpha.28 tag: "v7.1.0-alpha.29" + merge version-next-from-7.1.0-alpha.28 tag: "v7.1.0-alpha.29" ``` When publishing at the last commit with tag `v7.1.0-alpha.29`, it will publish whatever the content is at that point (all the square dots), which includes the "whoops!" commit from merging the bugfix. But the bugfix was never part of the release pull request because it got prepared before the bugfix was merged in. @@ -586,19 +586,19 @@ gitGraph branch some-simultanous-bugfix commit checkout next - branch version-prerelease-from-7.1.0-alpha.28 + branch version-next-from-7.1.0-alpha.28 commit id: "write changelog" checkout next merge some-simultanous-bugfix id: "whoops!" checkout next-release - merge version-prerelease-from-7.1.0-alpha.28 + merge version-next-from-7.1.0-alpha.28 commit id: "bump versions" tag: "v7.1.0-alpha.29" checkout next merge next-release - branch version-prerelease-from-7.1.0-alpha.29 + branch version-next-from-7.1.0-alpha.29 commit id: "write changelog again" checkout next-release - merge version-prerelease-from-7.1.0-alpha.29 + merge version-next-from-7.1.0-alpha.29 commit id: "bump versions again" tag: "v7.1.0-alpha.30" checkout next merge next-release diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index f912c588522..7049a1aa44c 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -52,6 +52,7 @@ const CHANGE_TITLES_TO_IGNORE = [ /\[ci skip\]/i, /^Update CHANGELOG\.md for.*/i, /^Release: (Pre)?(Patch|Minor|Major|Release).*\d+$/i, + /^Update \.\/docs\/versions/, ]; export const mapToChangelist = ({ diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 70289b5369d..27d3a37b765 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -12,7 +12,7 @@ program .description( 'returns true if the versioning pull request associated with the current branch has the "freeze" label' ) - .option('-P, --patch', 'Look for patch PR instead of prerelease PR', false) + .option('-P, --patch', 'Look for hotfix PR instead of next PR', false) .option('-V, --verbose', 'Enable verbose logging', false); const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code'); @@ -43,10 +43,10 @@ const getRepo = async (verbose?: boolean): Promise => { }; export const run = async (options: unknown) => { - const { verbose, patch } = options as { verbose?: boolean; patch?: boolean }; + const { verbose, hotfix } = options as { verbose?: boolean; hotfix?: boolean }; const version = await getCurrentVersion(); - const branch = `version-${patch ? 'patch' : 'prerelease'}-from-${version}`; + const branch = `version-${hotfix ? 'hotfix' : 'next'}-from-${version}`; console.log(`💬 Determining if pull request from branch '${chalk.blue(branch)}' is frozen`); From 1161b7ada4b93a0f0bb6a9dda7a34abacbf479dd Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Tue, 29 Aug 2023 14:42:41 +0200 Subject: [PATCH 03/27] more renaming of new release workflows --- CONTRIBUTING/RELEASING.md | 42 +++++++++++----------- scripts/release/generate-pr-description.ts | 2 +- scripts/release/is-pr-frozen.ts | 2 +- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index ab71e8fe6b4..05533fccb38 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -45,8 +45,8 @@ This document explains the release process for the Storybook monorepo. There are two types: -1. Prereleases and major/minor releases - releasing any content that is on the `next` branch -2. Patch releases - picking any content from `next` to `main`, that needs to be patched back to the current stable minor release +1. `next`-releases - releasing any content that is on the `next` branch, either prereleases or stable releases +2. Hotfix releases - picking any content from `next` to `main`, that needs to be patched back to the current stable minor release The release process is based on automatically created "Release Pull Requests", that when merged will trigger a new version to be released. @@ -57,7 +57,7 @@ A designated Releaser -- which may rotate between core team members -- will go t - [Publish](../.github/workflows/publish.yml) > **Note** -> This document distinguishes between **patch** releases and **prereleases**. This is a simplification; stable major and minor releases work the same way as prereleases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. +> This document distinguishes between **`next`-releases** and **hotfix** releases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. ### Branches @@ -103,7 +103,7 @@ Two GitHub Actions workflows automatically create release pull requests, one for The high-level flow is: 1. When a PR is merged to `next` (or a commit is pushed), both release pull requests are (re)generated. -2. They create a new branch - `version-(patch|prerelease)-from-`. +2. They create a new branch - `version-(hotfix|next)-from-`. 3. They calculate which version to bump to according to the version strategy. 4. They update `CHANGELOG(.prerelease).md` with all changes detected. 5. They commit everything. @@ -126,7 +126,7 @@ A few key points to note in this flow: The default versioning strategy is to increase the current prerelease number, as described in [Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13). If there is no prerelease number (i.e., we just released a new stable minor/major version), it will add one to a patch bump, so it would go from `7.2.0` to `7.2.1-0` by default. -Prerelease PRs are only created if there are actual changes to release. Content labeled with "build" or "documentation" is [not considered "releasable"](#which-changes-are-considered-releasable-and-what-does-it-mean) and is not user-facing, so it doesn't make sense to create a release. This is explained in more detail in [Why are no release PRs being prepared?](#why-are-no-release-prs-being-prepared). +`next`-PRs are only created if there are actual changes to release. Content labeled with "build" or "documentation" is [not considered "releasable"](#which-changes-are-considered-releasable-and-what-does-it-mean) and is not user-facing, so it doesn't make sense to create a release. This is explained in more detail in [Why are no release PRs being prepared?](#why-are-no-release-prs-being-prepared). The preparation workflow will create a new branch from `next`, called `version-next-from-`, and open a pull request targeting `next-release`. When the Releaser merges it, the [publish workflow](#publishing) will merge `next-release` into `next`. @@ -245,9 +245,9 @@ The high-level workflow for a Releaser is: Look for the release pull request that has been prepared for the type of release you're about to release: -- "Release: Prerelease ``" for prereleases -- "Release: Patch ``" for patch releases -- "Release: Merge patches to `main` (without version bump)" for patches without releases +- "Release: Prerelease|Minor|Major ``" for releases from `next` +- "Release: Hotfix ``" for hotfix releases +- "Release: Merge patches to `main` (without version bump)" for hotfixes without releases For example: https://github.com/storybookjs/storybook/pull/23148 @@ -267,7 +267,7 @@ It is important to verify that the release includes the right content. Key eleme For example, check if it's a breaking change that isn't allowed in a minor prerelease, or if it's a new feature in a patch release. If it's not suitable, revert the pull request and notify the author. -Sometimes when doing a patch release, a pull request can have the "patch" label but you don't want that change to be part of this release. Maybe you're not confident in the change, or you require more input from maintainers before releasing it. In those situations you should remove the "patch" label from the pull request and follow through with the release (make sure to re-trigger the workflow). When the release is done, add the patch label back again, so it will be part of the next release. +Sometimes when doing a patch release, a pull request can have the "patch:yes" label but you don't want that change to be part of this release. Maybe you're not confident in the change, or you require more input from maintainers before releasing it. In those situations you should remove the "patch:yes" label from the pull request and follow through with the release (make sure to re-trigger the workflow). When the release is done, add the "patch:yes" label back again, so it will be part of the next release. 2. Is the pull request title correct? @@ -280,9 +280,9 @@ If a pull request changes multiple places, it can be hard to choose an area - th Some labels have specific meanings when it comes to releases. It's important that each pull request has labels that accurately describe the change, as labels can determine if a pull request is included in the changelog or not. This is explained further in the [Which changes are considered "releasable", and what does it mean?](#which-changes-are-considered-releasable-and-what-does-it-mean) section. -4. Patches: has it already been released in a prerelease? +4. Hotfixes: has it already been released in a prerelease? -If this is a patch release, make sure that all pull requests have already been released in a prerelease. If some haven't, create a new prerelease first. +If this is a hotfix release, make sure that all pull requests have already been released in a prerelease. If some haven't, create a new prerelease first. This is not a technical requirement, but it's a good practice to ensure that a change doesn't break a prerelease before releasing it to stable. @@ -301,12 +301,12 @@ When triggering the workflows, always choose the `next` branch as the base, unle The workflows can be triggered here: -- [Prepare prerelease PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) -- [Prepare patch PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml) +- [Prepare next PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) +- [Prepare hotfix PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml) Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - `next`-releases](#next-releases). When triggering the prerelease workflow manually, you can optionally add inputs: -![Screenshot of triggering the prerelease workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) +![Screenshot of triggering the next-release workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) See [Versioning Scenarios](#versioning-scenarios) for a description of each version bump scenario, how to activate it and what it does, and [Which combination of inputs creates the version bump I need?](#which-combination-of-inputs-creates-the-version-bump-i-need) for a detailed description of the workflow inputs. @@ -340,11 +340,11 @@ You can inspect the workflows to see what they are running and copy that, but he Before you start you should make sure that your working tree is clean and the repository is in a clean state by running `git clean -xdf`. -1. Create a new branch from either `next` (prereleases) or `main` (patches) +1. Create a new branch from either `next` or `main` (hotfixes) 2. Get all tags: `git fetch --tags origin` 3. Install dependencies: `yarn task --task=install --start-from=install` 4. `cd scripts` -5. (If patch release) Cherry pick: +5. (If hotfix release) Cherry pick: 1. `yarn release:pick-patches` 2. Manually cherry pick any necessary patches based on the previous output 6. Bump versions: @@ -362,21 +362,21 @@ Before you start you should make sure that your working tree is clean and the re 12. (If automatic publishing is still working, it should kick in now and the rest of the steps can be skipped) 13. `cd ..` 14. Publish to the registry: `YARN_NPM_AUTH_TOKEN= yarn release:publish --tag <"next" OR "latest"> --verbose` -15. (If patch release) `yarn release:label-patches` +15. (If hotfix release) `yarn release:label-patches` 16. Manually create a GitHub Release with a tag that is the new version and the target being `latest-release` or `next-release`. 17. Merge to core branch: 1. `git checkout <"next"|"main">` 2. `git pull` 3. `git merge <"next-release"|"latest-release">` 4. `git push origin` -18. (If patch release) Sync `CHANGELOG.md` to `next` with: +18. (If hotfix release) Sync `CHANGELOG.md` to `next` with: 1. `git checkout next` 2. `git pull` 3. `git checkout origin/main ./CHANGELOG.md` 4. `git add ./CHANGELOG.md` 5. `git commit -m "Update CHANGELOG.md for v"` 6. `git push origin` -19. (If prerelease) Sync `versions/next.json` from `next` to `main` +19. (If `next`-release) Sync `versions/next.json` from `next` to `main` 1. `git checkout main` 2. `git pull` 3. `git checkout origin/next ./docs/versions/next.json` @@ -448,11 +448,9 @@ To promote a prerelease to a new prerelease ID, during the [Re-trigger the Workf To promote a prerelease to a stable reelase, during the [Re-trigger the Workflow](#4-re-trigger-the-workflow) step, choose: -- Release type: Patch +- Release type: Patch, Minor or Major - Prerelease ID: Leave empty -The "Patch" release type ensures the current prerelease version gets promoted to a stable version without any major/minor/patch bumps. - This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-from- `latest-release` -> `main`. When this is done, the Releaser will need to trigger a new release on `next` that bumps the version to a new prerelease minor as described [just below](#first-prerelease-of-new-majorminor---710---720-alpha0-or-800-alpha0). ### First prerelease of new major/minor - `7.1.0` -> `7.2.0-alpha.0` or `8.0.0-alpha.0` diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index 7049a1aa44c..aa7dfe7e53e 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -18,7 +18,7 @@ program 'Which version to generate changelog from, eg. "7.0.7". Defaults to the version at code/package.json' ) .option('-N, --next-version ', 'Which version to generate changelog to, eg. "7.0.8"') - .option('-P, --unpicked-patches', 'Set to only consider PRs labeled with "patch" label') + .option('-P, --unpicked-patches', 'Set to only consider PRs labeled with "patch:yes" label') .option( '-M, --manual-cherry-picks ', 'A stringified JSON array of commit hashes, of patch PRs that needs to be cherry-picked manually' diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 27d3a37b765..56f75d0ba33 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -12,7 +12,7 @@ program .description( 'returns true if the versioning pull request associated with the current branch has the "freeze" label' ) - .option('-P, --patch', 'Look for hotfix PR instead of next PR', false) + .option('-H, --hotfix', 'Look for hotfix PR instead of next PR', false) .option('-V, --verbose', 'Enable verbose logging', false); const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code'); From d6297bff9975b1e1d2949eab0461fe0ac24338e0 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 30 Aug 2023 09:19:30 +0200 Subject: [PATCH 04/27] only commit changelog changes when there are actual changes --- .github/workflows/publish.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 18e23c17416..e52af0dc1c7 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -160,8 +160,10 @@ jobs: git pull git checkout origin/main ./CHANGELOG.md git add ./CHANGELOG.md - git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" - git push origin next + if ! git diff-index --quiet HEAD; then + git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" + git push origin next + fi - name: Sync version JSONs from `next-release` to `main` if: github.ref_name == 'next-release' From 670bc5167af3f3ec70653460c864e5c91dadb633 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 30 Aug 2023 10:01:55 +0200 Subject: [PATCH 05/27] add comments --- .github/workflows/publish.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e52af0dc1c7..821bbf1d15e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -111,6 +111,12 @@ jobs: id: target run: echo "target=${{ github.ref_name == 'next-release' && 'next' || 'main' }}" >> $GITHUB_OUTPUT + # TODO: create a commit on next that does the following: + # bump version accordingly + # update changelog + # update version JSONs + # push to next + # TODO: ensure all of this logic works if a release from next targets latest-release. steps.targets.outputs? - name: Get changelog for ${{ steps.version.outputs.current-version }} if: steps.publish-needed.outputs.published == 'false' id: changelog @@ -160,6 +166,7 @@ jobs: git pull git checkout origin/main ./CHANGELOG.md git add ./CHANGELOG.md + # only commit if there are changes if ! git diff-index --quiet HEAD; then git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" git push origin next From d5df684fc7c107bceffddfa176d2799c60c9a58e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 30 Aug 2023 10:44:03 +0200 Subject: [PATCH 06/27] simplify --- .github/workflows/publish.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 821bbf1d15e..47e9798ca46 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -166,11 +166,8 @@ jobs: git pull git checkout origin/main ./CHANGELOG.md git add ./CHANGELOG.md - # only commit if there are changes - if ! git diff-index --quiet HEAD; then - git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" - git push origin next - fi + git commit -m "Update CHANGELOG.md for v${{ steps.version.outputs.current-version }} [skip ci]" || true + git push origin next - name: Sync version JSONs from `next-release` to `main` if: github.ref_name == 'next-release' From 431ec639fe559b0911427087a8f1d3c0b53cea53 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 31 Aug 2023 11:14:00 +0200 Subject: [PATCH 07/27] ensure next is always ahead of main during stable releases --- .github/workflows/publish.yml | 38 ++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 47e9798ca46..c31ef7c6a0d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -63,7 +63,6 @@ jobs: yarn install - name: Apply deferred version bump and commit - id: version-bump working-directory: . run: | CURRENT_VERSION=$(cat ./code/package.json | jq '.version') @@ -111,11 +110,6 @@ jobs: id: target run: echo "target=${{ github.ref_name == 'next-release' && 'next' || 'main' }}" >> $GITHUB_OUTPUT - # TODO: create a commit on next that does the following: - # bump version accordingly - # update changelog - # update version JSONs - # push to next # TODO: ensure all of this logic works if a release from next targets latest-release. steps.targets.outputs? - name: Get changelog for ${{ steps.version.outputs.current-version }} if: steps.publish-needed.outputs.published == 'false' @@ -129,6 +123,7 @@ jobs: # 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 + # TODO: this won't work anymore for releases from next to latest-release - 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: @@ -157,6 +152,33 @@ jobs: git merge ${{ github.ref_name }} git push origin ${{ steps.target.outputs.target }} + # This step ensures that next is always one minor ahead of main + # this is needed when releasing a stable from next + # next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0 + # then we release 7.4.0 by merging next to latest-release to main + # we then ensure here that next is bumped to 7.5.0 - without releasing it + # if this is a hotfix release bumping main to 7.3.1, next will not be touched because it's already ahead + - name: Ensure `next` is a minor version ahead of `main` + if: github.ref_name == 'latest-release' + run: | + git checkout next + git pull + + CODE_PKG_JSON=$(cat ../code/package.json) + VERSION_ON_NEXT=$(echo $CODE_PKG_JSON | jq --raw-output '.version') + VERSION_ON_MAIN="${{ steps.version.outputs.current-version }}" + + # check if next is behind current version + if NEXT_IS_BEHIND=$(npx semver --include-prerelease --range "<$VERSION_ON_MAIN" "$VERSION_ON_NEXT" 2>/dev/null); then + # temporarily set the version on next to be the same as main... + echo "$CODE_PKG_JSON" | jq --arg version "$VERSION_ON_MAIN" '.version = $version' > ../code/package.json + # ... then bump it by one minor + yarn release:version --release-type minor + git add .. + git commit -m "Bump next to be one minor ahead of main [skip ci]" + git push origin next + fi + - name: Sync CHANGELOG.md from `main` to `next` if: github.ref_name == 'latest-release' working-directory: . @@ -182,10 +204,6 @@ jobs: git commit -m "Update $VERSION_FILE for v${{ steps.version.outputs.current-version }}" git push origin main - - name: Overwrite main with next - if: steps.target.outputs.target == 'next' && steps.is-prerelease.outputs.prerelease == 'false' - run: git push --force origin next:main - - name: Report job failure to Discord if: failure() env: From 507516571bdf628c944632e78c136d859c7ee68e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 31 Aug 2023 12:51:27 +0200 Subject: [PATCH 08/27] improve readability of publish script --- .github/workflows/publish.yml | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c31ef7c6a0d..4f71006b763 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -110,7 +110,6 @@ jobs: id: target run: echo "target=${{ github.ref_name == 'next-release' && 'next' || 'main' }}" >> $GITHUB_OUTPUT - # TODO: ensure all of this logic works if a release from next targets latest-release. steps.targets.outputs? - name: Get changelog for ${{ steps.version.outputs.current-version }} if: steps.publish-needed.outputs.published == 'false' id: changelog @@ -122,13 +121,11 @@ jobs: 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 - # TODO: this won't work anymore for releases from next to latest-release - 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) + if: github.ref_name == 'latest-release' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release:label-patches ${{ steps.target.outputs.target == 'next' && '--all' || '' }} + run: yarn release:label-patches - name: Create GitHub Release if: steps.publish-needed.outputs.published == 'false' @@ -159,7 +156,7 @@ jobs: # we then ensure here that next is bumped to 7.5.0 - without releasing it # if this is a hotfix release bumping main to 7.3.1, next will not be touched because it's already ahead - name: Ensure `next` is a minor version ahead of `main` - if: github.ref_name == 'latest-release' + if: steps.target.outputs.target == 'main' run: | git checkout next git pull @@ -168,19 +165,21 @@ jobs: VERSION_ON_NEXT=$(echo $CODE_PKG_JSON | jq --raw-output '.version') VERSION_ON_MAIN="${{ steps.version.outputs.current-version }}" - # check if next is behind current version - if NEXT_IS_BEHIND=$(npx semver --include-prerelease --range "<$VERSION_ON_MAIN" "$VERSION_ON_NEXT" 2>/dev/null); then - # temporarily set the version on next to be the same as main... - echo "$CODE_PKG_JSON" | jq --arg version "$VERSION_ON_MAIN" '.version = $version' > ../code/package.json - # ... then bump it by one minor - yarn release:version --release-type minor - git add .. - git commit -m "Bump next to be one minor ahead of main [skip ci]" - git push origin next + # skip if next is already ahead of main + if NEXT_IS_AHEAD=$(npx semver --include-prerelease --range ">=$VERSION_ON_MAIN" "$VERSION_ON_NEXT" 2>/dev/null); then + return fi + # temporarily set the version on next to be the same as main... + echo "$CODE_PKG_JSON" | jq --arg version "$VERSION_ON_MAIN" '.version = $version' > ../code/package.json + # ... then bump it by one minor + yarn release:version --release-type minor + git add .. + git commit -m "Bump next to be one minor ahead of main [skip ci]" + git push origin next + - name: Sync CHANGELOG.md from `main` to `next` - if: github.ref_name == 'latest-release' + if: steps.target.outputs.target == 'main' working-directory: . run: | git fetch origin next From 44e988cd10aac138cf5da37a9335618e3b68935e Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Thu, 31 Aug 2023 12:53:37 +0200 Subject: [PATCH 09/27] pul all release workflows in same concurrency group --- .github/workflows/prepare-hotfix-release.yml | 4 ++-- .github/workflows/prepare-next-release.yml | 4 ++-- .github/workflows/publish.yml | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-hotfix-release.yml index 2a331fb5f3e..83109bcf52c 100644 --- a/.github/workflows/prepare-hotfix-release.yml +++ b/.github/workflows/prepare-hotfix-release.yml @@ -12,8 +12,8 @@ env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true + group: release-group + cancel-in-progress: false jobs: prepare-hotfix-pull-request: diff --git a/.github/workflows/prepare-next-release.yml b/.github/workflows/prepare-next-release.yml index ad79b5f1b3f..4add96125d3 100644 --- a/.github/workflows/prepare-next-release.yml +++ b/.github/workflows/prepare-next-release.yml @@ -30,8 +30,8 @@ env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 concurrency: - group: ${{ github.workflow }} - cancel-in-progress: true + group: release-group + cancel-in-progress: false jobs: prepare-next-pull-request: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4f71006b763..f2ee9cbc12a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,8 @@ permissions: pull-requests: write concurrency: - group: ${{ github.workflow }}-${{ github.ref_name }} + group: release-group + cancel-in-progress: false jobs: publish: From bff632d0474999cb07989d1620b8cb782eeb1094 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 1 Sep 2023 11:17:24 +0200 Subject: [PATCH 10/27] add todos --- .github/workflows/publish.yml | 2 +- scripts/release/is-pr-frozen.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f2ee9cbc12a..505c9f301a0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,7 +36,7 @@ jobs: run: | gh run cancel ${{ github.run_id }} gh run watch ${{ github.run_id }} - + # TODO: here, cancel any running AND pending runs for the preparation workflows: https://stackoverflow.com/questions/60753453/how-to-cancel-run-for-all-scheduled-github-actions-at-once - name: Checkout ${{ github.ref_name }} uses: actions/checkout@v3 with: diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 56f75d0ba33..d053f3f5973 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -78,6 +78,7 @@ export const run = async (options: unknown) => { console.log(`🔍 Found pull request: ${JSON.stringify(pullRequest, null, 2)}`); + // TODO: check if pull request is still open const isFrozen = pullRequest.labels?.includes('freeze'); if (process.env.GITHUB_ACTIONS === 'true') { setOutput('frozen', isFrozen); From ba8e5feb7208822b175c0cf5f4b154e78a902832 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 8 Sep 2023 14:35:29 +0200 Subject: [PATCH 11/27] cancel any release-preparation runs in progress --- .github/workflows/publish.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1bca24753ab..272fa1af341 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,7 +36,23 @@ jobs: run: | gh run cancel ${{ github.run_id }} gh run watch ${{ github.run_id }} - # TODO: here, cancel any running AND pending runs for the preparation workflows: https://stackoverflow.com/questions/60753453/how-to-cancel-run-for-all-scheduled-github-actions-at-once + + - name: Cancel all release preparation runs + run: | + # Get a list of all running or pending release preparation runs + # combining both the prepare-hotfix-release.yml and prepare-next-release.yml workflows + RUNNING_RELEASE_PREPARATIONS=$( + { + gh run list --limit 50 --workflow=prepare-hotfix-release.yml --json databaseId,status + gh run list --limit 50 --workflow=prepare-next-release.yml --json databaseId,status + } | jq -rc '.[] | select(.status | contains("in_progress", "pending", "queued", "requested", "waiting")) | .databaseId' + ) + + # Loop through each run and pass it to the "gh run cancel" command + while IFS= read -r databaseId; do + gh run cancel "$databaseId" + done <<< "$RUNNING_RELEASE_PREPARATIONS" + - name: Checkout ${{ github.ref_name }} uses: actions/checkout@v3 with: From a84e270f67944997ca5870a465088a48187915bb Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 8 Sep 2023 14:53:55 +0200 Subject: [PATCH 12/27] cleanup --- .github/workflows/prepare-hotfix-release.yml | 4 ++-- .github/workflows/prepare-next-release.yml | 6 +++--- .github/workflows/publish.yml | 3 +-- CONTRIBUTING/RELEASING.md | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-hotfix-release.yml index f5a44306fe5..032357306f5 100644 --- a/.github/workflows/prepare-hotfix-release.yml +++ b/.github/workflows/prepare-hotfix-release.yml @@ -12,8 +12,8 @@ env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 concurrency: - group: release-group - cancel-in-progress: false + group: ${{ github.workflow }} + cancel-in-progress: true jobs: prepare-hotfix-pull-request: diff --git a/.github/workflows/prepare-next-release.yml b/.github/workflows/prepare-next-release.yml index 4ea2c9b3c08..e1b4b45a016 100644 --- a/.github/workflows/prepare-next-release.yml +++ b/.github/workflows/prepare-next-release.yml @@ -30,8 +30,8 @@ env: PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 1 concurrency: - group: release-group - cancel-in-progress: false + group: ${{ github.workflow }} + cancel-in-progress: true jobs: prepare-next-pull-request: @@ -140,7 +140,7 @@ jobs: git pull origin latest-release git checkout --ours . git add . - git commit -m "Merge latest-release into version-1 with conflicts resolved to ours [skip ci]" + git commit -m "Merge latest-release into version-next-from-${{ steps.bump-version.outputs.current-version }} with conflicts resolved to ours [skip ci]" - name: Generate PR description id: description diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 272fa1af341..f5efac64565 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,8 +16,7 @@ permissions: pull-requests: write concurrency: - group: release-group - cancel-in-progress: false + group: ${{ github.workflow }}-${{ github.ref_name }} jobs: publish: diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index 05533fccb38..c22bf923e07 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -451,7 +451,7 @@ To promote a prerelease to a stable reelase, during the [Re-trigger the Workflow - Release type: Patch, Minor or Major - Prerelease ID: Leave empty -This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-from- `latest-release` -> `main`. When this is done, the Releaser will need to trigger a new release on `next` that bumps the version to a new prerelease minor as described [just below](#first-prerelease-of-new-majorminor---710---720-alpha0-or-800-alpha0). +This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-from- `latest-release` -> `main`. ### First prerelease of new major/minor - `7.1.0` -> `7.2.0-alpha.0` or `8.0.0-alpha.0` From e1a2172dfb662622b6e656692d3c6b84b6bd0256 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 8 Sep 2023 15:15:05 +0200 Subject: [PATCH 13/27] only consider open PRs when looking for frozen state --- scripts/release/is-pr-frozen.ts | 8 ++++++++ scripts/release/utils/get-github-info.ts | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index d053f3f5973..27ec56c65c6 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -78,6 +78,14 @@ export const run = async (options: unknown) => { console.log(`🔍 Found pull request: ${JSON.stringify(pullRequest, null, 2)}`); + if (pullRequest.state !== 'OPEN') { + console.log('❌ The pull request is already closed, ignoring it'); + if (process.env.GITHUB_ACTIONS === 'true') { + setOutput('frozen', false); + } + return false; + } + // TODO: check if pull request is still open const isFrozen = pullRequest.labels?.includes('freeze'); if (process.env.GITHUB_ACTIONS === 'true') { diff --git a/scripts/release/utils/get-github-info.ts b/scripts/release/utils/get-github-info.ts index 6bd7126aec0..ce58b782235 100644 --- a/scripts/release/utils/get-github-info.ts +++ b/scripts/release/utils/get-github-info.ts @@ -40,6 +40,7 @@ function makeQuery(repos: ReposWithCommitsAndPRsToFetch) { number id title + state url mergedAt labels(first: 50) { @@ -63,6 +64,7 @@ function makeQuery(repos: ReposWithCommitsAndPRsToFetch) { : `pr__${data.pull}: pullRequest(number: ${data.pull}) { url title + state author { login url @@ -161,6 +163,7 @@ export type PullRequestInfo = { user: string | null; id: string | null; title: string | null; + state: string | null; commit: string | null; pull: number | null; labels: string[] | null; @@ -197,6 +200,7 @@ export async function getPullInfoFromCommit(request: { pull: null, commit: request.commit, title: null, + state: null, labels: null, links: { commit: request.commit, @@ -245,6 +249,7 @@ export async function getPullInfoFromCommit(request: { pull: associatedPullRequest ? associatedPullRequest.number : null, commit: request.commit, title: associatedPullRequest ? associatedPullRequest.title : null, + state: associatedPullRequest ? associatedPullRequest.state : null, labels: associatedPullRequest ? (associatedPullRequest.labels.nodes || []).map((label: { name: string }) => label.name) : null, @@ -287,6 +292,7 @@ export async function getPullInfoFromPullRequest(request: { pull: request.pull, commit: commit ? commit.oid : null, title: title || null, + state: data?.state || null, labels: data ? (data.labels.nodes || []).map((label: { name: string }) => label.name) : null, links: { commit: commit ? `[\`${commit.oid}\`](${commit.commitUrl})` : null, From 4d569f2f6735a7fe55440a7c450566ddf5f47837 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Fri, 8 Sep 2023 15:20:16 +0200 Subject: [PATCH 14/27] update tests --- .../__tests__/generate-pr-description.test.ts | 8 ++++---- scripts/release/__tests__/is-pr-frozen.test.ts | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/scripts/release/__tests__/generate-pr-description.test.ts b/scripts/release/__tests__/generate-pr-description.test.ts index b0f1bbe89db..fdd412462ae 100644 --- a/scripts/release/__tests__/generate-pr-description.test.ts +++ b/scripts/release/__tests__/generate-pr-description.test.ts @@ -213,7 +213,7 @@ For each pull request below, you need to either manually cherry pick it, or disc - [ ] [#42](https://github.com/storybookjs/storybook/pull/42): \\\`git cherry-pick -m1 -x abc123\\\` - 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. + 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-next-release.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. @@ -273,7 +273,7 @@ For each pull request below, you need to either manually cherry pick it, or disc - [ ] [#42](https://github.com/storybookjs/storybook/pull/42): \\\`git cherry-pick -m1 -x abc123\\\` - 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. + 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-hotfix-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. @@ -340,7 +340,7 @@ 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. + 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-next-release.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. @@ -395,7 +395,7 @@ 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. + 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-hotfix-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. diff --git a/scripts/release/__tests__/is-pr-frozen.test.ts b/scripts/release/__tests__/is-pr-frozen.test.ts index 63747a863dd..d5d1e8a16b7 100644 --- a/scripts/release/__tests__/is-pr-frozen.test.ts +++ b/scripts/release/__tests__/is-pr-frozen.test.ts @@ -26,6 +26,7 @@ describe('isPrFrozen', () => { it('should return true when PR is frozen', async () => { getPullInfoFromCommit.mockResolvedValue({ labels: ['freeze'], + state: 'OPEN', }); await expect(isPrFrozen({ patch: false })).resolves.toBe(true); }); @@ -33,17 +34,26 @@ describe('isPrFrozen', () => { it('should return false when PR is not frozen', async () => { getPullInfoFromCommit.mockResolvedValue({ labels: [], + state: 'OPEN', }); await expect(isPrFrozen({ patch: false })).resolves.toBe(false); }); - it('should look for patch PRs when patch is true', async () => { + it('should return false when PR is closed', async () => { + getPullInfoFromCommit.mockResolvedValue({ + labels: ['freeze'], + state: 'CLOSED', + }); + await expect(isPrFrozen({ patch: false })).resolves.toBe(false); + }); + + it('should look for patch PRs when hotfix is true', async () => { getPullInfoFromCommit.mockResolvedValue({ labels: [], }); - await isPrFrozen({ patch: true }); + await isPrFrozen({ hotfix: true }); - expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-patch-from-1.0.0', { + expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-hotfix-from-1.0.0', { '--depth': 1, }); }); @@ -54,7 +64,7 @@ describe('isPrFrozen', () => { }); await isPrFrozen({ patch: false }); - expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-prerelease-from-1.0.0', { + expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-next-from-1.0.0', { '--depth': 1, }); }); From 4bf6ba4df6ab08d758b2d7f5bdc0da6aa4f1f61c Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Sun, 24 Sep 2023 08:53:41 +0200 Subject: [PATCH 15/27] cleanup --- scripts/release/is-pr-frozen.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 27ec56c65c6..364d7289cc9 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -86,7 +86,6 @@ export const run = async (options: unknown) => { return false; } - // TODO: check if pull request is still open const isFrozen = pullRequest.labels?.includes('freeze'); if (process.env.GITHUB_ACTIONS === 'true') { setOutput('frozen', isFrozen); From 25ebf5c4dce553632e62f638422d6b2bbe59e2dc Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Sun, 24 Sep 2023 09:03:10 +0200 Subject: [PATCH 16/27] fix type errors --- scripts/release/__tests__/generate-pr-description.test.ts | 6 ++++++ scripts/release/__tests__/label-patches.test.ts | 1 + scripts/release/generate-pr-description.ts | 4 ++-- scripts/release/utils/get-changes.ts | 2 +- scripts/release/utils/get-github-info.ts | 7 ++----- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/release/__tests__/generate-pr-description.test.ts b/scripts/release/__tests__/generate-pr-description.test.ts index fdd412462ae..306ad627ee7 100644 --- a/scripts/release/__tests__/generate-pr-description.test.ts +++ b/scripts/release/__tests__/generate-pr-description.test.ts @@ -15,6 +15,7 @@ describe('Generate PR Description', () => { labels: ['bug', 'build', 'other label', 'patch:yes'], commit: 'abc123', pull: 42, + state: 'MERGED', links: { commit: '[abc123](https://github.com/storybookjs/storybook/commit/abc123)', pull: '[#42](https://github.com/storybookjs/storybook/pull/42)', @@ -26,6 +27,7 @@ describe('Generate PR Description', () => { id: null, user: 'storybook-bot', pull: null, + state: null, commit: '012b58140c3606efeacbe99c0c410624b0a1ed1f', title: 'Bump version on `next`: preminor (alpha) from 7.2.0 to 7.3.0-alpha.0', labels: null, @@ -41,6 +43,7 @@ describe('Generate PR Description', () => { user: 'shilman', title: 'Some title for a "direct commit"', labels: null, + state: null, commit: '22bb11', pull: null, links: { @@ -55,6 +58,7 @@ describe('Generate PR Description', () => { title: 'Another PR `title` for docs', labels: ['another label', 'documentation', 'patch:yes'], commit: 'ddd222', + state: 'MERGED', pull: 11, links: { commit: '[ddd222](https://github.com/storybookjs/storybook/commit/ddd222)', @@ -69,6 +73,7 @@ describe('Generate PR Description', () => { labels: ['feature request', 'other label'], commit: 'wow1337', pull: 48, + state: 'MERGED', links: { commit: '[wow1337](https://github.com/storybookjs/storybook/commit/wow1337)', pull: '[#48](https://github.com/storybookjs/storybook/pull/48)', @@ -81,6 +86,7 @@ describe('Generate PR Description', () => { title: 'Some PR title with a missing label', labels: ['incorrect label', 'other label'], commit: 'bad999', + state: 'MERGED', pull: 77, links: { commit: '[bad999](https://github.com/storybookjs/storybook/commit/bad999)', diff --git a/scripts/release/__tests__/label-patches.test.ts b/scripts/release/__tests__/label-patches.test.ts index d98abc7eb76..d43290a1828 100644 --- a/scripts/release/__tests__/label-patches.test.ts +++ b/scripts/release/__tests__/label-patches.test.ts @@ -58,6 +58,7 @@ const pullInfoMock = { commit: '930b47f011f750c44a1782267d698ccdd3c04da3', title: 'Legal: Fix license', labels: ['documentation', 'patch:yes', 'patch:done'], + state: 'MERGED', links: { commit: '[`930b47f011f750c44a1782267d698ccdd3c04da3`](https://github.com/storybookjs/storybook/commit/930b47f011f750c44a1782267d698ccdd3c04da3)', diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index aa7dfe7e53e..8a83e20d4f1 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -66,7 +66,7 @@ export const mapToChangelist = ({ .filter((change) => { // eslint-disable-next-line no-restricted-syntax for (const titleToIgnore of CHANGE_TITLES_TO_IGNORE) { - if (change.title.match(titleToIgnore)) { + if (change.title?.match(titleToIgnore)) { return false; } } @@ -91,7 +91,7 @@ export const mapToChangelist = ({ )[0] || 'unknown') as keyof typeof LABELS_BY_IMPORTANCE; return `- [ ] **${LABELS_BY_IMPORTANCE[label]}**: ${change.title} ${change.links.pull}${ - !unpickedPatches && change.labels.includes('patch:yes') ? ' (will also be patched)' : '' + !unpickedPatches && change.labels?.includes('patch:yes') ? ' (will also be patched)' : '' }`; }) .join('\n'); diff --git a/scripts/release/utils/get-changes.ts b/scripts/release/utils/get-changes.ts index 1ad2a075919..416ea624fb5 100644 --- a/scripts/release/utils/get-changes.ts +++ b/scripts/release/utils/get-changes.ts @@ -53,7 +53,7 @@ export const getFromCommit = async (from?: string | undefined, verbose?: boolean console.log(`🔍 No 'from' specified, found latest tag: ${chalk.blue(latest)}`); } } - const commit = await getCommitAt(actualFrom, verbose); + const commit = await getCommitAt(actualFrom!, verbose); if (verbose) { console.log(`🔍 Found 'from' commit: ${chalk.blue(commit)}`); } diff --git a/scripts/release/utils/get-github-info.ts b/scripts/release/utils/get-github-info.ts index ce58b782235..bdfe995a900 100644 --- a/scripts/release/utils/get-github-info.ts +++ b/scripts/release/utils/get-github-info.ts @@ -168,7 +168,7 @@ export type PullRequestInfo = { pull: number | null; labels: string[] | null; links: { - commit: string; + commit: string | null; pull: string | null; user: string | null; }; @@ -209,10 +209,7 @@ export async function getPullInfoFromCommit(request: { }, }; } - let user = null; - if (data.author && data.author.user) { - user = data.author.user; - } + let user = data?.author?.user || null; const associatedPullRequest = data.associatedPullRequests && From 9ca7ed4ce4aae1372d40c52e17ea7f9a4bfdfde6 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 06:22:21 -0500 Subject: [PATCH 17/27] cancel when 0 patches to pick --- .github/workflows/prepare-hotfix-release.yml | 9 +++++++++ scripts/release/pick-patches.ts | 1 + 2 files changed, 10 insertions(+) diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-hotfix-release.yml index 032357306f5..43636b8294a 100644 --- a/.github/workflows/prepare-hotfix-release.yml +++ b/.github/workflows/prepare-hotfix-release.yml @@ -88,6 +88,15 @@ jobs: git config --global user.email '32066757+storybook-bot@users.noreply.github.com' yarn release:pick-patches + - name: Cancel when no hotfixes to pick + if: steps.pick-patches.outputs.pr-count == '0' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # From https://stackoverflow.com/a/75809743 + run: | + gh run cancel ${{ github.run_id }} + gh run watch ${{ github.run_id }} + - name: Bump version deferred id: bump-version if: steps.unreleased-changes.outputs.has-changes-to-release == 'true' diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index eab1743dcc7..9d3169d78c8 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -80,6 +80,7 @@ export const run = async (_: unknown) => { } if (process.env.GITHUB_ACTIONS === 'true') { + setOutput('pr-count', JSON.stringify(patchPRs.length)); setOutput('failed-cherry-picks', JSON.stringify(failedCherryPicks)); } }; From 22f12e68f4ebb89c8f39111ee2e709fef0105f6d Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 06:23:23 -0500 Subject: [PATCH 18/27] temp rename workflow to test --- .../{prepare-hotfix-release.yml => prepare-patch-release.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{prepare-hotfix-release.yml => prepare-patch-release.yml} (100%) diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-patch-release.yml similarity index 100% rename from .github/workflows/prepare-hotfix-release.yml rename to .github/workflows/prepare-patch-release.yml From e719c5394499fc1232687c29e7eceed5b61aea0b Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 07:15:33 -0500 Subject: [PATCH 19/27] try number when reading pr-count --- .github/workflows/prepare-patch-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 43636b8294a..7fd805c34a6 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -89,7 +89,7 @@ jobs: yarn release:pick-patches - name: Cancel when no hotfixes to pick - if: steps.pick-patches.outputs.pr-count == '0' + if: steps.pick-patches.outputs.pr-count == 0 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # From https://stackoverflow.com/a/75809743 From d7575904db51b5e167ec7167a1be10bf49b4c498 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 07:21:37 -0500 Subject: [PATCH 20/27] compare less than 1 --- .github/workflows/prepare-patch-release.yml | 2 +- scripts/release/pick-patches.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 7fd805c34a6..99460e11cce 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -89,7 +89,7 @@ jobs: yarn release:pick-patches - name: Cancel when no hotfixes to pick - if: steps.pick-patches.outputs.pr-count == 0 + if: steps.pick-patches.outputs.pr-count < 1 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # From https://stackoverflow.com/a/75809743 diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index 9d3169d78c8..449a31ceaeb 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -80,7 +80,7 @@ export const run = async (_: unknown) => { } if (process.env.GITHUB_ACTIONS === 'true') { - setOutput('pr-count', JSON.stringify(patchPRs.length)); + setOutput('pr-count', patchPRs.length); setOutput('failed-cherry-picks', JSON.stringify(failedCherryPicks)); } }; From 8466a08300c98079922f59f1820f81f59c8351a4 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 07:33:32 -0500 Subject: [PATCH 21/27] check non-null in pr count --- .github/workflows/prepare-patch-release.yml | 2 +- scripts/release/pick-patches.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-patch-release.yml index 99460e11cce..bd773a24610 100644 --- a/.github/workflows/prepare-patch-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -89,7 +89,7 @@ jobs: yarn release:pick-patches - name: Cancel when no hotfixes to pick - if: steps.pick-patches.outputs.pr-count < 1 + if: steps.pick-patches.outputs.pr-count == '0' && steps.pick-patches.outputs.pr-count != null env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # From https://stackoverflow.com/a/75809743 diff --git a/scripts/release/pick-patches.ts b/scripts/release/pick-patches.ts index 449a31ceaeb..9d3169d78c8 100644 --- a/scripts/release/pick-patches.ts +++ b/scripts/release/pick-patches.ts @@ -80,7 +80,7 @@ export const run = async (_: unknown) => { } if (process.env.GITHUB_ACTIONS === 'true') { - setOutput('pr-count', patchPRs.length); + setOutput('pr-count', JSON.stringify(patchPRs.length)); setOutput('failed-cherry-picks', JSON.stringify(failedCherryPicks)); } }; From d83d4227898982548a0a9ca4b7da736966115dd0 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Mon, 25 Sep 2023 07:41:01 -0500 Subject: [PATCH 22/27] rename patch workflow --- .../{prepare-patch-release.yml => prepare-hotfix-release.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{prepare-patch-release.yml => prepare-hotfix-release.yml} (100%) diff --git a/.github/workflows/prepare-patch-release.yml b/.github/workflows/prepare-hotfix-release.yml similarity index 100% rename from .github/workflows/prepare-patch-release.yml rename to .github/workflows/prepare-hotfix-release.yml From ca7f6f8e7fe8cbd08e9d0f5a98dd9bbdb5543ebe Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 4 Oct 2023 11:11:38 +0200 Subject: [PATCH 23/27] hotfix -> patch, next-release -> non-patch-release --- ...ease.yml => prepare-non-patch-release.yml} | 0 ...-release.yml => prepare-patch-release.yml} | 8 +-- .github/workflows/publish.yml | 10 +-- CONTRIBUTING/RELEASING.md | 72 +++++++++---------- .../__tests__/generate-pr-description.test.ts | 8 +-- .../release/__tests__/is-pr-frozen.test.ts | 6 +- scripts/release/generate-pr-description.ts | 6 +- scripts/release/is-pr-frozen.ts | 6 +- 8 files changed, 58 insertions(+), 58 deletions(-) rename .github/workflows/{prepare-next-release.yml => prepare-non-patch-release.yml} (100%) rename .github/workflows/{prepare-hotfix-release.yml => prepare-patch-release.yml} (97%) diff --git a/.github/workflows/prepare-next-release.yml b/.github/workflows/prepare-non-patch-release.yml similarity index 100% rename from .github/workflows/prepare-next-release.yml rename to .github/workflows/prepare-non-patch-release.yml diff --git a/.github/workflows/prepare-hotfix-release.yml b/.github/workflows/prepare-patch-release.yml similarity index 97% rename from .github/workflows/prepare-hotfix-release.yml rename to .github/workflows/prepare-patch-release.yml index bd773a24610..5cdc1ba44fc 100644 --- a/.github/workflows/prepare-hotfix-release.yml +++ b/.github/workflows/prepare-patch-release.yml @@ -16,8 +16,8 @@ concurrency: cancel-in-progress: true jobs: - prepare-hotfix-pull-request: - name: Prepare hotfix pull request + prepare-patch-pull-request: + name: Prepare patch pull request runs-on: ubuntu-latest environment: release defaults: @@ -56,7 +56,7 @@ jobs: id: check-frozen env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn release:is-pr-frozen --hotfix + run: yarn release:is-pr-frozen --patch - name: Cancel when frozen if: steps.check-frozen.outputs.frozen == 'true' && github.event_name != 'workflow_dispatch' @@ -88,7 +88,7 @@ jobs: git config --global user.email '32066757+storybook-bot@users.noreply.github.com' yarn release:pick-patches - - name: Cancel when no hotfixes to pick + - name: Cancel when no patches to pick if: steps.pick-patches.outputs.pr-count == '0' && steps.pick-patches.outputs.pr-count != null env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f5efac64565..acb78742484 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,7 +5,7 @@ on: push: branches: - latest-release - - next-release + - non-patch-release env: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 @@ -39,11 +39,11 @@ jobs: - name: Cancel all release preparation runs run: | # Get a list of all running or pending release preparation runs - # combining both the prepare-hotfix-release.yml and prepare-next-release.yml workflows + # combining both the prepare-patch-release.yml and prepare-non-patch-release.yml workflows RUNNING_RELEASE_PREPARATIONS=$( { - gh run list --limit 50 --workflow=prepare-hotfix-release.yml --json databaseId,status - gh run list --limit 50 --workflow=prepare-next-release.yml --json databaseId,status + gh run list --limit 50 --workflow=prepare-patch-release.yml --json databaseId,status + gh run list --limit 50 --workflow=prepare-non-patch-release.yml --json databaseId,status } | jq -rc '.[] | select(.status | contains("in_progress", "pending", "queued", "requested", "waiting")) | .databaseId' ) @@ -170,7 +170,7 @@ jobs: # next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0 # then we release 7.4.0 by merging next to latest-release to main # we then ensure here that next is bumped to 7.5.0 - without releasing it - # if this is a hotfix release bumping main to 7.3.1, next will not be touched because it's already ahead + # if this is a patch release bumping main to 7.3.1, next will not be touched because it's already ahead - name: Ensure `next` is a minor version ahead of `main` if: steps.target.outputs.target == 'main' run: | diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index c22bf923e07..8ad3380821e 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -8,8 +8,8 @@ - [Introduction](#introduction) - [Branches](#branches) - [Release Pull Requests](#release-pull-requests) - - [`next`-releases](#next-releases) - - [Hotfix Releases](#hotfix-releases) + - [Non-patch-releases](#non-patch-releases) + - [Patch Releases](#patch-releases) - [Publishing](#publishing) - [👉 How to Release](#-how-to-release) - [1. Find the Prepared Pull Request](#1-find-the-prepared-pull-request) @@ -45,19 +45,19 @@ This document explains the release process for the Storybook monorepo. There are two types: -1. `next`-releases - releasing any content that is on the `next` branch, either prereleases or stable releases -2. Hotfix releases - picking any content from `next` to `main`, that needs to be patched back to the current stable minor release +1. non-patch-releases - releasing any content that is on the `next` branch, either prereleases or stable releases +2. Patch releases - picking any content from `next` to `main`, that needs to be patched back to the current stable minor release The release process is based on automatically created "Release Pull Requests", that when merged will trigger a new version to be released. A designated Releaser -- which may rotate between core team members -- will go through the release process in the current Release PR. This process is implemented with NodeJS scripts in [`scripts/release`](../scripts/release/) and three GitHub Actions workflows: -- [Prepare `next` PR](../.github/workflows/prepare-next-release.yml) -- [Prepare hotfix PR](../.github/workflows/prepare-hotfix-release.yml) +- [Prepare `next` PR](../.github/workflows/prepare-non-patch-release.yml) +- [Prepare patch PR](../.github/workflows/prepare-patch-release.yml) - [Publish](../.github/workflows/publish.yml) > **Note** -> This document distinguishes between **`next`-releases** and **hotfix** releases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. +> This document distinguishes between **non-patch-releases** and **patch** releases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. ### Branches @@ -103,7 +103,7 @@ Two GitHub Actions workflows automatically create release pull requests, one for The high-level flow is: 1. When a PR is merged to `next` (or a commit is pushed), both release pull requests are (re)generated. -2. They create a new branch - `version-(hotfix|next)-from-`. +2. They create a new branch - `version-(patch|next)-from-`. 3. They calculate which version to bump to according to the version strategy. 4. They update `CHANGELOG(.prerelease).md` with all changes detected. 5. They commit everything. @@ -117,12 +117,12 @@ A few key points to note in this flow: - The changelogs are committed during the preparation, but the packages are not version bumped and not published until later. - The release pull requests don't target their working branches (`next` and `main`), but rather `next-release` and `latest-release`. -### `next`-releases +### Non-patch-releases > **Note** -> Workflow: [`prepare-next-release.yml`](../.github/workflows/prepare-next-release.yml) +> Workflow: [`prepare-non-patch-release.yml`](../.github/workflows/prepare-non-patch-release.yml) -`next`-releases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. +Non-patch-releases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. The default versioning strategy is to increase the current prerelease number, as described in [Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13). If there is no prerelease number (i.e., we just released a new stable minor/major version), it will add one to a patch bump, so it would go from `7.2.0` to `7.2.1-0` by default. @@ -159,18 +159,18 @@ gitGraph merge next-release ``` -### Hotfix Releases +### Patch Releases > **Note** -> Workflow: [`prepare-hotfix-release.yml`](../.github/workflows/prepare-hotfix-release.yml) +> Workflow: [`prepare-patch-release.yml`](../.github/workflows/prepare-patch-release.yml) -Hotfix releases are created by [cherry-picking](https://www.atlassian.com/git/tutorials/cherry-pick) any merged, unreleased pull requests that have the "**patch:yes**" label applied to the `next` branch. The merge commit of said pull requests are cherry-picked. +Patch releases are created by [cherry-picking](https://www.atlassian.com/git/tutorials/cherry-pick) any merged, unreleased pull requests that have the "**patch:yes**" label applied to the `next` branch. The merge commit of said pull requests are cherry-picked. -Sometimes it is desired to pick pull requests back to `main` even if they are not considered "releasable". Unlike `next`-release preparation, hotfix releases will not be canceled if the content is not releasable. It might not make sense to create a new hotfix release if the changes are only for documentation and/or internal build systems. However, getting the changes back to `main` is the only way to deploy the documentation to the production docs site. You may also want to cherry-pick changes to internal CI to fix issues. These are valid scenarios where you want to cherry-pick the changes without being blocked on "releasable" content. In these cases, where all cherry picks are non-releasable, the preparation workflow creates a "merging" pull request instead of a "releasing" pull request. This pull request does not bump versions or update changelogs; it just cherry-picks the changes and allows you to merge them into `latest-release` -> `main`. +Sometimes it is desired to pick pull requests back to `main` even if they are not considered "releasable". Unlike non-patch-release preparation, patch releases will not be canceled if the content is not releasable. It might not make sense to create a new patch release if the changes are only for documentation and/or internal build systems. However, getting the changes back to `main` is the only way to deploy the documentation to the production docs site. You may also want to cherry-pick changes to internal CI to fix issues. These are valid scenarios where you want to cherry-pick the changes without being blocked on "releasable" content. In these cases, where all cherry picks are non-releasable, the preparation workflow creates a "merging" pull request instead of a "releasing" pull request. This pull request does not bump versions or update changelogs; it just cherry-picks the changes and allows you to merge them into `latest-release` -> `main`. The preparation workflow sequentially cherry-picks each patch pull request to its branch. If this cherry-picking fails due to conflicts or other reasons, it is ignored and the next pull request is processed. All failing cherry-picks are listed in the release pull request's description, for the Releaser to manually cherry-pick during the release process. This problem occurs more often when `main` and `next` diverge, i.e. the longer it has been since a stable major/minor release. -Similar to the `next`-release flow, the preparation workflow for patches will create a new branch from `main` called `version-hotfix-from-`, and open a pull request that targets `latest-release`. When the pull request is merged by the Releaser, the [publish workflow](#publishing) will eventually merge `latest-release` into `main`. +Similar to the non-patch-release flow, the preparation workflow for patches will create a new branch from `main` called `version-patch-from-`, and open a pull request that targets `latest-release`. When the pull request is merged by the Releaser, the [publish workflow](#publishing) will eventually merge `latest-release` into `main`. Here is an example of a workflow where a feature and two bug fixes have been merged to `next`. Only the bug fixes have the "**patch:yes**" label, so only those two go into the new `7.0.19` release. Note that it is the merge commits to `next` that are cherry-picked, not the commits on the bugfix branches. @@ -215,15 +215,15 @@ gitGraph > **Note** > Workflow: [`publish.yml`](../.github/workflows/publish.yml) -When either a `next`-release or a hotfix release branch is merged into `latest-release` or `next-release`, the publishing workflow is triggered. This workflow performs the following tasks: +When either a non-patch-release or a patch release branch is merged into `latest-release` or `next-release`, the publishing workflow is triggered. This workflow performs the following tasks: 1. Bump versions of all packages according to the plan from the prepared PRs 2. Install dependencies and build all packages. 3. Publish packages to npm. -4. (If this is a hotfix release, add the "**patch:done**" label to all relevant pull requests.) +4. (If this is a patch release, add the "**patch:done**" label to all relevant pull requests.) 5. Create a new GitHub Release, including a version tag in the release branch (`latest-release` or `next-release`). 6. Merge the release branch into the core branch (`main` or `next`). -7. (If this is a hotfix release, copy the `CHANGELOG.md` changes from `main` to `next`.) +7. (If this is a patch release, copy the `CHANGELOG.md` changes from `main` to `next`.) The publish workflow runs in the "release" GitHub environment, which has the npm token required to publish packages to the `@storybook` npm organization. For security reasons, this environment can only be accessed from the four "core" branches: `main`, `next`, `latest-release` and `next-release`. @@ -246,8 +246,8 @@ The high-level workflow for a Releaser is: Look for the release pull request that has been prepared for the type of release you're about to release: - "Release: Prerelease|Minor|Major ``" for releases from `next` -- "Release: Hotfix ``" for hotfix releases -- "Release: Merge patches to `main` (without version bump)" for hotfixes without releases +- "Release: Patch ``" for patch releases +- "Release: Merge patches to `main` (without version bump)" for patches without releases For example: https://github.com/storybookjs/storybook/pull/23148 @@ -280,9 +280,9 @@ If a pull request changes multiple places, it can be hard to choose an area - th Some labels have specific meanings when it comes to releases. It's important that each pull request has labels that accurately describe the change, as labels can determine if a pull request is included in the changelog or not. This is explained further in the [Which changes are considered "releasable", and what does it mean?](#which-changes-are-considered-releasable-and-what-does-it-mean) section. -4. Hotfixes: has it already been released in a prerelease? +4. Patches: has it already been released in a prerelease? -If this is a hotfix release, make sure that all pull requests have already been released in a prerelease. If some haven't, create a new prerelease first. +If this is a patch release, make sure that all pull requests have already been released in a prerelease. If some haven't, create a new prerelease first. This is not a technical requirement, but it's a good practice to ensure that a change doesn't break a prerelease before releasing it to stable. @@ -301,12 +301,12 @@ When triggering the workflows, always choose the `next` branch as the base, unle The workflows can be triggered here: -- [Prepare next PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) -- [Prepare hotfix PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml) +- [Prepare next PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-non-patch-release.yml) +- [Prepare patch PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) -Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - `next`-releases](#next-releases). When triggering the prerelease workflow manually, you can optionally add inputs: +Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - non-patch-releases](#non-patch-releases). When triggering the prerelease workflow manually, you can optionally add inputs: -![Screenshot of triggering the next-release workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) +![Screenshot of triggering the non-patch-release workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) See [Versioning Scenarios](#versioning-scenarios) for a description of each version bump scenario, how to activate it and what it does, and [Which combination of inputs creates the version bump I need?](#which-combination-of-inputs-creates-the-version-bump-i-need) for a detailed description of the workflow inputs. @@ -340,11 +340,11 @@ You can inspect the workflows to see what they are running and copy that, but he Before you start you should make sure that your working tree is clean and the repository is in a clean state by running `git clean -xdf`. -1. Create a new branch from either `next` or `main` (hotfixes) +1. Create a new branch from either `next` or `main` (patches) 2. Get all tags: `git fetch --tags origin` 3. Install dependencies: `yarn task --task=install --start-from=install` 4. `cd scripts` -5. (If hotfix release) Cherry pick: +5. (If patch release) Cherry pick: 1. `yarn release:pick-patches` 2. Manually cherry pick any necessary patches based on the previous output 6. Bump versions: @@ -362,21 +362,21 @@ Before you start you should make sure that your working tree is clean and the re 12. (If automatic publishing is still working, it should kick in now and the rest of the steps can be skipped) 13. `cd ..` 14. Publish to the registry: `YARN_NPM_AUTH_TOKEN= yarn release:publish --tag <"next" OR "latest"> --verbose` -15. (If hotfix release) `yarn release:label-patches` +15. (If patch release) `yarn release:label-patches` 16. Manually create a GitHub Release with a tag that is the new version and the target being `latest-release` or `next-release`. 17. Merge to core branch: 1. `git checkout <"next"|"main">` 2. `git pull` 3. `git merge <"next-release"|"latest-release">` 4. `git push origin` -18. (If hotfix release) Sync `CHANGELOG.md` to `next` with: +18. (If patch release) Sync `CHANGELOG.md` to `next` with: 1. `git checkout next` 2. `git pull` 3. `git checkout origin/main ./CHANGELOG.md` 4. `git add ./CHANGELOG.md` 5. `git commit -m "Update CHANGELOG.md for v"` 6. `git push origin` -19. (If `next`-release) Sync `versions/next.json` from `next` to `main` +19. (If non-patch-release) Sync `versions/next.json` from `next` to `main` 1. `git checkout main` 2. `git pull` 3. `git checkout origin/next ./docs/versions/next.json` @@ -435,7 +435,7 @@ There are multiple types of releases that use the same principles, but are done ### Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13` -This is the default strategy for `next`-releases, there's nothing special needed to trigger this scenario. +This is the default strategy for non-patch-releases, there's nothing special needed to trigger this scenario. ### Prerelease promotions - `7.1.0-alpha.13` -> `7.1.0-beta.0` @@ -462,7 +462,7 @@ This is the first prerelease after a stable major/minor has been released. The d ### Patch releases to stable - subset of `7.1.0-alpha.13` -> `7.0.14` -This is the default hotfix release scenario, which cherry picks patches to `main`. +This is the default patch release scenario, which cherry picks patches to `main`. ### Patch releases to earlier versions - subset of `7.1.0-alpha.13` -> `6.5.14` @@ -476,7 +476,7 @@ No process is defined for this. ### Merges to `main` without versioning -As described in more details in [the Hotfix Releases section](#hotfix-releases), there are scenarios where you want to patch [unreleasable](#which-changes-are-considered-releasable-and-what-does-it-mean) content back to `main` without bumping versions or publishing a new release. This happens automatically as long as all the unpicked patch pull requests have unreleasable labels. In that case the prepared patch pull request will change form slighty, to just cherry-picking the patches without bumping the versions. +As described in more details in [the Patch Releases section](#patch-releases), there are scenarios where you want to patch [unreleasable](#which-changes-are-considered-releasable-and-what-does-it-mean) content back to `main` without bumping versions or publishing a new release. This happens automatically as long as all the unpicked patch pull requests have unreleasable labels. In that case the prepared patch pull request will change form slighty, to just cherry-picking the patches without bumping the versions. ## FAQ @@ -538,7 +538,7 @@ If a pull request does not have any of the above labels at the time of release, This is most likely because `next` only contains [unreleasable changes](#which-changes-are-considered-releasable-and-what-does-it-mean), which causes the preparation workflow to cancel itself. That's because it doesn't make sense to prepare a new release if all the changes are unreleasable, as that wouldn't bump the version nor write a new changelog entry, so "releasing" it would just merge it back to `next` without any differences. -You can always see the workflows and if they have been cancelled [here for `next`-releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-next-release.yml) and [here for hotfix releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-hotfix-release.yml). +You can always see the workflows and if they have been cancelled [here for non-patch-releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-non-patch-release.yml) and [here for patch releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml). ### Why do we need separate release branches? diff --git a/scripts/release/__tests__/generate-pr-description.test.ts b/scripts/release/__tests__/generate-pr-description.test.ts index 306ad627ee7..50aa5de8e01 100644 --- a/scripts/release/__tests__/generate-pr-description.test.ts +++ b/scripts/release/__tests__/generate-pr-description.test.ts @@ -219,7 +219,7 @@ For each pull request below, you need to either manually cherry pick it, or disc - [ ] [#42](https://github.com/storybookjs/storybook/pull/42): \\\`git cherry-pick -m1 -x abc123\\\` - 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-next-release.yml) and wait for it to finish. It will wipe your progress in this to do, which is expected. + 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-non-patch-release.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. @@ -279,7 +279,7 @@ For each pull request below, you need to either manually cherry pick it, or disc - [ ] [#42](https://github.com/storybookjs/storybook/pull/42): \\\`git cherry-pick -m1 -x abc123\\\` - 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-hotfix-release.yml) and wait for it to finish. + 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. @@ -346,7 +346,7 @@ 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-next-release.yml) and wait for it to finish. It will wipe your progress in this to do, which is expected. + 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-non-patch-release.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. @@ -401,7 +401,7 @@ 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-hotfix-release.yml) and wait for it to finish. + 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. diff --git a/scripts/release/__tests__/is-pr-frozen.test.ts b/scripts/release/__tests__/is-pr-frozen.test.ts index d5d1e8a16b7..ab9a386b19e 100644 --- a/scripts/release/__tests__/is-pr-frozen.test.ts +++ b/scripts/release/__tests__/is-pr-frozen.test.ts @@ -47,13 +47,13 @@ describe('isPrFrozen', () => { await expect(isPrFrozen({ patch: false })).resolves.toBe(false); }); - it('should look for patch PRs when hotfix is true', async () => { + it('should look for patch PRs when patch is true', async () => { getPullInfoFromCommit.mockResolvedValue({ labels: [], }); - await isPrFrozen({ hotfix: true }); + await isPrFrozen({ patch: true }); - expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-hotfix-from-1.0.0', { + expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-patch-from-1.0.0', { '--depth': 1, }); }); diff --git a/scripts/release/generate-pr-description.ts b/scripts/release/generate-pr-description.ts index 8a83e20d4f1..d437e8dc87b 100644 --- a/scripts/release/generate-pr-description.ts +++ b/scripts/release/generate-pr-description.ts @@ -143,8 +143,8 @@ export const generateReleaseDescription = ({ manualCherryPicks?: string; }): string => { const workflow = semver.prerelease(nextVersion) - ? 'prepare-next-release' - : 'prepare-hotfix-release'; + ? 'prepare-non-patch-release' + : 'prepare-patch-release'; const workflowUrl = `https://github.com/storybookjs/storybook/actions/workflows/${workflow}.yml`; return ( @@ -218,7 +218,7 @@ export const generateNonReleaseDescription = ( ${manualCherryPicks || ''} - 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-hotfix-release.yml) and wait for it to finish. + 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. diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 364d7289cc9..7468b994330 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -12,7 +12,7 @@ program .description( 'returns true if the versioning pull request associated with the current branch has the "freeze" label' ) - .option('-H, --hotfix', 'Look for hotfix PR instead of next PR', false) + .option('-H, --patch', 'Look for patch PR instead of next PR', false) .option('-V, --verbose', 'Enable verbose logging', false); const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code'); @@ -43,10 +43,10 @@ const getRepo = async (verbose?: boolean): Promise => { }; export const run = async (options: unknown) => { - const { verbose, hotfix } = options as { verbose?: boolean; hotfix?: boolean }; + const { verbose, patch } = options as { verbose?: boolean; patch?: boolean }; const version = await getCurrentVersion(); - const branch = `version-${hotfix ? 'hotfix' : 'next'}-from-${version}`; + const branch = `version-${patch ? 'patch' : 'next'}-from-${version}`; console.log(`💬 Determining if pull request from branch '${chalk.blue(branch)}' is frozen`); From 15b59338df7636b1a17e4b3b99376237c51b0147 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 4 Oct 2023 11:23:04 +0200 Subject: [PATCH 24/27] more renaming --- .../workflows/prepare-non-patch-release.yml | 12 +- CONTRIBUTING/RELEASING.md | 112 +++++++++--------- .../release/__tests__/is-pr-frozen.test.ts | 2 +- scripts/release/is-pr-frozen.ts | 2 +- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/.github/workflows/prepare-non-patch-release.yml b/.github/workflows/prepare-non-patch-release.yml index e1b4b45a016..ea72d924d91 100644 --- a/.github/workflows/prepare-non-patch-release.yml +++ b/.github/workflows/prepare-non-patch-release.yml @@ -34,7 +34,7 @@ concurrency: cancel-in-progress: true jobs: - prepare-next-pull-request: + prepare-non-patch-pull-request: name: Prepare prerelease pull request runs-on: ubuntu-latest environment: release @@ -122,15 +122,15 @@ jobs: run: | yarn release:write-changelog ${{ steps.bump-version.outputs.next-version }} --verbose - - name: 'Commit changes to branch: version-next-from-${{ steps.bump-version.outputs.current-version }}' + - name: 'Commit changes to branch: version-non-patch-from-${{ steps.bump-version.outputs.current-version }}' working-directory: . run: | git config --global user.name 'storybook-bot' git config --global user.email '32066757+storybook-bot@users.noreply.github.com' - git checkout -b version-next-from-${{ steps.bump-version.outputs.current-version }} + git checkout -b version-non-patch-from-${{ steps.bump-version.outputs.current-version }} git add . git commit -m "Write changelog for ${{ steps.bump-version.outputs.next-version }} [skip ci]" || true - git push --force origin version-next-from-${{ steps.bump-version.outputs.current-version }} + git push --force origin version-non-patch-from-${{ steps.bump-version.outputs.current-version }} - name: Resolve merge-conflicts with base branch if: steps.is-prerelease.outputs.prerelease == 'true' @@ -140,7 +140,7 @@ jobs: git pull origin latest-release git checkout --ours . git add . - git commit -m "Merge latest-release into version-next-from-${{ steps.bump-version.outputs.current-version }} with conflicts resolved to ours [skip ci]" + git commit -m "Merge latest-release into version-non-patch-from-${{ steps.bump-version.outputs.current-version }} with conflicts resolved to ours [skip ci]" - name: Generate PR description id: description @@ -166,7 +166,7 @@ jobs: --title "Release: $CAPITALIZED_RELEASE_TYPE ${{ inputs.pre-id && format('{0} ', inputs.pre-id) }}${{ steps.bump-version.outputs.next-version }}" \ --label "release" \ --base ${{ steps.is-prerelease.outputs.prerelease == 'true' && 'next-release' || 'latest-release' }} \ - --head version-next-from-${{ steps.bump-version.outputs.current-version }} \ + --head version-non-patch-from-${{ steps.bump-version.outputs.current-version }} \ --body "${{ steps.description.outputs.description }}" fi diff --git a/CONTRIBUTING/RELEASING.md b/CONTRIBUTING/RELEASING.md index 8ad3380821e..e92fa8fa36a 100644 --- a/CONTRIBUTING/RELEASING.md +++ b/CONTRIBUTING/RELEASING.md @@ -8,8 +8,8 @@ - [Introduction](#introduction) - [Branches](#branches) - [Release Pull Requests](#release-pull-requests) - - [Non-patch-releases](#non-patch-releases) - [Patch Releases](#patch-releases) + - [Non-patch Releases](#non-patch-releases) - [Publishing](#publishing) - [👉 How to Release](#-how-to-release) - [1. Find the Prepared Pull Request](#1-find-the-prepared-pull-request) @@ -45,7 +45,7 @@ This document explains the release process for the Storybook monorepo. There are two types: -1. non-patch-releases - releasing any content that is on the `next` branch, either prereleases or stable releases +1. Non-patch releases - releasing any content that is on the `next` branch, either prereleases or stable releases 2. Patch releases - picking any content from `next` to `main`, that needs to be patched back to the current stable minor release The release process is based on automatically created "Release Pull Requests", that when merged will trigger a new version to be released. @@ -57,7 +57,7 @@ A designated Releaser -- which may rotate between core team members -- will go t - [Publish](../.github/workflows/publish.yml) > **Note** -> This document distinguishes between **non-patch-releases** and **patch** releases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. +> This document distinguishes between **patch** and **non-patch** releases. The distinction reflects the difference between patching an existing minor version on `main` or releasing a new minor/major/prerelease from `next`. ### Branches @@ -103,7 +103,7 @@ Two GitHub Actions workflows automatically create release pull requests, one for The high-level flow is: 1. When a PR is merged to `next` (or a commit is pushed), both release pull requests are (re)generated. -2. They create a new branch - `version-(patch|next)-from-`. +2. They create a new branch - `version-(patch|non-patch)-from-`. 3. They calculate which version to bump to according to the version strategy. 4. They update `CHANGELOG(.prerelease).md` with all changes detected. 5. They commit everything. @@ -117,48 +117,6 @@ A few key points to note in this flow: - The changelogs are committed during the preparation, but the packages are not version bumped and not published until later. - The release pull requests don't target their working branches (`next` and `main`), but rather `next-release` and `latest-release`. -### Non-patch-releases - -> **Note** -> Workflow: [`prepare-non-patch-release.yml`](../.github/workflows/prepare-non-patch-release.yml) - -Non-patch-releases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. - -The default versioning strategy is to increase the current prerelease number, as described in [Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13). If there is no prerelease number (i.e., we just released a new stable minor/major version), it will add one to a patch bump, so it would go from `7.2.0` to `7.2.1-0` by default. - -`next`-PRs are only created if there are actual changes to release. Content labeled with "build" or "documentation" is [not considered "releasable"](#which-changes-are-considered-releasable-and-what-does-it-mean) and is not user-facing, so it doesn't make sense to create a release. This is explained in more detail in [Why are no release PRs being prepared?](#why-are-no-release-prs-being-prepared). - -The preparation workflow will create a new branch from `next`, called `version-next-from-`, and open a pull request targeting `next-release`. When the Releaser merges it, the [publish workflow](#publishing) will merge `next-release` into `next`. - -Here's an example of a workflow where a feature and a bugfix have been created and then released to a new `7.1.0-alpha.29` version. All the commits highlighted with square dots are the ones that will be considered when generating the changelog. - -```mermaid -%%{init: { 'gitGraph': { 'mainBranchName': 'next' } } }%% -gitGraph - commit - branch next-release - commit tag: "7.1.0-alpha.28" - checkout next - merge next-release - commit type: HIGHLIGHT id: "direct commit" - branch new-feature - commit - commit - checkout next - merge new-feature type: HIGHLIGHT - branch some-bugfix - commit - checkout next - merge some-bugfix type: HIGHLIGHT - branch version-next-from-7.1.0-alpha.28 - commit id: "write changelog" - checkout next-release - merge version-next-from-7.1.0-alpha.28 - commit id: "bump versions" tag: "7.1.0-alpha.29" - checkout next - merge next-release -``` - ### Patch Releases > **Note** @@ -210,6 +168,48 @@ gitGraph merge latest-release ``` +### Non-patch Releases + +> **Note** +> Workflow: [`prepare-non-patch-release.yml`](../.github/workflows/prepare-non-patch-release.yml) + +Non-patch-releases are prepared with all content from the `next` branch. The changelog is generated by examining the git history, and looking up all the commits and pull requests between the current prerelease (on `next-release`) and `HEAD` of `next`. + +The default versioning strategy is to increase the current prerelease number, as described in [Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13`](#prereleases---710-alpha12---710-alpha13). If there is no prerelease number (i.e., we just released a new stable minor/major version), it will add one to a patch bump, so it would go from `7.2.0` to `7.2.1-0` by default. + +`next`-PRs are only created if there are actual changes to release. Content labeled with "build" or "documentation" is [not considered "releasable"](#which-changes-are-considered-releasable-and-what-does-it-mean) and is not user-facing, so it doesn't make sense to create a release. This is explained in more detail in [Why are no release PRs being prepared?](#why-are-no-release-prs-being-prepared). + +The preparation workflow will create a new branch from `next`, called `version-non-patch-from-`, and open a pull request targeting `next-release`. When the Releaser merges it, the [publish workflow](#publishing) will merge `next-release` into `next`. + +Here's an example of a workflow where a feature and a bugfix have been created and then released to a new `7.1.0-alpha.29` version. All the commits highlighted with square dots are the ones that will be considered when generating the changelog. + +```mermaid +%%{init: { 'gitGraph': { 'mainBranchName': 'next' } } }%% +gitGraph + commit + branch next-release + commit tag: "7.1.0-alpha.28" + checkout next + merge next-release + commit type: HIGHLIGHT id: "direct commit" + branch new-feature + commit + commit + checkout next + merge new-feature type: HIGHLIGHT + branch some-bugfix + commit + checkout next + merge some-bugfix type: HIGHLIGHT + branch version-non-patch-from-7.1.0-alpha.28 + commit id: "write changelog" + checkout next-release + merge version-non-patch-from-7.1.0-alpha.28 + commit id: "bump versions" tag: "7.1.0-alpha.29" + checkout next + merge next-release +``` + ### Publishing > **Note** @@ -304,7 +304,7 @@ The workflows can be triggered here: - [Prepare next PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-non-patch-release.yml) - [Prepare patch PR](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml) -Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - non-patch-releases](#non-patch-releases). When triggering the prerelease workflow manually, you can optionally add inputs: +Crucially for prereleases, this is also where you change the versioning strategy if you need something else than the default as described in [Preparing - Non-patch Releases](#non-patch-releases). When triggering the prerelease workflow manually, you can optionally add inputs: ![Screenshot of triggering the non-patch-release workflow in GitHub Actions, with a form that shows a release type selector and a prerelease identifier text field](prerelease-workflow-inputs.png) @@ -435,7 +435,7 @@ There are multiple types of releases that use the same principles, but are done ### Prereleases - `7.1.0-alpha.12` -> `7.1.0-alpha.13` -This is the default strategy for non-patch-releases, there's nothing special needed to trigger this scenario. +This is the default strategy for Non-patch releases, there's nothing special needed to trigger this scenario. ### Prerelease promotions - `7.1.0-alpha.13` -> `7.1.0-beta.0` @@ -451,7 +451,7 @@ To promote a prerelease to a stable reelase, during the [Re-trigger the Workflow - Release type: Patch, Minor or Major - Prerelease ID: Leave empty -This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-from- `latest-release` -> `main`. +This scenario is special as it will target `latest-release` instead of `next-release`, and thus merge into `main` when done, and not `next`. So it goes `next` -> `version-non-patch-from-` -> `latest-release` -> `main`. ### First prerelease of new major/minor - `7.1.0` -> `7.2.0-alpha.0` or `8.0.0-alpha.0` @@ -538,7 +538,7 @@ If a pull request does not have any of the above labels at the time of release, This is most likely because `next` only contains [unreleasable changes](#which-changes-are-considered-releasable-and-what-does-it-mean), which causes the preparation workflow to cancel itself. That's because it doesn't make sense to prepare a new release if all the changes are unreleasable, as that wouldn't bump the version nor write a new changelog entry, so "releasing" it would just merge it back to `next` without any differences. -You can always see the workflows and if they have been cancelled [here for non-patch-releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-non-patch-release.yml) and [here for patch releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml). +You can always see the workflows and if they have been cancelled [here for non-patch releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-non-patch-release.yml) and [here for patch releases](https://github.com/storybookjs/storybook/actions/workflows/prepare-patch-release.yml). ### Why do we need separate release branches? @@ -560,11 +560,11 @@ gitGraph branch some-simultaneous-bugfix commit checkout next - branch version-next-from-7.1.0-alpha.28 + branch version-non-patch-from-7.1.0-alpha.28 commit id checkout next merge some-simultaneous-bugfix type: HIGHLIGHT id: "whoops!" - merge version-next-from-7.1.0-alpha.28 tag: "v7.1.0-alpha.29" + merge version-non-patch-from-7.1.0-alpha.28 tag: "v7.1.0-alpha.29" ``` When publishing at the last commit with tag `v7.1.0-alpha.29`, it will publish whatever the content is at that point (all the square dots), which includes the "whoops!" commit from merging the bugfix. But the bugfix was never part of the release pull request because it got prepared before the bugfix was merged in. @@ -584,19 +584,19 @@ gitGraph branch some-simultanous-bugfix commit checkout next - branch version-next-from-7.1.0-alpha.28 + branch version-non-patch-from-7.1.0-alpha.28 commit id: "write changelog" checkout next merge some-simultanous-bugfix id: "whoops!" checkout next-release - merge version-next-from-7.1.0-alpha.28 + merge version-non-patch-from-7.1.0-alpha.28 commit id: "bump versions" tag: "v7.1.0-alpha.29" checkout next merge next-release - branch version-next-from-7.1.0-alpha.29 + branch version-non-patch-from-7.1.0-alpha.29 commit id: "write changelog again" checkout next-release - merge version-next-from-7.1.0-alpha.29 + merge version-non-patch-from-7.1.0-alpha.29 commit id: "bump versions again" tag: "v7.1.0-alpha.30" checkout next merge next-release diff --git a/scripts/release/__tests__/is-pr-frozen.test.ts b/scripts/release/__tests__/is-pr-frozen.test.ts index ab9a386b19e..00331e0555c 100644 --- a/scripts/release/__tests__/is-pr-frozen.test.ts +++ b/scripts/release/__tests__/is-pr-frozen.test.ts @@ -64,7 +64,7 @@ describe('isPrFrozen', () => { }); await isPrFrozen({ patch: false }); - expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-next-from-1.0.0', { + expect(simpleGit.__fetch).toHaveBeenCalledWith('origin', 'version-non-patch-from-1.0.0', { '--depth': 1, }); }); diff --git a/scripts/release/is-pr-frozen.ts b/scripts/release/is-pr-frozen.ts index 7468b994330..e81610a451b 100644 --- a/scripts/release/is-pr-frozen.ts +++ b/scripts/release/is-pr-frozen.ts @@ -46,7 +46,7 @@ export const run = async (options: unknown) => { const { verbose, patch } = options as { verbose?: boolean; patch?: boolean }; const version = await getCurrentVersion(); - const branch = `version-${patch ? 'patch' : 'next'}-from-${version}`; + const branch = `version-${patch ? 'patch' : 'non-patch'}-from-${version}`; console.log(`💬 Determining if pull request from branch '${chalk.blue(branch)}' is frozen`); From ed18a24d6477ddd3de7a41d6a91d31fb7ac22451 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 4 Oct 2023 14:45:08 +0200 Subject: [PATCH 25/27] move ensure-next-ahead from a bash script to a tested node script --- .github/workflows/publish.yml | 19 +--- scripts/package.json | 1 + .../__tests__/ensure-next-ahead.test.ts | 85 +++++++++++++++ scripts/release/ensure-next-ahead.ts | 102 ++++++++++++++++++ 4 files changed, 189 insertions(+), 18 deletions(-) create mode 100644 scripts/release/__tests__/ensure-next-ahead.test.ts create mode 100644 scripts/release/ensure-next-ahead.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index acb78742484..be37b132b1f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -165,31 +165,14 @@ jobs: git merge ${{ github.ref_name }} git push origin ${{ steps.target.outputs.target }} - # This step ensures that next is always one minor ahead of main - # this is needed when releasing a stable from next - # next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0 - # then we release 7.4.0 by merging next to latest-release to main - # we then ensure here that next is bumped to 7.5.0 - without releasing it - # if this is a patch release bumping main to 7.3.1, next will not be touched because it's already ahead - name: Ensure `next` is a minor version ahead of `main` if: steps.target.outputs.target == 'main' run: | git checkout next git pull - CODE_PKG_JSON=$(cat ../code/package.json) - VERSION_ON_NEXT=$(echo $CODE_PKG_JSON | jq --raw-output '.version') - VERSION_ON_MAIN="${{ steps.version.outputs.current-version }}" + yarn release:ensure-next-ahead --main-version "${{ steps.version.outputs.current-version }}" - # skip if next is already ahead of main - if NEXT_IS_AHEAD=$(npx semver --include-prerelease --range ">=$VERSION_ON_MAIN" "$VERSION_ON_NEXT" 2>/dev/null); then - return - fi - - # temporarily set the version on next to be the same as main... - echo "$CODE_PKG_JSON" | jq --arg version "$VERSION_ON_MAIN" '.version = $version' > ../code/package.json - # ... then bump it by one minor - yarn release:version --release-type minor git add .. git commit -m "Bump next to be one minor ahead of main [skip ci]" git push origin next diff --git a/scripts/package.json b/scripts/package.json index 906bb2b9592..4cd557a0736 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -13,6 +13,7 @@ "lint:js:cmd": "cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives", "lint:package": "sort-package-json", "migrate-docs": "node --require esbuild-register ./ts-to-ts49.ts", + "release:ensure-next-ahead": "ts-node --swc ./release/ensure-next-ahead.ts", "release:generate-pr-description": "ts-node --swc ./release/generate-pr-description.ts", "release:get-changelog-from-file": "ts-node --swc ./release/get-changelog-from-file.ts", "release:get-current-version": "ts-node --swc ./release/get-current-version.ts", diff --git a/scripts/release/__tests__/ensure-next-ahead.test.ts b/scripts/release/__tests__/ensure-next-ahead.test.ts new file mode 100644 index 00000000000..0b192d39b10 --- /dev/null +++ b/scripts/release/__tests__/ensure-next-ahead.test.ts @@ -0,0 +1,85 @@ +/* eslint-disable global-require */ +/* eslint-disable no-underscore-dangle */ +import path from 'path'; +import { run as ensureNextAhead } from '../ensure-next-ahead'; +import * as gitClient_ from '../utils/git-client'; +import * as bumpVersion_ from '../version'; + +jest.mock('../utils/git-client', () => jest.requireActual('jest-mock-extended').mockDeep()); +const gitClient = jest.mocked(gitClient_); + +// eslint-disable-next-line jest/no-mocks-import +jest.mock('fs-extra', () => require('../../../code/__mocks__/fs-extra')); +const fsExtra = require('fs-extra'); + +jest.mock('../version', () => jest.requireActual('jest-mock-extended').mockDeep()); +const bumpVersion = jest.mocked(bumpVersion_); + +jest.spyOn(console, 'log').mockImplementation(() => {}); +jest.spyOn(console, 'warn').mockImplementation(() => {}); +jest.spyOn(console, 'error').mockImplementation(() => {}); + +const CODE_PACKAGE_JSON_PATH = path.join(__dirname, '..', '..', '..', 'code', 'package.json'); + +describe('Ensure next ahead', () => { + beforeEach(() => { + jest.clearAllMocks(); + gitClient.git.status.mockResolvedValue({ current: 'next' } as any); + fsExtra.__setMockFiles({ + [CODE_PACKAGE_JSON_PATH]: JSON.stringify({ version: '2.0.0' }), + }); + }); + + it('should throw when main-version is missing', async () => { + await expect(ensureNextAhead({})).rejects.toThrowErrorMatchingInlineSnapshot(` + "[ + { + "code": "invalid_type", + "expected": "string", + "received": "undefined", + "path": [ + "mainVersion" + ], + "message": "Required" + } + ]" + `); + }); + + it('should throw when main-version is not a semver string', async () => { + await expect(ensureNextAhead({ mainVersion: '200' })).rejects + .toThrowErrorMatchingInlineSnapshot(` + "[ + { + "code": "custom", + "message": "main-version must be a valid semver version string like '7.4.2'.", + "path": [] + } + ]" + `); + }); + + it('should not bump version when next is already ahead of main', async () => { + await expect(ensureNextAhead({ mainVersion: '1.0.0' })).resolves.toBeUndefined(); + expect(bumpVersion.run).not.toHaveBeenCalled(); + }); + + it('should bump version to 3.1.0-alpha.0 when main is 3.0.0 and next is 2.0.0', async () => { + await expect(ensureNextAhead({ mainVersion: '3.0.0' })).resolves.toBeUndefined(); + expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '3.1.0-alpha.0' }); + }); + + it('should bump version to 2.1.0-alpha.0 when main and next are both 2.0.0', async () => { + await expect(ensureNextAhead({ mainVersion: '2.0.0' })).resolves.toBeUndefined(); + expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '2.1.0-alpha.0' }); + }); + + it('should bump version to 2.1.0-alpha.0 when main is 2.0.0 and and next is 2.0.0-rc.10', async () => { + fsExtra.__setMockFiles({ + [CODE_PACKAGE_JSON_PATH]: JSON.stringify({ version: '2.0.0-rc.10' }), + }); + + await expect(ensureNextAhead({ mainVersion: '2.0.0' })).resolves.toBeUndefined(); + expect(bumpVersion.run).toHaveBeenCalledWith({ exact: '2.1.0-alpha.0' }); + }); +}); diff --git a/scripts/release/ensure-next-ahead.ts b/scripts/release/ensure-next-ahead.ts new file mode 100644 index 00000000000..280f5fd00fa --- /dev/null +++ b/scripts/release/ensure-next-ahead.ts @@ -0,0 +1,102 @@ +/** + * This script ensures that next is always one minor ahead of main. + * This is needed when releasing a stable from next. + * Next will be at eg. 7.4.0-alpha.4, and main will be at 7.3.0. + * Then we release 7.4.0 by merging next to latest-release to main. + * We then ensure here that next is bumped to 7.5.0-alpha.0 - without releasing it. + * If this is a patch release bumping main to 7.3.1, next will not be touched because it's already ahead. + */ + +/* eslint-disable no-console */ +import chalk from 'chalk'; +import path from 'path'; +import program from 'commander'; +import semver from 'semver'; +import { z } from 'zod'; +import { readJson } from 'fs-extra'; +import dedent from 'ts-dedent'; +import { run as bumpVersion } from './version'; +import { git } from './utils/git-client'; + +program + .name('ensure-next-ahead') + .description('ensure the "next" branch is always a minor version ahead of "main"') + .requiredOption('-M, --main-version ', 'The version currently on the "main" branch'); + +const optionsSchema = z + .object({ + mainVersion: z.string(), + }) + .refine((schema) => semver.valid(schema.mainVersion), { + message: "main-version must be a valid semver version string like '7.4.2'.", + }); + +type Options = { + mainVersion: string; +}; + +const CODE_DIR_PATH = path.join(__dirname, '..', '..', 'code'); +const CODE_PACKAGE_JSON_PATH = path.join(CODE_DIR_PATH, 'package.json'); + +const validateOptions = (options: { [key: string]: any }): options is Options => { + optionsSchema.parse(options); + return true; +}; + +const getCurrentVersion = async () => { + const { version } = await readJson(CODE_PACKAGE_JSON_PATH); + console.log(`📐 Current version of Storybook is ${chalk.green(version)}`); + return version; +}; + +export const run = async (options: unknown) => { + if (!validateOptions(options)) { + return; + } + const { mainVersion } = options; + + const { current: currentGitBranch } = await git.status(); + + if (currentGitBranch !== 'next') { + console.warn( + `🚧 The current branch is not "next" but "${currentGitBranch}", this only really makes sense to run on the "next" branch.` + ); + } + + // Get the current version from code/package.json + const currentNextVersion = await getCurrentVersion(); + if (semver.gt(currentNextVersion, mainVersion)) { + console.log( + `✅ The version on next (${chalk.green( + currentNextVersion + )}) is already ahead of the version on main (${chalk.green(mainVersion)}), no action needed.` + ); + return; + } + + const nextNextVersion = `${semver.inc(mainVersion, 'minor')}-alpha.0`; + + console.log( + `🤜 The version on next (${chalk.green( + currentNextVersion + )}) is behind the version on main (${chalk.green(mainVersion)}), bumping to ${chalk.blue( + nextNextVersion + )}...` + ); + + await bumpVersion({ exact: nextNextVersion }); + + console.log( + `✅ bumped all versions to ${chalk.green( + nextNextVersion + )}, remember to commit and push to next.` + ); +}; + +if (require.main === module) { + const parsed = program.parse(); + run(parsed.opts()).catch((err) => { + console.error(err); + process.exit(1); + }); +} From 930adf4da6bb7bd7b6065920c60b9471877c4038 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 4 Oct 2023 22:59:17 +0200 Subject: [PATCH 26/27] move bash script to cancel preparation runs to tested node script --- .github/workflows/publish.yml | 17 +-- scripts/package.json | 2 + .../__tests__/cancel-preparation-runs.test.ts | 107 ++++++++++++++++++ scripts/release/cancel-preparation-runs.ts | 107 ++++++++++++++++++ scripts/release/utils/github-client.ts | 9 ++ scripts/yarn.lock | 52 +++++++++ 6 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 scripts/release/__tests__/cancel-preparation-runs.test.ts create mode 100644 scripts/release/cancel-preparation-runs.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index be37b132b1f..df82ca33c9a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,20 +37,9 @@ jobs: gh run watch ${{ github.run_id }} - name: Cancel all release preparation runs - run: | - # Get a list of all running or pending release preparation runs - # combining both the prepare-patch-release.yml and prepare-non-patch-release.yml workflows - RUNNING_RELEASE_PREPARATIONS=$( - { - gh run list --limit 50 --workflow=prepare-patch-release.yml --json databaseId,status - gh run list --limit 50 --workflow=prepare-non-patch-release.yml --json databaseId,status - } | jq -rc '.[] | select(.status | contains("in_progress", "pending", "queued", "requested", "waiting")) | .databaseId' - ) - - # Loop through each run and pass it to the "gh run cancel" command - while IFS= read -r databaseId; do - gh run cancel "$databaseId" - done <<< "$RUNNING_RELEASE_PREPARATIONS" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: yarn release:cancel-preparation-runs - name: Checkout ${{ github.ref_name }} uses: actions/checkout@v3 diff --git a/scripts/package.json b/scripts/package.json index 4cd557a0736..9514d8fe586 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -13,6 +13,7 @@ "lint:js:cmd": "cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives", "lint:package": "sort-package-json", "migrate-docs": "node --require esbuild-register ./ts-to-ts49.ts", + "release:cancel-preparation-runs": "ts-node --swc ./release/cancel-preparation-runs.ts", "release:ensure-next-ahead": "ts-node --swc ./release/ensure-next-ahead.ts", "release:generate-pr-description": "ts-node --swc ./release/generate-pr-description.ts", "release:get-changelog-from-file": "ts-node --swc ./release/get-changelog-from-file.ts", @@ -74,6 +75,7 @@ "@jest/globals": "^29.3.1", "@nx/workspace": "16.2.1", "@octokit/graphql": "^5.0.5", + "@octokit/request": "^8.1.2", "@storybook/eslint-config-storybook": "^3.1.2", "@storybook/jest": "next", "@storybook/linter-config": "^3.1.2", diff --git a/scripts/release/__tests__/cancel-preparation-runs.test.ts b/scripts/release/__tests__/cancel-preparation-runs.test.ts new file mode 100644 index 00000000000..aaf8cdbec71 --- /dev/null +++ b/scripts/release/__tests__/cancel-preparation-runs.test.ts @@ -0,0 +1,107 @@ +/* eslint-disable global-require */ +/* eslint-disable no-underscore-dangle */ +import { + PREPARE_NON_PATCH_WORKFLOW_PATH, + PREPARE_PATCH_WORKFLOW_PATH, + run as cancelPreparationWorkflows, +} from '../cancel-preparation-runs'; +import * as github_ from '../utils/github-client'; + +jest.mock('../utils/github-client'); + +const github = jest.mocked(github_); + +jest.spyOn(console, 'log').mockImplementation(() => {}); +jest.spyOn(console, 'warn').mockImplementation(() => {}); +jest.spyOn(console, 'error').mockImplementation(() => {}); + +describe('Cancel preparation runs', () => { + beforeEach(() => { + jest.clearAllMocks(); + github.githubRestClient.mockImplementation(((route: string, options: any) => { + switch (route) { + case 'GET /repos/{owner}/{repo}/actions/workflows': + return { + data: { + workflows: [ + { + id: 1, + path: PREPARE_PATCH_WORKFLOW_PATH, + }, + { + id: 2, + path: PREPARE_NON_PATCH_WORKFLOW_PATH, + }, + ], + }, + }; + case 'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs': + return { + data: { + workflow_runs: [ + { + id: options.workflow_id === 1 ? 100 : 200, + status: 'in_progress', + }, + { + id: options.workflow_id === 1 ? 150 : 250, + status: 'completed', + }, + ], + }, + }; + case 'POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel': + return undefined; // success + default: + throw new Error(`Unexpected route: ${route}`); + } + }) as any); + }); + + it('should fail early when no GH_TOKEN is set', async () => { + delete process.env.GH_TOKEN; + await expect(cancelPreparationWorkflows()).rejects.toThrowErrorMatchingInlineSnapshot( + `"GH_TOKEN environment variable must be set, exiting."` + ); + }); + + it('should cancel all running preparation workflows in GitHub', async () => { + process.env.GH_TOKEN = 'MY_SECRET'; + + await expect(cancelPreparationWorkflows()).resolves.toBeUndefined(); + + expect(github.githubRestClient).toHaveBeenCalledTimes(5); + expect(github.githubRestClient).toHaveBeenCalledWith( + 'POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel', + { + owner: 'storybookjs', + repo: 'storybook', + run_id: 100, + } + ); + expect(github.githubRestClient).toHaveBeenCalledWith( + 'POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel', + { + owner: 'storybookjs', + repo: 'storybook', + run_id: 200, + } + ); + expect(github.githubRestClient).not.toHaveBeenCalledWith( + 'POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel', + { + owner: 'storybookjs', + repo: 'storybook', + run_id: 150, + } + ); + expect(github.githubRestClient).not.toHaveBeenCalledWith( + 'POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel', + { + owner: 'storybookjs', + repo: 'storybook', + run_id: 250, + } + ); + }); +}); diff --git a/scripts/release/cancel-preparation-runs.ts b/scripts/release/cancel-preparation-runs.ts new file mode 100644 index 00000000000..630bfb4847b --- /dev/null +++ b/scripts/release/cancel-preparation-runs.ts @@ -0,0 +1,107 @@ +/** + * This script cancels all running preparation workflows in GitHub. + * It will fetch all active runs for the preparation workflows, and cancel them. + */ +/* eslint-disable no-console */ +import chalk from 'chalk'; +import program from 'commander'; +import dedent from 'ts-dedent'; +import { githubRestClient } from './utils/github-client'; + +program + .name('cancel-preparation-workflows') + .description('cancel all running preparation workflows in GitHub'); + +export const PREPARE_PATCH_WORKFLOW_PATH = '.github/workflows/prepare-patch-release.yml'; +export const PREPARE_NON_PATCH_WORKFLOW_PATH = '.github/workflows/prepare-non-patch-release.yml'; + +export const run = async () => { + if (!process.env.GH_TOKEN) { + throw new Error('GH_TOKEN environment variable must be set, exiting.'); + } + + console.log(`🔎 Looking for workflows to cancel...`); + const allWorkflows = await githubRestClient('GET /repos/{owner}/{repo}/actions/workflows', { + owner: 'storybookjs', + repo: 'storybook', + }); + + const preparePatchWorkflowId = allWorkflows.data.workflows.find( + ({ path }) => path === PREPARE_PATCH_WORKFLOW_PATH + )?.id; + const prepareNonPatchWorkflowId = allWorkflows.data.workflows.find( + ({ path }) => path === PREPARE_NON_PATCH_WORKFLOW_PATH + )?.id; + + console.log(`Found workflow IDs for the preparation workflows: + ${chalk.blue(PREPARE_PATCH_WORKFLOW_PATH)}: ${chalk.green(preparePatchWorkflowId)} + ${chalk.blue(PREPARE_NON_PATCH_WORKFLOW_PATH)}: ${chalk.green(prepareNonPatchWorkflowId)}`); + + if (!preparePatchWorkflowId || !prepareNonPatchWorkflowId) { + throw new Error(dedent`🚨 Could not find workflow IDs for the preparation workflows + - Looked for paths: "${chalk.blue(PREPARE_PATCH_WORKFLOW_PATH)}" and "${chalk.blue( + PREPARE_NON_PATCH_WORKFLOW_PATH + )}", are they still correct? + - Found workflows: + ${JSON.stringify(allWorkflows.data.workflows, null, 2)}`); + } + + console.log('🔍 Fetching patch and non-patch runs for preparation workflows...'); + const [patchRuns, nonPatchRuns] = await Promise.all([ + githubRestClient('GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', { + owner: 'storybookjs', + repo: 'storybook', + workflow_id: preparePatchWorkflowId, + }), + githubRestClient('GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', { + owner: 'storybookjs', + repo: 'storybook', + workflow_id: prepareNonPatchWorkflowId, + }), + ]); + console.log('✅ Successfully fetched patch and non-patch runs for preparation workflows.'); + + const runsToCancel = patchRuns.data.workflow_runs + .concat(nonPatchRuns.data.workflow_runs) + .filter(({ status }) => + ['in_progress', 'pending', 'queued', 'requested', 'waiting'].includes(status) + ); + + if (runsToCancel.length === 0) { + console.log('👍 No runs to cancel.'); + return; + } + + console.log(`🔍 Found ${runsToCancel.length} runs to cancel. Cancelling them now: + ${runsToCancel + .map((r) => `${chalk.green(r.path)} - ${chalk.green(r.id)}: ${chalk.blue(r.status)}`) + .join('\n ')}`); + + const result = await Promise.allSettled( + runsToCancel.map((r) => + githubRestClient('POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel', { + owner: 'storybookjs', + repo: 'storybook', + run_id: r.id, + }) + ) + ); + + if (result.some((r) => r.status === 'rejected')) { + console.warn('⚠️ Some runs could not be cancelled:'); + result.forEach((r, index) => { + if (r.status === 'rejected') { + console.warn(`Run ID: ${runsToCancel[index].id} - Reason: ${r.reason}`); + } + }); + } else { + console.log('✅ Successfully cancelled all preparation runs.'); + } +}; + +if (require.main === module) { + run().catch((err) => { + console.error(err); + // this is non-critical work, so we don't want to fail the CI build if this fails + }); +} diff --git a/scripts/release/utils/github-client.ts b/scripts/release/utils/github-client.ts index 646ba100398..e81991414bf 100644 --- a/scripts/release/utils/github-client.ts +++ b/scripts/release/utils/github-client.ts @@ -1,6 +1,8 @@ /* eslint-disable no-console */ import type { GraphQlQueryResponseData } from '@octokit/graphql'; import { graphql } from '@octokit/graphql'; +import { request } from '@octokit/request'; +import fetch from 'node-fetch'; export interface PullRequest { number: number; @@ -14,6 +16,13 @@ export const githubGraphQlClient = graphql.defaults({ headers: { authorization: `token ${process.env.GH_TOKEN}` }, }); +export const githubRestClient = request.defaults({ + request: { + fetch, + }, + headers: { authorization: `token ${process.env.GH_TOKEN}`, 'X-GitHub-Api-Version': '2022-11-28' }, +}); + export async function getUnpickedPRs( baseBranch: string, verbose?: boolean diff --git a/scripts/yarn.lock b/scripts/yarn.lock index ee273eaf2d4..690e145e27e 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -2536,6 +2536,17 @@ __metadata: languageName: node linkType: hard +"@octokit/endpoint@npm:^9.0.0": + version: 9.0.1 + resolution: "@octokit/endpoint@npm:9.0.1" + dependencies: + "@octokit/types": ^12.0.0 + is-plain-object: ^5.0.0 + universal-user-agent: ^6.0.0 + checksum: 757505b1cd634bcd7b71a18c8fe07dfda47790598ddd0d9d13f47d68713070f49953a672ac40ec39787defc2a7e07d08dca97756def7b907118f8f8d4c653f5c + languageName: node + linkType: hard + "@octokit/graphql@npm:^4.3.1, @octokit/graphql@npm:^4.5.8": version: 4.8.0 resolution: "@octokit/graphql@npm:4.8.0" @@ -2572,6 +2583,13 @@ __metadata: languageName: node linkType: hard +"@octokit/openapi-types@npm:^19.0.0": + version: 19.0.0 + resolution: "@octokit/openapi-types@npm:19.0.0" + checksum: 8270e0a224bbef6d1c82396cda873a3528111cb25a772184b39e1fbada4e6433b41c5f4634ecf204e8a2816a802048197e0132b7615b579fab217f7c1e29545b + languageName: node + linkType: hard + "@octokit/plugin-paginate-rest@npm:^2.16.8, @octokit/plugin-paginate-rest@npm:^2.2.0": version: 2.21.3 resolution: "@octokit/plugin-paginate-rest@npm:2.21.3" @@ -2636,6 +2654,17 @@ __metadata: languageName: node linkType: hard +"@octokit/request-error@npm:^5.0.0": + version: 5.0.1 + resolution: "@octokit/request-error@npm:5.0.1" + dependencies: + "@octokit/types": ^12.0.0 + deprecation: ^2.0.0 + once: ^1.4.0 + checksum: e72a4627120de345b54876a1f007664095e5be9d624fce2e14fccf7668cd8f5e4929d444d8fc085d48e1fb5cd548538453974aab129a669101110d6679dce6c6 + languageName: node + linkType: hard + "@octokit/request@npm:^5.4.0, @octokit/request@npm:^5.6.0, @octokit/request@npm:^5.6.3": version: 5.6.3 resolution: "@octokit/request@npm:5.6.3" @@ -2664,6 +2693,19 @@ __metadata: languageName: node linkType: hard +"@octokit/request@npm:^8.1.2": + version: 8.1.2 + resolution: "@octokit/request@npm:8.1.2" + dependencies: + "@octokit/endpoint": ^9.0.0 + "@octokit/request-error": ^5.0.0 + "@octokit/types": ^12.0.0 + is-plain-object: ^5.0.0 + universal-user-agent: ^6.0.0 + checksum: 49219eb71b856acecc8268f05a7a7d074488f9eaeb59439f5c8872e5b4555a4e6c0cf0ebcadf0983466f88957e74c27765f582e473b0dd89b453274501f0dc37 + languageName: node + linkType: hard + "@octokit/rest@npm:^16.43.0 || ^17.11.0 || ^18.12.0, @octokit/rest@npm:^18.12.0": version: 18.12.0 resolution: "@octokit/rest@npm:18.12.0" @@ -2688,6 +2730,15 @@ __metadata: languageName: node linkType: hard +"@octokit/types@npm:^12.0.0": + version: 12.0.0 + resolution: "@octokit/types@npm:12.0.0" + dependencies: + "@octokit/openapi-types": ^19.0.0 + checksum: 6e5b68f8855775638db53244348d2ca07896d36a15aad41d3cb652fafaa1b307c3b6222319174dd5716accd9076e101da838b82f988a7c380a8e9d188e3aadf1 + languageName: node + linkType: hard + "@octokit/types@npm:^4.1.6": version: 4.1.10 resolution: "@octokit/types@npm:4.1.10" @@ -2906,6 +2957,7 @@ __metadata: "@jest/globals": ^29.3.1 "@nx/workspace": 16.2.1 "@octokit/graphql": ^5.0.5 + "@octokit/request": ^8.1.2 "@storybook/eslint-config-storybook": ^3.1.2 "@storybook/jest": next "@storybook/linter-config": ^3.1.2 From 72837aea51221146082d4676dab7a7fade8a9f87 Mon Sep 17 00:00:00 2001 From: Jeppe Reinhold Date: Wed, 4 Oct 2023 23:11:40 +0200 Subject: [PATCH 27/27] fix tests --- scripts/release/ensure-next-ahead.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/release/ensure-next-ahead.ts b/scripts/release/ensure-next-ahead.ts index 280f5fd00fa..1aa32bfd932 100644 --- a/scripts/release/ensure-next-ahead.ts +++ b/scripts/release/ensure-next-ahead.ts @@ -14,7 +14,6 @@ import program from 'commander'; import semver from 'semver'; import { z } from 'zod'; import { readJson } from 'fs-extra'; -import dedent from 'ts-dedent'; import { run as bumpVersion } from './version'; import { git } from './utils/git-client';