From f1f5426964c1cca011331c36e867a5c0b937a89e Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Mon, 24 Aug 2026 17:07:08 +0300 Subject: [PATCH 1/2] ci: run the test suite on pull requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `build-test` job built and smoke-tested the packed CLI but never ran a test, so nothing in `src/**/*.spec.*` gated a merge — a regression test could go red and CI would stay green. Scope Vitest to the sources first. `tsc` copies every spec into `dist/`, and stale builds and git worktrees leave further copies behind, so an unscoped run collects the same test two or three times and fails on copies whose source no longer exists. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/pr.yml | 2 ++ vitest.config.ts | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 07e3833..2cf244a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -38,6 +38,8 @@ jobs: cache: 'npm' - name: Install dependencies run: npm ci + - name: Run tests + run: npm test - name: Build project run: npm run build - name: Create package diff --git a/vitest.config.ts b/vitest.config.ts index 3f824fb..7cc6609 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -4,5 +4,10 @@ export default defineConfig({ test: { globals: true, environment: "node", + // Only the sources. `tsc` copies the specs into `dist/`, and stale builds + // and git worktrees leave more copies around, so an unscoped run collects + // the same test several times over — and fails on copies whose source is + // long gone. + include: ["src/**/*.spec.{ts,tsx}"], }, }); From 3776623d1464fc52e3532e2b4bc3543fe5c702a0 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 25 Aug 2026 14:13:03 +0300 Subject: [PATCH 2/2] ci: do not persist the job token during PR checks actions/checkout stores the job token as an extraheader in .git/config by default. Both jobs then run code from the pull request - npm lifecycle scripts, the test suite, knip and the packaged CLI - which can read that config and use or exfiltrate the token. Neither job performs an authenticated Git operation, so the credential is not needed at all. Co-Authored-By: Claude Opus 5 --- .github/workflows/pr.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 2cf244a..39d41f3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -15,6 +15,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 + with: + # These jobs run code from the pull request (install scripts, + # tests, the packaged CLI) and never push, so the checkout must + # not leave the job token readable in .git/config. + persist-credentials: false - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc' @@ -32,6 +37,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 + with: + # These jobs run code from the pull request (install scripts, + # tests, the packaged CLI) and never push, so the checkout must + # not leave the job token readable in .git/config. + persist-credentials: false - uses: actions/setup-node@v6 with: node-version-file: '.nvmrc'