From c78dccbae1ad1e2e0b1f69b5880f466f80563be2 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 5 May 2026 15:50:38 +0200 Subject: [PATCH] chore(ci): skip perf and commits gates on Renovate-authored PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The full CI pipeline (check, scan, commits, perf, a11y) was sized for human PRs. On Renovate's dep-bump PRs: - `perf` (Lighthouse build + 3-iteration median across the critical-routes list) costs 3-5 min per PR for a signal that is essentially zero on a patch/minor dep bump — the SPA today serves the static placeholder, and even with real routes the score on most bumps stays inside the median noise floor. - `commits` re-validates messages that Renovate generates from a Conventional-Commits-conformant template. Tautological. Skip both jobs when the PR author is the dedicated `apf-portal-bot` Gitea user. The `push` event on `main` still runs the full pipeline, so any regression a Renovate PR sneaks through is caught seconds after merge — fast enough to revert without disruption. ADR-0017 is amended to document the policy: the table of "where Lighthouse CI runs" now distinguishes human PR / bot PR / push to main; a new "Pre-merge gating policy" subsection records the rationale and the human-takeover edge case. The §Confirmation checklist is updated. The docs/development.md "Reviewing Renovate PRs" subsection is updated so contributors expect a leaner pipeline on bot PRs. --- .gitea/workflows/ci.yml | 12 +++++++- .../0017-performance-budgets-lighthouse-ci.md | 30 ++++++++++++++----- docs/development.md | 3 +- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e46aa76..2b69f2f 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -79,7 +79,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} commits: - if: github.event_name == 'pull_request' + # PRs opened by Renovate (apf-portal-bot) carry commit messages + # generated from a vetted Conventional-Commits template — running + # commitlint on them is tautological. Per ADR-0017 amendment. + if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'apf-portal-bot' runs-on: [self-hosted, on-prem] steps: - uses: actions/checkout@v4 @@ -93,6 +96,13 @@ jobs: - run: COMMIT_LINT_FROM=origin/main pnpm ci:commits perf: + # Skip the Lighthouse run on PRs opened by Renovate (apf-portal-bot): + # the per-PR perf signal on a dep bump is essentially zero (no + # routes yet, bundle is the static placeholder), and the Lighthouse + # round-trip burns several minutes per PR. Push events on `main` + # still run perf — we catch regressions immediately post-merge, + # not pre-merge. Per ADR-0017 amendment. + if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'apf-portal-bot' runs-on: [self-hosted, on-prem] # Lighthouse CI drives a real Chrome instance; the default act runner # image (catthehacker/ubuntu:act-22.04) ships without one. The :full diff --git a/docs/decisions/0017-performance-budgets-lighthouse-ci.md b/docs/decisions/0017-performance-budgets-lighthouse-ci.md index c3eda71..7b4754d 100644 --- a/docs/decisions/0017-performance-budgets-lighthouse-ci.md +++ b/docs/decisions/0017-performance-budgets-lighthouse-ci.md @@ -103,11 +103,27 @@ Each route is scored against the thresholds above. **Failure on any threshold is ### Where Lighthouse CI runs -| Environment | Cadence | Purpose | Blocking? | -| ------------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------- | -| **CI on every PR** | per push | Catch regressions before merge | **Yes** | -| **CI scheduled (weekly)** on prod env | cron in `security-scheduled.yml` from [ADR-0015](0015-cicd-gitea-actions.md), extended to cover perf | Detect drift / regressions in real environment | Reports as alerts; doesn't block, but triggers triage | -| **Local dev** | manual via `pnpm nx run portal-shell:lighthouse` | Developer-side feedback | Non-blocking | +| Environment | Cadence | Purpose | Blocking? | +| --------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------- | +| **CI on human-authored PRs** | per push | Catch regressions before merge | **Yes** | +| **CI on Renovate (bot) PRs** | _skipped_ | Per-PR perf signal on a dep bump is ≈0; Lighthouse cost is ≥3-5 min per PR | _No (skipped)_ | +| **CI on `push` to `main`** (post-merge) | per merge | Catch regressions immediately after merge — applies to bot PRs too | **Yes** | +| **CI scheduled (weekly)** on prod env | cron in `security-scheduled.yml` from [ADR-0015](0015-cicd-gitea-actions.md), extended to cover perf | Detect drift / regressions in real environment | Reports as alerts; doesn't block, but triggers triage | +| **Local dev** | manual via `pnpm nx run portal-shell:lighthouse` | Developer-side feedback | Non-blocking | + +### Pre-merge gating policy: human PRs vs bot PRs + +The `perf` job is skipped on PRs whose author is the dedicated Renovate bot user (`apf-portal-bot`). Rationale: + +- A dependency bump on this codebase has near-zero direct impact on the bundle the user receives — the SPA today serves the static placeholder, and even once real routes ship, the Lighthouse score on a patch/minor dep bump rarely moves outside the median noise floor (±2-5 points). +- A full Lighthouse round-trip (build prod + 3-iteration median across the critical-routes list) costs 3-5 minutes per PR. Multiplied by the steady-state Renovate cadence (~5-15 PRs per week once dependencies are diversified), this is a real wall-clock cost on shared runners with no reliable signal in return. +- The post-merge `push` to `main` still runs the full `perf` gate. A regression that does sneak through a Renovate PR is caught seconds after merge, before the next PR queues a build — fast enough to revert without disruption. + +This policy is **conservative on human PRs** (the perf gate stays mandatory pre-merge, where the latitude for change is highest) and **pragmatic on bot PRs** (where the change vocabulary is narrow and the post-merge gate already provides the safety net). + +The same skip is applied to the `commits` job: Renovate generates commit messages from a Conventional-Commits-conformant template, so re-validating them with commitlint is tautological. + +Edge case: if a human takes over a Renovate-authored PR and force-pushes substantive changes, the gate stays skipped (the PR _author_ is still the bot). This is accepted as a marginal case; the human can either run `ci:perf` locally or accept that the post-merge `push` gate will catch any regression. Re-evaluation if it happens often enough to be a real annoyance. ### Variability mitigation @@ -143,7 +159,7 @@ These budgets are **not** enforced as hard CI gates — load profile in CI is un ### Performance regressions are bugs, not backlog -A regression on any of the front-end gates blocks the merge. A regression on a back-end SLO that surfaces in production (alert fires) is triaged with the same priority as a security finding — root-caused, fixed, post-mortemed if it took the SLO out for more than 24 h. +A regression on any of the front-end gates blocks the merge for human PRs, and blocks `push` to `main` for any PR (including Renovate-authored ones — see the gating-policy subsection above). A regression on a back-end SLO that surfaces in production (alert fires) is triaged with the same priority as a security finding — root-caused, fixed, post-mortemed if it took the SLO out for more than 24 h. ### a11y / perf trade-off @@ -195,7 +211,7 @@ No browser-side RUM SDK in v1. The OTel browser tracing from [ADR-0012](0012-obs - `lighthouserc.js` exists at the repo root with the critical-routes list, the assertions matching the thresholds above, and a 3-iteration median configuration. - `apps/portal-shell/project.json` declares `budgets` of type `"error"` with the values above. - `package.json` exposes `ci:perf`, runnable locally with the same exit code as CI. -- CI's `perf` gate from [ADR-0015](0015-cicd-gitea-actions.md) calls `pnpm ci:perf` and is blocking. +- CI's `perf` gate from [ADR-0015](0015-cicd-gitea-actions.md) calls `pnpm ci:perf`. It is blocking on human PRs and on `push` to `main`; skipped on PRs authored by the Renovate bot user (`apf-portal-bot`) per the gating-policy subsection above. - `apps/portal-shell` exposes a Nx target `analyze` invoking `source-map-explorer` against the production build's source maps. - The BFF p95/p99 budgets are documented in `apps/portal-bff/README.md` and translate into alert rules in the production observability backend (configured per future infrastructure ADR). - The `security-scheduled.yml` workflow includes a weekly Lighthouse CI run against the prod URL set, with reports uploaded as build artefacts and an alert on any threshold breach. diff --git a/docs/development.md b/docs/development.md index cd1da10..54dd613 100644 --- a/docs/development.md +++ b/docs/development.md @@ -244,7 +244,8 @@ Repo → Actions → "Renovate" workflow → Run workflow. Useful when you've ju ### Reviewing Renovate PRs -- Each Renovate PR is gated by the same CI as a human PR — `check`, `scan`, `commits`, `perf`, `a11y`. Don't merge until all are green. +- Renovate PRs run a leaner CI pipeline than human PRs — `check`, `scan`, `a11y` only. The `perf` and `commits` gates are skipped (per [ADR-0017](decisions/0017-performance-budgets-lighthouse-ci.md) — Lighthouse signal on a dep bump is essentially zero, commitlint on bot-generated messages is tautological). The full `perf` gate still runs on `push` to `main` post-merge, so regressions are caught seconds after merge rather than before. +- Don't merge until the remaining gates are green. - The "Renovate Dependency Dashboard" issue (auto-created on first run) lists every pending update grouped by status. Use it to triage which PRs to expedite. - For a major bump that introduces breaking changes, **don't reflexively merge**: read the changelog, then either accept the work or close the PR with a "rejected" label. Renovate respects that label and won't keep re-opening the same major. - **Adding or removing** a dependency belongs in a feature PR, not in Renovate's scope. Renovate only updates _versions_ of existing deps. -- 2.30.2