chore(ci): add a validate workflow so the published CLI has a gate (W37-1545) - #31
Merged
Merged
Conversation
…37-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) <noreply@anthropic.com>
…-1545) 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) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
One
validateworkflow for this repo:npm ci→ build → lint → unit tests, on every pull request and every push tomain.Why
Affitor/cliis public and publishesaffitorto npm, and it had no CI at all — no.github/directory onmain,gh pr checks 30reports no checks, and PR #29 merged without a single check running. Every gate was a person remembering to run three commands by hand. That was measured on 2026-09-20 while reviewing PR #30, and it is why this ticket exists.Concretely, the 9 exit-code cases
packages/cli/tests/exit-codes.test.tsadded in #29 went in with nothing watching them. After this, an agent-facingonboard/whoamiexit-code regression goes red.Measured before writing it (origin/main
e431706)So the gate starts green on arrival rather than red — a CI job that fails the moment it lands is worse than no CI, because it blocks the open PRs instead of protecting them.
npm run build --workspacesnpm run lint --workspacestsc --noEmit× 3), exit 0npm run test --workspacesnpm ci --dry-runpackage-lock.jsonis in sync with the three workspacesAn independent reviewer re-ran all three on the base and got the same numbers. The workflow also already proved itself:
validateran green on this PR's own head in 27s, becausepull_requesttakes the workflow from the PR.Hardening (second commit, after review)
v4tag — a tag can be repointed, and this repo publishes to npm. SHAs resolved against the GitHub API, not copied from the review.persist-credentials: false— the job runs code from the pull request; the token should not sit in.git/configfor that code to read.--if-present— all three workspaces declare all three scripts today, so a missing script means a package quietly stopped being checked. Re-measured without the flag: still 132/132.timeout-minutes: 15andconcurrencywith cancel-in-progress.permissions: contents: read.Deliberately left out, written down rather than hidden
packages/clideclaresengines.node >= 18, so a break that only shows on the oldest supported runtime still gets through.vitest@3.2.6does support Node 18, so the matrix is plausible — but it could not be measured here, and an unverified matrix risks the red-on-arrival problem above. Follow-up, same ticket.validateis not a required check. Measured:branches/main/protection→ 404 not protected,rulesets→[]. A check cannot be required before it has ever run onmain, so this is a step for whoever merges, not a change to this file. Until then the workflow is advisory.npm audit, a publish dry-run /--versionsmoke test.Scope
Adds one file. Touches no source, no tests, no package manifest, no publish path. Nothing here can change what the CLI does for a user.
Note on the other open PRs
A workflow landing on
maindoes not retroactively appear on PRs #30, #24 and #1 — they each need a newpull_requestevent (a push or a reopen) beforevalidateruns on them.🤖 Generated with Claude Code