ci(lint): nx cache + affected, plus the escape-hatch ratchet#2661
ci(lint): nx cache + affected, plus the escape-hatch ratchet#2661jordan-simonovski wants to merge 12 commits into
Conversation
The lint CI job rebuilt and re-linted every package from scratch on each run with no cross-run caching, taking 3-4 minutes. Make the ci:build and ci:lint targets nx-cacheable and wire ci:lint's dependsOn to ^ci:build in nx.json, so a downstream project's tsc/lint is correctly invalidated when an upstream package such as common-utils changes (no false cache hits). Track root tsconfig.base.json via sharedGlobals, and declare the common-utils build output so it can be replayed from cache. In the workflow, persist .nx/cache via actions/cache, checkout with full history, run nx affected -t ci:lint against the PR base on pull_request, and keep a full nx run-many on push. make ci-lint / make ci-build are unchanged and still work locally.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🔴 Tier 4 — CriticalTouches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD. Why this tier:
Review process: Deep review from a domain expert. Synchronous walkthrough may be required. Stats
|
|
Placeholder |
Greptile SummaryThis PR wires up nx computation caching and
Confidence Score: 5/5Safe to merge — this is a pure CI infrastructure change with no production code affected. All changes are confined to CI configuration, the Makefile, and a new standalone Node script. The nx cache wiring correctly threads inputs/outputs and dependsOn so cache hits are accurate. The ratchet logic is straightforward and well-guarded. The two findings are cosmetic output quality issues that do not affect correctness. No files require special attention; .github/workflows/main.yml has a minor note about the ratchet being skippable on lint failure. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Push / PR event] --> B{event type?}
B -- pull_request --> C[nx affected -t ci:lint
--base=origin/BASE_REF]
B -- push to main/v1 --> D[nx run-many -t ci:lint]
C --> E{affected projects}
D --> F[all projects]
E --> G[For each affected project:
run ci:build deps via dependsOn
then run ci:lint]
F --> H[For each project:
run ci:build deps via dependsOn
then run ci:lint]
G --> I{nx cache hit?}
H --> I
I -- yes --> J[replay cached result]
I -- no --> K[run lint + tsc
cache result to .nx/cache]
J --> L[Escape-hatch ratchet
node scripts/ci/ratchet.mjs]
K --> L
L --> M{counts vs baseline}
M -- above baseline --> N[exit 1 — CI fails]
M -- below baseline --> O[warn + exit 0]
M -- at baseline --> P[exit 0 — CI passes]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Push / PR event] --> B{event type?}
B -- pull_request --> C[nx affected -t ci:lint
--base=origin/BASE_REF]
B -- push to main/v1 --> D[nx run-many -t ci:lint]
C --> E{affected projects}
D --> F[all projects]
E --> G[For each affected project:
run ci:build deps via dependsOn
then run ci:lint]
F --> H[For each project:
run ci:build deps via dependsOn
then run ci:lint]
G --> I{nx cache hit?}
H --> I
I -- yes --> J[replay cached result]
I -- no --> K[run lint + tsc
cache result to .nx/cache]
J --> L[Escape-hatch ratchet
node scripts/ci/ratchet.mjs]
K --> L
L --> M{counts vs baseline}
M -- above baseline --> N[exit 1 — CI fails]
M -- below baseline --> O[warn + exit 0]
M -- at baseline --> P[exit 0 — CI passes]
Reviews (10): Last reviewed commit: "Merge branch 'main' into jordansimonovsk..." | Re-trigger Greptile |
E2E Test Results✅ All tests passed • 241 passed • 1 skipped • 838s
Tests ran across 4 shards in parallel. |
Adds scripts/ci/ratchet.mjs, which counts tracked escape hatches (as any, @ts-ignore, eslint-disable) per package and compares them against a committed baseline. Counts above baseline fail CI (a new occurrence was added); counts below baseline also fail, prompting the contributor to run yarn ratchet:update so the improvement is locked in. This two-way-strict behaviour keeps the baseline honest and stops the escape-hatch count from silently creeping up. The check is wired into the ci-lint Makefile target so the existing lint CI job enforces it, and yarn ratchet / yarn ratchet:update scripts are exposed for local use.
Deep ReviewScope: CI lint pipeline — Environment caveat: ✅ No critical issues found. 🟡 P2 — recommended
🔵 P3 nitpicks (2)
Reviewers (6): correctness, reliability, security, maintainability, testing, project-standards. Testing gaps: |
…-nx-cache-affected # Conflicts: # nx.json
Merging main raised the tracked escape-hatch counts above the frozen baseline (notably common-utils as-any 69->116), all from code that landed on main independently of this PR. Regenerate the baseline with `yarn ratchet:update` to lock in current counts, which also captures the reductions in app and cli.
Two related lint-pipeline changes, combined into one PR so the CI lint job stays coherent (they'd otherwise conflict at merge and the ratchet could be silently dropped).
What changed
ci:lint/ci:buildare now cacheable innx.json; thelintjob persists.nx/cache, runsnx affected -t ci:linton PRs (only changed projects) and the fullnx run-manyon push. Locally, a warmci:lintdrops from ~41s to ~0.4s.scripts/ci/ratchet.mjscountsas any/@ts-ignore/eslint-disableper package against a committed baseline; above-baseline fails CI, below-baseline warns (non-fatal). Exposed asyarn ratchet/yarn ratchet:update.node scripts/ci/ratchet.mjs), unconditionally — because the lint job now callsnxdirectly rather thanmake ci-lint, and the ratchet is a whole-repo check that must not be gated bynx affected.Key decisions
ci:lint's inputs include^production, so editingpackages/common-utilsinvalidatespackages/app's cached lint (app'stsctypechecks against it). Verified. The root.prettierrc/.prettierignoreare insharedGlobalsso a root Prettier change busts every project's cached lint too.mainor block other PRs when two count-lowering PRs merge close together. Above-baseline still hard-fails — that's the gate that matters.Impact
affectedon PRs, fullrun-manyon push (nothing skipped on trunk).make ci-lint/make ci-buildunchanged for local use.Implementation detail
ci:lintdependsOn: ["^ci:build"]so upstream builds run first; nx hashes inputs internally, so a restored cache only replays on an input-hash match. Cache keynx-${{ runner.os }}-${{ github.sha }}+ prefix restore.grepexit-2 on a missing/renamed package dir is guarded withexistsSync(returns 0, no crash). Verified: above-baseline exits 1, below-baseline warns + exits 0, at-baseline "ok".