From 5beb0ddc334deb862ba90cedbf03f052b58e4974 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Sun, 5 Apr 2026 08:22:17 +0100 Subject: [PATCH] refactor: split smoke and integration test configs into separate CI jobs (#264) * refactor: split smoke and integration test configs into separate CI jobs * fix: move CLA labeling from triage to CLA workflow * fix: install formatters in temp dir to avoid catalog: protocol error * fix: handle 404 when removing labels that don't exist on the PR --- .github/workflows/auto-format.yml | 13 ++--- .github/workflows/ci.yml | 20 +++++++- .github/workflows/cla.yml | 44 +++++++++++++++++ .github/workflows/format-command.yml | 11 ++--- .github/workflows/pr-triage.yml | 57 +++++----------------- packages/core/package.json | 3 +- packages/core/vitest.integration.config.ts | 14 ++++++ packages/core/vitest.smoke.config.ts | 6 +-- 8 files changed, 97 insertions(+), 71 deletions(-) create mode 100644 packages/core/vitest.integration.config.ts diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml index d6fa36b..2bcb699 100644 --- a/.github/workflows/auto-format.yml +++ b/.github/workflows/auto-format.yml @@ -56,20 +56,13 @@ jobs: ref: ${{ steps.pr.outputs.sha }} persist-credentials: false - # --- Install formatters directly (no pnpm install, no postinstall scripts) --- - - name: Setup Node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: node-version: 22 - - name: Install formatters - run: npm install --no-save --ignore-scripts oxfmt prettier prettier-plugin-astro - - - name: Run formatters - run: | - npx oxfmt --ignore-path .gitignore - npx prettier --write . + - name: Run formatter + run: npx oxfmt --ignore-path .gitignore - name: Check for changes id: diff @@ -125,5 +118,5 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ steps.pr.outputs.number }}, - body: `Could not push formatting changes to this fork. The contributor may have "Allow edits by maintainers" disabled.\n\nPlease run the formatter locally:\n\n\`\`\`\nnpx oxfmt --ignore-path .gitignore\nnpx prettier --write .\n\`\`\``, + body: `Could not push formatting changes to this fork. The contributor may have "Allow edits by maintainers" disabled.\n\nPlease run the formatter locally:\n\n\`\`\`\npnpm format\n\`\`\``, }); diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02e6379..3220481 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,7 @@ jobs: test-smoke: name: Smoke Tests runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 services: postgres: image: postgres:17 @@ -144,10 +144,26 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm build - run: pnpm --filter emdash exec vitest run --config vitest.smoke.config.ts - timeout-minutes: 5 env: DATABASE_URL: postgres://postgres:test@localhost:5432/emdash_smoke + test-integration: + name: Integration Tests + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 22 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm build + - run: pnpm --filter emdash exec vitest run --config vitest.integration.config.ts + test-browser: name: Browser Tests runs-on: ubuntu-latest diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 949d318..18c553a 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -27,3 +27,47 @@ jobs: branch: "cla-signatures" allowlist: dependabot[bot] lock-pullrequest-aftermerge: false + + - name: Label CLA status + if: always() && (github.event_name == 'pull_request_target' || github.event.issue.pull_request) + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const prNumber = context.payload.pull_request?.number || context.payload.issue?.number; + if (!prNumber) return; + + const owner = context.repo.owner; + const repo = context.repo.repo; + + // Get the PR to read head SHA + const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); + + // Read the CLA commit status + const { data: statuses } = await github.rest.repos.listCommitStatusesForRef({ + owner, repo, ref: pr.head.sha, + }); + const claStatus = statuses.find(s => s.context === 'license/cla'); + if (!claStatus) return; + + const signed = claStatus.state === 'success'; + const addLabel = signed ? 'cla: signed' : 'cla: needed'; + const removeLabel = signed ? 'cla: needed' : 'cla: signed'; + + // Ensure labels exist + const labelColors = { 'cla: signed': '0e8a16', 'cla: needed': 'b60205' }; + try { + await github.rest.issues.getLabel({ owner, repo, name: addLabel }); + } catch { + await github.rest.issues.createLabel({ owner, repo, name: addLabel, color: labelColors[addLabel] }); + } + + // Add the correct label + const currentLabels = pr.labels.map(l => l.name); + if (!currentLabels.includes(addLabel)) { + await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: [addLabel] }); + } + + // Remove the stale label + if (currentLabels.includes(removeLabel)) { + await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: removeLabel }); + } diff --git a/.github/workflows/format-command.yml b/.github/workflows/format-command.yml index 9560c77..7239946 100644 --- a/.github/workflows/format-command.yml +++ b/.github/workflows/format-command.yml @@ -77,13 +77,8 @@ jobs: with: node-version: 22 - - name: Install formatters - run: npm install --no-save --ignore-scripts oxfmt prettier prettier-plugin-astro - - - name: Run formatters - run: | - npx oxfmt --ignore-path .gitignore - npx prettier --write . + - name: Run formatter + run: npx oxfmt --ignore-path .gitignore - name: Check for changes id: diff @@ -133,7 +128,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, - body: `Could not push formatting changes to this fork. The contributor may have "Allow edits by maintainers" disabled.\n\nPlease run the formatter locally:\n\n\`\`\`\nnpx oxfmt --ignore-path .gitignore\nnpx prettier --write .\n\`\`\``, + body: `Could not push formatting changes to this fork. The contributor may have "Allow edits by maintainers" disabled.\n\nPlease run the formatter locally:\n\n\`\`\`\npnpm format\n\`\`\``, }); - name: React to comment (result) diff --git a/.github/workflows/pr-triage.yml b/.github/workflows/pr-triage.yml index 2c567df..829e4f3 100644 --- a/.github/workflows/pr-triage.yml +++ b/.github/workflows/pr-triage.yml @@ -81,29 +81,7 @@ jobs: // Ignore -- mergeable state may not be computed yet } - // --- CLA status --- - // The CLA assistant sets a commit status named "license/cla". - // Check the latest status for this PR's head SHA. - try { - const { data: statuses } = await github.rest.repos.listCommitStatusesForRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: pr.head.sha, - }); - const claStatus = statuses.find(s => s.context === 'license/cla'); - if (claStatus) { - if (claStatus.state === 'success') { - labels.add('cla: signed'); - } else { - labels.add('cla: needed'); - } - } else { - // CLA check hasn't run yet -- mark as pending - labels.add('cla: needed'); - } - } catch { - // If we can't read statuses, don't add CLA labels - } + // CLA labels are managed by the CLA workflow (cla.yml), not here. // --- Ensure all labels exist, then apply --- const labelColors = { @@ -121,8 +99,6 @@ jobs: 'area/ci': '000000', 'area/auth': 'd4c5f9', 'area/cloudflare': 'f9a825', - 'cla: signed': '0e8a16', - 'cla: needed': 'b60205', 'needs-rebase': 'e11d48', }; @@ -152,39 +128,30 @@ jobs: // Get current labels on the PR to remove stale ones const currentLabels = new Set(pr.labels.map(l => l.name)); - // Remove stale size labels (only one size label at a time) - for (const sl of sizeLabels) { - if (sl !== sizeLabel && currentLabels.has(sl)) { + // Helper: remove a label, ignoring 404 if it's already gone + async function safeRemoveLabel(name) { + try { await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number, - name: sl, + name, }); + } catch (e) { + if (e.status !== 404) throw e; } } - // Remove stale CLA labels - const claLabels = ['cla: signed', 'cla: needed']; - for (const cl of claLabels) { - if (!labels.has(cl) && currentLabels.has(cl)) { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: cl, - }); + // Remove stale size labels (only one size label at a time) + for (const sl of sizeLabels) { + if (sl !== sizeLabel && currentLabels.has(sl)) { + await safeRemoveLabel(sl); } } // Remove needs-rebase if PR is now mergeable if (!labels.has('needs-rebase') && currentLabels.has('needs-rebase')) { - await github.rest.issues.removeLabel({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - name: 'needs-rebase', - }); + await safeRemoveLabel('needs-rebase'); } // Add new labels diff --git a/packages/core/package.json b/packages/core/package.json index 20748c3..660facd 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -150,7 +150,8 @@ "typecheck": "tsgo --noEmit", "check": "publint && attw --pack --ignore-rules=cjs-resolves-to-esm --ignore-rules=no-resolution --ignore-rules=internal-resolution-error", "test": "vitest", - "test:smoke": "vitest run --config vitest.smoke.config.ts" + "test:smoke": "vitest run --config vitest.smoke.config.ts", + "test:integration": "vitest run --config vitest.integration.config.ts" }, "dependencies": { "@emdash-cms/admin": "workspace:*", diff --git a/packages/core/vitest.integration.config.ts b/packages/core/vitest.integration.config.ts new file mode 100644 index 0000000..09cc955 --- /dev/null +++ b/packages/core/vitest.integration.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + globals: true, + environment: "node", + include: ["tests/integration/cli/**/*.test.ts", "tests/integration/client/**/*.test.ts"], + // These tests boot real Astro dev servers in beforeAll hooks. + // Default hookTimeout (10s) is too short -- server startup + + // migrations + seed can take 30-60s. + testTimeout: 30_000, + hookTimeout: 120_000, + }, +}); diff --git a/packages/core/vitest.smoke.config.ts b/packages/core/vitest.smoke.config.ts index 1eb0c6f..16c99e1 100644 --- a/packages/core/vitest.smoke.config.ts +++ b/packages/core/vitest.smoke.config.ts @@ -4,11 +4,7 @@ export default defineConfig({ test: { globals: true, environment: "node", - include: [ - "tests/integration/smoke/**/*.test.ts", - "tests/integration/cli/**/*.test.ts", - "tests/integration/client/**/*.test.ts", - ], + include: ["tests/integration/smoke/**/*.test.ts"], // Smoke tests boot real Astro dev servers in beforeAll hooks. // Default hookTimeout (10s) is too short -- server startup + // migrations + seed can take 30-60s, especially on first run