Commit Graph

5 Commits

Author SHA1 Message Date
Julien Gautier 1a20490320 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
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 ✓.
2026-05-14 16:21:10 +02:00
Julien Gautier f776ce732a feat(ci): assert gzip transfer sizes against ADR-0017 budgets
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 ✓
2026-05-14 16:17:35 +02:00
julien 0d31937aeb feat(portal-shell): replace nx-welcome with real shell and home + budgets per ADR-0017 (#74)
CI / check (push) Successful in 2m4s
CI / commits (push) Has been skipped
CI / scan (push) Successful in 2m6s
CI / a11y (push) Successful in 1m14s
CI / perf (push) Successful in 3m3s
## Summary

Cuts the Nx scaffold's `NxWelcome` page (938 LOC of placeholder markup) and lays down the actual portal layout — header, main, footer landmarks plus a skip link — with a real home page and the two RGAA-mandated accessibility statement routes. Bundle budgets in `project.json` are tightened to match ADR-0017.

## What lands

**Layout shell** (`apps/portal-shell/src/app/`):

- `components/header/` — brand link + primary nav landmark stub
- `components/footer/` — accessibility statement links (FR + EN) + version badge
- `app.html` / `app.scss` — flex column with sticky footer + skip link to `#main-content` (WCAG 2.4.1)

**Pages**:

- `pages/home/` — welcome heading + "system status" widget that fetches `/api/health` from the BFF. Doubles as a smoke test of the full SPA → BFF stack: CORS, OTel trace propagation, Pino log correlation. After page load, http://localhost:16686 should show one trace whose root span is the SPA `document_load`, with a child `fetch` span that has its own child `HTTP GET /api/health` BFF span.
- `pages/accessibility/` — single component, content selected by route data (`lang: 'fr' | 'en'`). Mounted at `/accessibility` (en) and `/accessibilite` (fr). v1 carries placeholder copy explicitly framed as "awaiting APF user panel review" — RGAA + ADR-0016 require the routes to exist; the substantive statement (audit grid, gaps list, remediation plan) lands separately.

**Routing & wiring**:

- `app.routes.ts` adds the three routes, all lazy-loaded.
- `app.config.ts` adds `provideHttpClient(withFetch())` so HttpClient delegates to the patched browser `fetch` and the W3C `traceparent` header propagates automatically (per ADR-0012 phase 2).
- `nx-welcome.ts` removed; `app.ts` no longer imports it.

**Bundle budgets** (`apps/portal-shell/project.json`):

| Type                | Limit (raw)  | ADR-0017 (gzip) |
| ------------------- | ------------ | --------------- |
| `initial`           | 1 MB         | ≤ 300 KB        |
| `anyScript`         | 300 KB       | ≤ 100 KB / chunk |
| `anyComponentStyle` | 6 KB         | ≤ 6 KB           |
| `bundle "styles"`   | 150 KB       | ≤ 150 KB         |

`maximumWarning == maximumError` so an overshoot fails the build (matching `type: "error"` in spirit). Angular CLI compares **raw** sizes; ADR-0017 §Confirmation is updated to record the gzip→raw translation and the deferred follow-up that will add a CI check on the actual gzipped transfer size (Angular has no native gzip-mode budget).

## Verified locally

- `pnpm exec nx run-many -t lint test build` → 8 projects green; **12 tests pass** for `portal-shell` across 5 specs (App, Header, Footer, Home, AccessibilityStatement).
- `pnpm nx build portal-shell --configuration=production` → initial bundle **326.99 KB raw / 89.82 KB transfer** (gzip) — well under the ADR-0017 300 KB gzip target. Lazy chunks: home 2.66 KB, accessibility 2.90 KB.
- `pnpm audit` clean.

After `./infra/local/dev.sh up observability` + `pnpm nx serve portal-bff` + `pnpm nx serve portal-shell`, opening http://localhost:4200 shows the new layout, the system-status widget connects to the BFF, and the corresponding trace appears in Jaeger.

## Out of scope (separate PRs)

- **Real RGAA audit content** — APF user-panel review.
- **Design tokens system** in `libs/shared/tokens`.
- **Reusable UI primitives** in `libs/shared/ui`.
- **User-preferences panel** (ADR-0016 §"User-preferences panel").
- **i18n machinery** (`@angular/localize`) — for v1 the FR/EN copy is inlined and route-driven; real i18n is a separate ADR.
- **Env-config** (Angular `environment.ts`) — the hard-coded `http://localhost:3000/api/health` will move there alongside the OTLP endpoint when the env-config PR lands.
- **CI gzip-transfer-size assertion** to complement the raw-size Angular budgets.

## Test plan

- [ ] CI green on this PR.
- [ ] Local: home page renders, system-status widget transitions from "Checking the backend…" to the success state when the BFF is up; falls back to "Backend unreachable" with the BFF stopped.
- [ ] `/accessibility` and `/accessibilite` render the matching language with `<article lang="en|fr">`.
- [ ] Skip link reachable via Tab from address bar; clicking it focuses `#main-content`.
- [ ] Production build size summary roughly matches the figures above (initial total ≈ 90 KB transfer).

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #74
2026-05-10 02:38:42 +02:00
julien f9ed3cf82a chore(ci): skip perf and commits gates on Renovate-authored PRs (#23)
CI / commits (push) Has been skipped
CI / check (push) Successful in 2m20s
CI / scan (push) Failing after 2m34s
CI / a11y (push) Successful in 1m7s
CI / perf (push) Successful in 3m43s
## Summary
Renovate's dep-bump PRs run the full pipeline today (`check`, `scan`, `commits`, `perf`, `a11y`). Two of those gates have near-zero signal on a typical bump and dominate the wall-clock cost:

- **`perf`** — Lighthouse build + 3-iteration median across the critical-routes list. 3-5 min per PR for a metric that is essentially zero on a patch/minor dep bump (the SPA today serves the static placeholder; even with real routes a typical bump stays inside the median noise floor).
- **`commits`** — re-validates commit messages that Renovate generates from a Conventional-Commits-conformant template. Tautological.

Skip both when the PR author is the `apf-portal-bot` Gitea user. The `push` event on `main` still runs the full pipeline post-merge, so any regression caught by `perf` is detected seconds after merge — fast enough to revert.

Net result: Renovate PRs run `check + scan + a11y` only, ≈ 4-5 min faster per PR.

## ADR amendment

ADR-0017 is amended in the same change:
- "Where Lighthouse CI runs" table now distinguishes human PR / bot PR / push to main / scheduled / local.
- New "Pre-merge gating policy: human PRs vs bot PRs" subsection records the rationale and the human-takeover edge case.
- §Confirmation entry for `perf` is reworded to reflect the conditional gate.

## Test plan
- [ ] After merge, the next Renovate-triggered PR (auto-rebase or new bump) shows only `check`, `scan`, `a11y` queued — no `perf`, no `commits`.
- [ ] A human-opened PR (e.g. this one) still queues all 5 gates.
- [ ] On `push` to `main` post-merge, the full pipeline runs including `perf`.

---------

Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #23
2026-05-05 16:12:19 +02:00
Julien Gautier 0e58e32d29 chore: relocate ADRs from decisions/ to docs/decisions/ to consolidate documentation
Move the ADR folder under docs/ alongside the rest of the project
documentation. Convention (flat folder, globally-sequential 4-digit
numbering, tags-based categorization, MADR 4.0.0 format) is unchanged
- only the path moved.

- git mv decisions docs/decisions preserves history for all 18 ADRs +
  README + template (19 files renamed in this commit).
- ADR-0001 amended in-place with a dated note documenting the
  relocation. Status remains 'accepted' - the location detail
  changed, the decision did not.
- All cross-references updated:
  - CLAUDE.md (~17 ADR links + 3 mentions of decisions/ in the Project
    rules section)
  - docs/README.md (now references decisions/ as a sibling under docs/)
  - docs/setup/03-angular-nx-monorepo.md (paths shortened from
    ../../decisions/ to ../decisions/, since setup/ and decisions/ are
    now both inside docs/)
  - docs/decisions/0003 ../CLAUDE.md adjusted to ../../CLAUDE.md
    (one extra level of nesting)
  - docs/decisions/template.md mention of the README path
  - notes/asvs-level-decision-briefing-rssi.md mention of the index

Sanity verified: every ADR link in CLAUDE.md, docs/setup/03, and
docs/decisions/0001 resolves to an existing file. pnpm nx run-many
-t lint passes on 8 projects.
2026-04-30 18:57:59 +02:00