mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 20:21:29 +00:00
c7f3ec6290
Sprint 5 CI follow-up. Pre-fix: Sprint 5 ARCH-001-A (commit 38f1200)
landed 316 Orval-generated files under web/src/api/generated/.
Orval's mutation template emits bare `useMutation(mutationOptions,
queryClient)` calls at every operation site (~100 hits across the
generated tree) because the codegen layer sits one abstraction
below the useTrackedMutation wrapper. The M-009 hard-zero guard
(scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh) treats any
`useMutation(` call outside the wrapper as a regression, so the
Sprint 5 push immediately tripped CI's Frontend Build job with the
generated sites listed verbatim.
The fix mirrors the existing _test.go exclusion: add a grep -v line
for `^web/src/api/generated/` after the existing wrapper-internal
+ test-file exclusions. The contract going forward is composition:
hand-written feature code consumes the generated hook AND wraps the
mutation through useTrackedMutation at the call site (the wrapper's
`mutationFn` argument receives the generated hook's mutationFn).
Hand-editing the generated tree to add the wrapper inline is not an
option — every regenerate would blow it away.
Smuggling-via-codegen risk: the drift guard
(scripts/ci-guards/openapi-codegen-drift.sh) was flipped to a hard
gate in the same Sprint 5 ARCH-001-A commit. It pins the generated
tree against the canonical api/openapi.yaml — any hand-edit shows
up as a regenerate-diff red. So a malicious or accidental
`useMutation` snuck into the generated tree as a hand-edit gets
caught by the drift guard before this M-009 carve-out can apply.
Verified locally:
bash scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh
# M-009 bare-usemutation: clean (wrapper-internal call + test files excluded).
# M-009 informational: useTrackedMutation sites = 66; invalidation surface = 129.
Closes the M-009 leg of the Sprint 5 CI redness.
61 lines
3.1 KiB
Bash
Executable File
61 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh
|
|
#
|
|
# Audit M-009 + M-029 Pass 1 closure:
|
|
#
|
|
# Pre-Bundle-8 the codebase had 56 bare useMutation sites with
|
|
# discretionary invalidation. Bundle 8 shipped the useTrackedMutation
|
|
# wrapper (web/src/hooks/useTrackedMutation.ts) that requires every
|
|
# caller to declare `invalidates: QueryKey[] | 'noop'`. M-029 Pass 1
|
|
# then migrated all 56 sites to the wrapper across 6 batches.
|
|
#
|
|
# This guard pins the contract going forward: every useMutation call
|
|
# in src/ MUST be inside useTrackedMutation.ts (the wrapper itself
|
|
# is the only legitimate caller of useMutation). Any bare useMutation
|
|
# call elsewhere is a regression — adding a new mutation site means
|
|
# going through the wrapper so the invalidates contract is enforced
|
|
# per-site, not by a soft budget guard.
|
|
#
|
|
# If you genuinely need raw useMutation (extremely unlikely — the
|
|
# wrapper supports invalidates: 'noop' for fire-and-forget mutations),
|
|
# update this guard's exclusion list and document the carve-out.
|
|
|
|
set -e
|
|
# Test files (web/src/**/*.test.{ts,tsx}) are excluded so existing
|
|
# useMutation-mocking test patterns and the wrapper's own unit
|
|
# tests don't trip the production guard — symmetric with L-015
|
|
# and L-019 above.
|
|
#
|
|
# Sprint 5 ARCH-001-A carve-out (2026-05-16): web/src/api/generated/**
|
|
# is the Orval-generated React Query layer (TanStack Query client
|
|
# mode, tags-split). Orval's mutation template calls bare useMutation
|
|
# directly — by design, because the generated layer is one
|
|
# abstraction below the wrapper. The Composition pattern is: hand-
|
|
# written feature code consumes the generated hooks AND wraps any
|
|
# mutation through useTrackedMutation at the call site (which routes
|
|
# through the generated hook's mutationFn under the hood). Adding the
|
|
# wrapper inside the generated tree would be overwritten on every
|
|
# regenerate, so the contract is "wrapper at the feature layer, not
|
|
# the codegen layer." The drift guard (openapi-codegen-drift.sh)
|
|
# already pins the generated tree against the canonical openapi.yaml,
|
|
# so this exclusion can't be abused to smuggle in a hand-edit.
|
|
BARE=$(grep -rnE '\buseMutation\(' web/src/ 2>/dev/null \
|
|
| grep -v 'web/src/hooks/useTrackedMutation\.ts' \
|
|
| grep -vE '\.test\.(ts|tsx)(:[0-9]+)?:' \
|
|
| grep -v '^web/src/api/generated/' \
|
|
|| true)
|
|
if [ -n "$BARE" ]; then
|
|
echo "::error::M-009 hard-zero regression: bare useMutation() call(s) outside the wrapper:"
|
|
echo "$BARE"
|
|
echo
|
|
echo "Every mutation must go through useTrackedMutation"
|
|
echo "(web/src/hooks/useTrackedMutation.ts) with explicit"
|
|
echo "invalidates: QueryKey[] | 'noop'. See file header for usage."
|
|
exit 1
|
|
fi
|
|
# Sanity counts (informational, not a gate).
|
|
TRACKED=$(grep -rcE '\buseTrackedMutation\(' web/src/ 2>/dev/null | awk -F: '{s+=$2} END{print s}')
|
|
INVALIDATIONS=$(grep -rcE 'invalidateQueries|setQueryData|removeQueries|invalidates:' web/src/ 2>/dev/null | awk -F: '{s+=$2} END{print s}')
|
|
echo "M-009 bare-usemutation: clean (wrapper-internal call + test files excluded)."
|
|
echo "M-009 informational: useTrackedMutation sites = $TRACKED; invalidation surface = $INVALIDATIONS."
|