From 8e3e6e2e6be4d07440f1ad2dbf2bd75c3375a3b1 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 7 Mar 2026 13:07:42 +0000 Subject: [PATCH 1/3] refactor(ci): extract composite actions and improve workflow hygiene - Add composite actions: setup-node-pnpm, build-vinext, deploy-example, comment-preview-urls - Cache Playwright browsers in CI to avoid ~150MB download per E2E run - Add concurrency group to deploy-examples.yml to prevent deploy pileup - Replace inline CJS node script in create-next-app job with portable bash - Convert ecosystem-run.yml inline Node scripts from CJS require() to ESM - Apply composite actions across all workflows to eliminate repeated boilerplate --- .github/actions/build-vinext/action.yml | 29 +++++ .../actions/comment-preview-urls/action.yml | 68 +++++++++++ .github/actions/deploy-example/action.yml | 49 ++++++++ .github/actions/setup-node-pnpm/action.yml | 35 ++++++ .github/workflows/benchmarks.yml | 16 +-- .github/workflows/bigbonk.yml | 17 +-- .github/workflows/bonk.yml | 17 +-- .github/workflows/ci.yml | 113 ++++++------------ .github/workflows/deploy-examples.yml | 106 ++++------------ .github/workflows/deploy-preview-command.yml | 90 ++------------ .github/workflows/ecosystem-run.yml | 59 ++++----- .github/workflows/preview-release.yml | 9 +- .github/workflows/publish.yml | 15 +-- 13 files changed, 282 insertions(+), 341 deletions(-) create mode 100644 .github/actions/build-vinext/action.yml create mode 100644 .github/actions/comment-preview-urls/action.yml create mode 100644 .github/actions/deploy-example/action.yml create mode 100644 .github/actions/setup-node-pnpm/action.yml diff --git a/.github/actions/build-vinext/action.yml b/.github/actions/build-vinext/action.yml new file mode 100644 index 0000000000..5417cc3938 --- /dev/null +++ b/.github/actions/build-vinext/action.yml @@ -0,0 +1,29 @@ +name: Build vinext plugin +description: Set up the environment and build the vinext package. + +inputs: + node-version: + description: Node.js version to use + required: false + default: "24" + fetch-depth: + description: Number of commits to fetch + required: false + default: "1" + ref: + description: The branch, tag or SHA to checkout + required: false + default: "" + +runs: + using: composite + steps: + - uses: ./.github/actions/setup-node-pnpm + with: + node-version: ${{ inputs.node-version }} + fetch-depth: ${{ inputs.fetch-depth }} + ref: ${{ inputs.ref }} + + - name: Build vinext plugin + shell: bash + run: pnpm run build diff --git a/.github/actions/comment-preview-urls/action.yml b/.github/actions/comment-preview-urls/action.yml new file mode 100644 index 0000000000..0c06f8a2e7 --- /dev/null +++ b/.github/actions/comment-preview-urls/action.yml @@ -0,0 +1,68 @@ +name: Comment preview URLs on PR +description: > + Post (or update) a comment on a pull request listing preview and production + deployment URLs for all vinext examples. + +inputs: + pr-number: + description: Pull request number + required: true + +runs: + using: composite + steps: + - name: Comment PR with preview URLs + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number('${{ inputs.pr-number }}'); + const alias = `pr-${prNumber}`; + + const examples = [ + { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, + { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, + { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, + { name: 'realworld-api-rest', project: 'realworld-api-rest' }, + { name: 'nextra-docs-template', project: 'nextra-docs-template' }, + { name: 'benchmarks', project: 'benchmarks' }, + { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, + ]; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const marker = ''; + const existingComment = comments.find(c => c.body?.includes(marker)); + + const rows = examples + .map(e => { + const orig = e.original ? `[original](${e.original})` : ''; + return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; + }) + .join('\n'); + + const body = [ + marker, + '| Example | Preview | Production | Original |', + '|---------|---------|------------|----------|', + rows, + ].join('\n'); + + if (existingComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } diff --git a/.github/actions/deploy-example/action.yml b/.github/actions/deploy-example/action.yml new file mode 100644 index 0000000000..d6cf507ab6 --- /dev/null +++ b/.github/actions/deploy-example/action.yml @@ -0,0 +1,49 @@ +name: Deploy example to Cloudflare Workers +description: Build a vinext example and deploy or upload a preview version to Cloudflare Workers. + +inputs: + example-name: + description: Directory name of the example under examples/ + required: true + wrangler-config: + description: Path to the wrangler config file (relative to the example directory) + required: true + mode: + description: "'production' to deploy, 'preview' to upload a preview version" + required: true + preview-alias: + description: Preview alias (e.g. pr-42). Required when mode is 'preview'. + required: false + default: "" + cloudflare-api-token: + description: Cloudflare API token + required: true + cloudflare-account-id: + description: Cloudflare account ID + required: true + +runs: + using: composite + steps: + - name: Build example + shell: bash + working-directory: examples/${{ inputs.example-name }} + run: pnpm exec vite build + + - name: Deploy (production) + if: inputs.mode == 'production' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ inputs.cloudflare-api-token }} + accountId: ${{ inputs.cloudflare-account-id }} + workingDirectory: examples/${{ inputs.example-name }} + command: deploy --config ${{ inputs.wrangler-config }} + + - name: Upload preview version + if: inputs.mode == 'preview' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ inputs.cloudflare-api-token }} + accountId: ${{ inputs.cloudflare-account-id }} + workingDirectory: examples/${{ inputs.example-name }} + command: versions upload --config ${{ inputs.wrangler-config }} --preview-alias ${{ inputs.preview-alias }} diff --git a/.github/actions/setup-node-pnpm/action.yml b/.github/actions/setup-node-pnpm/action.yml new file mode 100644 index 0000000000..7b5b877126 --- /dev/null +++ b/.github/actions/setup-node-pnpm/action.yml @@ -0,0 +1,35 @@ +name: Setup Node.js and pnpm +description: Checkout, install pnpm, set up Node.js with cache, and install dependencies. + +inputs: + node-version: + description: Node.js version to use + required: false + default: "24" + fetch-depth: + description: Number of commits to fetch (0 = full history) + required: false + default: "1" + ref: + description: The branch, tag or SHA to checkout + required: false + default: "" + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: ${{ inputs.fetch-depth }} + ref: ${{ inputs.ref }} + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ inputs.node-version }} + cache: pnpm + + - name: Install dependencies + shell: bash + run: pnpm install --frozen-lockfile diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 4f216c5b7f..309fcec210 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -16,16 +16,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@v4 + - uses: ./.github/actions/build-vinext with: - fetch-depth: 0 # Need full history for commit info - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm + fetch-depth: "0" # full history for commit info - name: Install hyperfine run: | @@ -33,11 +26,6 @@ jobs: echo "5ffe6996aefbbf547f383116eea53569263cc7cf6598ee53f0081e37b702055d hyperfine_1.19.0_amd64.deb" | sha256sum -c sudo dpkg -i hyperfine_1.19.0_amd64.deb - # Install monorepo deps (builds vinext plugin via pnpm run build) - - run: pnpm install --frozen-lockfile - - name: Build vinext plugin - run: pnpm run build - # Generate the shared benchmark app (copies app/ into each project dir) - name: Generate benchmark app run: node benchmarks/generate-app.mjs diff --git a/.github/workflows/bigbonk.yml b/.github/workflows/bigbonk.yml index 67c172f24a..40eba8ca22 100644 --- a/.github/workflows/bigbonk.yml +++ b/.github/workflows/bigbonk.yml @@ -21,22 +21,9 @@ jobs: issues: write pull-requests: write steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm with: - fetch-depth: 30 # 30 is a balance in providing enough context for the agent vs. pulling down the entire repo history. It is not a scientific number. 27 or 31 or 36 would likely work as well. - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile + fetch-depth: "30" # enough context for the agent without full history - name: Run Bonk uses: ask-bonk/ask-bonk/github@main diff --git a/.github/workflows/bonk.yml b/.github/workflows/bonk.yml index 544dbc763f..80dfd4dbbe 100644 --- a/.github/workflows/bonk.yml +++ b/.github/workflows/bonk.yml @@ -21,22 +21,9 @@ jobs: issues: write pull-requests: write steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-node-pnpm with: - fetch-depth: 30 # 30 is a balance in providing enough context for the agent vs. pulling down the entire repo history. It is not a scientific number. 27 or 31 or 36 would likely work as well. - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile + fetch-depth: "30" # enough context for the agent without full history - name: Run Bonk uses: ask-bonk/ask-bonk/github@main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55f35e30ff..0f33a3c08d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,41 +19,21 @@ jobs: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - run: pnpm run lint typecheck: name: Typecheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - run: pnpm run typecheck test: name: Vitest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile - - name: Build plugin (needed by ecosystem fixtures) - run: pnpm run build + - uses: ./.github/actions/build-vinext - run: pnpm test create-next-app: @@ -64,68 +44,44 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile - - name: Build plugin - run: pnpm run build + - uses: ./.github/actions/build-vinext + - name: Pack vinext for local install run: npm pack --pack-destination "${{ runner.temp }}" working-directory: packages/vinext + - name: Scaffold a fresh create-next-app project run: npx create-next-app@latest "${{ runner.temp }}/cna-test" --yes + - name: Install vinext from local tarball working-directory: ${{ runner.temp }}/cna-test shell: bash run: npm install "${{ runner.temp }}"/vinext-*.tgz + - name: Run vinext init working-directory: ${{ runner.temp }}/cna-test run: npx vinext init --skip-check + - name: Start dev server and verify HTTP 200 working-directory: ${{ runner.temp }}/cna-test - shell: node {0} + shell: bash run: | - const { spawn } = require("child_process"); - const http = require("http"); - - const isWin = process.platform === "win32"; - const server = spawn("npx", ["vite", "dev", "--port", "3099"], { - cwd: process.cwd(), - stdio: "inherit", - shell: true, - }); - - let attempts = 0; - const maxAttempts = 30; - const interval = setInterval(() => { - attempts++; - http.get("http://localhost:3099/", (res) => { - if (res.statusCode === 200) { - console.log(`Server responded with HTTP 200 (attempt ${attempts})`); - clearInterval(interval); - if (isWin) { - spawn("taskkill", ["/pid", String(server.pid), "/T", "/F"], { stdio: "ignore" }); - } else { - server.kill("SIGTERM"); - } - process.exit(0); - } - }).on("error", () => {}); - if (attempts >= maxAttempts) { - console.error("Server did not respond with HTTP 200 within 30 seconds"); - clearInterval(interval); - if (isWin) { - spawn("taskkill", ["/pid", String(server.pid), "/T", "/F"], { stdio: "ignore" }); - } else { - server.kill("SIGTERM"); - } - process.exit(1); - } - }, 1000); + npx vite dev --port 3099 & + SERVER_PID=$! + + for i in $(seq 1 30); do + STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3099/ || true) + if [ "$STATUS" = "200" ]; then + echo "Server responded with HTTP 200 (attempt $i)" + kill "$SERVER_PID" 2>/dev/null || true + exit 0 + fi + sleep 1 + done + + echo "Server did not respond with HTTP 200 within 30 seconds" + kill "$SERVER_PID" 2>/dev/null || true + exit 1 e2e: name: E2E (${{ matrix.project }}) @@ -140,20 +96,21 @@ jobs: - pages-router-prod - cloudflare-workers steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 + - uses: ./.github/actions/build-vinext + + - name: Cache Playwright browsers + uses: actions/cache@v4 with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile - - name: Build plugin - run: pnpm run build + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ hashFiles('**/package.json') }} + - run: pnpm exec playwright install chromium + - run: pnpm exec playwright test env: CI: true PLAYWRIGHT_PROJECT: ${{ matrix.project }} + - uses: actions/upload-artifact@v4 if: failure() with: diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml index c2f610eedb..4afe7db24a 100644 --- a/.github/workflows/deploy-examples.yml +++ b/.github/workflows/deploy-examples.yml @@ -6,6 +6,10 @@ on: pull_request: branches: [main] +concurrency: + group: deploy-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: true + jobs: deploy: # Skip entirely for fork PRs — they don't have access to deploy secrets. @@ -42,23 +46,7 @@ jobs: project: hackernews wrangler_config: dist/server/wrangler.json steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - run: pnpm install --frozen-lockfile - - - name: Build vinext plugin - run: pnpm run build - - - name: Build example - run: pnpm exec vite build - working-directory: examples/${{ matrix.example.name }} + - uses: ./.github/actions/build-vinext - name: Apply D1 migrations (benchmarks only) if: matrix.example.name == 'benchmarks' && github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -69,23 +57,26 @@ jobs: workingDirectory: examples/benchmarks command: d1 migrations apply vinext-benchmarks --remote - - name: Deploy to Cloudflare Workers (Production) + - name: Deploy (production) if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: cloudflare/wrangler-action@v3 + uses: ./.github/actions/deploy-example with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: examples/${{ matrix.example.name }} - command: deploy --config ${{ matrix.example.wrangler_config }} + example-name: ${{ matrix.example.name }} + wrangler-config: ${{ matrix.example.wrangler_config }} + mode: production + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - - name: Deploy Preview Version + - name: Deploy (preview) if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - uses: cloudflare/wrangler-action@v3 + uses: ./.github/actions/deploy-example with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: examples/${{ matrix.example.name }} - command: versions upload --config ${{ matrix.example.wrangler_config }} --preview-alias pr-${{ github.event.pull_request.number }} + example-name: ${{ matrix.example.name }} + wrangler-config: ${{ matrix.example.wrangler_config }} + mode: preview + preview-alias: pr-${{ github.event.pull_request.number }} + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} smoke-test: name: Smoke Test Deployments @@ -110,58 +101,7 @@ jobs: permissions: pull-requests: write steps: - - name: Comment PR with preview URLs - uses: actions/github-script@v7 + - uses: actions/checkout@v4 + - uses: ./.github/actions/comment-preview-urls with: - script: | - const prNumber = context.issue.number; - const alias = `pr-${prNumber}`; - - const examples = [ - { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, - { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, - { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, - { name: 'realworld-api-rest', project: 'realworld-api-rest' }, - { name: 'nextra-docs-template', project: 'nextra-docs-template' }, - { name: 'benchmarks', project: 'benchmarks' }, - { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, - ]; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const marker = ''; - const existingComment = comments.find(c => c.body?.includes(marker)); - - const rows = examples - .map(e => { - const orig = e.original ? `[original](${e.original})` : ''; - return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; - }) - .join('\n'); - - const body = [ - marker, - '| Example | Preview | Production | Original |', - '|---------|---------|------------|----------|', - rows, - ].join('\n'); - - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body, - }); - } + pr-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/deploy-preview-command.yml b/.github/workflows/deploy-preview-command.yml index a09befa0cf..dfee714545 100644 --- a/.github/workflows/deploy-preview-command.yml +++ b/.github/workflows/deploy-preview-command.yml @@ -83,33 +83,18 @@ jobs: project: hackernews wrangler_config: dist/server/wrangler.json steps: - - uses: actions/checkout@v4 + - uses: ./.github/actions/build-vinext with: ref: ${{ needs.check.outputs.sha }} - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: ./.github/actions/deploy-example with: - node-version: 24 - cache: pnpm - - - run: pnpm install --frozen-lockfile - - - name: Build vinext plugin - run: pnpm run build - - - name: Build example - run: pnpm exec vite build - working-directory: examples/${{ matrix.example.name }} - - - name: Deploy Preview Version - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - workingDirectory: examples/${{ matrix.example.name }} - command: versions upload --config ${{ matrix.example.wrangler_config }} --preview-alias pr-${{ needs.check.outputs.pr_number }} + example-name: ${{ matrix.example.name }} + wrangler-config: ${{ matrix.example.wrangler_config }} + mode: preview + preview-alias: pr-${{ needs.check.outputs.pr_number }} + cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} + cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} smoke-test: name: Smoke Test Previews @@ -131,58 +116,9 @@ jobs: permissions: pull-requests: write steps: - - name: Comment PR with preview URLs - uses: actions/github-script@v7 + - uses: actions/checkout@v4 with: - script: | - const prNumber = ${{ needs.check.outputs.pr_number }}; - const alias = `pr-${prNumber}`; - - const examples = [ - { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, - { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, - { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, - { name: 'realworld-api-rest', project: 'realworld-api-rest' }, - { name: 'nextra-docs-template', project: 'nextra-docs-template' }, - { name: 'benchmarks', project: 'benchmarks' }, - { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, - ]; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const marker = ''; - const existingComment = comments.find(c => c.body?.includes(marker)); - - const rows = examples - .map(e => { - const orig = e.original ? `[original](${e.original})` : ''; - return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; - }) - .join('\n'); - - const body = [ - marker, - '| Example | Preview | Production | Original |', - '|---------|---------|------------|----------|', - rows, - ].join('\n'); - - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body, - }); - } + ref: ${{ needs.check.outputs.sha }} + - uses: ./.github/actions/comment-preview-urls + with: + pr-number: ${{ needs.check.outputs.pr_number }} diff --git a/.github/workflows/ecosystem-run.yml b/.github/workflows/ecosystem-run.yml index bf72495107..cb9dcf4dca 100644 --- a/.github/workflows/ecosystem-run.yml +++ b/.github/workflows/ecosystem-run.yml @@ -22,30 +22,17 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Checkout vinext - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - uses: ./.github/actions/build-vinext with: node-version: ${{ inputs.node_version }} - cache: pnpm - - - name: Build vinext - run: | - pnpm install --frozen-lockfile - pnpm run build - name: Read vinext versions id: versions run: | - node - <<'NODE' - const fs = require('fs'); - const path = require('path'); - const pkg = require('./packages/vinext/package.json'); + node --input-type=module <<'NODE' + import { readFileSync, appendFileSync } from 'node:fs'; + + const pkg = JSON.parse(readFileSync('./packages/vinext/package.json', 'utf8')); const clean = (value) => value.replace(/^[^0-9]*/, ''); const react = clean(pkg.peerDependencies.react); const reactDom = clean(pkg.peerDependencies['react-dom']); @@ -57,18 +44,18 @@ jobs: `vite=${vite}`, `rsc=${rsc}`, ].join('\n'); - fs.appendFileSync(process.env.GITHUB_OUTPUT, `${lines}\n`); + appendFileSync(process.env.GITHUB_OUTPUT, `${lines}\n`); NODE - name: Validate repo URL env: REPO_URL: ${{ inputs.repo_url }} run: | - node - <<'NODE' - const fs = require('fs'); + node --input-type=module <<'NODE' + import { readFileSync } from 'node:fs'; const input = process.env.REPO_URL; - const allowed = JSON.parse(fs.readFileSync('.github/repos.json', 'utf8')) + const allowed = JSON.parse(readFileSync('.github/repos.json', 'utf8')) .map((repo) => repo.url); if (!allowed.includes(input)) { @@ -109,42 +96,37 @@ jobs: - name: Verify build output working-directory: target-repo run: | - node - <<'NODE' - const fs = require('fs'); - const path = require('path'); + node --input-type=module <<'NODE' + import { existsSync, readdirSync } from 'node:fs'; + import { join } from 'node:path'; const ensureNonEmptyDir = (dir) => { - if (!fs.existsSync(dir)) { + if (!existsSync(dir)) { console.error(`missing ${dir}`); process.exit(1); } - const entries = fs.readdirSync(dir); + const entries = readdirSync(dir); if (entries.length === 0) { console.error(`empty ${dir}`); process.exit(1); } }; - const clientDir = 'dist/client'; - ensureNonEmptyDir(clientDir); - const hasJs = (dir) => { - const entries = fs.readdirSync(dir, { withFileTypes: true }); + const entries = readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { - const entryPath = path.join(dir, entry.name); + const entryPath = join(dir, entry.name); if (entry.isDirectory()) { - if (hasJs(entryPath)) { - return true; - } + if (hasJs(entryPath)) return true; continue; } - if (entry.name.endsWith('.js') || entry.name.endsWith('.mjs')) { - return true; - } + if (entry.name.endsWith('.js') || entry.name.endsWith('.mjs')) return true; } return false; }; + const clientDir = 'dist/client'; + ensureNonEmptyDir(clientDir); if (!hasJs(clientDir)) { console.error(`no js artifacts in ${clientDir}`); process.exit(1); @@ -152,7 +134,6 @@ jobs: const serverDir = 'dist/server'; ensureNonEmptyDir(serverDir); - if (!hasJs(serverDir)) { console.error(`no js artifacts in ${serverDir}`); process.exit(1); diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index d2f01c40d4..0c75799d43 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -14,12 +14,5 @@ jobs: if: github.repository_owner == 'cloudflare' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm run build + - uses: ./.github/actions/build-vinext - run: pnpm dlx pkg-pr-new publish --pnpm './packages/vinext' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 66f58cadcb..c7fb695e96 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,24 +32,15 @@ jobs: if: github.repository_owner == 'cloudflare' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: ./.github/actions/build-vinext with: - fetch-depth: 0 # full history for changelog generation - - - uses: pnpm/action-setup@v4 + fetch-depth: "0" # full history for changelog generation + node-version: "24" - uses: actions/setup-node@v4 with: - node-version: 24 registry-url: https://registry.npmjs.org - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Build - run: pnpm run build - working-directory: packages/vinext - - name: Bump version id: version working-directory: packages/vinext From 757244b570cbe0677a530e101da28a75d206897d Mon Sep 17 00:00:00 2001 From: James Date: Sat, 7 Mar 2026 13:14:46 +0000 Subject: [PATCH 2/3] refactor(ci): revert to inline steps, keep only setup-node-pnpm composite action Remove the build-vinext, deploy-example, and comment-preview-urls composite actions and inline their steps back into each workflow. Keep setup-node-pnpm as the one reusable action (pnpm install + node setup). Retain all other improvements from the previous commit: concurrency groups, Playwright browser cache, bash-based dev-server check, ESM node scripts in ecosystem-run. --- .github/actions/build-vinext/action.yml | 29 ----- .../actions/comment-preview-urls/action.yml | 68 ------------ .github/actions/deploy-example/action.yml | 49 --------- .github/actions/setup-node-pnpm/action.yml | 15 +-- .github/workflows/benchmarks.yml | 15 ++- .github/workflows/bigbonk.yml | 17 ++- .github/workflows/bonk.yml | 17 ++- .github/workflows/ci.yml | 32 +++++- .github/workflows/deploy-examples.yml | 102 ++++++++++++++---- .github/workflows/deploy-preview-command.yml | 90 +++++++++++++--- .github/workflows/ecosystem-run.yml | 13 ++- .github/workflows/preview-release.yml | 9 +- .github/workflows/publish.yml | 15 ++- 13 files changed, 265 insertions(+), 206 deletions(-) delete mode 100644 .github/actions/build-vinext/action.yml delete mode 100644 .github/actions/comment-preview-urls/action.yml delete mode 100644 .github/actions/deploy-example/action.yml diff --git a/.github/actions/build-vinext/action.yml b/.github/actions/build-vinext/action.yml deleted file mode 100644 index 5417cc3938..0000000000 --- a/.github/actions/build-vinext/action.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build vinext plugin -description: Set up the environment and build the vinext package. - -inputs: - node-version: - description: Node.js version to use - required: false - default: "24" - fetch-depth: - description: Number of commits to fetch - required: false - default: "1" - ref: - description: The branch, tag or SHA to checkout - required: false - default: "" - -runs: - using: composite - steps: - - uses: ./.github/actions/setup-node-pnpm - with: - node-version: ${{ inputs.node-version }} - fetch-depth: ${{ inputs.fetch-depth }} - ref: ${{ inputs.ref }} - - - name: Build vinext plugin - shell: bash - run: pnpm run build diff --git a/.github/actions/comment-preview-urls/action.yml b/.github/actions/comment-preview-urls/action.yml deleted file mode 100644 index 0c06f8a2e7..0000000000 --- a/.github/actions/comment-preview-urls/action.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: Comment preview URLs on PR -description: > - Post (or update) a comment on a pull request listing preview and production - deployment URLs for all vinext examples. - -inputs: - pr-number: - description: Pull request number - required: true - -runs: - using: composite - steps: - - name: Comment PR with preview URLs - uses: actions/github-script@v7 - with: - script: | - const prNumber = Number('${{ inputs.pr-number }}'); - const alias = `pr-${prNumber}`; - - const examples = [ - { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, - { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, - { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, - { name: 'realworld-api-rest', project: 'realworld-api-rest' }, - { name: 'nextra-docs-template', project: 'nextra-docs-template' }, - { name: 'benchmarks', project: 'benchmarks' }, - { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, - ]; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - - const marker = ''; - const existingComment = comments.find(c => c.body?.includes(marker)); - - const rows = examples - .map(e => { - const orig = e.original ? `[original](${e.original})` : ''; - return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; - }) - .join('\n'); - - const body = [ - marker, - '| Example | Preview | Production | Original |', - '|---------|---------|------------|----------|', - rows, - ].join('\n'); - - if (existingComment) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body, - }); - } diff --git a/.github/actions/deploy-example/action.yml b/.github/actions/deploy-example/action.yml deleted file mode 100644 index d6cf507ab6..0000000000 --- a/.github/actions/deploy-example/action.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Deploy example to Cloudflare Workers -description: Build a vinext example and deploy or upload a preview version to Cloudflare Workers. - -inputs: - example-name: - description: Directory name of the example under examples/ - required: true - wrangler-config: - description: Path to the wrangler config file (relative to the example directory) - required: true - mode: - description: "'production' to deploy, 'preview' to upload a preview version" - required: true - preview-alias: - description: Preview alias (e.g. pr-42). Required when mode is 'preview'. - required: false - default: "" - cloudflare-api-token: - description: Cloudflare API token - required: true - cloudflare-account-id: - description: Cloudflare account ID - required: true - -runs: - using: composite - steps: - - name: Build example - shell: bash - working-directory: examples/${{ inputs.example-name }} - run: pnpm exec vite build - - - name: Deploy (production) - if: inputs.mode == 'production' - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ inputs.cloudflare-api-token }} - accountId: ${{ inputs.cloudflare-account-id }} - workingDirectory: examples/${{ inputs.example-name }} - command: deploy --config ${{ inputs.wrangler-config }} - - - name: Upload preview version - if: inputs.mode == 'preview' - uses: cloudflare/wrangler-action@v3 - with: - apiToken: ${{ inputs.cloudflare-api-token }} - accountId: ${{ inputs.cloudflare-account-id }} - workingDirectory: examples/${{ inputs.example-name }} - command: versions upload --config ${{ inputs.wrangler-config }} --preview-alias ${{ inputs.preview-alias }} diff --git a/.github/actions/setup-node-pnpm/action.yml b/.github/actions/setup-node-pnpm/action.yml index 7b5b877126..aa4118f943 100644 --- a/.github/actions/setup-node-pnpm/action.yml +++ b/.github/actions/setup-node-pnpm/action.yml @@ -1,28 +1,15 @@ name: Setup Node.js and pnpm -description: Checkout, install pnpm, set up Node.js with cache, and install dependencies. +description: Install pnpm, set up Node.js with cache, and install dependencies. Requires the repo to already be checked out. inputs: node-version: description: Node.js version to use required: false default: "24" - fetch-depth: - description: Number of commits to fetch (0 = full history) - required: false - default: "1" - ref: - description: The branch, tag or SHA to checkout - required: false - default: "" runs: using: composite steps: - - uses: actions/checkout@v4 - with: - fetch-depth: ${{ inputs.fetch-depth }} - ref: ${{ inputs.ref }} - - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 309fcec210..0864543330 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -16,9 +16,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 with: - fetch-depth: "0" # full history for commit info + fetch-depth: 0 # Need full history for commit info + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - run: pnpm install --frozen-lockfile + - name: Build vinext plugin + run: pnpm run build - name: Install hyperfine run: | diff --git a/.github/workflows/bigbonk.yml b/.github/workflows/bigbonk.yml index 40eba8ca22..67c172f24a 100644 --- a/.github/workflows/bigbonk.yml +++ b/.github/workflows/bigbonk.yml @@ -21,9 +21,22 @@ jobs: issues: write pull-requests: write steps: - - uses: ./.github/actions/setup-node-pnpm + - name: Checkout repository + uses: actions/checkout@v4 with: - fetch-depth: "30" # enough context for the agent without full history + fetch-depth: 30 # 30 is a balance in providing enough context for the agent vs. pulling down the entire repo history. It is not a scientific number. 27 or 31 or 36 would likely work as well. + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile - name: Run Bonk uses: ask-bonk/ask-bonk/github@main diff --git a/.github/workflows/bonk.yml b/.github/workflows/bonk.yml index 80dfd4dbbe..544dbc763f 100644 --- a/.github/workflows/bonk.yml +++ b/.github/workflows/bonk.yml @@ -21,9 +21,22 @@ jobs: issues: write pull-requests: write steps: - - uses: ./.github/actions/setup-node-pnpm + - name: Checkout repository + uses: actions/checkout@v4 with: - fetch-depth: "30" # enough context for the agent without full history + fetch-depth: 30 # 30 is a balance in providing enough context for the agent vs. pulling down the entire repo history. It is not a scientific number. 27 or 31 or 36 would likely work as well. + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile - name: Run Bonk uses: ask-bonk/ask-bonk/github@main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f33a3c08d..8b6feb0201 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: name: Lint runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/setup-node-pnpm - run: pnpm run lint @@ -26,6 +27,7 @@ jobs: name: Typecheck runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - uses: ./.github/actions/setup-node-pnpm - run: pnpm run typecheck @@ -33,7 +35,15 @@ jobs: name: Vitest runs-on: ubuntu-latest steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Build plugin (needed by ecosystem fixtures) + run: pnpm run build - run: pnpm test create-next-app: @@ -44,7 +54,15 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Build plugin + run: pnpm run build - name: Pack vinext for local install run: npm pack --pack-destination "${{ runner.temp }}" @@ -96,7 +114,15 @@ jobs: - pages-router-prod - cloudflare-workers steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - name: Build plugin + run: pnpm run build - name: Cache Playwright browsers uses: actions/cache@v4 diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml index 4afe7db24a..a618426007 100644 --- a/.github/workflows/deploy-examples.yml +++ b/.github/workflows/deploy-examples.yml @@ -46,7 +46,23 @@ jobs: project: hackernews wrangler_config: dist/server/wrangler.json steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Build vinext plugin + run: pnpm run build + + - name: Build example + run: pnpm exec vite build + working-directory: examples/${{ matrix.example.name }} - name: Apply D1 migrations (benchmarks only) if: matrix.example.name == 'benchmarks' && github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -57,26 +73,23 @@ jobs: workingDirectory: examples/benchmarks command: d1 migrations apply vinext-benchmarks --remote - - name: Deploy (production) + - name: Deploy to Cloudflare Workers (Production) if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: ./.github/actions/deploy-example + uses: cloudflare/wrangler-action@v3 with: - example-name: ${{ matrix.example.name }} - wrangler-config: ${{ matrix.example.wrangler_config }} - mode: production - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: examples/${{ matrix.example.name }} + command: deploy --config ${{ matrix.example.wrangler_config }} - - name: Deploy (preview) + - name: Deploy Preview Version if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository - uses: ./.github/actions/deploy-example + uses: cloudflare/wrangler-action@v3 with: - example-name: ${{ matrix.example.name }} - wrangler-config: ${{ matrix.example.wrangler_config }} - mode: preview - preview-alias: pr-${{ github.event.pull_request.number }} - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: examples/${{ matrix.example.name }} + command: versions upload --config ${{ matrix.example.wrangler_config }} --preview-alias pr-${{ github.event.pull_request.number }} smoke-test: name: Smoke Test Deployments @@ -101,7 +114,58 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/comment-preview-urls + - name: Comment PR with preview URLs + uses: actions/github-script@v7 with: - pr-number: ${{ github.event.pull_request.number }} + script: | + const prNumber = context.issue.number; + const alias = `pr-${prNumber}`; + + const examples = [ + { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, + { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, + { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, + { name: 'realworld-api-rest', project: 'realworld-api-rest' }, + { name: 'nextra-docs-template', project: 'nextra-docs-template' }, + { name: 'benchmarks', project: 'benchmarks' }, + { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, + ]; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const marker = ''; + const existingComment = comments.find(c => c.body?.includes(marker)); + + const rows = examples + .map(e => { + const orig = e.original ? `[original](${e.original})` : ''; + return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; + }) + .join('\n'); + + const body = [ + marker, + '| Example | Preview | Production | Original |', + '|---------|---------|------------|----------|', + rows, + ].join('\n'); + + if (existingComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } diff --git a/.github/workflows/deploy-preview-command.yml b/.github/workflows/deploy-preview-command.yml index dfee714545..a09befa0cf 100644 --- a/.github/workflows/deploy-preview-command.yml +++ b/.github/workflows/deploy-preview-command.yml @@ -83,18 +83,33 @@ jobs: project: hackernews wrangler_config: dist/server/wrangler.json steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 with: ref: ${{ needs.check.outputs.sha }} - - uses: ./.github/actions/deploy-example + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 with: - example-name: ${{ matrix.example.name }} - wrangler-config: ${{ matrix.example.wrangler_config }} - mode: preview - preview-alias: pr-${{ needs.check.outputs.pr_number }} - cloudflare-api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }} - cloudflare-account-id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + node-version: 24 + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Build vinext plugin + run: pnpm run build + + - name: Build example + run: pnpm exec vite build + working-directory: examples/${{ matrix.example.name }} + + - name: Deploy Preview Version + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: examples/${{ matrix.example.name }} + command: versions upload --config ${{ matrix.example.wrangler_config }} --preview-alias pr-${{ needs.check.outputs.pr_number }} smoke-test: name: Smoke Test Previews @@ -116,9 +131,58 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@v4 - with: - ref: ${{ needs.check.outputs.sha }} - - uses: ./.github/actions/comment-preview-urls + - name: Comment PR with preview URLs + uses: actions/github-script@v7 with: - pr-number: ${{ needs.check.outputs.pr_number }} + script: | + const prNumber = ${{ needs.check.outputs.pr_number }}; + const alias = `pr-${prNumber}`; + + const examples = [ + { name: 'app-router-cloudflare', project: 'app-router-cloudflare' }, + { name: 'pages-router-cloudflare', project: 'pages-router-cloudflare' }, + { name: 'app-router-playground', project: 'app-router-playground', original: 'https://app-router.vercel.app' }, + { name: 'realworld-api-rest', project: 'realworld-api-rest' }, + { name: 'nextra-docs-template', project: 'nextra-docs-template' }, + { name: 'benchmarks', project: 'benchmarks' }, + { name: 'hackernews', project: 'hackernews', original: 'https://next-react-server-components.vercel.app' }, + ]; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + + const marker = ''; + const existingComment = comments.find(c => c.body?.includes(marker)); + + const rows = examples + .map(e => { + const orig = e.original ? `[original](${e.original})` : ''; + return `| ${e.name} | [preview](https://${alias}-${e.project}.vinext.workers.dev) | [production](https://${e.project}.vinext.workers.dev) | ${orig} |`; + }) + .join('\n'); + + const body = [ + marker, + '| Example | Preview | Production | Original |', + '|---------|---------|------------|----------|', + rows, + ].join('\n'); + + if (existingComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } diff --git a/.github/workflows/ecosystem-run.yml b/.github/workflows/ecosystem-run.yml index cb9dcf4dca..fc2ded39e5 100644 --- a/.github/workflows/ecosystem-run.yml +++ b/.github/workflows/ecosystem-run.yml @@ -22,9 +22,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: ./.github/actions/build-vinext + - name: Checkout vinext + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node_version }} + cache: pnpm + + - run: pnpm install --frozen-lockfile + + - name: Build vinext plugin + run: pnpm run build - name: Read vinext versions id: versions diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index 0c75799d43..d2f01c40d4 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -14,5 +14,12 @@ jobs: if: github.repository_owner == 'cloudflare' runs-on: ubuntu-latest steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm run build - run: pnpm dlx pkg-pr-new publish --pnpm './packages/vinext' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c7fb695e96..881b83c934 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -32,15 +32,24 @@ jobs: if: github.repository_owner == 'cloudflare' runs-on: ubuntu-latest steps: - - uses: ./.github/actions/build-vinext + - uses: actions/checkout@v4 with: - fetch-depth: "0" # full history for changelog generation - node-version: "24" + fetch-depth: 0 # full history for changelog generation + + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: + node-version: 24 + cache: pnpm registry-url: https://registry.npmjs.org + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm run build + - name: Bump version id: version working-directory: packages/vinext From 56ead1b2ee22b78619a0ec6256383b9fc9a4cb90 Mon Sep 17 00:00:00 2001 From: James Date: Sat, 7 Mar 2026 13:17:23 +0000 Subject: [PATCH 3/3] refactor(ci): use setup-node-pnpm composite action consistently Every workflow that does pnpm/node setup + install now uses the setup-node-pnpm composite action instead of the inline 3-step triplet. Added optional registry-url input to support publish.yml's npm auth. Exceptions (intentionally kept inline): - bonk.yml / bigbonk.yml: issue_comment trigger, GHAS constraint - tip.yml notify-on-failure: node-only, no pnpm install needed --- .github/actions/setup-node-pnpm/action.yml | 5 +++++ .github/workflows/benchmarks.yml | 8 +------- .github/workflows/ci.yml | 21 +++----------------- .github/workflows/deploy-examples.yml | 10 +--------- .github/workflows/deploy-preview-command.yml | 9 +-------- .github/workflows/ecosystem-run.yml | 7 +------ .github/workflows/preview-release.yml | 7 +------ .github/workflows/publish.yml | 9 +-------- 8 files changed, 14 insertions(+), 62 deletions(-) diff --git a/.github/actions/setup-node-pnpm/action.yml b/.github/actions/setup-node-pnpm/action.yml index aa4118f943..c256324553 100644 --- a/.github/actions/setup-node-pnpm/action.yml +++ b/.github/actions/setup-node-pnpm/action.yml @@ -6,6 +6,10 @@ inputs: description: Node.js version to use required: false default: "24" + registry-url: + description: npm registry URL (e.g. https://registry.npmjs.org). Leave empty to skip. + required: false + default: "" runs: using: composite @@ -16,6 +20,7 @@ runs: with: node-version: ${{ inputs.node-version }} cache: pnpm + registry-url: ${{ inputs.registry-url }} - name: Install dependencies shell: bash diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 0864543330..be6cbf495d 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -20,14 +20,8 @@ jobs: with: fetch-depth: 0 # Need full history for commit info - - uses: pnpm/action-setup@v4 + - uses: ./.github/actions/setup-node-pnpm - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - run: pnpm install --frozen-lockfile - name: Build vinext plugin run: pnpm run build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b6feb0201..d9f2c1cb8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,12 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - name: Build plugin (needed by ecosystem fixtures) run: pnpm run build - run: pnpm test @@ -55,12 +50,7 @@ jobs: os: [ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - name: Build plugin run: pnpm run build @@ -115,12 +105,7 @@ jobs: - cloudflare-workers steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - name: Build plugin run: pnpm run build diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml index a618426007..02febba46e 100644 --- a/.github/workflows/deploy-examples.yml +++ b/.github/workflows/deploy-examples.yml @@ -47,15 +47,7 @@ jobs: wrangler_config: dist/server/wrangler.json steps: - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - name: Build vinext plugin run: pnpm run build diff --git a/.github/workflows/deploy-preview-command.yml b/.github/workflows/deploy-preview-command.yml index a09befa0cf..fbadd6a667 100644 --- a/.github/workflows/deploy-preview-command.yml +++ b/.github/workflows/deploy-preview-command.yml @@ -87,14 +87,7 @@ jobs: with: ref: ${{ needs.check.outputs.sha }} - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - name: Build vinext plugin run: pnpm run build diff --git a/.github/workflows/ecosystem-run.yml b/.github/workflows/ecosystem-run.yml index fc2ded39e5..a9fa8e398c 100644 --- a/.github/workflows/ecosystem-run.yml +++ b/.github/workflows/ecosystem-run.yml @@ -25,14 +25,9 @@ jobs: - name: Checkout vinext uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: ./.github/actions/setup-node-pnpm with: node-version: ${{ inputs.node_version }} - cache: pnpm - - - run: pnpm install --frozen-lockfile - name: Build vinext plugin run: pnpm run build diff --git a/.github/workflows/preview-release.yml b/.github/workflows/preview-release.yml index d2f01c40d4..0d9af9ccac 100644 --- a/.github/workflows/preview-release.yml +++ b/.github/workflows/preview-release.yml @@ -15,11 +15,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/setup-node-pnpm - run: pnpm run build - run: pnpm dlx pkg-pr-new publish --pnpm './packages/vinext' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 881b83c934..69a5bf5f4d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -36,17 +36,10 @@ jobs: with: fetch-depth: 0 # full history for changelog generation - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 + - uses: ./.github/actions/setup-node-pnpm with: - node-version: 24 - cache: pnpm registry-url: https://registry.npmjs.org - - name: Install dependencies - run: pnpm install --frozen-lockfile - - name: Build run: pnpm run build