feat(ci): assert gzip transfer sizes against ADR-0017 budgets #133

Merged
julien merged 2 commits from feat/ci/gzip-transfer-size-budgets-v2 into main 2026-05-14 16:22:55 +02:00
Owner

Summary

Closes the ADR-0017 follow-up that was sitting on an unpushed local branch (feat/ci/gzip-transfer-size-budgets) for ~5 days, while ADRs 0018–0021 and the auth/admin/security tracks landed. Cherry-picked onto current main, with a small follow-up fix to make the script work against the locale-split build layout introduced by ADR-0019 between the two commits.

What lands

Commit 1 — feat(ci): assert gzip transfer sizes against ADR-0017 budgets

scripts/check-gzip-budgets.mjs — plain-Node, no deps, ~120 LOC at landing. Closes the ADR-0017 §Confirmation gap (Angular CLI's budgets only compare RAW sizes):

  • Parses Angular's emitted index.html to separate initial assets (anything referenced via src= / href=) from lazy chunks (the rest).
  • Gzips every JS / CSS file at level 9 — what most HTTP servers serve for static assets — and reports a per-file + per-bucket table.
  • Asserts the ADR-0017 thresholds (initial JS total ≤ 300 KB, any lazy chunk ≤ 100 KB, total CSS ≤ 150 KB) and exits non-zero on any breach.
  • Wired through:
    • pnpm ci:gzip-budgets invokes the script with the default dist path.
    • ci:perf chains build → gzip check → Lighthouse, so a budget breach short-circuits before Lighthouse even runs.

ADR-0017 §Confirmation also updated to point at the script instead of describing it as a "future follow-up".

Commit 2 — fix(ci): adapt gzip-budget check for the @angular/localize multi-bundle layout

The original commit predated PR #91 / ADR-0019, which made @angular/localize emit one self-contained bundle per locale under dist/apps/portal-shell/browser/<locale>/. The script looked for index.html directly under the dist root and failed with ENOENT on every build since.

Fix:

  • Auto-detects layout. If dist/index.html exists → flat mode (unchanged behaviour, kept for single-locale apps like portal-admin). Otherwise enumerates immediate subdirectories with their own index.html and runs the check per locale.
  • Budget thresholds apply per locale — each bundle is what the user's browser actually downloads. Aggregating across locales would understate the worst case.
  • Violations across locales are collected + reported together with the locale tag prefixed so a single CI run surfaces every breach.
  • ADR-0017 §Confirmation amendment expanded to spell out the per-locale check + cite ADR-0019.

Test plan

  • node --check scripts/check-gzip-budgets.mjs — syntax OK.
  • pnpm ci:gzip-budgets against the current production build:
    • 2 locale bundles detected (en, fr).
    • Initial JS total: ~141 KB / 300 KB budget ✓ (each locale).
    • CSS total: 4.62 KB / 150 KB budget ✓ (each locale).
    • Largest lazy chunk: 1.55 KB / 100 KB per-chunk budget ✓.
  • No conflict on package.json despite 11 PRs touching it since the branch base — cherry-pick auto-merged cleanly.
  • CI: pnpm ci:perf end-to-end (build → gzip-budgets → Lighthouse). Validates on the runner.

Notes for the reviewer

  • The script lives at the repo root under scripts/ rather than as an Nx target — it's a pure CI helper, not project-scoped. Matches the existing ci:audit / ci:commits pattern in package.json.
  • No dependencies added. Uses node:fs/promises and node:zlib.
  • The original branch (feat/ci/gzip-transfer-size-budgets) is preserved in local-only state for paranoia; once this PR merges, it can be git branch -D'd.
## Summary Closes the ADR-0017 follow-up that was sitting on an unpushed local branch (`feat/ci/gzip-transfer-size-budgets`) for ~5 days, while ADRs 0018–0021 and the auth/admin/security tracks landed. Cherry-picked onto current `main`, with a small follow-up fix to make the script work against the locale-split build layout introduced by ADR-0019 between the two commits. ## What lands ### Commit 1 — `feat(ci): assert gzip transfer sizes against ADR-0017 budgets` [`scripts/check-gzip-budgets.mjs`](scripts/check-gzip-budgets.mjs) — plain-Node, no deps, ~120 LOC at landing. Closes the ADR-0017 §Confirmation gap (Angular CLI's `budgets` only compare RAW sizes): - Parses Angular's emitted `index.html` to separate **initial** assets (anything referenced via `src=` / `href=`) from **lazy** chunks (the rest). - Gzips every JS / CSS file at level 9 — what most HTTP servers serve for static assets — and reports a per-file + per-bucket table. - Asserts the [ADR-0017](docs/decisions/0017-performance-budgets-lighthouse-ci.md) thresholds (`initial JS total ≤ 300 KB`, `any lazy chunk ≤ 100 KB`, `total CSS ≤ 150 KB`) and exits non-zero on any breach. - Wired through: - `pnpm ci:gzip-budgets` invokes the script with the default dist path. - `ci:perf` chains build → gzip check → Lighthouse, so a budget breach short-circuits before Lighthouse even runs. ADR-0017 §Confirmation also updated to point at the script instead of describing it as a "future follow-up". ### Commit 2 — `fix(ci): adapt gzip-budget check for the @angular/localize multi-bundle layout` The original commit predated PR #91 / [ADR-0019](docs/decisions/0019-internationalisation-angular-localize.md), which made `@angular/localize` emit one self-contained bundle per locale under `dist/apps/portal-shell/browser/<locale>/`. The script looked for `index.html` directly under the dist root and failed with ENOENT on every build since. Fix: - Auto-detects layout. If `dist/index.html` exists → flat mode (unchanged behaviour, kept for single-locale apps like `portal-admin`). Otherwise enumerates immediate subdirectories with their own `index.html` and runs the check **per locale**. - Budget thresholds apply per locale — each bundle is what the user's browser actually downloads. Aggregating across locales would understate the worst case. - Violations across locales are collected + reported together with the locale tag prefixed so a single CI run surfaces every breach. - ADR-0017 §Confirmation amendment expanded to spell out the per-locale check + cite ADR-0019. ## Test plan - [x] `node --check scripts/check-gzip-budgets.mjs` — syntax OK. - [x] `pnpm ci:gzip-budgets` against the current production build: - 2 locale bundles detected (`en`, `fr`). - Initial JS total: ~141 KB / 300 KB budget ✓ (each locale). - CSS total: 4.62 KB / 150 KB budget ✓ (each locale). - Largest lazy chunk: 1.55 KB / 100 KB per-chunk budget ✓. - [x] No conflict on `package.json` despite 11 PRs touching it since the branch base — cherry-pick auto-merged cleanly. - [ ] CI: `pnpm ci:perf` end-to-end (build → gzip-budgets → Lighthouse). Validates on the runner. ## Notes for the reviewer - The script lives at the repo root under `scripts/` rather than as an Nx target — it's a pure CI helper, not project-scoped. Matches the existing `ci:audit` / `ci:commits` pattern in `package.json`. - No dependencies added. Uses `node:fs/promises` and `node:zlib`. - The original branch (`feat/ci/gzip-transfer-size-budgets`) is preserved in local-only state for paranoia; once this PR merges, it can be `git branch -D`'d.
julien added 2 commits 2026-05-14 16:22:37 +02:00
Angular CLI's `budgets` only compare RAW sizes — there's no native
gzip-mode budget. ADR-0017 specifies thresholds in gzip-transfer
terms (initial bundle ≤ 300 KB gzip, any lazy chunk ≤ 100 KB gzip,
total stylesheet ≤ 150 KB gzip), so the project.json values today
are an approximate raw-size translation. The follow-up flagged in
ADR-0017's confirmation list — a CI check that asserts the actual
gzipped transfer size — lands here.

`scripts/check-gzip-budgets.mjs`:

- Parses Angular's emitted index.html to separate initial assets
  (anything referenced via src=/href=) from lazy chunks (the rest).
- Gzips every JS / CSS file at level 9 — what most HTTP servers
  serve for static assets — and reports a per-file + per-bucket
  size table.
- Compares against the ADR-0017 budgets and exits non-zero on any
  breach. Plain Node, no deps; ~120 lines.

Wired through:

- `pnpm ci:gzip-budgets` invokes the script with the default dist
  path (`dist/apps/portal-shell/browser`).
- `ci:perf` chains build → gzip check → Lighthouse, so a
  budget breach short-circuits before Lighthouse even runs (saves
  several minutes on a failing PR).

ADR-0017 §Confirmation updated: the previous "future follow-up
will add a CI check" line is replaced by a description of how the
script works, with a link to it.

Verified locally on the production build:
- Initial JS total: 92.88 KB / 300 KB budget ✓
- Lazy chunks largest: 1.38 KB / 100 KB per-chunk budget ✓
- CSS total: 3.35 KB / 150 KB budget ✓
fix(ci): adapt gzip-budget check for the @angular/localize multi-bundle layout
CI / commits (pull_request) Successful in 2m27s
CI / check (pull_request) Successful in 2m33s
CI / scan (pull_request) Successful in 2m42s
CI / a11y (pull_request) Successful in 1m58s
CI / perf (pull_request) Successful in 5m37s
1a20490320
The original commit on this branch (9ed74ca, then cherry-picked as
f776ce7) predated ADR-0019 / PR #91, which switched the portal-shell
build to `@angular/localize` and started emitting one self-contained
bundle per locale under `dist/apps/portal-shell/browser/<locale>/`.
The script looked for `index.html` directly under the dist root and
failed with ENOENT on every modern build.

Fix

- `scripts/check-gzip-budgets.mjs`:
  - Auto-detects layout: if `dist/index.html` exists → flat mode
    (unchanged behaviour). Otherwise enumerate immediate
    subdirectories with their own `index.html` and run the check
    per locale.
  - Budget thresholds apply *per locale* — each bundle is what the
    user's browser actually downloads. Aggregating across locales
    would understate the worst case.
  - Violations across locales are collected and reported together
    so a single CI run surfaces every breach; the locale tag is
    prefixed to each violation message for clarity.

- `docs/decisions/0017-...md` §Confirmation: spell out the per-locale
  check + cite ADR-0019 so a future reader of either ADR sees the
  connection.

Verified locally on the production build:
- 2 locale bundles detected (en, fr).
- Initial JS total: ~141 KB / 300 KB budget ✓ (each locale).
- CSS total: 4.62 KB / 150 KB budget ✓ (each locale).
- Largest lazy chunk: 1.55 KB / 100 KB per-chunk budget ✓.
julien merged commit aea395ae65 into main 2026-05-14 16:22:55 +02:00
julien deleted branch feat/ci/gzip-transfer-size-budgets-v2 2026-05-14 16:22:56 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: julien/apf_portal#133