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
45 changes: 45 additions & 0 deletions .github/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Releasing Packages

All publishable packages in this repository use a pull-request-driven release flow. GitHub Actions prepares a release PR containing the version bump and changelog, and merging that PR triggers publishing.

## Prepare a Release

1. Run the [Prepare Release](workflows/prepare-release.yml) workflow from the `main` branch.
2. Select the package to release.
3. Select a `release` type. The default `next` creates the next patch for a stable version or advances an existing prerelease. Alternatively, select another supported release type or enter an exact `version`, which takes precedence over `release`. Prereleases must use an `alpha` or `beta` identifier so npm assigns the correct distribution tag.
4. Wait for `vite-release-bot` to open the release PR. The PR contains the selected package's version bump and changelog entry.

The `plugin-react` and `plugin-react-swc` releases move their manually maintained `Unreleased` notes under the new version. The `plugin-rsc` release generates its changelog from conventional commits.

To preview the available release types and versions locally, run the following command from the repository root and cancel before confirming so no files are changed:

```sh
node scripts/prepare-release.ts plugin-react
```

Replace `plugin-react` with `plugin-react-swc` or `plugin-rsc` as needed.

## Review and Publish

1. Review the generated version and changelog, and wait for the release PR checks to pass.
2. Merge the PR while retaining the `release: <package>@<version>` commit subject. A matching commit at the tip of `main` triggers the publish job.
3. Open the [Publish Package](workflows/publish.yml) run and approve its `Release` environment deployment.
4. Wait for the workflow to publish the package, create the `<package>@<version>` tag, and create the corresponding GitHub release.
5. Verify the new version on npm and check that the tag and GitHub release point to the merged release commit.

## Repository Configuration

The release flow depends on configuration outside this repository:

- The `vite-release-bot` GitHub App must be installed only on `vitejs/vite-plugin-react` with Contents and Pull requests read/write permissions. Webhooks must be disabled.
- `RELEASE_GITHUB_APP_CLIENT_ID` must be a repository variable containing the app client ID.
- `RELEASE_GITHUB_APP_PRIVATE_KEY` must be a repository secret containing the app private key.
- The `Release` environment must require maintainer approval before publishing.
- The npm trusted publishers for `@vitejs/plugin-react`, `@vitejs/plugin-react-swc`, and `@vitejs/plugin-rsc` must be restricted to `vitejs/vite-plugin-react`, `.github/workflows/publish.yml`, and the `Release` environment.

## Recovery

- If release preparation fails, fix the cause and rerun the preparation workflow. Each run creates a uniquely named branch.
- If publishing fails before npm accepts the package, fix the cause and rerun the failed workflow.
- If npm publishing succeeds but tag or GitHub release creation fails, do not rerun the entire publish job blindly because npm versions are immutable. Confirm the package state first, then manually create and push the `<package>@<version>` tag at the original release commit and create the GitHub release from the corresponding changelog entry.
- If the release tag already exists, investigate whether the package was previously published before retrying or changing any release state.
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
name: Prepare RSC Release
name: Prepare Release

on:
workflow_dispatch:
inputs:
package:
description: Package to release
required: true
default: plugin-react
type: choice
options:
- plugin-react
- plugin-react-swc
- plugin-rsc
release:
description: Bumpp release type
required: true
Expand All @@ -22,15 +31,15 @@ on:
type: string

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || inputs.release }}
group: ${{ github.workflow }}-${{ inputs.package }}-${{ inputs.version || inputs.release }}
cancel-in-progress: false

permissions: {}

jobs:
prepare:
if: github.repository == 'vitejs/vite-plugin-react'
name: Prepare RSC release PR
name: Prepare release PR
runs-on: ubuntu-latest
permissions:
contents: read
Expand Down Expand Up @@ -59,11 +68,11 @@ jobs:
- name: Prepare release
id: prepare
env:
RELEASE_TYPE: ${{ inputs.release }}
RELEASE_VERSION: ${{ inputs.version }}
RELEASE: ${{ inputs.version || inputs.release }}
RELEASE_PACKAGE: ${{ inputs.package }}
run: |
node scripts/prepare-rsc-release.ts
echo "version=$(jq -r .version packages/plugin-rsc/package.json)" >> "$GITHUB_OUTPUT"
node scripts/prepare-release.ts "$RELEASE_PACKAGE" "$RELEASE"
echo "version=$(jq -r .version packages/$RELEASE_PACKAGE/package.json)" >> "$GITHUB_OUTPUT"

- id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
Expand All @@ -79,18 +88,28 @@ jobs:
env:
APP_SLUG: ${{ steps.generate-token.outputs.app-slug }}
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
PACKAGE: ${{ inputs.package }}
PR_BODY_FILE: ${{ runner.temp }}/release-pr-body.md
VERSION: ${{ steps.prepare.outputs.version }}
run: |
BRANCH="release-plugin-rsc-$VERSION-$GITHUB_RUN_ID"
BRANCH="release-$PACKAGE-$VERSION-$GITHUB_RUN_ID"
BOT_ID="$(gh api "/users/$APP_SLUG%5Bbot%5D" --jq .id)"
{
echo "$PACKAGE release PR generated by the Prepare Release workflow: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
echo
echo "## Changelog"
echo
node scripts/extract-changelog.ts "packages/$PACKAGE/CHANGELOG.md" "$VERSION"
} > "$PR_BODY_FILE"
git config user.name "$APP_SLUG[bot]"
git config user.email "$BOT_ID+$APP_SLUG[bot]@users.noreply.github.com"
git switch -c "$BRANCH"
git add packages/plugin-rsc/package.json packages/plugin-rsc/CHANGELOG.md
git commit -m "release: plugin-rsc@$VERSION"
git add "packages/$PACKAGE/package.json" "packages/$PACKAGE/CHANGELOG.md"
git commit -m "release: $PACKAGE@$VERSION"
git push -u "https://x-access-token:$GH_TOKEN@github.com/$GITHUB_REPOSITORY.git" HEAD
gh pr create \
--assignee "$GITHUB_ACTOR" \
--base main \
--head "$BRANCH" \
--title "release: plugin-rsc@$VERSION" \
--body "RSC release PR generated by the Prepare RSC Release workflow: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
--title "release: $PACKAGE@$VERSION" \
--body-file "$PR_BODY_FILE"
136 changes: 40 additions & 96 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,113 +2,48 @@ name: Publish Package

on:
push:
# RSC releases start when a generated release PR is merged.
branches:
- main
# Other packages retain the existing tag-driven release flow.
tags:
- 'plugin-*'

permissions: {}

jobs:
# Publish tag-driven releases, excluding RSC's commit-driven flow.
publish:
if: github.repository == 'vitejs/vite-plugin-react' && github.ref_type == 'tag' && !startsWith(github.ref, 'refs/tags/plugin-rsc@')
runs-on: ubuntu-latest
permissions:
contents: write # for ArnaudBarre/github-release to create a release
id-token: write # for provenance generation
environment: Release
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false

- name: Install pnpm
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9

- name: Set node version
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
registry-url: https://registry.npmjs.org/
# disable cache, to avoid cache poisoning (https://docs.zizmor.sh/audits/#cache-poisoning)
package-manager-cache: false

- name: Disallow installation scripts
run: yq '.onlyBuiltDependencies = []' -i pnpm-workspace.yaml

- name: Install deps
run: pnpm install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'

- name: Get pkgName for tag
id: tag
run: |
# Check if the tag contains "alpha"
if [[ $GITHUB_REF_NAME =~ alpha ]]; then
echo "isAlpha=true" >> $GITHUB_OUTPUT
else
echo "isAlpha=false" >> $GITHUB_OUTPUT
fi

# `%@*` truncates @ and version number from the right side.
# https://stackoverflow.com/questions/9532654/expression-after-last-specific-character
pkgName=${GITHUB_REF_NAME%@*}
echo "pkgName=$pkgName" >> $GITHUB_OUTPUT

- if: steps.tag.outputs.pkgName == 'plugin-react-swc'
working-directory: packages/plugin-react-swc
run: pnpm build

- name: Publish package
# npm 11.5.2+ is required for trusted publishing.
# zizmor: ignore[adhoc-packages]
run: npm i -g npm@^11.5.2 && pnpm run ci-publish "$GITHUB_REF_NAME"

- if: steps.tag.outputs.isAlpha == 'false'
uses: ArnaudBarre/github-release@4fa6eafe8e2449c7c1c5a91ae50de4ee34db0b40 # v1.5.0
with:
path: packages/${{ steps.tag.outputs.pkgName }}/CHANGELOG.md
tag-name: ${{ github.ref_name }}

# Only main pushes can contain an RSC release commit from a merged release PR.
detect-rsc-release:
if: github.repository == 'vitejs/vite-plugin-react' && github.ref == 'refs/heads/main'
name: Detect RSC release commit
detect-release:
if: github.repository == 'vitejs/vite-plugin-react'
name: Detect release commit
runs-on: ubuntu-slim
outputs:
package: ${{ steps.detect.outputs.package }}
release: ${{ steps.detect.outputs.release }}
version: ${{ steps.detect.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
persist-credentials: false

- name: Detect release
id: detect
run: |
SUBJECT="$(git log -1 --format=%s)"
VERSION="$(jq -r .version packages/plugin-rsc/package.json)"
EXPECTED="release: plugin-rsc@$VERSION"
if [[ "$SUBJECT" == "$EXPECTED"* ]]; then
echo "release=true" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Detected RSC release $VERSION"
else
echo "release=false" >> "$GITHUB_OUTPUT"
echo "No RSC release commit found at HEAD"
fi

publish-rsc:
if: needs.detect-rsc-release.outputs.release == 'true'
needs: detect-rsc-release
name: Publish RSC
for PACKAGE in plugin-react plugin-react-swc plugin-rsc; do
VERSION="$(jq -r .version "packages/$PACKAGE/package.json")"
EXPECTED="release: $PACKAGE@$VERSION"
if [[ "$SUBJECT" == "$EXPECTED"* ]]; then
echo "package=$PACKAGE" >> "$GITHUB_OUTPUT"
echo "release=true" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Detected $PACKAGE release $VERSION"
exit 0
fi
done
echo "release=false" >> "$GITHUB_OUTPUT"
echo "No release commit found at HEAD"

publish-release:
if: needs.detect-release.outputs.release == 'true'
needs: detect-release
name: Publish ${{ needs.detect-release.outputs.package }}
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -123,9 +58,10 @@ jobs:

- name: Check release tag
env:
VERSION: ${{ needs.detect-rsc-release.outputs.version }}
PACKAGE: ${{ needs.detect-release.outputs.package }}
VERSION: ${{ needs.detect-release.outputs.version }}
run: |
TAG="plugin-rsc@$VERSION"
TAG="$PACKAGE@$VERSION"
if git rev-parse --verify --quiet "refs/tags/$TAG"; then
echo "Tag $TAG already exists"
exit 1
Expand All @@ -149,10 +85,17 @@ jobs:
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'

- name: Build plugin-react-swc package
if: needs.detect-release.outputs.package == 'plugin-react-swc'
run: pnpm --filter @vitejs/plugin-react-swc build

- name: Publish package
# TODO: plugin-rsc doesn't currently publish alpha/beta prereleases. If it does,
# specify an npm dist-tag with `publish --tag ...`.
run: pnpm --filter @vitejs/plugin-rsc --fail-if-no-match publish --access public --no-git-checks
env:
PACKAGE: ${{ needs.detect-release.outputs.package }}
VERSION: ${{ needs.detect-release.outputs.version }}
# npm 11.5.2+ is required for trusted publishing.
# zizmor: ignore[adhoc-packages]
run: npm i -g npm@^11.5.2 && pnpm run ci-publish "$PACKAGE@$VERSION"

- id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
Expand All @@ -165,8 +108,8 @@ jobs:

- name: Create GitHub release
run: |
TAG="plugin-rsc@$VERSION"
node scripts/extract-changelog.ts packages/plugin-rsc/CHANGELOG.md "$VERSION" > "$NOTES_FILE"
TAG="$PACKAGE@$VERSION"
node scripts/extract-changelog.ts "packages/$PACKAGE/CHANGELOG.md" "$VERSION" > "$NOTES_FILE"

prerelease=()
if [[ "${VERSION%%+*}" == *-* ]]; then
Expand All @@ -180,5 +123,6 @@ jobs:
"${prerelease[@]}"
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
NOTES_FILE: ${{ runner.temp }}/plugin-rsc-release-notes.md
VERSION: ${{ needs.detect-rsc-release.outputs.version }}
NOTES_FILE: ${{ runner.temp }}/release-notes.md
PACKAGE: ${{ needs.detect-release.outputs.package }}
VERSION: ${{ needs.detect-release.outputs.version }}
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ In many test cases, we need to mock dependencies using `link:` and `file:` proto

For a mock dependency, make sure you add a `@vitejs/test-` prefix to the package name. This will avoid possible issues like false-positive alerts.

## Releasing

See [RELEASING.md](./.github/RELEASING.md) for the maintainer release process.

## Contributing to `@vitejs/plugin-rsc`

See [CONTRIBUTING.md](packages/plugin-rsc/CONTRIBUTING.md) in the `@vitejs/plugin-rsc` package for specific guidelines on contributing to the React Server Components plugin.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
"debug-build": "VITE_TEST_BUILD=1 VITE_PRESERVE_BUILD_ARTIFACTS=1 vitest run -c playground/vitest.config.e2e.ts",
"build": "pnpm -r --filter='./packages/*' run build",
"dev": "pnpm -r --parallel --filter='./packages/*' run dev",
"release": "node scripts/release.ts",
"ci-publish": "node scripts/publishCI.ts",
"override-react": "bash scripts/override-react.sh",
"override-vite7": "yq -i '.overrides.vite = \"^7\"' pnpm-workspace.yaml && yq -i '.overrides.\"@vitejs/plugin-react\" = \"^5\"' pnpm-workspace.yaml && yq -i '.packages += [\"!packages/plugin-react\"]' pnpm-workspace.yaml"
Expand All @@ -44,7 +43,6 @@
"globals": "^17.8.0",
"lint-staged": "^17.2.0",
"oxfmt": "^0.60.0",
"picocolors": "^1.1.1",
"playwright-chromium": "^1.62.0",
"simple-git-hooks": "^2.13.1",
"typescript": "^6.0.3",
Expand Down
4 changes: 0 additions & 4 deletions packages/plugin-rsc/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,3 @@ pnpm -C packages/plugin-rsc test-e2e -g "hmr"
# Test projects created with `setupInlineFixture` are locally runnable. For example:
pnpm -C packages/plugin-rsc/examples/e2e/temp/react-compiler dev
```

## Releasing

See [RELEASING.md](RELEASING.md) for the maintainer release process.
40 changes: 0 additions & 40 deletions packages/plugin-rsc/RELEASING.md

This file was deleted.

Loading
Loading