diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df41ce2bf4..ba82b897f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,28 +42,28 @@ jobs: NODE_OPTIONS: '--max-old-space-size=4096' GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Manual recovery: build and upload through the Chrome Web Store dashboard. + # See docs/releases/next-channel.md#manual-chrome-extension-recovery. - name: Build Chrome Extension if: steps.changesets.outputs.published == 'true' run: NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension - name: Publish Chrome Extension - uses: mnao305/chrome-extension-upload@v5.0.0 if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/main' - with: - extension-id: 'pdaldeopoccdhlkabbkcjmecmmoninhe' - file-path: ./packages/web-extension/dist/chrome.zip - client-id: ${{ secrets.CWS_CLIENT_ID }} - client-secret: ${{ secrets.CWS_CLIENT_SECRET }} - refresh-token: ${{ secrets.CWS_REFRESH_TOKEN }} - publish: true + run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip + env: + EXTENSION_ID: 'pdaldeopoccdhlkabbkcjmecmmoninhe' + PUBLISHER_ID: ${{ secrets.CWS_PUBLISHER_ID }} + CLIENT_ID: ${{ secrets.CWS_CLIENT_ID }} + CLIENT_SECRET: ${{ secrets.CWS_CLIENT_SECRET }} + REFRESH_TOKEN: ${{ secrets.CWS_REFRESH_TOKEN }} - name: Publish Next Chrome Extension - uses: mnao305/chrome-extension-upload@v5.0.0 if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/next' - with: - extension-id: ${{ secrets.NEXT_CWS_EXTENSION_ID }} - file-path: ./packages/web-extension/dist/chrome.zip - client-id: ${{ secrets.NEXT_CWS_CLIENT_ID }} - client-secret: ${{ secrets.NEXT_CWS_CLIENT_SECRET }} - refresh-token: ${{ secrets.NEXT_CWS_REFRESH_TOKEN }} - publish: true + run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip + env: + EXTENSION_ID: ${{ secrets.NEXT_CWS_EXTENSION_ID }} + PUBLISHER_ID: ${{ secrets.NEXT_CWS_PUBLISHER_ID }} + CLIENT_ID: ${{ secrets.NEXT_CWS_CLIENT_ID }} + CLIENT_SECRET: ${{ secrets.NEXT_CWS_CLIENT_SECRET }} + REFRESH_TOKEN: ${{ secrets.NEXT_CWS_REFRESH_TOKEN }} diff --git a/docs/releases/next-channel.md b/docs/releases/next-channel.md index 26f334ac36..9512010239 100644 --- a/docs/releases/next-channel.md +++ b/docs/releases/next-channel.md @@ -21,19 +21,19 @@ The `next` branch intentionally carries `.changeset/pre.json`: Keep this file in prerelease mode on `next`. The `tag` must stay `next` so Changesets publishes prerelease packages with the npm `next` dist-tag. -Do not put `master` into the `next` prerelease mode. Current `master` should not -have `.changeset/pre.json`; stable releases from `master` continue to publish as +Do not put `main` into the `next` prerelease mode. Current `main` should not +have `.changeset/pre.json`; stable releases from `main` continue to publish as normal and move `latest` only through the regular stable release process. ## Regular operation -Bring stable branch updates into `next` as needed, either by merging `master` or +Bring stable branch updates into `next` as needed, either by merging `main` or by cherry-picking specific commits: ```sh git switch next git fetch origin -git merge origin/master +git merge origin/main ``` If the merge conflicts on `.changeset/pre.json`, resolve the conflict by keeping @@ -44,20 +44,20 @@ before promotion. Include normal Changesets for user-facing package changes; the release workflow will turn those changesets into generated prerelease version commits on `next`. -## Promoting work back to master +## Promoting work back to main -Do not merge `next` wholesale back to `master`. Promotion should copy only the +Do not merge `next` wholesale back to `main`. Promotion should copy only the human-authored feature work that is ready for stable release: ```sh -git switch master +git switch main git fetch origin git cherry-pick ``` Cherry-pick source, docs, tests, and normal changeset commits that describe the feature. Never cherry-pick generated `Version Packages (next)` commits from -`next` to `master`. +`next` to `main`. When promoting, exclude release-generated files from the cherry-pick if they are present in the selected commits: @@ -67,12 +67,12 @@ present in the selected commits: - version-only `package.json` changes Those files are generated separately by the stable release workflow after the -promoted changesets land on `master`. +promoted changesets land on `main`. If a cherry-pick includes both feature code and generated release changes, use a non-committing cherry-pick and reset generated release files before committing. Restore prerelease metadata and generated changelogs outright, then inspect -package manifests hunk-by-hunk so version-only changes do not move to `master`: +package manifests hunk-by-hunk so version-only changes do not move to `main`: ```sh git cherry-pick --no-commit @@ -92,17 +92,45 @@ metadata changes only when they are part of the promoted feature. ## Release workflow and Chrome extension -The release workflow runs on both `master` and `next`. On `next`, Changesets +The release workflow runs on both `main` and `next`. On `next`, Changesets publishes prerelease packages to npm using the `next` dist-tag. Chrome extension publication is also branch-specific: -- `master` publishes the production extension listing. Its extension ID is - hard-coded in `.github/workflows/release.yml`, and it uses the existing - `CWS_CLIENT_ID`, `CWS_CLIENT_SECRET`, and `CWS_REFRESH_TOKEN` secrets. +- `main` publishes the production extension listing. Its extension ID is + hard-coded in `.github/workflows/release.yml`, and it uses the + `CWS_PUBLISHER_ID`, `CWS_CLIENT_ID`, `CWS_CLIENT_SECRET`, and + `CWS_REFRESH_TOKEN` secrets. - `next` publishes the prerelease extension listing with - `NEXT_CWS_EXTENSION_ID`, `NEXT_CWS_CLIENT_ID`, `NEXT_CWS_CLIENT_SECRET`, and - `NEXT_CWS_REFRESH_TOKEN`. + `NEXT_CWS_EXTENSION_ID`, `NEXT_CWS_PUBLISHER_ID`, `NEXT_CWS_CLIENT_ID`, + `NEXT_CWS_CLIENT_SECRET`, and `NEXT_CWS_REFRESH_TOKEN`. Keep those listings and credentials separate. Changes to the `next` listing -should not affect the production Chrome Web Store listing used by `master`. +should not affect the production Chrome Web Store listing used by `main`. + +## Manual Chrome extension recovery + +Use this fallback only when the npm release was recovered separately and the +automatic Chrome step did not publish the matching extension version. + +1. Check out the exact `main` or `next` release commit whose extension version + should be published. +2. Install dependencies and build the archive: + + ```sh + yarn install --frozen-lockfile + NODE_OPTIONS='--max-old-space-size=4096' \ + DISABLE_WORKER_INLINING=true \ + yarn turbo run prepublish --filter=@rrweb/web-extension + unzip -t packages/web-extension/dist/chrome.zip + ``` + +3. Confirm that `packages/web-extension/dist/chrome/manifest.json` contains the + intended version. +4. In the Chrome Web Store developer dashboard, select the production listing + for `main` or the prerelease listing for `next`, upload + `packages/web-extension/dist/chrome.zip`, and submit it for review. + +GitHub Actions secrets cannot be read back for local CLI use, so dashboard upload +is the supported manual recovery path. Keep production and prerelease listings +separate. diff --git a/docs/superpowers/plans/2026-07-22-chrome-extension-release-simplification.md b/docs/superpowers/plans/2026-07-22-chrome-extension-release-simplification.md new file mode 100644 index 0000000000..de25cedfc0 --- /dev/null +++ b/docs/superpowers/plans/2026-07-22-chrome-extension-release-simplification.md @@ -0,0 +1,199 @@ +# Chrome Extension Release Simplification Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Remove manual dispatch from the privileged release workflow while preserving automatic Chrome Web Store API v2 publishing and documenting dashboard-based recovery. + +**Architecture:** Keep `.github/workflows/release.yml` push-only and use the Changesets `published` output as the sole extension build/upload trigger. Put recovery instructions in the existing release-channel documentation, with a workflow comment linking maintainers to it. + +**Tech Stack:** GitHub Actions YAML, Changesets, Yarn/Turborepo, `chrome-webstore-upload-cli`, Markdown, Node.js assertions, Prettier, actionlint + +--- + +### Task 1: Simplify the release workflow + +**Files:** + +- Modify: `.github/workflows/release.yml:3-69` + +- [ ] **Step 1: Run the failing workflow regression assertion** + +```sh +node -e "const fs=require('fs');const s=fs.readFileSync('.github/workflows/release.yml','utf8');for(const x of ['workflow_dispatch','publish_chrome_extension','github.event.inputs'])if(s.includes(x))throw new Error('manual release logic remains: '+x);if((s.match(/steps\.changesets\.outputs\.published == 'true'/g)||[]).length!==3)throw new Error('automatic publish gates changed');console.log('push-only release assertions passed')" +``` + +Expected: FAIL with `manual release logic remains: workflow_dispatch`. + +- [ ] **Step 2: Remove manual-dispatch behavior and add the recovery pointer** + +Make the trigger and relevant steps equivalent to: + +```yaml +on: + push: + branches: + - main + - next + +jobs: + release: + name: Release + # Existing setup and Changesets steps remain unchanged. + + # Manual recovery: build and upload through the Chrome Web Store dashboard. + # See docs/releases/next-channel.md#manual-chrome-extension-recovery. + - name: Build Chrome Extension + if: steps.changesets.outputs.published == 'true' + run: NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension + + - name: Publish Chrome Extension + if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/main' + run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip + + - name: Publish Next Chrome Extension + if: steps.changesets.outputs.published == 'true' && github.ref == 'refs/heads/next' + run: npx --yes chrome-webstore-upload-cli@4.0.1 --source ./packages/web-extension/dist/chrome.zip +``` + +Preserve the existing environment mappings, including `CWS_PUBLISHER_ID` and `NEXT_CWS_PUBLISHER_ID`. + +- [ ] **Step 3: Run workflow validation** + +```sh +node -e "const fs=require('fs');const yaml=require('yaml');const s=fs.readFileSync('.github/workflows/release.yml','utf8');yaml.parse(s);for(const x of ['workflow_dispatch','publish_chrome_extension','github.event.inputs'])if(s.includes(x))throw new Error('manual release logic remains: '+x);if((s.match(/steps\.changesets\.outputs\.published == 'true'/g)||[]).length!==3)throw new Error('automatic publish gates changed');for(const x of ['chrome-webstore-upload-cli@4.0.1','CWS_PUBLISHER_ID','NEXT_CWS_PUBLISHER_ID'])if(!s.includes(x))throw new Error('missing '+x);console.log('push-only API v2 workflow assertions passed')" +actionlint -ignore 'runner of .* action is too old' .github/workflows/release.yml +yarn prettier --check .github/workflows/release.yml +git diff --check +``` + +Expected: all commands exit 0 and the assertion prints `push-only API v2 workflow assertions passed`. + +### Task 2: Document dashboard-based manual recovery + +**Files:** + +- Modify: `docs/releases/next-channel.md:1-110` + +- [ ] **Step 1: Run the failing documentation assertion** + +```sh +node -e "const fs=require('fs');const s=fs.readFileSync('docs/releases/next-channel.md','utf8');if(/\bmaster\b/.test(s))throw new Error('stale master reference remains');for(const x of ['## Manual Chrome extension recovery','packages/web-extension/dist/chrome.zip','unzip -t packages/web-extension/dist/chrome.zip'])if(!s.includes(x))throw new Error('missing recovery documentation: '+x);console.log('release recovery documentation assertions passed')" +``` + +Expected: FAIL with `stale master reference remains`. + +- [ ] **Step 2: Correct branch names and add exact recovery instructions** + +Replace `master` with `main` throughout the document. Add this section after the branch-specific Chrome publication description: + +````markdown +## Manual Chrome extension recovery + +Use this fallback only when the npm release was recovered separately and the +automatic Chrome step did not publish the matching extension version. + +1. Check out the exact `main` or `next` release commit whose extension version + should be published. +2. Install dependencies and build the archive: + + ```sh + yarn install --frozen-lockfile + NODE_OPTIONS='--max-old-space-size=4096' \ + DISABLE_WORKER_INLINING=true \ + yarn turbo run prepublish --filter=@rrweb/web-extension + unzip -t packages/web-extension/dist/chrome.zip + ``` + +3. Confirm that `packages/web-extension/dist/chrome/manifest.json` contains the + intended version. +4. In the Chrome Web Store developer dashboard, select the production listing + for `main` or the prerelease listing for `next`, upload + `packages/web-extension/dist/chrome.zip`, and submit it for review. + +GitHub Actions secrets cannot be read back for local CLI use, so dashboard upload +is the supported manual recovery path. Keep production and prerelease listings +separate. +```` + +Add `CWS_PUBLISHER_ID` and `NEXT_CWS_PUBLISHER_ID` to the preceding secret lists. + +- [ ] **Step 3: Run documentation validation** + +```sh +node -e "const fs=require('fs');const s=fs.readFileSync('docs/releases/next-channel.md','utf8');if(/\bmaster\b/.test(s))throw new Error('stale master reference remains');for(const x of ['## Manual Chrome extension recovery','packages/web-extension/dist/chrome.zip','unzip -t packages/web-extension/dist/chrome.zip','CWS_PUBLISHER_ID','NEXT_CWS_PUBLISHER_ID'])if(!s.includes(x))throw new Error('missing recovery documentation: '+x);console.log('release recovery documentation assertions passed')" +yarn prettier --check docs/releases/next-channel.md +git diff --check +``` + +Expected: all commands exit 0 and the assertion prints `release recovery documentation assertions passed`. + +- [ ] **Step 4: Verify the documented build commands** + +```sh +NODE_OPTIONS='--max-old-space-size=4096' DISABLE_WORKER_INLINING=true yarn turbo run prepublish --filter=@rrweb/web-extension +unzip -t packages/web-extension/dist/chrome.zip +node -e "const m=require('./packages/web-extension/dist/chrome/manifest.json');if(!m.version)throw new Error('manifest version missing');console.log('Chrome extension version:',m.version)" +``` + +Expected: build exits 0, `unzip` reports no errors, and the manifest assertion prints a version. + +### Task 3: Commit, update the pull request, and verify remotely + +**Files:** + +- Modify: GitHub pull request `rrweb-io/rrweb#1908` description + +- [ ] **Step 1: Run final local verification** + +Repeat Task 1 Step 3 and Task 2 Steps 3-4, then run: + +```sh +git status --short +git diff --check +``` + +Expected: only the workflow, release documentation, and plan are changed; every command exits 0. + +- [ ] **Step 2: Commit and push the implementation** + +```sh +git add .github/workflows/release.yml docs/releases/next-channel.md docs/superpowers/plans/2026-07-22-chrome-extension-release-simplification.md +git commit -m "Simplify Chrome extension release recovery" +git push +``` + +Expected: the commit and push succeed with only the listed files. + +- [ ] **Step 3: Update the PR description** + +Use `gh pr edit 1908 --repo rrweb-io/rrweb --body-file -` with a body that states: + +```markdown +## Summary + +- migrate Chrome Web Store publishing to pinned API v2 tooling +- preserve automatic stable and `next` publishing after successful npm releases +- document dashboard-based recovery without broadening the npm release workflow +- correct stale `master` references in release documentation + +## Required repository setup + +Production uses `CWS_PUBLISHER_ID`; `next` uses `NEXT_CWS_PUBLISHER_ID` with its +existing `NEXT_CWS_*` credentials. + +## Validation + +- workflow assertions, YAML parsing, Prettier, actionlint, and diff checks pass +- extension build and ZIP integrity checks pass +``` + +Expected: `gh pr edit` prints the PR URL. + +- [ ] **Step 4: Verify CI and review state** + +```sh +gh pr checks 1908 --repo rrweb-io/rrweb --watch --interval 10 +python3 /Users/justin/.codex/plugins/cache/openai-curated-remote/github/0.1.8-2841cf9749ae/skills/gh-address-comments/scripts/fetch_comments.py --repo rrweb-io/rrweb --pr 1908 +``` + +Expected: all required checks pass. Do not reply to or resolve review threads without explicit authorization. diff --git a/docs/superpowers/specs/2026-07-22-chrome-extension-release-simplification-design.md b/docs/superpowers/specs/2026-07-22-chrome-extension-release-simplification-design.md new file mode 100644 index 0000000000..1677b39b06 --- /dev/null +++ b/docs/superpowers/specs/2026-07-22-chrome-extension-release-simplification-design.md @@ -0,0 +1,68 @@ +# Chrome Extension Release Simplification + +## Objective + +Keep Chrome extension publishing automatic during normal npm releases while +removing the manual-dispatch logic that complicates and broadens the privileged +release workflow. Preserve a documented manual recovery path through the Chrome +Web Store dashboard. + +## Workflow design + +`.github/workflows/release.yml` will return to a push-only workflow for `main` +and `next`. The release job will run Changesets normally. Chrome extension build +and upload steps will run only when `steps.changesets.outputs.published` is +`true`. + +The Chrome upload will continue using the pinned +`chrome-webstore-upload-cli@4.0.1` API v2 client and the publisher ID secrets +introduced by this pull request. Stable releases will use the production listing +and `CWS_*` secrets; `next` releases will use the prerelease listing and +`NEXT_CWS_*` secrets. + +The following manual-dispatch behavior will be removed: + +- the `workflow_dispatch` trigger and `publish_chrome_extension` input; +- the job-level ref guard added for arbitrary manual refs; +- event-name and event-input branches in Chrome build and publish conditions. + +## Manual recovery + +A short comment beside the Chrome build step will identify the manual fallback +and link to `docs/releases/next-channel.md`. That document will explain how to: + +1. check out the intended release commit; +2. install dependencies and build `packages/web-extension/dist/chrome.zip`; +3. verify the archive before upload; +4. upload the archive to the appropriate Chrome Web Store listing through the + developer dashboard. + +The documentation will distinguish stable (`main`) from prerelease (`next`), +list the relevant publisher ID secrets, and replace stale `master` branch +references with `main`. + +This fallback intentionally uses the dashboard instead of local API credentials, +because GitHub Actions secrets cannot be read back for local use. + +## Pull request maintenance + +The pull request description will be updated to describe API v2 migration, +automatic publishing, and the documented dashboard fallback. Existing review +threads made obsolete by removing manual dispatch will not be replied to or +resolved without separate authorization. + +## Validation + +Before pushing the implementation: + +- parse the workflow as YAML; +- assert that `workflow_dispatch`, `publish_chrome_extension`, and manual event + conditions are absent; +- assert that automatic stable and `next` publish conditions remain; +- assert that the API v2 CLI and publisher ID variables remain; +- run Prettier, `actionlint`, and `git diff --check`; +- verify the documented build command produces a valid Chrome ZIP archive. + +After pushing, all required pull request checks must pass. No Changeset is +required because the change affects release infrastructure and documentation, +not a published package.