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
31 changes: 31 additions & 0 deletions docs/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,37 @@ remapping this repo's `blob/main` URLs to the checkout.

A repo with a docs site MUST have a `docs` job running `just docs-build` (`mkdocs build --strict`).

### CI9 · Shared ADR check { #CI9 }

The `lint` job MUST run `just adr-check` after `just install lint-ci`. The recipe MUST be this,
byte for byte, and a repo MUST NOT keep a copy of `tests/test_adr_citations.py`:

```just
adr_check_source := "https://raw.githubusercontent.com/modern-python/.github/main/tests/test_adr_citations.py"

# Tracks main on purpose: the shared check is unpinned.
adr-check:
#!/usr/bin/env sh
set -eu
dir="$(mktemp -d .adr-check.XXXXXX)"
trap 'rm -rf "$dir"' EXIT
curl -fsSL "{{ adr_check_source }}" -o "$dir/test_adr_citations.py"
uv run --no-sync pytest --rootdir=. --noconftest -o addopts= "$dir/test_adr_citations.py"
```

The check asserts that every ADR cited anywhere in the repo, by `docs/adr/NNNN-slug.md` path or by
bare `ADR-NNNN` number, exists in `docs/adr/` ([FL4](#FL4)).

*Why:* one rule, one file. Twenty-five byte-identical copies of this test drifted within a month
of being written. The recipe tracks `main` unpinned on purpose: a change to the rule lands once,
with no release and no bump across the org. The trade is that it reaches every repo's next run at
once, so a change to the file in this repo is run against every repo's `main` before it merges,
and a repo's own pull request sees the fix only once its branch carries it. `--rootdir=.` makes
the repo the scanned root; `--noconftest` and `-o addopts=` keep the repo's conftests and
coverage gate out of a one-file run; the temporary directory sits inside the repo because
plugins such as `pytest-alembic` reject collected files outside the root. `adr_check_source` can
be pointed at a `file://` path to run an unmerged version of the check.

## Release

### RL1 · Tags { #RL1 }
Expand Down
3 changes: 3 additions & 0 deletions tests/test_adr_citations.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def unresolved_citations(root: pathlib.Path) -> list[tuple[str, str]]:
def test_every_adr_citation_in_the_repo_resolves(pytestconfig: pytest.Config) -> None:
"""INVARIANT: every ADR named in this repo resolves, by full path or by bare `ADR-NNNN` number.

Every org repo fetches this file from `main` and runs it against its own tree (CI9), so a change
here reaches all of them on their next run.

Broken by renaming, renumbering or pruning an ADR without following its citations. The offline
link gate reads Markdown links only, so a path in a docstring, a comment, a guard message or a
`pyproject.toml` dependency rationale is otherwise checked by nothing, and neither is the bare
Expand Down
Loading