Repository navigation
chore(deps): update peerdependency nuxt to ^4.6.1 #6823
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: bundle-snapshot | |
| # Companion to the `Check package bundle size` step in `ci.yml`. | |
| # | |
| # The bundle-size test (`test/bundle.test.ts`) asserts an inline snapshot of the | |
| # published size of every package. That snapshot legitimately changes whenever | |
| # output or dependencies change, so a failure is often "expected, please review | |
| # and accept" rather than "something is broken". | |
| # | |
| # This workflow: | |
| # 1. reports the failure on the PR with the diff, and | |
| # 2. gives maintainers a one-click button (a checkbox in that comment) that | |
| # re-runs the bundle test with `--update` and commits the new snapshot to | |
| # the PR branch. | |
| # | |
| # It runs from the default branch (`workflow_run` / `issue_comment`), so it has | |
| # the write permissions that `ci.yml` deliberately does not have — including for | |
| # pull requests opened from forks. | |
| on: | |
| # Report the result once `ci` finishes. | |
| workflow_run: | |
| workflows: [ci] | |
| types: [completed] | |
| # The button: ticking the checkbox edits the comment, or a `/update-bundle-snapshot` command. | |
| issue_comment: | |
| types: [created, edited] | |
| # Manual escape hatch: Actions → bundle-snapshot → Run workflow. | |
| workflow_dispatch: | |
| inputs: | |
| pr: | |
| description: Pull request number to update the bundle snapshot on | |
| required: true | |
| type: string | |
| concurrency: | |
| group: bundle-snapshot-${{ github.event.issue.number || inputs.pr || github.event.workflow_run.id }} | |
| cancel-in-progress: false | |
| env: | |
| MARKER: '<!-- bundle-snapshot-check -->' | |
| BUTTON: '- [ ] 🔄 **Update the bundle snapshot**' | |
| jobs: | |
| # --------------------------------------------------------------------------- | |
| # Report: post / update / resolve the PR comment after `ci` completes. | |
| # --------------------------------------------------------------------------- | |
| report: | |
| name: report | |
| if: github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Download bundle report | |
| id: download | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: bundle-report | |
| path: bundle-report | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Comment on the pull request | |
| if: steps.download.outcome == 'success' | |
| uses: actions/github-script@v9 | |
| env: | |
| RUN_URL: ${{ github.event.workflow_run.html_url }} | |
| with: | |
| script: | | |
| const fs = require('node:fs') | |
| const { owner, repo } = context.repo | |
| const marker = process.env.MARKER | |
| const button = process.env.BUTTON | |
| const read = (file) => { | |
| try { | |
| return fs.readFileSync(`bundle-report/${file}`, 'utf8').trim() | |
| } catch { | |
| return '' | |
| } | |
| } | |
| const prNumberRaw = read('pr-number') | |
| const outcome = read('outcome') | |
| // The artifact is produced by the PR's own workflow run, and its | |
| // content is controlled by the PR author — validate before use. | |
| if (!/^\d+$/.test(prNumberRaw) || !['success', 'failure'].includes(outcome)) { | |
| core.info('No usable bundle report — nothing to do.') | |
| return | |
| } | |
| const prNumber = Number(prNumberRaw) | |
| // Confirm the referenced PR actually belongs to this run, | |
| // otherwise a crafted artifact could target any issue number. | |
| const { data: reportedPr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }) | |
| if (reportedPr.head.sha !== context.payload.workflow_run.head_sha) { | |
| core.setFailed('Bundle report artifact does not match the workflow run head SHA.') | |
| return | |
| } | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| per_page: 100, | |
| }) | |
| const existing = comments.find((c) => c.user?.type === 'Bot' && c.body?.includes(marker)) | |
| // Bundle size is back within the snapshot — clean up after ourselves. | |
| if (outcome === 'success') { | |
| core.info('Bundle size check passed.') | |
| if (existing) { | |
| await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id }) | |
| } | |
| return | |
| } | |
| // Keep the interesting part of the vitest output: the snapshot diff. | |
| // Trim the leading noise, and stop before vitest's summary and the | |
| // raw `::error` workflow annotations, which are unreadable in a comment. | |
| const lines = read('output.txt').split('\n') | |
| const start = lines.findIndex((l) => /Snapshot\s+.*mismatched|FAIL|AssertionError/.test(l)) | |
| const rest = start === -1 ? lines : lines.slice(start) | |
| const end = rest.findIndex((l) => /^\s*(Snapshots|Test Files)\s+\d|^::(error|warning)/.test(l)) | |
| const excerpt = (end === -1 ? rest : rest.slice(0, end)) | |
| .join('\n') | |
| .trimEnd() | |
| .slice(0, 20_000) | |
| // The excerpt is attacker-controlled text — pick a comment fence | |
| // longer than any backtick run it contains so it cannot break out | |
| // of the code block. | |
| const fenceRun = Math.max(3, ...[...excerpt.matchAll(/`+/g)].map((m) => m[0].length + 1)) | |
| const excerptFence = '`'.repeat(fenceRun) | |
| const workflowUrl = `${context.serverUrl}/${owner}/${repo}/actions/workflows/bundle-snapshot.yml` | |
| const testUrl = `${context.serverUrl}/${owner}/${repo}/blob/main/test/bundle.test.ts` | |
| const body = [ | |
| marker, | |
| '## 📦 Bundle size snapshot is out of date', | |
| '', | |
| 'The published size of one or more packages changed, so the inline snapshot in', | |
| `[\`test/bundle.test.ts\`](${testUrl}) no longer matches.`, | |
| '', | |
| '**Please review the diff below.** If the change is expected, accept the new snapshot:', | |
| '', | |
| `${button} — tick this box and CI will run \`vitest run bundle --update\` and commit the result to this branch.`, | |
| '', | |
| `[](${workflowUrl})`, | |
| '', | |
| '<sub>Only maintainers with write access can trigger the update. You can also comment `/update-bundle-snapshot`, or run it locally with `pnpm prepack && pnpm vitest run bundle -u`.</sub>', | |
| '', | |
| '<details><summary>Bundle size diff</summary>', | |
| '', | |
| `${excerptFence}diff`, | |
| excerpt, | |
| excerptFence, | |
| '', | |
| `[Full CI log](${process.env.RUN_URL})`, | |
| '', | |
| '</details>', | |
| ].join('\n') | |
| if (existing) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }) | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body }) | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Authorize: resolve the PR and make sure the actor may push to it. | |
| # --------------------------------------------------------------------------- | |
| authorize: | |
| name: authorize | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event_name == 'issue_comment' && github.event.issue.pull_request && | |
| ((github.event.action == 'edited' && contains(github.event.comment.body, '- [x] 🔄 **Update the bundle snapshot**')) || | |
| (github.event.action == 'created' && contains(github.event.comment.body, '/update-bundle-snapshot')))) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| # `write`, not `read`: reacting to a comment on a pull request goes through | |
| # the pull-requests scope even via the `issues` API. | |
| pull-requests: write | |
| outputs: | |
| authorized: ${{ steps.check.outputs.authorized }} | |
| pr: ${{ steps.check.outputs.pr }} | |
| head_repo: ${{ steps.check.outputs.head_repo }} | |
| head_ref: ${{ steps.check.outputs.head_ref }} | |
| is_fork: ${{ steps.check.outputs.is_fork }} | |
| steps: | |
| - name: Check permissions and resolve PR | |
| id: check | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const { owner, repo } = context.repo | |
| const marker = process.env.MARKER | |
| const prNumber = Number(context.payload.issue?.number ?? context.payload.inputs?.pr ?? 0) | |
| core.setOutput('authorized', 'false') | |
| if (!prNumber) { | |
| core.setFailed('Could not determine which pull request to update.') | |
| return | |
| } | |
| // Only collaborators with write access may push to a PR branch. | |
| const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner, | |
| repo, | |
| username: context.actor, | |
| }) | |
| const allowed = ['admin', 'maintain', 'write'].includes(perm.permission) | |
| // Acknowledge the click. Cosmetic only — never fail the run over it. | |
| const commentId = context.payload.comment?.id | |
| if (commentId) { | |
| try { | |
| await github.rest.reactions.createForIssueComment({ | |
| owner, | |
| repo, | |
| comment_id: commentId, | |
| content: allowed ? 'eyes' : '-1', | |
| }) | |
| } catch (error) { | |
| core.warning(`Could not react to the comment: ${error.message}`) | |
| } | |
| } | |
| if (!allowed) { | |
| core.setFailed(`@${context.actor} does not have write access to this repository.`) | |
| return | |
| } | |
| const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }) | |
| core.setOutput('authorized', 'true') | |
| core.setOutput('pr', String(prNumber)) | |
| core.setOutput('head_repo', pr.head.repo.full_name) | |
| core.setOutput('head_ref', pr.head.ref) | |
| core.setOutput('is_fork', String(pr.head.repo.full_name !== `${owner}/${repo}`)) | |
| // Untick the button right away so it can be used again later. | |
| const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}` | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| per_page: 100, | |
| }) | |
| const existing = comments.find((c) => c.user?.type === 'Bot' && c.body?.includes(marker)) | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, | |
| repo, | |
| comment_id: existing.id, | |
| body: existing.body | |
| .replace('- [x] 🔄 **Update the bundle snapshot**', process.env.BUTTON) | |
| .replace(marker, `${marker}\n<!-- ⏳ [Updating the snapshot…](${runUrl}) -->`), | |
| }) | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Build: regenerate the snapshot from the PR's code — WITHOUT any token. | |
| # Fork PR code executes in this job, so it gets `permissions: {}` and hands | |
| # the result to the privileged `update` job as an artifact. | |
| # --------------------------------------------------------------------------- | |
| build: | |
| name: build snapshot | |
| needs: authorize | |
| if: needs.authorize.outputs.authorized == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: {} | |
| outputs: | |
| changed: ${{ steps.diff.outputs.changed }} | |
| verified: ${{ steps.verify.outcome }} | |
| steps: | |
| # checkout v7 refuses to check out fork PR code under `pull_request_target` | |
| # and `workflow_run`. This job runs under `issue_comment` / `workflow_dispatch` | |
| # behind an explicit write-access check, so the guard does not apply and | |
| # `allow-unsafe-pr-checkout` stays off. | |
| - uses: actions/checkout@v7 | |
| with: | |
| repository: ${{ needs.authorize.outputs.head_repo }} | |
| ref: ${{ needs.authorize.outputs.head_ref }} | |
| persist-credentials: false | |
| - uses: actions/setup-node@v7 | |
| with: | |
| package-manager-cache: false | |
| node-version: '24.21.0' | |
| - run: npm i -g --force corepack && corepack enable | |
| - run: pnpm install | |
| - run: pnpm dev:prepare | |
| # Bundle sizes are only meaningful after a real build — `postinstall` only stubs `dist/`. | |
| - run: pnpm prepack | |
| - name: Update bundle snapshot | |
| env: | |
| NO_COLOR: '1' | |
| run: pnpm vitest run bundle --update | |
| - name: Detect changes | |
| id: diff | |
| shell: bash | |
| run: | | |
| if git diff --quiet -- test/bundle.test.ts; then | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| git --no-pager diff --stat | |
| # Re-run the check against the refreshed snapshot. GitHub suppresses the | |
| # events a GITHUB_TOKEN commit would normally raise (to avoid workflow | |
| # recursion), so `ci` may not re-run on its own. Verifying here means the | |
| # snapshot is proven green by the same job that wrote it, and a re-run is | |
| # a convenience rather than the only signal. | |
| - name: Verify updated snapshot | |
| id: verify | |
| if: steps.diff.outputs.changed == 'true' | |
| continue-on-error: true | |
| env: | |
| NO_COLOR: '1' | |
| run: pnpm vitest run bundle | |
| - name: Collect the refreshed snapshot | |
| if: steps.diff.outputs.changed == 'true' && steps.verify.outcome == 'success' | |
| shell: bash | |
| run: | | |
| mkdir -p snapshot | |
| cp test/bundle.test.ts snapshot/bundle.test.ts | |
| git --no-pager diff -- test/bundle.test.ts > snapshot/patch.diff | |
| - name: Upload snapshot | |
| if: steps.diff.outputs.changed == 'true' && steps.verify.outcome == 'success' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: bundle-snapshot | |
| path: snapshot/ | |
| if-no-files-found: error | |
| # --------------------------------------------------------------------------- | |
| # Update: apply the refreshed snapshot and report the result. This is the | |
| # only write-scoped job — it never checks out or executes PR code. | |
| # --------------------------------------------------------------------------- | |
| update: | |
| name: update snapshot | |
| needs: [authorize, build] | |
| if: always() && needs.authorize.outputs.authorized == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Download snapshot | |
| id: download | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: bundle-snapshot | |
| path: snapshot | |
| - name: Apply and report | |
| uses: actions/github-script@v9 | |
| env: | |
| PR: ${{ needs.authorize.outputs.pr }} | |
| HEAD_REF: ${{ needs.authorize.outputs.head_ref }} | |
| IS_FORK: ${{ needs.authorize.outputs.is_fork }} | |
| BUILD_RESULT: ${{ needs.build.result }} | |
| CHANGED: ${{ needs.build.outputs.changed }} | |
| VERIFIED: ${{ needs.build.outputs.verified }} | |
| DOWNLOADED: ${{ steps.download.outcome }} | |
| with: | |
| script: | | |
| const fs = require('node:fs') | |
| const { owner, repo } = context.repo | |
| const marker = process.env.MARKER | |
| const prNumber = Number(process.env.PR) | |
| const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}` | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| per_page: 100, | |
| }) | |
| const existing = comments.find((c) => c.user?.type === 'Bot' && c.body?.includes(marker)) | |
| const post = async (body) => { | |
| if (existing) { | |
| await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }) | |
| } else { | |
| await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body }) | |
| } | |
| } | |
| if (process.env.BUILD_RESULT === 'failure') { | |
| await post( | |
| [ | |
| marker, | |
| '## ❌ Could not update the bundle snapshot', | |
| '', | |
| `The update run failed. See the [logs](${runUrl}) for details, or update it locally:`, | |
| '', | |
| '```bash', | |
| 'pnpm install && pnpm prepack && pnpm vitest run bundle -u', | |
| '```', | |
| ].join('\n') | |
| ) | |
| return | |
| } | |
| if (process.env.CHANGED !== 'true') { | |
| await post( | |
| [ | |
| marker, | |
| '## ✅ Bundle snapshot is already up to date', | |
| '', | |
| `Re-ran \`vitest run bundle --update\` in [this run](${runUrl}) and \`test/bundle.test.ts\` did not change.`, | |
| ].join('\n') | |
| ) | |
| return | |
| } | |
| // The refreshed snapshot did not pass a clean re-run — something is | |
| // off (flaky sizes, a broken build), so do not commit it. | |
| if (process.env.VERIFIED !== 'success' || process.env.DOWNLOADED !== 'success') { | |
| await post( | |
| [ | |
| marker, | |
| '## ⚠️ Refreshed snapshot did not verify', | |
| '', | |
| 'The snapshot was regenerated, but re-running the bundle check against it still failed,', | |
| 'so nothing was committed. This usually means the build is unstable rather than simply larger.', | |
| '', | |
| `See the [logs](${runUrl}).`, | |
| ].join('\n') | |
| ) | |
| return | |
| } | |
| const snapshotContent = fs.readFileSync('snapshot/bundle.test.ts', 'utf8') | |
| // Fork PR: we cannot push, so hand over a ready-to-apply patch. | |
| if (process.env.IS_FORK === 'true') { | |
| const patch = fs.readFileSync('snapshot/patch.diff', 'utf8').slice(0, 20_000) | |
| // The patch is PR-controlled text — use a comment fence longer | |
| // than any backtick run it contains. | |
| const fenceRun = Math.max(3, ...[...patch.matchAll(/`+/g)].map((m) => m[0].length + 1)) | |
| const patchFence = '`'.repeat(fenceRun) | |
| await post( | |
| [ | |
| marker, | |
| '## 📦 New bundle snapshot', | |
| '', | |
| 'This pull request comes from a fork, so the snapshot cannot be committed automatically.', | |
| 'Apply the patch below (or run `pnpm prepack && pnpm vitest run bundle -u` locally) and push:', | |
| '', | |
| `${patchFence}diff`, | |
| patch, | |
| patchFence, | |
| '', | |
| `<sub>Produced by [this run](${runUrl}).</sub>`, | |
| ].join('\n') | |
| ) | |
| return | |
| } | |
| // Committing through the API keeps the commit signed/verified, which the | |
| // `commit-signature` workflow requires. | |
| const path = 'test/bundle.test.ts' | |
| const branch = process.env.HEAD_REF | |
| try { | |
| const { data: current } = await github.rest.repos.getContent({ owner, repo, path, ref: branch }) | |
| const { data: result } = await github.rest.repos.createOrUpdateFileContents({ | |
| owner, | |
| repo, | |
| path, | |
| branch, | |
| sha: current.sha, | |
| message: 'test: update bundle size snapshot', | |
| content: Buffer.from(snapshotContent, 'utf8').toString('base64'), | |
| }) | |
| await post( | |
| [ | |
| marker, | |
| '## ✅ Bundle snapshot updated', | |
| '', | |
| `Committed the new snapshot as ${result.commit.sha}, verified by re-running the bundle check against it.`, | |
| '', | |
| 'GitHub suppresses the events a workflow commit would normally raise, so the `ci` check above may', | |
| 'still show the earlier failure. Re-run it to refresh the status — the snapshot itself is confirmed green.', | |
| '', | |
| `<sub>Produced by [this run](${runUrl}).</sub>`, | |
| ].join('\n') | |
| ) | |
| } catch (error) { | |
| await post( | |
| [ | |
| marker, | |
| '## ❌ Could not update the bundle snapshot', | |
| '', | |
| `Committing the refreshed snapshot failed: ${error.message}`, | |
| '', | |
| `See the [logs](${runUrl}).`, | |
| ].join('\n') | |
| ) | |
| } |