From df085f1b6dd34798e1d90eaedd2ad7389ca1c2f9 Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Mon, 21 Sep 2026 02:14:11 -0700 Subject: [PATCH 1/2] chore(ci): add a validate workflow so the published CLI has a gate (W37-1545) `Affitor/cli` is public and publishes `affitor` to npm, and it had no CI at all: no `.github/` on main, `gh pr checks` reported none, and PR #29 merged without a single check. Every gate was a person remembering to run three commands locally. One `validate` job on pull_request and pushes to main, mirroring the CMS repo: npm ci, then build, lint (`tsc --noEmit` per workspace) and the vitest suites across all three workspaces. Measured on origin/main e431706 (2026-09-21 02:2x PT) before writing this, so the gate starts green rather than red on arrival: build clean, lint clean, 132/132 tests pass (recipes 19, cli 104, mcp 9). `npm ci --dry-run` resolves, so package-lock.json is in sync with the workspaces. Known debt, deliberately not in this first pass: `packages/cli` declares engines.node >= 18 but the job only runs Node 20, so a break that only shows on the oldest supported runtime still gets through. A matrix is the fix; it is left out here because it could not be measured on this machine and a gate that goes red on arrival is worse than none. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..15d505b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + pull_request: + push: + branches: [main] + +# This repo is public and publishes to npm; a workflow here never needs write access. +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build --workspaces --if-present + + - name: Lint + run: npm run lint --workspaces --if-present + + - name: Unit tests + run: npm run test --workspaces --if-present From dfa9bbd2ec06399304d074c10f78390c20d58d94 Mon Sep 17 00:00:00 2001 From: sonpiaz Date: Mon, 21 Sep 2026 02:22:06 -0700 Subject: [PATCH 2/2] chore(ci): harden the validate workflow after independent review (W37-1545) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round 1 review was ĐẠT with 0 P1. Four of its findings were cheap and in-file, and they matter more here than in a private repo because this one publishes to npm from a public repository. - Pin both actions to a commit instead of the `v4` tag. A tag can be repointed at any time. SHAs resolved independently against the GitHub API rather than copied from the review: actions/checkout v4 -> 11d5960, setup-node v4 -> 49933ea. - `persist-credentials: false`. The job runs code from the pull request, so the token should not be sitting in .git/config where that code can read it. - Drop `--if-present`. All three workspaces declare build, lint and test today, so a missing script means a package quietly stopped being checked; a gate should fail loudly instead. Re-measured without the flag: 132/132 still pass (recipes 19, cli 104, mcp 9), build and lint clean. - `timeout-minutes: 15` and `concurrency` with cancel-in-progress, so a hung job cannot occupy a runner and superseded pushes stop burning minutes. Not taken here, on purpose: making `validate` a required check. Measured `gh api repos/Affitor/cli/branches/main/protection` -> 404 not protected and `rulesets` -> []. A check cannot be required before it has ever run on main, so that is a step for whoever merges this, not a change to this file. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15d505b..31ce555 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,19 +5,30 @@ on: push: branches: [main] -# This repo is public and publishes to npm; a workflow here never needs write access. +# This repo is public and publishes `affitor` to npm; a workflow here never needs write access. permissions: contents: read +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + jobs: validate: runs-on: ubuntu-latest + timeout-minutes: 15 steps: + # Actions are pinned to a commit, not a moving tag: a tag can be repointed, + # and this repo publishes to npm. The comment records which tag was pinned. - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + # This job runs code from the pull request. Do not leave the token in + # .git/config where that code can read it. + persist-credentials: false - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 20 cache: npm @@ -25,11 +36,13 @@ jobs: - name: Install dependencies run: npm ci + # No `--if-present`: all three workspaces declare build, lint and test today, + # so a missing script means a package quietly stopped being checked. Fail loudly. - name: Build - run: npm run build --workspaces --if-present + run: npm run build --workspaces - name: Lint - run: npm run lint --workspaces --if-present + run: npm run lint --workspaces - name: Unit tests - run: npm run test --workspaces --if-present + run: npm run test --workspaces