From 5c849297d0113c648a291e426469af688ac59ebf Mon Sep 17 00:00:00 2001 From: shadcn Date: Fri, 29 May 2026 15:13:21 +0400 Subject: [PATCH] feat(release): add beta and rc prerelease labels (#10806) --- .github/version-script-beta.js | 21 -------- .github/version-script-prerelease.js | 37 +++++++++++++ .github/workflows/prerelease-comment.yml | 36 +++++++++---- .github/workflows/release.yml | 67 +++++++++++++++++++++--- package.json | 1 + packages/shadcn/package.json | 1 + 6 files changed, 124 insertions(+), 39 deletions(-) delete mode 100644 .github/version-script-beta.js create mode 100644 .github/version-script-prerelease.js diff --git a/.github/version-script-beta.js b/.github/version-script-beta.js deleted file mode 100644 index 4c95b49f71..0000000000 --- a/.github/version-script-beta.js +++ /dev/null @@ -1,21 +0,0 @@ -// ORIGINALLY FROM CLOUDFLARE WRANGLER: -// https://github.com/cloudflare/wrangler2/blob/main/.github/version-script.js - -import { exec } from "child_process" -import fs from "fs" - -const pkgJsonPath = "packages/shadcn/package.json" -try { - const pkg = JSON.parse(fs.readFileSync(pkgJsonPath)) - exec("git rev-parse --short HEAD", (err, stdout) => { - if (err) { - console.log(err) - process.exit(1) - } - pkg.version = "0.0.0-beta." + stdout.trim() - fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, "\t") + "\n") - }) -} catch (error) { - console.error(error) - process.exit(1) -} diff --git a/.github/version-script-prerelease.js b/.github/version-script-prerelease.js new file mode 100644 index 0000000000..3a2dff9b28 --- /dev/null +++ b/.github/version-script-prerelease.js @@ -0,0 +1,37 @@ +import fs from "fs" + +const pkgJsonPath = "packages/shadcn/package.json" +const channel = process.argv[2] +const headSha = process.argv[3] + +if (!["beta", "rc"].includes(channel)) { + console.error( + `Expected prerelease channel to be "beta" or "rc", got "${channel}".` + ) + process.exit(1) +} + +if (!headSha) { + console.error("Expected pull request head SHA.") + process.exit(1) +} + +try { + const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf8")) + const shortSha = headSha.trim().slice(0, 7) + const baseVersion = channel === "beta" ? "0.0.0" : pkg.version + + if (channel === "rc" && baseVersion.includes("-")) { + console.error( + `Expected a stable planned version for rc, got "${baseVersion}".` + ) + process.exit(1) + } + + pkg.version = `${baseVersion}-${channel}.${shortSha}` + fs.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, "\t") + "\n") + console.log(`Prepared shadcn@${pkg.version}`) +} catch (error) { + console.error(error) + process.exit(1) +} diff --git a/.github/workflows/prerelease-comment.yml b/.github/workflows/prerelease-comment.yml index 238612231b..701d1af8d0 100644 --- a/.github/workflows/prerelease-comment.yml +++ b/.github/workflows/prerelease-comment.yml @@ -1,5 +1,5 @@ # Adapted from create-t3-app. -name: Write Beta Release comment +name: Write Prerelease comment on: workflow_run: @@ -32,9 +32,13 @@ jobs: const match = /^npm-package-shadcn@(.*?)-pr-(\d+)/.exec(artifact.name); if (match) { + const version = match[1]; + const channel = version.includes("-rc.") ? "rc" : "beta"; require("fs").appendFileSync( process.env.GITHUB_ENV, - `\nBETA_PACKAGE_VERSION=${match[1]}` + + `\nPRERELEASE_PACKAGE_VERSION=${version}` + + `\nPRERELEASE_CHANNEL=${channel}` + + `\nPRERELEASE_LABEL=release: ${channel}` + `\nWORKFLOW_RUN_PR=${match[2]}` + `\nWORKFLOW_RUN_ID=${context.payload.workflow_run.id}` ); @@ -47,20 +51,30 @@ jobs: with: number: ${{ env.WORKFLOW_RUN_PR }} message: | - A new prerelease is available for testing: + A new ${{ env.PRERELEASE_CHANNEL }} prerelease is available for testing: ```sh - pnpm dlx shadcn@${{ env.BETA_PACKAGE_VERSION }} + pnpm dlx shadcn@${{ env.PRERELEASE_PACKAGE_VERSION }} ``` - - name: "Remove the autorelease label once published" + View on npm: https://www.npmjs.com/package/shadcn/v/${{ env.PRERELEASE_PACKAGE_VERSION }} + + - name: "Remove the prerelease label once published" uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: '${{ env.WORKFLOW_RUN_PR }}', - name: '🚀 autorelease', - }); + try { + await github.rest.issues.removeLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: '${{ env.WORKFLOW_RUN_PR }}', + name: '${{ env.PRERELEASE_LABEL }}', + }); + } catch (error) { + if (error.status !== 404) { + throw error; + } + + core.info("The prerelease label was already removed."); + } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c02d3f47ae..bf42298067 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release -run-name: ${{ github.event_name == 'pull_request' && format('Release Beta - PR {0}', github.event.number) || 'Release Stable' }} +run-name: ${{ github.event_name == 'pull_request' && format('Release Prerelease - PR {0}', github.event.number) || 'Release Stable' }} on: pull_request: @@ -15,8 +15,8 @@ on: jobs: prerelease: - if: ${{ github.event_name == 'pull_request' && github.repository_owner == 'shadcn-ui' && contains(github.event.pull_request.labels.*.name, '🚀 autorelease') }} - name: Publish Beta to NPM + if: "${{ github.event_name == 'pull_request' && github.repository_owner == 'shadcn-ui' && (contains(github.event.pull_request.labels.*.name, 'release: beta') || contains(github.event.pull_request.labels.*.name, 'release: rc')) }}" + name: Publish Prerelease to NPM runs-on: ubuntu-latest environment: Preview permissions: @@ -24,10 +24,49 @@ jobs: contents: read steps: + - name: Select prerelease channel + id: prerelease + uses: actions/github-script@v7 + with: + script: | + const prereleaseLabels = [ + { name: "release: beta", channel: "beta" }, + { name: "release: rc", channel: "rc" }, + ]; + const labels = context.payload.pull_request.labels.map((label) => label.name); + const selectedLabels = prereleaseLabels.filter((label) => + labels.includes(label.name) + ); + + if (selectedLabels.length !== 1) { + throw new Error( + `Expected exactly one prerelease label, found: ${ + selectedLabels.map((label) => label.name).join(", ") || "none" + }.` + ); + } + + const selected = selectedLabels[0]; + const pullRequest = context.payload.pull_request; + + if ( + selected.channel === "rc" && + (pullRequest.head.ref !== "changeset-release/main" || + pullRequest.title !== "chore(release): version packages") + ) { + throw new Error( + "The release: rc label can only be used on the Changesets version PR from changeset-release/main." + ); + } + + core.setOutput("channel", selected.channel); + core.setOutput("label", selected.name); + - name: Checkout Repo uses: actions/checkout@v4 with: fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} - name: Use PNPM uses: pnpm/action-setup@v4 @@ -48,10 +87,7 @@ jobs: run: pnpm install - name: Modify package.json version - run: node .github/version-script-beta.js - - - name: Publish Beta to NPM - run: pnpm pub:beta + run: node .github/version-script-prerelease.js ${{ steps.prerelease.outputs.channel }} ${{ github.event.pull_request.head.sha }} - name: get-npm-version id: package-version @@ -59,6 +95,23 @@ jobs: with: path: packages/shadcn + - name: Check package version on NPM + id: package-exists + run: | + if npm view "shadcn@${{ steps.package-version.outputs.current-version }}" version >/dev/null 2>&1; then + echo "exists=true" >> "$GITHUB_OUTPUT" + else + echo "exists=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish Prerelease to NPM + if: ${{ steps.package-exists.outputs.exists == 'false' }} + run: pnpm pub:${{ steps.prerelease.outputs.channel }} + + - name: Build packaged artifact + if: ${{ steps.package-exists.outputs.exists == 'true' }} + run: pnpm shadcn:build + - name: Upload packaged artifact uses: actions/upload-artifact@v4 with: diff --git a/package.json b/package.json index 77e2857668..0bcffcf2b7 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "check": "turbo lint typecheck format:check", "release": "changeset version", "pub:beta": "cd packages/shadcn && pnpm pub:beta", + "pub:rc": "cd packages/shadcn && pnpm pub:rc", "pub:release": "cd packages/shadcn && pnpm pub:release", "test:dev": "turbo run test --filter=!shadcn-ui --force", "test": "pnpm --filter=v4 registry:build && start-server-and-test v4:dev http://localhost:4000 test:dev", diff --git a/packages/shadcn/package.json b/packages/shadcn/package.json index 6ff0ea9c3e..aa53c7c042 100644 --- a/packages/shadcn/package.json +++ b/packages/shadcn/package.json @@ -73,6 +73,7 @@ "format:check": "prettier --check \"**/*.{ts,tsx,mdx}\" --cache", "release": "changeset version", "pub:beta": "pnpm build && pnpm publish --no-git-checks --access public --tag beta", + "pub:rc": "pnpm build && pnpm publish --no-git-checks --access public --tag rc", "pub:next": "pnpm build && pnpm publish --no-git-checks --access public --tag next", "pub:release": "pnpm build && pnpm publish --access public", "test": "vitest run",