fix: pre-commit runs zero hooks — restore the hooks, add a CI gate, apply Prettier - #202
Conversation
CI status: the new
|
| Checkout | Exit | Finding |
|---|---|---|
main |
1 | CVE-2026-84304 — google.golang.org/grpc v1.82.1 → fixed in 1.83.1 |
| this branch | 1 | identical, same two files |
Present in both gen/go/go.mod and remote-worker/go.mod, and v1.82.1 is byte-identical on main. pnpm-lock.yaml reports 0 vulnerabilities, so the root package.json this PR adds is not implicated — Prettier is a devDependency and Trivy suppresses dev deps anyway.
Recent PRs (#195, #199, #201) passed because they ran before this CVE entered Trivy's DB; this PR is just the first run after it. Consistent with how #162 handled the previous grpc bump, this belongs in its own issue/PR — happy to open one bumping grpc to 1.83.1 in both modules.
2. CodeQL — a pre-existing alert re-attributed to the reformat
One high-severity alert, Polynomial regular expression used on uncontrolled data, at harness/src/run-leaf.ts:152. GitHub's own summary carries the caveat: "Alerts not introduced by this pull request might have been detected because the code changes were too large."
That is exactly what happened. The flagged regex is workspaceRef.replace(/\/+$/, ...), which exists unchanged on main (line 109 there, line 152 here). The only edit on that line is the replacement string's quote style, " → ', per .prettierrc:
- const filePath = workspaceRef ? `${workspaceRef.replace(/\/+$/, "")}/${item.file}` : item.file;
+ const filePath = workspaceRef ? `${workspaceRef.replace(/\/+$/, '')}/${item.file}` : item.file;The regex itself is untouched, so this is a line-movement artifact of the 267-file reformat, not a new alert. It can be dismissed, or hardened separately if you'd like the pattern changed on its merits — I left it alone deliberately, since silently editing a regex is not what an issue about pre-commit config should do.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
pdettori
left a comment
There was a problem hiding this comment.
Reviewed the fix commit closely and verified the reformat commit's safety claims independently rather than taking them from the description.
Author: pdettori (MEMBER — maintainer)
Areas reviewed: pre-commit config, CI workflows, shell, YAML/JSON reformat, docs, dependency manifest
Agent/IDE config (.claude/.vscode): none
Commits: 2, all signed off: yes
CI status: 2 red (trivy-scan, CodeQL) — both pre-existing, see below
Verified independently
| Claim | Result |
|---|---|
| Reformat is semantics-preserving | ✅ Parsed base vs head for all 34 modified YAML/JSON files: 31 identical as parsed documents. The only 3 semantic diffs are the intended ones (ci.yml, .pre-commit-config.yaml, pnpm-lock.yaml). This covers the deploy/ manifests where Prettier reflowed flow sequences and switched quote style. |
| Prettier pinned exactly | ✅ 3.9.6 is npm's current latest; lockfile delta is +13/-0 — a root importer with prettier as its sole devDependency, no lockfileVersion change, no other packages. |
| All hooks actually run | ✅ 9 hook ids declared; CI lint job green. |
| Least privilege / supply chain | ✅ ci.yml has top-level permissions: contents: read, inherited by the new lint job. All actions SHA-pinned; no new third-party action. No .claude/ or .vscode/ changes in the diff. |
language: system prerequisite |
✅ CONTRIBUTING.md orders pnpm install before pre-commit install, so the hook's dependency on the workspace binary is documented in the right order. |
The hadolint entry restoration and the -S warning alignment with security-scans.yml both check out: the hook rev (v2.12.0) and the pinned image tag match, and the shellcheck severity is now identical to the CI gate, so the two cannot disagree. The for i → for _ SC2034 fix is correct. Splitting the 267-file reformat into its own commit was the right call.
Both red checks are pre-existing — not introduced here
trivy-scan — I ran trivy against the base tree and the head tree separately. The findings are identical: google.golang.org/grpc v1.82.1 → CVE-2026-84304 (HIGH, fixed in 1.83.1) in both gen/go/go.mod and remote-worker/go.mod, plus zero pnpm findings and zero secrets on both sides. Neither go.mod is touched by this PR. The job uses format: sarif, so nothing prints to stdout and the failure looks opaque in the log.
Why recent PRs were green: the CVE published 2026-09-01T18:19Z and was last modified 2026-09-02T03:47Z, entering trivy's DB after #201's green run at 2026-09-01T19:39Z. This is the same class of breakage as #163 (grpc CVE red-lining the gate) and needs a separate 1.82.1 → 1.83.1 bump.
CodeQL — 1 HIGH "Polynomial regular expression used on uncontrolled data" at harness/src/run-leaf.ts:152. The regex /\/+$/ exists verbatim on main at line 109; Prettier's import expansion shifted it down and changed only the quote style, so CodeQL attributed a pre-existing alert to this PR — exactly the caveat its own summary raises ("Alerts not introduced by this pull request might have been detected because the code changes were too large"). Needs dismissal or its own fix to unblock merge.
Neither is a reason to change anything in this PR, but both block it mechanically.
Minor aside
CONTRIBUTING.md:17 and :96 still point at kagenti/* (clone URL and Code of Conduct) while the repo lives under rossoctl/. Pre-existing and possibly deliberate, so noting rather than flagging.
No must-fix findings. Submitted as a comment rather than an approval only because GitHub does not permit approving your own PR.
Assisted-By: Claude Code
| name: prettier | ||
| entry: pnpm exec prettier --write --ignore-unknown | ||
| language: system | ||
| types_or: [javascript, jsx, ts, tsx, json, yaml, markdown] |
There was a problem hiding this comment.
suggestion — exclude: on the next line duplicates a subset of .prettierignore: it carries pi-fork/, packages/k8s-sandbox/src/gen/ and gen/, but not node_modules/, dist/, *.log or pnpm-lock.yaml.
Behavior is already correct today, because Prettier applies .prettierignore even to paths passed explicitly on the command line — which this PR incidentally demonstrates: pnpm-lock.yaml is in .prettierignore and came through the repo-wide reformat with only the +13/-0 dependency addition, no reflow.
So this is not a bug, just a second list that has to stay in agreement with the first. Since the stated goal here is that make fmt and the hook "cannot disagree by construction", dropping exclude entirely and letting .prettierignore be the single source of truth would extend that same property to the ignore set. Non-blocking.
There was a problem hiding this comment.
Applied in e34b5e9 — exclude is gone, .prettierignore is now the only ignore list.
Agreed on the reasoning, and I checked the mechanism rather than trusting it. Appending deliberately misformatted code to packages/k8s-sandbox/src/gen/sandbox/v1/sandbox.ts and passing that path directly to pnpm exec prettier --write --ignore-unknown:
$ tail -1 packages/k8s-sandbox/src/gen/sandbox/v1/sandbox.ts
const ugly={a:1, b:"x"}
$ pnpm exec prettier --write --ignore-unknown packages/k8s-sandbox/src/gen/sandbox/v1/sandbox.ts; echo "exit=$?"
exit=0
$ tail -1 packages/k8s-sandbox/src/gen/sandbox/v1/sandbox.ts
const ugly={a:1, b:"x"} # untouchedTwo things that confirm it is safe beyond the happy path: the file is left alone even though it was named explicitly, and exit is 0 even though every path passed was ignored — so pre-commit batches consisting entirely of ignored files do not fail the hook.
Coverage-wise .prettierignore is a strict superset of what exclude carried, so nothing widens: pi-fork/, packages/k8s-sandbox/src/gen/ and gen/ are all in it, plus the four it was missing.
make lint still exits 0 with all nine hooks running, and no generated or submodule file is touched. I left a comment at the hook explaining why there is no exclude, so it does not get re-added later as an apparent oversight.
|
Opened #203 to clear the That PR is independent of this one — it branches from Once #203 lands, |
…fy tag `pre-commit run` executed zero hooks: the config failed validation outright, so shellcheck, gitleaks, check-yaml and the whitespace fixers never ran either. The cause was `types_or: [javascript, typescript, ...]` on the prettier hook -- identify has no `typescript` tag (the tags are `ts` and `tsx`), so `.ts` files were never matched even before newer pre-commit turned the unknown tag into a hard InvalidConfigError. Prettier was also unrunnable outside the hook: `.prettierrc` and `.prettierignore` were committed but no `package.json` declared prettier, so `make fmt` died with ERR_PNPM_RECURSIVE_EXEC_NO_PACKAGE. With the config loading again, two more hooks turned out to be broken -- both invisible for as long as nothing ran them: - hadolint never linted anything. `entry` was overridden to a bare `hadolint`, which is the entry of upstream's *system* hook, so `language: docker_image` resolved an image literally named `hadolint` and every run failed before reaching a Dockerfile. All five Dockerfiles are in fact clean. - shellcheck found a real SC2034 in remote-worker/deploy-incluster.sh, outside the `deploy/` tree that the security-scans.yml shellcheck job covers. Changes: - Add a root `package.json` pinning prettier 3.9.6 exactly, so `make fmt` works from a clean `pnpm install`. - Drive the hook from that same pinned binary via `repo: local` rather than pre-commit/mirrors-prettier, which is archived upstream and was pinned to a Prettier 4 pre-release -- two Prettiers that could format differently. One binary for both means `make fmt` and the hook cannot disagree. - Add `ts`/`tsx` (and `jsx`) so TypeScript is actually covered. - Restore hadolint's image reference, pinned to match `rev` (upstream's own entry is untagged, i.e. :latest). - Set shellcheck to `-S warning`, matching the severity security-scans.yml already enforces, so the hook and that job cannot disagree. The scripts are clean at warning+ once the SC2034 above is fixed; the remaining info/style findings include SC1091 false positives from relative `source` paths and are left to a follow-up. - Add a CI `lint` job running `make lint`, since nothing in CI ran the hooks -- which is why a config running zero hooks looked exactly like a green build. - Document `pre-commit install` in CONTRIBUTING.md. `pre-commit run --all-files` now exits 0 with all nine hooks executing. The repo-wide reformat this unblocks lands in the next commit, kept separate so it does not bury this change. Fixes rossoctl#196 Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
Mechanical output of `make fmt`, no hand edits. This is the backlog from the previous commit: the prettier hook aborted config validation, and its `types_or` never named a real TypeScript tag anyway, so `.ts` files were never formatted even when the config loaded. Kept as its own commit so the fix that unblocks it stays reviewable. Mostly quote style (`.prettierrc` sets singleQuote), trailing commas, comment alignment, markdown table padding and YAML flow-sequence reflow. Verified semantics-preserving: - all 31 changed YAML/JSON files parse to documents identical to their previous contents (compared as parsed structures, not text) - `make typecheck` clean - `pnpm -r test`: 851 passed, 15 skipped - `make test-deploy` passes - `pre-commit run --all-files` exits 0 with all nine hooks running Regenerated rather than replayed when rebasing onto main after rossoctl#203 and rossoctl#204, so it also covers the code rossoctl#204 added -- replaying the old diff would have conflicted with it on run-leaf.ts for no benefit, formatting being mechanical. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
…list Review follow-up. `exclude` duplicated a subset of `.prettierignore` (`pi-fork/`, `packages/k8s-sandbox/src/gen/`, `gen/`) and omitted the rest (`node_modules/`, `dist/`, `*.log`, `pnpm-lock.yaml`) -- a second list that had to stay in agreement with the first. Prettier applies `.prettierignore` even to paths passed explicitly on the command line, which is exactly how pre-commit invokes it, so `.prettierignore` alone gives the same result. Verified rather than assumed: appending deliberately misformatted code to a file under `packages/k8s-sandbox/src/gen/` and passing that path directly to `pnpm exec prettier --write --ignore-unknown` leaves it untouched and exits 0 -- including when every path passed is ignored. This extends the property the pinned binary already gives (`make fmt` and the hook cannot disagree about formatting) to the ignore set: they now cannot disagree about what to skip either. `pre-commit run --all-files` still exits 0 with all nine hooks running, and no generated or submodule file is touched. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
e34b5e9 to
0409617
Compare
Rebased onto
|
| Check | Before | Now | Why |
|---|---|---|---|
trivy-scan |
❌ | ✅ | #203 bumped grpc to v1.83.1 |
CodeQL |
❌ | ✅ | #204 replaced the polynomial regex with a linear scan |
lint |
✅ | ✅ | unchanged |
Everything else (check, proto, deploy-scripts, shellcheck, hadolint, DCO, dependency-review, Trivy, codeql) is green too.
The reformat commit was regenerated, not replayed
#204 edited harness/src/run-leaf.ts and its test — the same files the reformat touches — so replaying the old 267-file diff would have conflicted for no benefit. Formatting is mechanical output, so I re-ran make fmt on the rebased tree instead. That also formats the code #204 added, which I had deliberately written in main's double-quote style.
Verified after rebasing: pre-commit run --all-files exits 0 with all nine hooks; make typecheck clean; pnpm -r test 851 passed, 15 skipped; make test-deploy passes; make fmt idempotent. I also confirmed #204's fix survived intact — stripTrailingSlashes still shared by both builders, no polynomial trim regex anywhere in harness/src, and the ReDoS guard test still present.
Commit order is unchanged (fix → reformat → review follow-up), so the messages still read correctly.
One addition worth re-reviewing: .prettierignore now excludes sibling worktrees
+.worktrees/
+.claude/worktrees/Found by running this for real: make fmt is prettier --write ., which walks the directory tree and reads only .prettierignore — not .gitignore. On a checkout with git worktrees under .worktrees/, it therefore reformats files belonging to other branches' checkouts. It did exactly that here, dirtying 13 sibling worktrees with 200–260 modified files each, all of them unrelated to this PR.
Worth flagging that my original acceptance evidence for "make fmt works" missed this: I counted only the main checkout's tracked files, which showed a clean 0 and hid the side effect entirely. The two ignore entries stop it at the source, and prettier --check . now passes scoped to this checkout alone.
This is the third instance of the same theme in this PR — a formatting tool doing something invisible because nothing checked the blast radius.
Assisted-By: Claude (Anthropic AI) noreply@anthropic.com
Fixes #196.
What was broken
pre-commit runexecuted zero hooks — the config failed validation, so shellcheck, gitleaks, check-yaml and the whitespace fixers never ran either. Root cause exactly as reported:types_ornamedtypescript, which is not an identify tag (the tags arets/tsx), so.tsfiles were never matched even before newer pre-commit turned the unknown tag into a hardInvalidConfigError.Getting the config to load then surfaced two more broken hooks, both invisible for as long as nothing ran them:
entrywas overridden to a barehadolint— that is the entry of upstream's system hook — solanguage: docker_imageresolved an image literally namedhadolintand every run failed before reaching a Dockerfile. All five Dockerfiles are in fact clean.remote-worker/deploy-incluster.sh(unused loop var), outside thedeploy/tree that thesecurity-scans.ymlshellcheck job covers.make fmtwas separately broken:.prettierrc/.prettierignorewere committed but nopackage.jsondeclared Prettier, sopnpm exec prettierdied withERR_PNPM_RECURSIVE_EXEC_NO_PACKAGE.Changes
Commit 1 —
fix(pre-commit)package.jsonpinningprettierto3.9.6exactly →make fmtworks from a cleanpnpm install.repo: localdriving that same pinned binary, replacingpre-commit/mirrors-prettier(archived upstream, and pinned to a Prettier 4 pre-release — two Prettiers that could format differently). One binary for both meansmake fmtand the hook cannot disagree. This is the issue's "optional follow-up", taken up because two acceptance criteria jointly require it.ts/tsx(andjsx) added so TypeScript is actually covered.rev(upstream's own entry is untagged, i.e.:latest).-S warning, matching the severitysecurity-scans.ymlalready enforces, so the hook and that job cannot disagree.lintjob runningmake lint. It reuses only actions already pinned in this repo plus preinstalledpipx, so no new third-party action to vet.CONTRIBUTING.mddocumentspre-commit install,make lintandmake fmt.Commit 2 —
style:the repo-wide Prettier reformat (267 files), kept separate so it does not bury the fix.Judgment call worth reviewing
-S warningfor shellcheck is a deliberate choice over fixing ~40info/stylefindings in a PR about pre-commit config. The scripts are clean at warning+ once the SC2034 is fixed, the existing CI gate already uses that exact severity, and the info-level set includes SC1091 false positives (shellcheck runs from the repo root and cannot resolvesource ./lib.sh). Tightening severity is a reasonable follow-up; happy to do it here instead if you prefer.Verification
pre-commit validate-configexits 0exit=0pre-commit run --all-filesruns every hook and passesexit=0, all 9 hooks Passedmake fmtworks from a cleanpnpm installlintjobCONTRIBUTING.mdmentionspre-commit installThe reformat was checked for semantics, not just eyeballed:
deploy/manifests, where Prettier reflowed flow sequences and switched quote style.make typecheckclean.pnpm -r test: 827 passed, 15 skipped.make test-deploypasses..tsfile is now caught and fixed by the hook — the case the old config could never catch.Assisted-By: Claude (Anthropic AI) noreply@anthropic.com