Skip to content

Code review โ€” deploy-time prod web config injection (GAC-48) โ€‹

  • Branch: ci/gac-48-web-env-inject โ†’ master
  • Date: 2026-07-14
  • Scope: 1 commit, 2 files (+85) โ€” new scripts/inject-web-env.mjs, deploy.yml web-job wiring
  • Reviewer: code-reviewer agent over git diff master...HEAD, synthesized.

Deterministic gate โ€‹

CheckResult
nx lint webโœ“
nx test webโœ“
coverage gate (nx test web --configuration=coverage)โœ“
nx build webโœ“ (run twice โ€” clean tree, and with injected values)
nx affected -t lint test build --base=masterโœ“ no projects affected (CI/scripts-only diff)
Go gatesn/a โ€” apps/api untouched

What's in the batch โ€‹

The code half of GAC-48 (tier C โ€” web build-time values). environment.prod.ts deliberately ships empty supabaseUrl / supabaseAnonKey / apiUrl; nothing filled them at deploy time, so the web job produced an unconfigured bundle. Now:

  • scripts/inject-web-env.mjs โ€” reads DISPATCH_SUPABASE_URL, DISPATCH_SUPABASE_ANON_KEY, DISPATCH_API_URL; validates (https-only URLs, JWT or sb_publishable_โ€ฆ key shape); strips trailing slashes (consumers do apiUrl + path joins); rewrites the three literals in environment.prod.ts idempotently with an exactly-one-match guard; exits 1 loudly if anything is missing or malformed so a half-configured deploy cannot ship.
  • deploy.yml โ€” web job runs the script between bun install and bunx nx build web, fed from GitHub Actions repository variables (not secrets โ€” all three values are publishable and ship in the browser bundle by design); header comment documents them.

End-to-end verification โ€‹

Missing vars โ†’ exit 1 listing all three ยท http:// URL โ†’ rejected ยท truncated anon key โ†’ rejected ยท valid values โ†’ exactly 3 lines changed, trailing slash stripped ยท re-run with different values โ†’ clean overwrite (idempotent) ยท nx build web (production, fileReplacements confirmed active via defaultConfiguration) โ†’ injected values present in dist/apps/web/browser/chunk-*.js ยท env file restored afterwards.

Findings โ€‹

๐Ÿ”ด Blocking โ€” none. โ€‹

๐ŸŸก Should-fix โ€” none. โ€‹

๐ŸŸข Nit (resolved in-branch) โ€‹

  1. ANON_KEY regex looser than its comment claimed โ€” accepted any alnum string, so a truncated copy-paste key would pass. FIXED โ€” now requires three dot-separated base64url segments (JWT) or sb_publishable_ + โ‰ฅ8 chars; truncated-key rejection re-verified.

Checked and clean โ€‹

$-in-replacement / quote-injection into the TS literal (both excluded by validation char classes, validation runs before splice); fail-loud ordering (validation precedes any file write; no continue-on-error on the step); GH Actions script-injection surface (values passed via env:, never interpolated into run:); job ordering unchanged (web needs supabase only).

Note: the VS Code Actions extension warns "Context access might be invalid" on the three vars.* references โ€” expected until the repository variables are created (GAC-48 ops half).

Verdict โ€‹

Green. Deterministic gate passed, no blocking or should-fix findings, the one nit fixed and re-verified. Remaining GAC-48 work is operational (create the six Actions secrets + three repository variables, set Fly secrets, run deploy.yml, tag phase-1).