Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,8 @@ Ordered as the rules are: type, scope, description, body, breaking, issue.
### Adoption

This guide is the rule for commits in this repository. Deviating from it
requires a justification — an ADR under `doc/adr/`, or an update to this guide.
requires a justification — an ADR under `maintainers/adr/`, or an update to this
guide.

It applies from its adoption on, to every commit created after. Prior history is
not rewritten.
Expand Down
43 changes: 43 additions & 0 deletions maintainers/README.fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Documentation mainteneur

🌍 🇬🇧 [English](README.md) · 🇫🇷 Français (ce fichier)

> Documentation pour les **mainteneurs et opérateurs** de FirstClassErrors —
> comment le projet est construit, publié et maintenu en bonne santé. Elle ne
> fait **pas** partie de la documentation utilisateur de la librairie, sous
> [`doc/`](../doc/). La version anglaise est canonique ; les pages françaises sont
> tenues à jour en parallèle.

## Sommaire

### [Référence des workflows CI/CD](workflows/README.fr.md)

Une page par workflow GitHub Actions — à quoi il sert, quand et comment il
s'exécute, ses permissions, et les décisions non évidentes qu'il ne faut pas
modifier sans en comprendre la raison. Commencez par l'[index](workflows/README.fr.md) ;
il documente aussi les conventions transverses (actions épinglées par SHA,
permissions au moindre privilège, timeouts par job, checks *required* comme vrai
barrage).

### [Répétition de release à blanc (« dry run » manuel)](ReleaseDryRun.fr.md)

Le runbook opérationnel du dry run manuel via le dispatch `release` : comment le
lancer, ce qu'il touche (et ce qu'il ne touche volontairement pas), et quand
l'utiliser. Il complète les pages [`release`](workflows/release.fr.md) et
[`release-dryrun`](workflows/release-dryrun.fr.md) de la référence, qui décrivent
ces workflows structurellement. Aussi en [anglais](ReleaseDryRun.en.md).

### [Registres de décision d'architecture (ADR)](adr/)

Des enregistrements datés des décisions importantes — leur contexte, l'option
retenue et les conséquences. Un ADR est un journal historique : il est *superseded*
par un ADR plus récent, pas édité sur place.

- [ADR 0001 — Verrouiller le floor Roslyn de l'analyzer](adr/0001-lock-the-analyzer-roslyn-floor.md)
— pourquoi la version de Roslyn de l'analyzer est gelée, ce que le workflow
[`analyzers`](workflows/analyzers.fr.md) fait respecter. *(En anglais uniquement.)*

## En rapport

- [`CONTRIBUTING.md`](../CONTRIBUTING.md) — conventions de commit et de pull
request (imposées par le workflow [`commit-lint`](workflows/commit-lint.fr.md)).
41 changes: 41 additions & 0 deletions maintainers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Maintainer documentation

🌍 🇬🇧 English (this file) · 🇫🇷 [Français](README.fr.md)

> Documentation for **maintainers and operators** of FirstClassErrors — how the
> project is built, released, and kept healthy. It is **not** part of the
> library's user documentation, which lives under [`doc/`](../doc/). The English
> version is canonical; the French pages are kept in sync.

## Contents

### [CI/CD workflow reference](workflows/README.md)

A page per GitHub Actions workflow — what it is for, when and how it runs, its
permissions, and the non-obvious decisions you must not change without
understanding why. Start at the [index](workflows/README.md); it also documents
the cross-cutting conventions (SHA-pinned actions, least-privilege permissions,
per-job timeouts, required checks as the real gate).

### [Release dry run (manual)](ReleaseDryRun.en.md)

The operational runbook for the manual `release` dispatch dry run: how to launch
it, what it touches (and what it deliberately does not), and when to use it. It
complements the workflow reference's [`release`](workflows/release.en.md) and
[`release-dryrun`](workflows/release-dryrun.en.md) pages, which describe those
workflows structurally. Also in [French](ReleaseDryRun.fr.md).

### [Architecture Decision Records](adr/)

Dated records of significant decisions — their context, the option chosen, and
the consequences. An ADR is a historical log: it is superseded by a newer ADR, not
edited in place.

- [ADR 0001 — Lock the analyzer Roslyn floor](adr/0001-lock-the-analyzer-roslyn-floor.md)
— why the analyzer's Roslyn version is frozen, enforced by the
[`analyzers`](workflows/analyzers.en.md) workflow. *(English only.)*

## Related

- [`CONTRIBUTING.md`](../CONTRIBUTING.md) — commit and pull-request conventions
(enforced by the [`commit-lint`](workflows/commit-lint.en.md) workflow).
14 changes: 14 additions & 0 deletions maintainers/ReleaseDryRun.en.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release dry run (manual)

🌍 🇬🇧 English (this file) · 🇫🇷 [Français](ReleaseDryRun.fr.md)

↑ Part of the [maintainer documentation](README.md) · see also the workflow
reference for [`release`](workflows/release.en.md) and
[`release-dryrun`](workflows/release-dryrun.en.md).

> Maintainer / operational documentation. This is **not** part of the library's
> user documentation under `doc/`.

Expand Down Expand Up @@ -99,3 +105,11 @@ the **attestation / OIDC** path that the automatic one deliberately leaves out.
The actual **push to nuget.org** and the **repository-signed bytes** nuget.org
serves cannot be exercised without publishing — nuget.org has no "dry-run push".
That final link is only ever validated by a real release.

## Related

- [`release`](workflows/release.en.md) — the workflow this rehearses, described
structurally (triggers, jobs, the traps in its design).
- [`release-dryrun`](workflows/release-dryrun.en.md) — the automatic,
side-effect-free dry run that runs on every PR and push.
- [Maintainer documentation](README.md) — the index of all maintainer docs.
15 changes: 15 additions & 0 deletions maintainers/ReleaseDryRun.fr.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Répétition de release à blanc (« dry run » manuel)

🌍 🇬🇧 [English](ReleaseDryRun.en.md) · 🇫🇷 Français (ce fichier)

↑ Fait partie de la [documentation mainteneur](README.fr.md) · voir aussi la
référence des workflows pour [`release`](workflows/release.fr.md) et
[`release-dryrun`](workflows/release-dryrun.fr.md).

> Documentation technique / mainteneur. Elle ne fait **pas** partie de la
> documentation utilisateur de la librairie, sous `doc/`.

Expand Down Expand Up @@ -106,3 +112,12 @@ Le **push réel vers nuget.org** et les **octets re-signés par le dépôt** que
nuget.org sert ne peuvent pas être exercés sans publier — nuget.org n'a pas de
« push à blanc ». Ce dernier maillon n'est jamais validé que par une vraie
release.

## En rapport

- [`release`](workflows/release.fr.md) — le workflow que ceci répète, décrit
structurellement (déclencheurs, jobs, pièges de sa conception).
- [`release-dryrun`](workflows/release-dryrun.fr.md) — le dry run automatique et
sans effet de bord qui tourne sur chaque PR et push.
- [Documentation mainteneur](README.fr.md) — l'index de toutes les docs
mainteneur.
103 changes: 103 additions & 0 deletions maintainers/workflows/README.fr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Référence des workflows CI/CD

🌍 🇬🇧 [English](README.md) · 🇫🇷 Français (ce fichier)

↑ Fait partie de la [documentation mainteneur](../README.fr.md).

> Documentation mainteneur. Elle décrit les workflows GitHub Actions qui
> construisent, vérifient et publient FirstClassErrors. Elle ne fait **pas**
> partie de la documentation utilisateur de la librairie, sous `doc/`.

## De quoi il s'agit

Chaque workflow sous [`.github/workflows/`](../../.github/workflows/) porte une
intention qu'il est facile de casser en « faisant le ménage » : une permission
volontairement étroite, un ordre d'étapes qui protège d'une panne précise, une
version gelée pour une raison produit. Les fichiers de workflow eux-mêmes
contiennent le raisonnement ligne à ligne dans leurs commentaires — ces
commentaires sont la source de vérité la plus proche du code. **Ces pages sont la
couche pédagogique au-dessus :** à quoi sert chaque workflow, quand et comment il
s'exécute, et les quelques points qu'il ne faut pas modifier sans en comprendre
la raison.

Lisez la page d'un workflow avant d'y toucher. Si la page et le YAML se
contredisent, c'est le YAML qui fait foi — et la page doit être corrigée.

## Les conventions transverses

Quelques décisions sont partagées par (presque) tous les workflows. Elles sont
documentées une seule fois ici plutôt que répétées sur chaque page.

- **Les actions sont épinglées par SHA de commit, pas par tag.** Un tag comme
`@v4` peut être déplacé par son propriétaire vers du nouveau code ; un SHA de
40 caractères hexadécimaux, non. Chaque `uses:` épingle donc un SHA avec le tag
lisible en commentaire de fin (`# v4`). Quand vous montez une action, changez
**les deux**. L'écosystème `github-actions` de Dependabot propose ces montées.
- **Les `permissions:` partent en lecture seule et s'élargissent par job.** Le
bloc au niveau du workflow est le moindre privilège nécessaire (en général
`contents: read`) ; un job qui doit écrire quelque chose (uploader un SARIF,
publier une release, activer l'auto-merge) redéclare un bloc `permissions:` qui
ajoute *uniquement* ce périmètre. N'élargissez jamais le bloc de haut niveau
pour satisfaire un seul job.
- **Chaque job fixe `timeout-minutes`.** Le défaut GitHub est de six heures ; une
étape bloquée retiendrait sinon un runner tout ce temps. Chaque plafond est
fixé à quelques fois le temps observé, noté en commentaire à côté.
- **`concurrency` annule les runs remplacés.** Pousser deux fois sur la même
branche ou PR annule le run en cours. La seule exception est `release`, qui met
`cancel-in-progress: false` — on ne veut jamais annuler une publication à
moitié faite.
- **Les scanners de sécurité tournent aussi chaque semaine via `schedule`.**
`codeql` et `scorecard` se relancent sur du code inchangé pour que les
requêtes/checks nouvellement livrés soient appliqués même sans push.
- **Les forks ne peuvent pas lire les secrets.** Les workflows qui ont besoin
d'un secret (p. ex. `sonar`) détectent une PR issue d'un fork et s'abstiennent
au lieu d'échouer ; GitHub n'expose pas les secrets du dépôt à une PR ouverte
depuis un fork.
- **Ce sont les checks *required* qui font barrage.** Plusieurs workflows
(`dependency-review`, `dependabot-automerge`) ne font que *signaler* ou
*activer* — ils ne mergent rien seuls. Ce qui bloque réellement un mauvais
merge, c'est la configuration de protection de branche / ruleset sur `main` qui
marque ces checks comme **required**. C'est un réglage de dépôt, pas quelque
chose qu'un workflow peut imposer pour lui-même.

## Les workflows

### Build & qualité

| Workflow | Rôle |
| --- | --- |
| [`ci`](ci.fr.md) | Construit et teste toute la solution sous Linux et Windows, avec couverture. Le barrage principal. |
| [`sonar`](sonar.fr.md) | Analyse SonarQube Cloud — quality gate et remontée de couverture. |
| [`analyzers`](analyzers.fr.md) | Dogfood des analyzers Roslyn embarqués, y compris sur le plus vieux compilateur supporté (le floor Roslyn). |
| [`commit-lint`](commit-lint.fr.md) | Impose la convention Conventional Commits sur chaque commit de PR, via le même script que le hook local. |

### Sécurité & chaîne d'approvisionnement

| Workflow | Rôle |
| --- | --- |
| [`codeql`](codeql.fr.md) | Analyse statique GitHub CodeQL pour C#, résultats sur le tableau de bord code-scanning. |
| [`dependency-review`](dependency-review.fr.md) | Bloque une PR qui introduit une dépendance vulnérable connue. |
| [`scorecard`](scorecard.fr.md) | OpenSSF Scorecard — note la posture de sécurité du dépôt et alimente le badge du README. |

### Release

| Workflow | Rôle |
| --- | --- |
| [`release`](release.fr.md) | Construit, atteste et publie les packages NuGet sur un tag de version (avec un dry run manuel). |
| [`release-dryrun`](release-dryrun.fr.md) | Répète en continu la partie sans effet de bord de la release (pack + SBOM) sur chaque PR et push. |

### Maintenance des dépendances

| Workflow | Rôle |
| --- | --- |
| [`dependabot-automerge`](dependabot-automerge.fr.md) | Active l'auto-merge sur les mises à jour patch/minor de Dependabot ; laisse les majeures à un humain. |

## Docs mainteneur en rapport

- [Répétition de release à blanc (« dry run » manuel)](../ReleaseDryRun.fr.md) —
le guide opérationnel du dry run manuel via le dispatch `release`.
- [ADR 0001 — Verrouiller le floor Roslyn de l'analyzer](../adr/0001-lock-the-analyzer-roslyn-floor.md)
— pourquoi la version de Roslyn de l'analyzer est gelée, ce que le workflow
`analyzers` fait respecter. *(Rédigé en anglais.)*
- [`CONTRIBUTING.md`](../../CONTRIBUTING.md) — les conventions de commit et de PR
que le workflow `commit-lint` vérifie.
99 changes: 99 additions & 0 deletions maintainers/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# CI/CD workflow reference

🌍 🇬🇧 English (this file) · 🇫🇷 [Français](README.fr.md)

↑ Part of the [maintainer documentation](../README.md).

> Maintainer documentation. This describes the GitHub Actions workflows that
> build, check, and publish FirstClassErrors. It is **not** part of the
> library's user documentation under `doc/`.

## What this is

Each workflow under [`.github/workflows/`](../../.github/workflows/) carries a
fair amount of intent that is easy to break by "cleaning it up": a permission
that is narrow on purpose, a step ordering that guards a specific failure, a
version that is frozen for a product reason. The workflow files themselves hold
the line-by-line rationale in comments — those comments are the source of truth
closest to the code. **These pages are the pedagogical layer above them:** what
each workflow is *for*, when and how it runs, and the handful of things you must
not change without understanding why.

Read the page for a workflow before you touch it. When the page and the YAML
disagree, the YAML wins — and the page should be corrected.

## The cross-cutting conventions

A few decisions are shared by (almost) every workflow. They are documented once
here instead of being repeated on every page.

- **Actions are pinned by commit SHA, not by tag.** A tag like `@v4` can be
moved by its owner to point at new code; a 40-hex SHA cannot. Every `uses:`
therefore pins a SHA with the human-readable tag in a trailing comment
(`# v4`). When you bump an action, change **both**. Dependabot's
`github-actions` ecosystem proposes these bumps.
- **`permissions:` start read-only and widen per job.** The workflow-level block
is the least privilege the workflow needs (usually `contents: read`); a job
that must write something (upload SARIF, publish a release, enable auto-merge)
re-declares a `permissions:` block that adds *only* that scope. Never widen the
top-level block to satisfy one job.
- **Every job sets `timeout-minutes`.** The GitHub default is six hours; a hung
step would otherwise hold a runner for that long. Each cap is set a few times
the observed run time, noted in a comment next to it.
- **`concurrency` cancels superseded runs.** Pushing twice to the same branch or
PR cancels the in-flight run. The one exception is `release`, which sets
`cancel-in-progress: false` — you never want to cancel a half-finished
publish.
- **Security scanners also run weekly on a `schedule`.** `codeql` and
`scorecard` re-run against unchanged code so newly shipped queries/checks are
applied even when nothing was pushed.
- **Forks cannot read secrets.** Workflows that need a secret (e.g. `sonar`)
detect a fork PR and skip rather than fail; GitHub does not expose repository
secrets to a PR raised from a fork.
- **Required checks are the real gate.** Several workflows (`dependency-review`,
`dependabot-automerge`) only *signal* or *enable* — they merge nothing on their
own. What actually blocks a bad merge is the branch-protection / ruleset
configuration on `main` marking these checks as **required**. That is a
repository setting, not something a workflow can enforce for itself.

## The workflows

### Build & quality

| Workflow | Purpose |
| --- | --- |
| [`ci`](ci.en.md) | Build and test the whole solution on Linux and Windows, with coverage. The primary gate. |
| [`sonar`](sonar.en.md) | SonarQube Cloud analysis — quality gate and coverage reporting. |
| [`analyzers`](analyzers.en.md) | Dogfood the bundled Roslyn analyzers, including on the oldest supported compiler (the Roslyn floor). |
| [`commit-lint`](commit-lint.en.md) | Enforce the Conventional Commits convention on every PR commit, using the same script as the local hook. |

### Security & supply chain

| Workflow | Purpose |
| --- | --- |
| [`codeql`](codeql.en.md) | GitHub CodeQL static analysis for C#, results on the code-scanning dashboard. |
| [`dependency-review`](dependency-review.en.md) | Block a PR that introduces a known-vulnerable dependency. |
| [`scorecard`](scorecard.en.md) | OpenSSF Scorecard — scores the repo's security posture and powers the README badge. |

### Release

| Workflow | Purpose |
| --- | --- |
| [`release`](release.en.md) | Build, attest, and publish the NuGet packages on a version tag (with a manual dry run). |
| [`release-dryrun`](release-dryrun.en.md) | Continuously rehearse the side-effect-free part of the release (pack + SBOM) on every PR and push. |

### Dependency maintenance

| Workflow | Purpose |
| --- | --- |
| [`dependabot-automerge`](dependabot-automerge.en.md) | Enable auto-merge on Dependabot patch/minor updates; leave majors for a human. |

## Related maintainer docs

- [Release dry run (manual)](../ReleaseDryRun.en.md) — the operational guide to
the manual `release` dispatch dry run.
- [ADR 0001 — Lock the analyzer Roslyn floor](../adr/0001-lock-the-analyzer-roslyn-floor.md)
— why the analyzer's Roslyn version is frozen, which the `analyzers` workflow
enforces.
- [`CONTRIBUTING.md`](../../CONTRIBUTING.md) — the commit and PR conventions the
`commit-lint` workflow checks.
Loading