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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/publish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Releasing previous major release

## Preparation

1. `git checkout v9`
2. `git pull`
3. `git checkout -b hotfix/v<next-patch-release-version>`
4. Apply necessary hotfixes
5. `cd scripts && yarn release:version --deferred --release-type patch --verbose && cd .. && git add . && git commit -m "Bump deferred version"`
6. Add a new entry for the new version to the `CHANGELOG.md` file
7. Trigger canary release via dispatching the workflow for `publish-canary`
8. Test the canary release
9. Merge `hotfix/v<next-patch-release-version>` into `v9`
10. Observe the `publish-normal` job


## Prepare major release branch for publishing

1. Go to `https://github.com/storybookjs/storybook/settings/environments/1012979736/edit` (release environment) and add the major release branch (e.g. v9)
188 changes: 102 additions & 86 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ on:
push:
# Normal releases, major/minor/patch/prerelease
branches:
- latest-release
- next-release
- v9
workflow_dispatch:
# Manual canary releases on PRs
inputs:
Expand Down Expand Up @@ -40,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
if: |
github.event_name == 'push' &&
(github.ref_name == 'latest-release' || github.ref_name == 'next-release') &&
(github.ref_name == 'v9') &&
contains(github.event.head_commit.message, '[skip ci]') != true
environment: Release
defaults:
Expand All @@ -58,11 +57,6 @@ jobs:
with:
install-code-deps: true

- name: Cancel all release preparation runs
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn release:cancel-preparation-runs

- name: Apply deferred version bump and commit
working-directory: .
env:
Expand Down Expand Up @@ -95,20 +89,9 @@ jobs:
CURRENT_VERSION: ${{ steps.version.outputs.current-version }}
run: yarn release:is-version-published "$CURRENT_VERSION"

- name: Check release vs prerelease
if: steps.publish-needed.outputs.published == 'false'
id: is-prerelease
env:
CURRENT_VERSION: ${{ steps.version.outputs.current-version }}
run: yarn release:is-prerelease "$CURRENT_VERSION" --verbose

- name: Publish
if: steps.publish-needed.outputs.published == 'false'
run: yarn release:publish --tag ${{ steps.is-prerelease.outputs.prerelease == 'true' && 'next' || 'latest' }} --verbose

- name: Get target branch
id: target
run: echo "target=${{ github.ref_name == 'next-release' && 'next' || 'main' }}" >> $GITHUB_OUTPUT
run: yarn release:publish --tag v9 --verbose

- name: Get changelog for ${{ steps.version.outputs.current-version }}
if: steps.publish-needed.outputs.published == 'false'
Expand All @@ -117,18 +100,6 @@ jobs:
CURRENT_VERSION: ${{ steps.version.outputs.current-version }}
run: yarn release:get-changelog-from-file "$CURRENT_VERSION"

# tags are needed to get list of patches to label as picked
- name: Fetch git tags
if: github.ref_name == 'latest-release'
run: git fetch --tags origin

# when this is a patch release from main, label any patch PRs included in the release
- name: Label patch PRs as picked
if: github.ref_name == 'latest-release'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: yarn release:label-patches

- name: Create GitHub Release
if: steps.publish-needed.outputs.published == 'false'
env:
Expand All @@ -137,67 +108,14 @@ jobs:
REPOSITORY: ${{ github.repository }}
REF_NAME: ${{ github.ref_name }}
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
IS_PRERELEASE: ${{ steps.is-prerelease.outputs.prerelease == 'true' && '--prerelease' || '' }}
run: |
gh release create \
"v$CURRENT_VERSION" \
--repo "$REPOSITORY" \
--target "$REF_NAME" \
--title "v$CURRENT_VERSION" \
--notes "$CHANGELOG" \
$IS_PRERELEASE

- name: Merge ${{ github.ref_name }} into ${{ steps.target.outputs.target }}
env:
REF_NAME: ${{ github.ref_name }}
TARGET_BRANCH: ${{ steps.target.outputs.target }}
run: |
git config --global user.name "storybook-bot"
git config --global user.email "32066757+storybook-bot@users.noreply.github.com"
git fetch origin "$TARGET_BRANCH"
git checkout "$TARGET_BRANCH"
git merge "$REF_NAME"
git push origin "$TARGET_BRANCH"

- name: Force push from 'next' to 'latest-release' and 'main' on minor/major releases
if: github.ref_name == 'next-release' && steps.is-prerelease.outputs.prerelease == 'false'
run: |
git checkout next
git pull
git push origin --force next:latest-release
git push origin --force next:main

- name: Sync CHANGELOG.md from `main` to `next`
if: steps.target.outputs.target == 'main'
working-directory: .
env:
CURRENT_VERSION: ${{ steps.version.outputs.current-version }}
run: |
git fetch origin next
git checkout next
git pull
git checkout origin/main ./CHANGELOG.md
git add ./CHANGELOG.md
git commit -m "Update CHANGELOG.md for v$CURRENT_VERSION [skip ci]" || true
git push origin next

# Sync the next.json version file to the main branch so it gets deployed to the docs site
# but only if this is a prerelease, because in minor/major releases we're already force pushing next-release onto main, so it's already there
- name: Sync version JSONs from `next-release` to `main`
if: github.ref_name == 'next-release' && steps.is-prerelease.outputs.prerelease == 'true'
working-directory: .
env:
CURRENT_VERSION: ${{ steps.version.outputs.current-version }}
run: |
VERSION_FILE="./docs/versions/next.json"
git fetch origin main
git checkout main
git pull
git checkout origin/next-release $VERSION_FILE
git add $VERSION_FILE
git commit -m "Update $VERSION_FILE for v$CURRENT_VERSION"
git push origin main

--latest=false
- name: Create Sentry release
if: steps.publish-needed.outputs.published == 'false'
uses: getsentry/action-release@v3
Expand All @@ -216,3 +134,101 @@ jobs:
uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554
with:
args: "The GitHub Action for publishing version ${{ steps.version.outputs.current-version }} (triggered by ${{ github.triggering_actor }}) failed! See run at: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"

publish-canary:
name: Publish canary version
runs-on: ubuntu-latest
if: |
github.repository_owner == 'storybookjs' &&
github.event_name == 'workflow_dispatch' &&
contains(github.event.head_commit.message, '[skip ci]') != true
environment: Release
steps:
- name: Fail if triggering actor is not administrator
uses: prince-chrismc/check-actor-permissions-action@87c6d9b36c730377858fd9719fbbac1b58fa678d
with:
permission: admin

- name: Get pull request information
id: info
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
run: |
PR_INFO=$(gh pr view "$PR_NUMBER" --repo "$REPOSITORY" --json isCrossRepository,headRefOid,headRefName,headRepository,headRepositoryOwner --jq '{isFork: .isCrossRepository, owner: .headRepositoryOwner.login, repoName: .headRepository.name, branch: .headRefName, sha: .headRefOid}')
echo $PR_INFO
# Loop through each key-value pair in PR_INFO and set as step output
for key in $(echo "$PR_INFO" | jq -r 'keys[]'); do
value=$(echo "$PR_INFO" | jq -r ".$key")
echo "$key=$value" >> "$GITHUB_OUTPUT"
done
echo "repository=$(echo "$PR_INFO" | jq -r ".owner")/$(echo "$PR_INFO" | jq -r ".repoName")" >> $GITHUB_OUTPUT
echo "shortSha=$(echo "$PR_INFO" | jq -r ".sha" | cut -c 1-8)" >> $GITHUB_OUTPUT
echo "date=$(date)" >> $GITHUB_OUTPUT
echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT

- name: Checkout
uses: actions/checkout@v4
with:
repository: ${{ steps.info.outputs.isFork == 'true' && steps.info.outputs.repository || null }}
ref: ${{ steps.info.outputs.sha }}
token: ${{ secrets.GH_TOKEN }}

- name: Setup Node.js and Install Dependencies
uses: ./.github/actions/setup-node-and-install
with:
install-code-deps: true

- name: Set version
id: version
working-directory: scripts
env:
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.pull_request.number }}
SHORT_SHA: ${{ steps.info.outputs.shortSha }}
run: |
yarn release:version --exact "0.0.0-pr-$PR_NUMBER-sha-$SHORT_SHA" --verbose

- name: Publish v${{ steps.version.outputs.next-version }}
working-directory: scripts
run: yarn release:publish --tag v9-canary --verbose

- name: Replace Pull Request Body
uses: ivangabriele/find-and-replace-pull-request-body@042438c6cbfbacf6a4701d6042f59b1f73db2fd8
with:
githubToken: ${{ secrets.GH_TOKEN }}
prNumber: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || '' }}
find: "CANARY_RELEASE_SECTION"
isHtmlCommentTag: true
replace: |
This pull request has been released as version `${{ steps.version.outputs.next-version }}`. Try it out in a new sandbox by running `npx storybook@${{ steps.version.outputs.next-version }} sandbox` or in an existing project with `npx storybook@${{ steps.version.outputs.next-version }} upgrade`.
<details>
<summary>More information</summary>

| | |
| --- | --- |
| **Published version** | [`${{ steps.version.outputs.next-version }}`](https://npmjs.com/package/storybook/v/${{ steps.version.outputs.next-version }}) |
| **Triggered by** | @${{ github.triggering_actor }} |
| **Repository** | [${{ steps.info.outputs.repository }}](https://github.com/${{ steps.info.outputs.repository }}) |
| **Branch** | [`${{ steps.info.outputs.branch }}`](https://github.com/${{ steps.info.outputs.repository }}/tree/${{ steps.info.outputs.branch }}) |
| **Commit** | [`${{ steps.info.outputs.shortSha }}`](https://github.com/${{ steps.info.outputs.repository }}/commit/${{ steps.info.outputs.sha }}) |
| **Datetime** | ${{ steps.info.outputs.date }} (`${{ steps.info.outputs.timestamp }}`) |
| **Workflow run** | [${{ github.run_id }}](https://github.com/storybookjs/storybook/actions/runs/${{ github.run_id }}) |

To request a new release of this pull request, mention the `@storybookjs/core` team.

_core team members can create a new canary release [here](https://github.com/storybookjs/storybook/actions/workflows/publish.yml) or locally with `gh workflow run --repo storybookjs/storybook publish.yml --field pr=${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.pull_request.number }}`_
</details>

- name: Create failing comment on PR
if: failure()
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PR_NUMBER: ${{ github.event_name == 'workflow_dispatch' && inputs.pr || github.event.pull_request.number }}
REPOSITORY: ${{ github.repository }}
TRIGGERING_ACTOR: ${{ github.triggering_actor }}
RUN_ID: ${{ github.run_id }}
run: |
gh pr comment "$PR_NUMBER"\
--repo "$REPOSITORY"\
--body "Failed to publish canary version of this pull request, triggered by @$TRIGGERING_ACTOR. See the failed workflow run at: https://github.com/$REPOSITORY/actions/runs/$RUN_ID"
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 9.1.18

- No-op release. No changes.

## 9.1.16

- CLI: Fix Nextjs project creation in empty directories - [#32828](https://github.com/storybookjs/storybook/pull/32828), thanks @yannbf!
Expand Down
9 changes: 8 additions & 1 deletion code/.swcrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"env": {
"targets": {
"chrome": 100,
"safari": 15,
"firefox": 91,
"node": 22
}
},
"jsc": {
"parser": {
"syntax": "typescript",
Expand All @@ -14,7 +22,6 @@
"development": false
}
},
"target": "es2020",
"loose": false,
"externalHelpers": false,
"keepClassNames": false
Expand Down
2 changes: 1 addition & 1 deletion code/lib/cli-storybook/src/sandbox-templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export const baseTemplates = {
'nextjs/default-ts': {
name: 'Next.js Latest (Webpack | TypeScript)',
script:
'npx create-next-app {{beforeDir}} --eslint --tailwind --app --import-alias="@/*" --src-dir',
'npx create-next-app@16.0.1 {{beforeDir}} --eslint --tailwind --app --import-alias="@/*" --src-dir',
expected: {
framework: '@storybook/nextjs',
renderer: '@storybook/react',
Expand Down
6 changes: 4 additions & 2 deletions code/lib/create-storybook/src/generators/baseGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ export async function baseGenerator(
const compiler = webpackCompiler ? webpackCompiler({ builder }) : undefined;

if (features.includes('test')) {
extraAddons.push('@chromatic-com/storybook');
extraAddons.push('@chromatic-com/storybook@^4');
}
Comment on lines 290 to 292

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid duplicate Chromatic addon specs when users already provide one.

If options.extraAddons already contains @chromatic-com/storybook (with or without a version), pushing @chromatic-com/storybook@^4 creates two specs for the same package. Because de-duping later is string-based, this can lead to conflicting installs or nondeterministic version selection. Consider guarding by base package name before adding.

💡 Suggested fix
 if (features.includes('test')) {
-  extraAddons.push('@chromatic-com/storybook@^4');
+  const chromaticPkg = '@chromatic-com/storybook';
+  const hasChromatic = extraAddons.some(
+    (addon) => getPackageDetails(addon)[0] === chromaticPkg
+  );
+  if (!hasChromatic) {
+    extraAddons.push(`${chromaticPkg}@^4`);
+  }
 }
🤖 Prompt for AI Agents
In `@code/lib/create-storybook/src/generators/baseGenerator.ts` around lines 290 -
292, Guard against adding a duplicate Chromatic addon by checking existing addon
specs before pushing: when handling features.includes('test') in the code that
mutates extraAddons (and/or reads options.extraAddons), first scan
extraAddons.concat(options.extraAddons || []) for any spec whose base package
equals "@chromatic-com/storybook" (e.g., strip any version suffix or match with
a regex like /^@chromatic-com\/storybook(@|$)/), and only push
"@chromatic-com/storybook@^4" if no match is found; update the block that
currently does extraAddons.push('@chromatic-com/storybook@^4') to perform this
conditional check.


if (features.includes('docs')) {
Expand All @@ -299,6 +299,8 @@ export async function baseGenerator(
extraAddons.push('@storybook/addon-onboarding');
}

const compilerVersion = compiler === 'babel' ? '^3.0.6' : '^4.0.0';

// added to main.js
const addons = [
...(compiler ? [`@storybook/addon-webpack5-compiler-${compiler}`] : []),
Expand All @@ -307,7 +309,7 @@ export async function baseGenerator(

// added to package.json
const addonPackages = [
...(compiler ? [`@storybook/addon-webpack5-compiler-${compiler}`] : []),
...(compiler ? [`@storybook/addon-webpack5-compiler-${compiler}@${compilerVersion}`] : []),
...extraAddons,
].filter(Boolean);

Expand Down
6 changes: 3 additions & 3 deletions code/lib/create-storybook/src/scaffold-new-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ const SUPPORTED_PROJECTS: Record<string, SupportedProject> = {
language: 'TS',
},
createScript: {
npm: 'npm create next-app . -- --turbopack --typescript --use-npm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
npm: 'npm create next-app@16.0.1 . -- --turbopack --typescript --use-npm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
// yarn doesn't support version ranges, so we have to use npx
yarn: 'npx create-next-app . --turbopack --typescript --use-yarn --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
pnpm: 'pnpm create next-app . --turbopack --typescript --use-pnpm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
yarn: 'npx create-next-app@16.0.1 . --turbopack --typescript --use-yarn --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
pnpm: 'pnpm create next-app@16.0.1 . --turbopack --typescript --use-pnpm --eslint --tailwind --no-app --import-alias="@/*" --src-dir --no-react-compiler',
Comment on lines +48 to +51

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify create-next-app@16.0.1 exists on npm and check available CLI options

echo "=== Checking create-next-app@16.0.1 ==="
npm view create-next-app@16.0.1 version 2>&1

echo -e "\n=== Checking available 16.x versions ==="
npm view create-next-app versions --json 2>&1 | grep -o '"16\.[^"]*"' | head -20

echo -e "\n=== Checking CLI help for create-next-app@16.0.1 ==="
npx create-next-app@16.0.1 --help 2>&1 | head -60

Repository: storybookjs/storybook

Length of output: 3326


Fix undocumented CLI flags that may cause the scaffold command to fail.

Version 16.0.1 is valid and most flags are supported. However, --no-app and --no-react-compiler are not shown in the create-next-app@16.0.1 help output. The CLI documents only --app and --react-compiler as positive flags. These negated forms may work as implicit boolean negations (common in some CLI tools) but are undocumented and risk command failure.

Verify these flags work with version 16.0.1, or replace them with documented equivalents or omit them if the defaults are already correct.

🤖 Prompt for AI Agents
In `@code/lib/create-storybook/src/scaffold-new-project.ts` around lines 48 - 51,
The scaffold commands include undocumented negated flags (--no-app and
--no-react-compiler) which may fail; update the command strings in
scaffold-new-project.ts (the npm, yarn, and pnpm entries) to remove these
undocumented flags, or replace them with documented equivalents after
verification — e.g., omit --no-app to rely on the default (absence of --app) and
remove --no-react-compiler (or use the documented --react-compiler option with
an allowed value) so the commands only use supported flags.

},
},
'vue-vite-ts': {
Expand Down
5 changes: 4 additions & 1 deletion code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"@types/node": "^22.0.0",
"@types/react": "^18.0.0",
"@vitest/expect@npm:3.2.4": "patch:@vitest/expect@npm%3A3.2.4#~/.yarn/patches/@vitest-expect-npm-3.2.4-97c526d5cc.patch",
"baseline-browser-mapping": "2.9.19",
"esbuild": "^0.25.3",
"playwright": "1.52.0",
"playwright-core": "1.52.0",
Expand Down Expand Up @@ -158,6 +159,7 @@
"@vitest/browser": "^3.2.4",
"@vitest/coverage-istanbul": "^3.2.4",
"@vitest/coverage-v8": "^3.2.4",
"baseline-browser-mapping": "^2.9.19",
"create-storybook": "workspace:*",
"cross-env": "^7.0.3",
"danger": "^13.0.4",
Expand Down Expand Up @@ -281,5 +283,6 @@
"Dependency Upgrades"
]
]
}
},
"deferredNextVersion": "9.1.18"
}
9 changes: 5 additions & 4 deletions code/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6957,6 +6957,7 @@ __metadata:
"@vitest/browser": "npm:^3.2.4"
"@vitest/coverage-istanbul": "npm:^3.2.4"
"@vitest/coverage-v8": "npm:^3.2.4"
baseline-browser-mapping: "npm:^2.9.19"
create-storybook: "workspace:*"
cross-env: "npm:^7.0.3"
danger: "npm:^13.0.4"
Expand Down Expand Up @@ -10479,12 +10480,12 @@ __metadata:
languageName: node
linkType: hard

"baseline-browser-mapping@npm:^2.8.3":
version: 2.8.4
resolution: "baseline-browser-mapping@npm:2.8.4"
"baseline-browser-mapping@npm:2.9.19":
version: 2.9.19
resolution: "baseline-browser-mapping@npm:2.9.19"
bin:
baseline-browser-mapping: dist/cli.js
checksum: 10c0/d85c8e9b919d4f7d5b46cf3d89e6a8be6e74086934ff6f362b720be8e06656021a0207e2ba1efe8ae2563dec893a76ad15633e06f3e153984fab8118e2dc4ae7
checksum: 10c0/569928db78bcd081953d7db79e4243a59a579a34b4ae1806b9b42d3b7f84e5bc40e6e82ae4fa06e7bef8291bf747b33b3f9ef5d3c6e1e420cb129d9295536129
languageName: node
linkType: hard

Expand Down