f4d5b673689d5434173e33344919c33ae76b9369
15 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e3a495ab46 |
docs(development): refresh after phase-3a + add topology / trace diagrams (#160)
## Summary `docs/development.md` drifted as `portal-admin` shipped, the docs site landed, Prisma stayed pinned at 6.x, and a fair chunk of the phase-2 "to come" roadmap quietly turned into "shipped". Surgical refresh of the affected sections + two Mermaid diagrams where prose alone wasn't carrying the cognitive load. ## What changed ### Section 1 — Repo layout - `apps/portal-admin/` + `apps/portal-admin-e2e/` added (both exist on `main` since #134, were never reflected in the tree). - `prisma.config.ts` removed (phantom — Prisma 6.x doesn't ship one). The "Prisma 7" tag corrected to "Prisma 6.x" with the [ADR-0006](docs/decisions/0006-persistence-postgresql-prisma.md) pin reference. - `docs/index.md`, `docs/architecture.md`, `docs/.vitepress/` added. - New workflows listed: `docs-site.yml`, `renovate.yml`. ### Section 3 — Initial setup, new diagram Mermaid `flowchart` of the local-dev topology. Host-side dev servers (`portal-shell:4200`, `portal-admin:4300`, `portal-bff:3000`, `docs:5173`) ↔ Compose containers (Postgres, Redis, OTel) ↔ viewer profiles (Jaeger, pgweb). Replaces a ports/services context that was scattered across §3 and §5. ### Section 4 — Daily commands - `portal-admin` added to serve / test / generate examples. - Single-test-file recipe corrected: Nx's vitest executor rejects `--testFile` (we hit this empirically while debugging the sidebar spec for #151). The new wording recommends positional path for Vitest, `--testPathPattern=…` for Jest. - **New "Documentation site" subsection** with the three commands (`docs:dev`, `docs:build`, `docs:preview`), plus the recipe for adding an ADR. ### Section 5 — Observability, new diagram Mermaid `sequenceDiagram` showing how a click becomes a trace: browser `user_interaction` span → `traceparent` header → BFF span → Pino log line with matching `trace_id` → OTLP batch → Jaeger UI. Anchors the prose's "trace_id is the correlation point" rule with a visual. ### Section 6 — Renovate New subsection **"Transitive vulnerabilities — `pnpm.overrides`"**. Documents the pattern from #159 so the next contributor hitting a transitive vuln (Renovate silent, dashboard empty) has the playbook on hand without re-discovering it. ### Section 9 — Sections to come Restructured into two groups: - **Code shipped — doc to write** (Auth dev-loop, session inspection, MFA step-up debugging, admin surface walkthroughs, audit-log workflow, downstream API recipe, OpenAPI/Scalar workflow, capabilities-driven SPA UX). The code is on `main`; only the prose is missing. Each entry now cites the PR(s) that landed the implementation. - **Not yet** (component patterns library, a11y testing workflow, perf debugging, release workflow, GitLab migration runbook). Both code and doc still pending. ## Notes for the reviewer - **Why diagrams now, not at the next chantier?** Two pieces of context were genuinely easier to grasp visually than as prose lists: the local-dev topology (which port goes where), and the trace-correlation flow. The other sections (Renovate, conventional commits, CI gates) already read well as tables — adding diagrams there would be ornament, not signal. - **Why two diagrams rather than ten?** Per the user instruction "use diagrams when useful" — restraint applies. The two added are the ones that *replace* explanatory prose rather than add to it. - **Why didn't I rewrite the "to come" roadmap from scratch?** The phase mapping was useful intent — kept the same structure, just moved entries between the two columns as code-shipping unlocked them. Future re-shuffles stay cheap. - **The doc still has phase-1 framing in places** (e.g. the "this doc starts as a phase-1 reference" paragraph in §9). I left it — promoting the doc to "phase-3a reference" is a larger editorial pass than this refresh deserves; the new diagrams + section-9 split already do the practical work. - **Open questions deferred to a future pass**: the §2 "Prerequisites" table doesn't mention the docs site (`pnpm docs:dev` adds nothing to the prereqs list — just Node + pnpm, both already required). Leaving the table as is. ## Test plan - [x] `rm -rf docs/.vitepress/{cache,dist} && pnpm docs:build` — clean, 6.3 s. - [x] Mermaid renders inside `docs/.vitepress/dist/development.html` — both new diagrams produce `class="mermaid"` markers. `grep -c 'class="mermaid"\|<svg' development.html` → **2**. - [ ] Visual smoke: `pnpm docs:dev`, navigate to `/development`, confirm both diagrams render (topology with port labels readable, sequence diagram with the trace flow). Toggle dark mode, confirm diagrams flip theme. - [ ] Spot-check the "Code shipped — doc to write" table — for any contributor reading this PR, do the PR-number citations match their memory of when each chantier landed? (`auth ≈ ADR-0009 series`, `admin ≈ #127, #128, #134, #136, #140–142`, `downstream ≈ #137–139`, `OpenAPI ≈ #143`, `capabilities ≈ #151`). ## What's next The two largest "doc to write" entries (Auth dev-loop, Audit-log inspection workflow) are good candidates for the next docs chantier — both have shipped code, RSSI-relevant content, and would benefit from a guided walkthrough. Not blocking anything; pick when the team has bandwidth. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #160 |
||
|
|
7579b25dfe |
feat(docs): vitepress site for docs/, mermaid rendering, ci build workflow (#154)
## Summary
Implementation of [ADR-0022](docs/decisions/0022-docs-site-vitepress.md). Stands up the static documentation site that renders `docs/**/*.md` (architecture diagrams, daily-dev guide, ADRs, onboarding) via **VitePress + `vitepress-plugin-mermaid`**, behind a Gitea Actions build gate.
Local dev: `pnpm docs:dev`. Full build: `pnpm docs:build` (~9 s, output in `docs/.vitepress/dist/`).
## What lands
### Dependencies
`vitepress 1.6.4`, `vitepress-plugin-mermaid 2.0.17`, `mermaid 11.15.0` — workspace devDependencies. No runtime impact on `portal-shell` / `portal-admin` / `portal-bff`.
### [`docs/.vitepress/config.mts`](docs/.vitepress/config.mts)
The single source of truth for the site. Highlights:
- **`srcExclude`** drops `docs/README.md` (git/IDE-only index per ADR-0022's option A) and `docs/decisions/template.md` (authoring scaffold).
- **`rewrites`** maps `decisions/README.md` → `decisions/index.md` so `/decisions/` resolves to the curated tag-grouped landing while the source filename stays git-conventional.
- **`ignoreDeadLinks`** skips:
- `localhost:*` URLs (Jaeger, OTLP — only resolve in a live dev session),
- cross-repo references (`../CLAUDE`, `../../apps/**`, `../../infra/**`, `../../notes/**`) — intentional from git/IDE consumers; not the site's job to render them,
- excluded targets (`./template`, `./README`) — file exists in the repo, just not in the site.
- **Auto-sidebar for `/decisions/`** — `adrSidebarItems()` walks `docs/decisions/00*-*.md` and emits sorted `ADR-NNNN — title` entries. Adding an ADR is a single-file change, no `config.mts` edit.
- **Hand-curated top-level nav** (Development, Architecture, Decisions, Onboarding).
- **Mermaid via `withMermaid()`** with `securityLevel: 'strict'` so diagrams can't inject arbitrary HTML.
### [`docs/index.md`](docs/index.md)
VitePress Hero landing with four feature cards (Architecture, Decisions, Development, Onboarding).
### [`docs/development.md`](docs/development.md) — two surgical fixes
- Line ~5: `[setup/](setup/)` → `[setup/01-wsl-terminal-setup.md](setup/01-wsl-terminal-setup.md)`. Folder-style links don't resolve cleanly under `cleanUrls: true`; pointing at the first onboarding page is both correct and useful.
- Line 330: wrap `${{ github.* }}` in `<code v-pre>…</code>`. VitePress runs every Markdown file through the Vue template compiler, which sees the inline `{{ … }}` as an interpolation. `v-pre` keeps the literal text intact. The rest of the source is unaffected.
### [`package.json`](package.json)
Three new scripts:
```
docs:dev → vitepress dev docs
docs:build → vitepress build docs
docs:preview → vitepress preview docs
```
Pure pnpm scripts, no Nx project — the site has no cross-project dependency graph to track.
### [`.gitea/workflows/docs-site.yml`](.gitea/workflows/docs-site.yml)
Triggers on push to `main` and on PR, scoped by `paths:` to `docs/**`, `package.json`, `pnpm-lock.yaml`, and the workflow itself. Three steps:
1. `pnpm install --frozen-lockfile`
2. `pnpm docs:build`
3. Regression fence: `grep` ADR-0009's rendered HTML for `class="mermaid"` or `<svg>` so a silent Mermaid-plugin breakage on a major upgrade fails the workflow rather than ship a site with raw code blocks where diagrams should be.
4. On push only: upload `docs/.vitepress/dist/` as a `docs-site` artifact (30-day retention). The actual rsync to the static host lands when the future infrastructure ADR locks the deployment target.
### [`.gitignore`](.gitignore)
Excludes `docs/.vitepress/{cache,dist}/` so local builds don't leak into commits.
## Notes for the reviewer
- **Why `config.mts` and not `config.ts`?** VitePress is ESM-only, and `vitepress-plugin-mermaid` follows. Vite loads `.ts` config files via its CJS bundler in this workspace's setup and chokes on the ESM imports. `.mts` flips the loader to ESM and the build succeeds. Same pattern is used elsewhere in the workspace (`jest.config.cts`, app `vite.config.mts`).
- **Why no Nx project (`docs/project.json`)?** The doc site has no Nx-trackable dependencies (it consumes `.md` files, not TypeScript projects). Putting it in the Nx graph adds ceremony with no caching benefit — VitePress's incremental rebuilds are sub-second already, and the site never has cross-project `affected` semantics. Pure pnpm scripts keep the surface small.
- **Why the regression fence on Mermaid?** ADR-0022 §"Confirmation" promises it. The plugin is a community dep (sub-1.0 wrapper around the official Mermaid renderer); a major upgrade or a Mermaid runtime change could leave fenced ` ```mermaid ` blocks rendered as raw code without anyone noticing — until an RSSI clicks ADR-0009 and sees no diagram. Cheap grep gate, real signal.
- **Why upload as artifact, not deploy?** Per [ADR-0022](docs/decisions/0022-docs-site-vitepress.md) §"Deployment & CI": the host (`docs.portal.apf.fr` or a sub-path) is provisional. Locking an rsync target now would couple this PR to a not-yet-made infra decision. Artifact upload is the staging mechanism — manual drop on the host until the infrastructure ADR formalises the target.
- **Why `ignoreDeadLinks` rather than fixing every cross-repo reference?** The cross-repo links are genuinely useful from a git/IDE perspective (where the docs/ markdown is browsed alongside the rest of the codebase). Rewriting them to `https://git.unespace.com/julien/apf_portal/src/branch/main/…` would make them work on the site but lose the IDE quick-jump. Skipping at site-build time is the right trade-off — the site reader gets a graceful "link doesn't exist here" if they click, the IDE reader gets a working jump.
## Test plan
- [x] `pnpm docs:build` succeeds in ~9 s. Output at `docs/.vitepress/dist/` contains an `index.html`, every ADR, the development guide, the architecture diagrams, and the three setup pages.
- [x] Mermaid renders: `grep 'class="mermaid"' docs/.vitepress/dist/decisions/0009-…html` returns a match.
- [x] `pnpm exec nx run-many -t format:check lint test build` for the 6 main projects — 18/18 tasks green, no Nx regression from the new top-level config.
- [ ] **Manual smoke**: `pnpm docs:dev`, open `http://localhost:5173`, walk through:
- Landing renders Hero + 4 feature cards.
- Search box returns hits for "audit", "MFA", "OBO".
- `/decisions/0009-…` renders the OIDC sequence diagram (Mermaid SVG, not raw text).
- `/decisions/0010-…` ERD or `/architecture` C4 diagrams likewise.
- Dark-mode toggle flips diagrams to dark theme without page reload.
- Sidebar shows the 22 ADRs auto-listed under `/decisions/`.
- The "Decisions" curated index at `/decisions/` lists ADRs by tag (no regression on the source markdown).
## What's next
Once the deployment target is fixed (future infra ADR), wire the rsync step into the workflow — that lands as a small follow-up PR. Until then the artifact carries the bundle.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #154
|
||
|
|
922a44bb50 |
chore(ci): auto-merge low-risk renovate updates (#77)
## Summary After ~10 days of clean Renovate track record (~30 PRs merged without regression beyond the TS/ESLint/webpack-cli majors that the dashboard-approval rule now catches), enable auto-merge on the lowest-risk update types. CI is the gate — `check` / `scan` / `a11y` going red leaves the PR open for manual triage. | Update type | Pre-PR | Post-PR | | - | - | - | | **patch** | manual review | **auto-merge if CI green** | | **pin** (rolling-tag → fixed-version pin) | manual | **auto-merge if CI green** | | **digest** (image digest pin refresh) | manual | **auto-merge if CI green** | | **lockFileMaintenance** (weekly transitive refresh) | manual | **auto-merge if CI green** | | **minor** | manual review (unchanged) | manual review | | **major** | dashboard approval (unchanged) | dashboard approval | `automergeStrategy: "squash"` matches our trunk-based squash-merge convention. `automergeType: "pr"` keeps the PR + CI run as the audit trail (vs branch-direct push), and Gitea auto-merges the PR once green via the bot's existing `repo:write` permission. ## Doc updates - `renovate.json` `dependencyDashboardHeader` — the "Open" row now reflects the new reality: mostly minors, with red patches surfacing briefly when CI fails. - `docs/development.md` §"Reviewing Renovate PRs" gains a bullet describing the auto-merge for contributors landing on the project later. ## Test plan - [ ] CI green on this PR. - [ ] After merge, the next patch-level Renovate run produces a PR that auto-squashes into `main` once CI clears (visible in the merged log; no human action required). - [ ] A patch with red CI stays open in "Open" with the `dependencies` label. - [ ] Minor / major Renovate PRs continue to require human merge. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #77 |
||
|
|
e2dd2e4dd8 |
fix(portal-shell): use .postcssrc.json so tailwind utilities are emitted (#75)
## Summary The portal-shell scaffold shipped a `postcss.config.js`, but `@angular/build` (Angular 17+ esbuild pipeline) does **not** load that file format — it only reads `.postcssrc.json`. Result: the `@tailwindcss/postcss` plugin was never registered, Tailwind ran with no source files visible, generated only its `@theme` block, and dropped every utility class on the floor. The page therefore rendered unstyled — black text on white — even though `nx build` reported success and shipped a 23 KB `styles*.css`. Rename / re-encode the config to `.postcssrc.json`. Same plugin, same options, just the file format Angular's esbuild adapter actually reads. ## Verification | | Before | After | | - | - | - | | Class selectors in `dist/apps/portal-shell/browser/styles*.css` | **0** | **75** (production) | | `.text-3xl`, `.bg-amber-50`, `.font-semibold` etc. emitted | ❌ | ✓ | | Pages render with intended Tailwind layout | ❌ | ✓ | ```bash pnpm exec nx build portal-shell --configuration=production grep -oE '\.[a-zA-Z][a-zA-Z0-9_-]*' dist/apps/portal-shell/browser/styles*.css | sort -u | wc -l # 75 ``` ## Doc update `docs/development.md` repo-layout walkthrough now reads `.postcssrc.json` and explicitly calls out that `postcss.config.js` is ignored by `@angular/build` — so a future contributor reaching for the legacy filename gets a hint instead of a silent failure. ## Test plan - [ ] CI green on this PR. - [ ] Local: bring up the SPA dev server, `localhost:4200` shows the styled layout — header bar with brand link, system-status widget with rounded card, footer with the two language links. - [ ] Production build: `nx build portal-shell --configuration=production` succeeds; `dist/.../styles*.css` contains tens of utility-class selectors (not just `@theme` tokens). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #75 |
||
|
|
288610b9ba |
docs(development): add observability dev-loop section (#73)
## Summary ADR-0012 phase 1 + phase 2 are wired (BFF + SPA), so the "Observability dev-loop" placeholder in the roadmap table now has real content to point at. Promote it from §9 future-work list to a full §5 walkthrough sitting between "Daily commands" and "Dependency updates". ## What §5 covers - **Bringing up the observability stack** — `./infra/local/dev.sh up observability`, with the endpoint table (Jaeger UI, OTLP receiver, BFF stdout, browser DevTools). - **Reading a trace in Jaeger** — service-dropdown filter, span attribute keys to look at (`http.method`, `db.statement`, `service.name`, `trace_id`), the trace-id-as-pivot pattern. - **Correlating a trace with the BFF Pino logs** via `trace_id` and `request_id` (the per-request UUID `nestjs-cls` provides). Concrete `grep` example. - **Reading SPA spans from the browser** — DevTools Network filter on `localhost:4318/v1/traces` + Jaeger UI cross-check. - **Common gotchas** — observability profile not active, CORS pre-flight on `/v1/traces`, `propagateTraceHeaderCorsUrls` mismatches, NODE_ENV mis-set, EADDRINUSE on serve restart. - **What's not in v1** — pointer at the "wired as features land" deltas (CLS keys for session/user/audience, redact list, custom domain spans, prod backend) so a contributor knows what's intentionally not yet there. ## Renumbering The new §5 pushes existing sections down. Final structure: 1. Repo layout / 2. Prerequisites / 3. Initial setup / 4. Daily commands / 5. **Observability dev-loop** / 6. Dependency updates (Renovate) / 7. Conventional commit cycle / 8. Where to look / 9. Roadmap. The "Observability dev-loop" line in §9's roadmap table is removed (now implemented). `docs/README.md` cross-link updated to mention the new section. ## Test plan - [ ] CI green on this PR (`format:check`). - [ ] In a fresh checkout, follow §5's "Bring up the observability stack" verbatim, reach the Jaeger UI, then walk a trace through the `grep` correlation example with a synthetic request — flag any step that's wrong / missing on this real-world rehearsal. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #73 |
||
|
|
fc9b63f24a |
feat(infra): add dev.sh wrapper for the local-dev compose stack (#68)
## Summary Two recurring frictions on the local-dev stack: 1. **Compose-profile asymmetry** — `docker compose down` only operates on services whose profile is currently active. Anything brought up with `--profile X` keeps running unless the same flag is passed on `down`. pgweb and Jaeger silently survived several `down -v` invocations before we noticed (#67 documented the gotcha; this PR makes it impossible to hit if you use the wrapper). 2. **Verbose invocations** — typing `docker compose -f infra/local/dev.compose.yml --profile … <verb>` for routine ops gets old fast. Add [`infra/local/dev.sh`](infra/local/dev.sh) as a thin wrapper. Always passes every profile in scope on teardown / status / log commands, exposes ergonomic verbs: | Command | Effect | | --------------------------------------------- | --------------------------------------------------------------- | | `./infra/local/dev.sh up` | Core only (postgres + redis + otel-collector) | | `./infra/local/dev.sh up all` | Core + every profile | | `./infra/local/dev.sh up dbtools` | Core + pgweb | | `./infra/local/dev.sh up observability` | Core + Jaeger | | `./infra/local/dev.sh down [-v]` | Tear down (every profile in scope, no orphaned services) | | `./infra/local/dev.sh stop <service>` | Stop one service (containers stay around) | | `./infra/local/dev.sh restart <service>` | Restart one service | | `./infra/local/dev.sh status` | `ps` with every profile visible | | `./infra/local/dev.sh logs [service]` | Follow logs | | `./infra/local/dev.sh exec <service> <cmd>` | Run a command inside a container | Anything not matching one of the named verbs is passed through to `docker compose -f dev.compose.yml ...` (with every profile flagged in), so the full Compose surface remains available. ## Doc updates - **`infra/README.md`** — new "Convenience script" subsection with the cheat-sheet table; "First-time setup" rewritten to use the script; the standalone "Profile symmetry" tip from #67 is collapsed into a one-liner since the script now handles it (the note remains as a fallback for direct `docker compose` users). - **`docs/development.md` §3** — points at the script for the typical setup flow. The compose file itself is unchanged. ## Test plan - [ ] `./infra/local/dev.sh help` prints the usage block. - [ ] `./infra/local/dev.sh up` brings up the 3 core services (no pgweb / no jaeger). - [ ] `./infra/local/dev.sh up all` adds pgweb and Jaeger. - [ ] `./infra/local/dev.sh down -v` stops and removes all 5 containers (incl. pgweb and Jaeger), wipes the postgres-data and redis-data volumes. - [ ] `./infra/local/dev.sh stop pgweb` stops just pgweb. - [ ] `./infra/local/dev.sh logs otel-collector` follows that service's logs. - [ ] `./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\du"` lists the audit roles. - [ ] `./infra/local/dev.sh stop` (no arg) errors with a clear message. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #68 |
||
|
|
a93b1067e6 |
docs(setup): add psql and redis-cli to prerequisites (#61)
## Summary Verifying the local-dev stack from the host (`docker compose up -d` + `psql ... -c "\du"` / `redis-cli PING`) requires the postgres and redis client binaries on the developer's machine. They were missing from the prereqs table, so `apt install postgresql-client` / `apt install redis-tools` was an implicit step nobody knew to run. Add both to §2's table, with one-line rationale for each. The Docker row is also tightened to point at the actual local-dev stack location ([`infra/local/`](infra/local/)) instead of the placeholder "Postgres + Redis containers" wording from before that recipe existed. `docker compose exec` remains a viable zero-install alternative for developers who prefer not to touch their host. Mentioned only informally — the host-install path is the documented one. ## Test plan - [ ] Fresh-clone a checkout, follow §2 + §3 verbatim, end with a working stack and successful `psql ... -c "\du"` against it. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #61 |
||
|
|
0f00d6d93f |
feat(infra): add local-dev Docker Compose stack (#57)
## Summary Bring up Postgres + Redis + OTel Collector in one command so contributors can run the BFF end-to-end without manually wiring each service. Replaces the throwaway `docker run postgres:17-alpine` one-liner that was in `docs/development.md` §3. ### What lands - **`infra/local/dev.compose.yml`** — three core services (`postgres:17.2-alpine`, `redis:7.4-alpine`, `otel/opentelemetry-collector-contrib:0.115.0`) plus two viewers gated behind Compose profiles: - `--profile dbtools` → `sosedoff/pgweb:0.16.2` (Postgres GUI on port 8081) - `--profile observability` → `jaegertracing/all-in-one:1.62` (Jaeger UI on 16686) - All ports overridable via `.env`. State in named volumes. Healthchecks on data services. - **`infra/local/.env.example`** — credentials + ports template. `POSTGRES_PASSWORD` and `REDIS_PASSWORD` are mandatory (compose refuses to boot without them); other keys default sensibly. - **`infra/local/init/postgres/01-init.sql`** — bootstrap SQL per **ADR-0013**: `audit_owner` / `audit_writer` / `audit_reader` / `audit_archiver` roles + `audit` schema. Default privileges encode the append-only contract (INSERT to writer, SELECT to reader, DELETE to archiver, no UPDATE/TRUNCATE to anyone). Applied on first Postgres boot only; documented re-run procedure. - **`infra/local/otel-collector.yaml`** — pipeline: OTLP gRPC/HTTP → batch → debug exporter (always) + forward to `jaeger:4317`. When the observability profile is off, the Jaeger export logs warn-level retries but doesn't block the debug pipeline. ### Surrounding doc updates - **`infra/README.md`** — new "Local-dev stack" section: service inventory, port table, first-time setup walkthrough, persistence/bootstrap-replay tips. The previous `local/` placeholder line is removed. - **`docs/development.md`** §3 — rewritten to walk through the compose-based setup; cross-links to `infra/README.md` for the full reference. Roadmap entry for "Local infra recipe" removed from §8 (now implemented); "Observability dev-loop" line adjusted to point at the new Jaeger profile. ### Out of scope - **Production parity** — HA Postgres, Redis Sentinel, real OTel backend (Tempo / Loki / etc.) — defer to the on-prem infrastructure ADR (phase 3b). The dev-only nature of this stack is called out explicitly in `infra/README.md`. - **Wiring the BFF** to actually use these endpoints (NestJS config, Prisma datasource URL, OTel SDK init) — that's the **B — Observability foundations** chantier, next up. ## Test plan - [ ] `cd infra/local && cp .env.example .env && docker compose -f dev.compose.yml up -d` → all three core services come up healthy; verify with `docker compose ps`. - [ ] `psql postgres://portal:<pwd>@localhost:5432/portal_dev -c "\dn"` shows the `audit` schema; `\dg` shows the four audit roles. - [ ] `redis-cli -a <pwd> PING` → `PONG`. - [ ] Send a fake OTLP trace via grpcurl → see it printed by `docker compose logs otel-collector`. - [ ] `--profile dbtools up -d` → http://localhost:8081 shows pgweb UI, can navigate to the audit schema. - [ ] `--profile observability up -d` → http://localhost:16686 shows Jaeger UI; collector logs no longer report Jaeger export retries. - [ ] `docker compose down -v` cleanly removes everything; next `up -d` re-runs the bootstrap SQL. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #57 |
||
|
|
f5d3697466 |
fix(deps): revert TS6/ESLint10/webpack-cli7 majors and gate future majors (#43)
## Summary Three Renovate major bumps merged silently because `nx affected` doesn't see deps-only PRs as affecting any project — CI passed trivially, the breakage only surfaced when `nx run-many` was run locally: - **TypeScript 5→6** (#33) — `tsconfig.lib.json` fails with `TS5101: Option 'baseUrl' is deprecated`. Revert to 5.9.x. - **ESLint 9→10** (#36) — `@nx/eslint@22.7.1` not compatible: project graph fails with "Unable to find eslint". Revert eslint, `@eslint/js`, `jsonc-eslint-parser`, `eslint-plugin-playwright` to ESLint-9-compatible versions. - **webpack-cli 5→7** (#34) — webpack-cli 7 removed the `--node-env=production` flag Nx generates. Revert to 5.x. Bonus side-fix: the `ajv@<8.18.0` override added in #42 was over-broad and was forcing ESLint's bundled ajv to v8 (incompatible with ESLint 9's option contract). Narrow the override to `@angular-devkit/core>ajv@<8.18.0` so only the targeted nestjs-prisma chain is bumped. ## Prevention — gate majors behind the dependency dashboard Add a Renovate `packageRule` with `dependencyDashboardApproval: true` for `matchUpdateTypes: ["major"]`. Renovate stops auto-creating PRs for majors; they appear as checkboxes in the dashboard issue, and only get a PR after a human ticks the box (presumably after reading the changelog and confirming Nx-plugin / Angular / NestJS readiness). This is the surgical fix for the gap. The deeper fix (making `nx affected` correctly mark all projects as affected on package.json changes) is a separate investigation worth doing later — but the dashboard gate prevents the same trap regardless. ## Verification Locally on this branch: - `pnpm exec nx run-many -t lint test build --parallel=2` → ✓ 8 projects pass. - `pnpm audit --audit-level=moderate` → 0 vulnerabilities. ## Test plan - [ ] `check` job goes green on this PR (would have caught the regressions if `nx affected` were broader). - [ ] After merge, the next Renovate run does not create new PRs for any major (TS, eslint, webpack-cli, etc.). - [ ] Any pending major in the dashboard issue still appears, but only as a checkbox awaiting approval. ## Out of scope (follow-up) Investigate why `nx affected` misses package.json-only changes. Likely a missing entry in `nx.json` `namedInputs` (`default`) or `targetDefaults`. Worth its own focused PR; the dashboard gate is the conservative fix in the meantime. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #43 |
||
|
|
f9ed3cf82a |
chore(ci): skip perf and commits gates on Renovate-authored PRs (#23)
## 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 |
||
|
|
f58cf13e33 |
fix(ci): give Renovate a git identity and a github.com token (#13)
## Summary First successful Renovate run (after the docker-image fix in #12) extracted 116 deps cleanly but every branch update failed with `fatal: empty ident name not allowed`, then the whole repo aborted on `Lock file error - aborting`. Two root causes: - **No git identity** — the bot user had an email but no **Full Name** on its Gitea profile, so Renovate produced incomplete git authorship. - **No github.com auth** — Renovate could not look up versions of GitHub-hosted Actions (`actions/checkout`, etc.) or `containerbase/node-prebuild` (the Node binary it dynamically fetches for lockfile maintenance) — anonymous rate limit (60 req/h) hit. Fixes: 1. **`renovate.json`** — pin `gitAuthor` explicitly. The Full Name was also set on the bot's Gitea profile for UI consistency, but the override in config means we don't depend on out-of-band UI state. 2. **`.gitea/workflows/renovate.yml`** — pass `RENOVATE_GITHUB_COM_TOKEN` from the new `GITHUBCOM_TOKEN` repo secret (no underscore between GITHUB and COM — Gitea reserves the `GITHUB_*` namespace). 3. **`docs/development.md`** — onboarding procedure now covers both tokens + the Full Name step. ## Manual setup required after merge Already done in this iteration: - Bot's Full Name set in Gitea ("APF Portal Bot"). - `GITHUBCOM_TOKEN` repo secret created with a zero-scope github.com PAT. (If the PAT was leaked during setup, regenerate before merging — the workflow only references the secret name, not the value.) ## Test plan - [ ] After merge, trigger Renovate manually (Actions → Renovate → Run workflow). - [ ] No `empty ident name` warning in the logs. - [ ] No `Lock file error - aborting`; repo finishes with `INFO: Repository finished … "cloned": true`. - [ ] Renovate creates the dependency dashboard issue + the first batch of grouped PRs (Angular, Nx, NestJS, Prisma, …) signed by `apf-portal-bot`. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #13 |
||
|
|
82911f9319 |
chore(ci): set up Renovate dependency automation (#11)
## Summary
Wire up Renovate to keep dependencies fresh without manual triage.
- **Workflow** — `.gitea/workflows/renovate.yml`: cron daily at 03:00 UTC + `workflow_dispatch`, runs on the existing self-hosted runners. Picked 03:00 specifically so Monday's tick sits inside Renovate's default `lockFileMaintenance.schedule` ("before 4am Monday") and triggers the weekly lockfile refresh in passing.
- **Config** — `renovate.json`: `config:recommended` baseline + groupings (Angular, Nx, NestJS, Prisma, Vitest, TypeScript tooling, ESLint, SWC, Tailwind), Conventional-Commits commit messages, OSV.dev as vulnerability source, dependency dashboard issue.
- **Docs** — new §5 in `docs/development.md` covering the bot onboarding (manual, one-shot), how to trigger Renovate manually, how to review its PRs. The placeholder roadmap entry is dropped from §8.
No ADR — Renovate is operational tooling, not an architectural decision (and on Gitea it's the only viable bot anyway, no real alternative to capture).
## Manual setup required after merge
The workflow is wired but inert until the bot is onboarded once on Gitea:
1. Create a non-admin Gitea user `apf-portal-bot`.
2. Add the bot as a **Write** collaborator on this repo.
3. Sign in as the bot, generate a PAT (scopes: read/write `repository`, read/write `issue`, read `user`).
4. Add the PAT as the repo secret `RENOVATE_TOKEN` (Settings → Actions → Secrets).
Detailed steps in [`docs/development.md`](docs/development.md) → "Dependency updates (Renovate)".
## Test plan
- [ ] After bot onboarding, trigger the workflow manually (Repo → Actions → Renovate → Run workflow).
- [ ] First run creates the "Renovate Dependency Dashboard" issue and a batch of grouped PRs (Angular, Nx, …).
- [ ] Each generated PR has CI green (`check`, `scan`, `commits`, `perf`, `a11y`).
- [ ] Commit subject matches `chore(deps): …` / `fix(deps): …` so the `commits` gate doesn't reject.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #11
|
||
|
|
156e7ca2df |
chore: add PR template and document title/body convention
Formalise the PR-flow conventions while we install the PR-flow itself. .gitea/pull_request_template.md auto-populates the PR body in Gitea with five sections: Summary / Motivation / Implementation notes / Verification (with CI-gate checkboxes + ADR/diagram update flags) / Related. Sections can be left blank when irrelevant; the template guides without adding ceremony. Header HTML comment reminds the contributor of the PR title format and links to the full convention. docs/development.md §5 (Conventional commit cycle) gains a 'PR conventions' subsection that: - explains why the PR title format matters (squash-merge subject on main, validated by commitlint in the CI 'commits' job) - separates feature-branch commit hygiene (exploratory OK) from PR title hygiene (must conform) - documents the type vocabulary (feat/fix/docs/style/refactor/perf/ test/build/ci/chore/revert) - proposes an optional scope vocabulary (apps, libs, cross-cutting domains like decisions/docs/ci/deps) - describes the body template No new ADR. The PR title format is derived from ADR-0007 (Conventional Commits at the commit-msg layer) plus ADR-0015 (squash-merge means PR title becomes the commit subject on main). The body template is tactical guidance, not architectural. |
||
|
|
7d27cd8773 |
docs: turn development.md §7 into a phase-mapped roadmap
The section was a short bullet list of 'sections to be added' - it underplayed how broad the future content really is, and gave no visibility on what triggers each. Replace it with a structured table that maps every planned section to (a) its ADR phase and (b) the specific implementation work that unlocks it. A contributor reading the doc today now sees: - which dev-loops will exist (auth, sessions, MFA step-up, OTel, audit, downstream APIs, component patterns, a11y, perf debugging, Renovate, release, GitLab migration, architecture diagrams) - under which ADR each lands - what concrete event in the codebase makes each section real Plus the explicit policy: each entry stays a subsection of this doc until we have at least three substantial sub-topics, at which point the file is split into docs/development/ with an index. Avoids creating empty placeholder files (per CLAUDE.md: 'documentation when genuinely useful, not just to tick a box') while signalling the future structure clearly. Cross-references each row to its triggering ADR so the table doubles as a 'what's pending implementation' radar. Foreshadows the §7 → file split that will happen once content density justifies it. |
||
|
|
b69d3a2206 |
docs: add development.md as the day-to-day reference for working on apf_portal
A new contributor (or returning lead) opening the repo gets: - the final repo layout, with one-line annotations per top-level dir - the prerequisite tooling list (Node 24 LTS, pnpm 10, mkcert, optional local Trivy/gitleaks, Docker for Postgres) - the fresh-clone setup steps (clone, pnpm install, prisma generate, sanity check) - the daily commands organised by intent: serve, test (incl. single file), lint, build, generate (apps / libs / components), Prisma, the four ci:* scripts that mirror the CI gates - the conventional commit cycle end-to-end (branch naming, hook enforcement, PR gates, squash-merge, release tagging) - a 'where to look' table cross-linking the project rules (CLAUDE.md), the ADRs, the setup guides, and the personal notes - an explicit 'to be added' section listing what the doc will grow into (local infra Docker Compose, auth dev-loop, component patterns, debugging tips, release workflow, Renovate policy) The doc is intentionally non-exhaustive at v1 - it captures what a contributor needs today and is structured to grow as the workflow sharpens. Indexed in docs/README.md under a new 'Daily development' section, separate from the one-off onboarding guides under docs/setup/. |