From c55c549b81baaf36848b83435eef7808bbcf525b Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 4 May 2026 16:39:33 +0200 Subject: [PATCH] chore(ci): set up Renovate dependency automation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renovate runs as a daily Gitea Actions cron (03:00 UTC, on self-hosted runners) and opens PRs against main for dependency updates. Picked daily-at-3am specifically so Monday's run sits inside Renovate's default `lockFileMaintenance.schedule` ("before 4am on Monday") and triggers the weekly lockfile refresh in passing. The workflow only handles invocation + Gitea-platform plumbing. Bot behaviour (groupings, labels, Conventional Commits, OSV.dev vulnerability source) lives in /renovate.json so it can evolve without redeploying the workflow. Groups defined: Angular, Nx, NestJS, Prisma (incl. nestjs-prisma), Vitest, TypeScript tooling, ESLint, SWC, Tailwind. Standalone packages get individual PRs. Bot identity: a dedicated `apf-portal-bot` Gitea user authenticates with a PAT stored as the RENOVATE_TOKEN repo secret. The full onboarding procedure (user creation, scopes, secret) is documented in docs/development.md → "Dependency updates (Renovate)". The roadmap entry for Renovate is removed from §8 since the section now exists. --- .gitea/workflows/renovate.yml | 45 +++++++++++++++++++++++ docs/README.md | 2 +- docs/development.md | 38 +++++++++++++++++--- renovate.json | 68 +++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 .gitea/workflows/renovate.yml create mode 100644 renovate.json diff --git a/.gitea/workflows/renovate.yml b/.gitea/workflows/renovate.yml new file mode 100644 index 0000000..2ac261d --- /dev/null +++ b/.gitea/workflows/renovate.yml @@ -0,0 +1,45 @@ +# Per ADR-0015 (CI/CD on Gitea Actions). Scheduled invocation of +# Renovate Bot, which proposes dependency updates as PRs against main. +# +# Configuration of the *bot's behaviour* (groupings, schedules, labels, +# auto-merge, vulnerability sources) lives in /renovate.json at the +# repo root. This workflow only controls *when* Renovate runs and how +# it authenticates against Gitea. +# +# Authentication: a Gitea Personal Access Token issued for the dedicated +# `apf-portal-bot` user, stored as the RENOVATE_TOKEN secret. The full +# bot-onboarding procedure lives in docs/development.md → "Dependency +# updates (Renovate)". + +name: Renovate + +on: + schedule: + # Daily 03:00 UTC — outside working hours, no contention with PR CI. + # Picked specifically to sit inside Renovate's default + # `lockFileMaintenance.schedule` window of "before 4am on Monday", + # so Monday's run also triggers the weekly lockfile refresh. + - cron: '0 3 * * *' + workflow_dispatch: + +jobs: + renovate: + runs-on: [self-hosted, on-prem] + steps: + - uses: renovatebot/github-action@v40 + with: + token: ${{ secrets.RENOVATE_TOKEN }} + configurationFile: renovate.json + env: + # Tell Renovate this is Gitea, not GitHub. + RENOVATE_PLATFORM: gitea + # Gitea API endpoint — derived from the workflow's own server + # URL so a Gitea → GitLab migration only requires changing + # github.server_url at the platform level (matches the + # CLAUDE.md rule about not hardcoding the project name / + # forge details outside repo metadata). + RENOVATE_ENDPOINT: ${{ github.server_url }}/api/v1/ + # Don't crawl the whole instance — only this repo. + RENOVATE_AUTODISCOVER: 'false' + RENOVATE_REPOSITORIES: ${{ github.repository }} + LOG_LEVEL: info diff --git a/docs/README.md b/docs/README.md index 5d70a6d..b5f0a14 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,7 @@ This is the entry point to all project documentation. It is maintained automatic ### Daily development -- [development.md](development.md) — repo layout, prerequisites, initial setup, daily commands, conventional commit cycle. Day-to-day reference for working on the project. +- [development.md](development.md) — repo layout, prerequisites, initial setup, daily commands, dependency updates (Renovate), conventional commit cycle. Day-to-day reference for working on the project. ### Architecture diff --git a/docs/development.md b/docs/development.md index 6817bb7..9005256 100644 --- a/docs/development.md +++ b/docs/development.md @@ -217,7 +217,38 @@ pnpm ci:perf # production build + Lighthouse CI against the static-served --- -## 5. Conventional commit cycle +## 5. Dependency updates (Renovate) + +[Renovate](https://docs.renovatebot.com/) runs as a scheduled workflow ([`.gitea/workflows/renovate.yml`](../.gitea/workflows/renovate.yml)) and opens PRs against `main` for dependency updates. Daily at 03:00 UTC, plus on-demand via `workflow_dispatch`. + +Behaviour is controlled by [`renovate.json`](../renovate.json) at the repo root: groupings (Angular, Nx, NestJS, Prisma, Vitest, TypeScript tooling, ESLint, SWC, Tailwind), Conventional-Commits-compatible commit messages (`chore(deps): …` / `fix(deps): …` for vulnerability fixes), weekly lockfile maintenance, OSV.dev as the vulnerability data source. + +### One-time bot onboarding + +Renovate authenticates as a dedicated bot user. Setup is manual on Gitea — done once per Gitea instance, then the workflow runs unattended. + +1. **Create a bot user.** Site Administration → Users → Create User. Suggested name: `apf-portal-bot`. Strong password, mark as **non-admin** (least privilege). +2. **Add the bot as a collaborator** on this repo with **Write** access (Settings → Collaborators). Without write, Renovate can't push branches. +3. **Generate a PAT for the bot.** Sign in as the bot, then User Settings → Applications → Generate New Token. Scopes needed: read/write `repository`, read/write `issue`, read `user`. Avoid `admin`. +4. **Store the PAT as a repo secret.** Settings → Actions → Secrets → New Secret. Name: `RENOVATE_TOKEN`. Value: the token from step 3. +5. **Sign out and forget the token locally.** It is now only retrievable via the secret store. + +To **rotate** the PAT: regenerate at step 3, update the secret at step 4. The schedule keeps running unattended with the new token. + +### Triggering manually + +Repo → Actions → "Renovate" workflow → Run workflow. Useful when you've just changed `renovate.json` and want the next pass to happen immediately rather than wait for the next 03:00 UTC tick. + +### 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. +- 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. + +--- + +## 6. Conventional commit cycle 1. Branch from `main` with a short slug: @@ -296,7 +327,7 @@ The template guides without enforcing — sections can be left blank when irrele --- -## 6. Where to look +## 7. Where to look | Question | Doc | | ------------------------------------------- | ---------------------------------------------------------------------------- | @@ -308,7 +339,7 @@ The template guides without enforcing — sections can be left blank when irrele --- -## 7. Sections to come — roadmap by phase +## 8. Sections to come — roadmap by phase This doc starts as a phase-1 + cross-cutting reference. As features for later phases land, the corresponding sections below are filled in directly. Each entry is mapped to the ADR / implementation work that unlocks it, so a contributor can see when each section becomes real and what triggers it. @@ -326,7 +357,6 @@ When a section grows beyond a short subsection, it is extracted to its own file | **Component patterns library** — the in-house, spartan-style components (Angular CDK + Tailwind) as they ship, with a11y notes per component (keyboard model, ARIA, screen-reader expectations). | 5b suite | First non-placeholder component in `libs/shared/ui/`. | | **a11y testing workflow** — running axe-core via Playwright locally, screen-reader testing notes (NVDA / VoiceOver / TalkBack), the APF user-panel cadence and how to triage findings. | 3a | First Playwright e2e suite touching real screens ([ADR-0016](decisions/0016-accessibility-baseline-wcag-aa-targeted-aaa.md)). | | **Performance debugging** — running Lighthouse CI locally with full config, reading the HTML reports, using `source-map-explorer` to investigate bundle bloat, interpreting BFF p95/p99 from OTel. | 3a | Lighthouse already wired in CI ([ADR-0017](decisions/0017-performance-budgets-lighthouse-ci.md)); section grows when first real route is added to the critical-routes list. | -| **Renovate / dep update policy** — bot setup, schedule, auto-merge rules, breaking-change handling. | 3a | Security baseline ADR (paused, awaiting RSSI input). | | **Debugging tips** — Angular DevTools, NestJS inspector, Prisma query log, OTel trace navigation, common gotchas. | cross | Accumulates organically as the team encounters them. | | **Release workflow** — tag-driven release, what `release.yml` does, version bumping, changelog generation from Conventional Commits. | 3b | On-prem infrastructure ADR + populated `release.yml`. | | **GitLab migration runbook** — when the org migrates Gitea → GitLab, how the workflows are ported, which level-2 sections of [ADR-0015](decisions/0015-cicd-gitea-actions.md) get superseded. | future | GitLab migration ADR (6–18 months horizon). | diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..9a60c5c --- /dev/null +++ b/renovate.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "dependencyDashboard": true, + "dependencyDashboardTitle": "Renovate Dependency Dashboard", + "labels": ["dependencies"], + "prHourlyLimit": 4, + "prConcurrentLimit": 10, + "lockFileMaintenance": { + "enabled": true + }, + "osvVulnerabilityAlerts": true, + "vulnerabilityAlerts": { + "enabled": true, + "labels": ["security", "dependencies"] + }, + "packageRules": [ + { + "groupName": "Angular", + "matchPackageNames": [ + "@angular/*", + "@angular-devkit/*", + "@angular-eslint/*", + "@schematics/angular", + "angular-eslint" + ] + }, + { + "groupName": "Nx", + "matchPackageNames": ["@nx/*", "nx"] + }, + { + "groupName": "NestJS", + "matchPackageNames": ["@nestjs/*"] + }, + { + "groupName": "Prisma", + "matchPackageNames": ["prisma", "@prisma/*", "nestjs-prisma"] + }, + { + "groupName": "Vitest", + "matchPackageNames": ["vitest", "@vitest/*", "/^vitest-/"] + }, + { + "groupName": "TypeScript tooling", + "matchPackageNames": [ + "typescript", + "typescript-eslint", + "@typescript-eslint/*", + "ts-node", + "ts-jest", + "tslib" + ] + }, + { + "groupName": "ESLint", + "matchPackageNames": ["eslint", "eslint-*", "@eslint/*"] + }, + { + "groupName": "SWC", + "matchPackageNames": ["@swc/*", "@swc-node/*"] + }, + { + "groupName": "Tailwind CSS", + "matchPackageNames": ["tailwindcss", "@tailwindcss/*", "postcss"] + } + ] +} -- 2.30.2