Break cli/contributing/changelog/archive/__main__.py into four single-responsibility modules under the same package: * versioning.py — pad_version / unpad_version / archive_filename (the canonical <padded-semver>-<date>.md filename schema). * archive_dir.py — existing_archives, archived_releases_from_directory, build_index_section (filesystem operations on docs/changelog/). * changelog_md.py — split_into_entries, md_body_after_header, trim_and_archive (CHANGELOG.md parsing and trimming, plus the always-rebuild-index logic). * package_mirror.py — mirror_to_debian_changelog, mirror_to_rpm_spec_changelog (Debian and RPM regeneration from CHANGELOG.md kept entries, with the trailing notice that points at docs.infinito.nexus and lists archived versions as plain text). __main__.py shrinks to argparse plus a thin orchestration layer that parses CHANGELOG.md once, runs trim_and_archive, reads the populated archive directory, and mirrors into the package files. Add `mirror_to_debian_changelog` and `mirror_to_rpm_spec_changelog` content-idempotency: skip the write when the regenerated content already matches what is on disk so re-runs leave file mtimes unchanged. Verified end-to-end: a follow-up run on a fully-processed tree leaves all three files (CHANGELOG.md, Debian changelog, RPM spec) byte-identical and mtime-stable. Replace `datetime.strptime` calls with `date.fromisoformat` so the DTZ007 lint stays clean: we only need calendar-date parsing for display formatting, not timezone-aware moments. Tests follow the same split: one test_<module>.py per source module, 40 tests in total. Shared fixtures live in _helpers.py (underscore prefix exempts it from the test-file-naming lint), and each test class explicitly mixes TempRepoMixin with unittest.TestCase so the test-files-have-tests AST lint sees the direct unittest base. Update docs/contributing/actions/release.md so the procedure description matches the current behaviour: package changelogs are "mirrored" (not "trimmed") from CHANGELOG.md, and the idempotence section calls out byte-idempotency on each of the three files. Run the CLI once against the live tree to bring packaging/debian/ changelog and packaging/fedora/infinito-nexus.spec back into sync with CHANGELOG.md and the docs/changelog/ archive directory. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.7 KiB
Release 🚀
Contributor guidance for cutting a tagged release of Infinito.Nexus. For local-deploy and edit/redeploy guidance see deploy.md. For commit-message conventions see commit.md.
Versioning 🔢
Release tags follow Semantic Versioning with a leading v: v<MAJOR>.<MINOR>.<PATCH> (for example v7.0.0).
| Component | Bump when |
|---|---|
MAJOR |
A change is incompatible with prior inventories, on-disk role-meta layout, deploy CLI, or stored runtime state. |
MINOR |
New roles, new CLI surfaces, or a non-breaking schema extension lands. |
PATCH |
Bug fixes, doc fixes, or test-only changes that do not move public surface. |
Pre-release suffixes (-rc1, -alpha2, +build.5) MAY be appended; the archive CLI preserves them in archived filenames.
Active Window vs. Archive 🗂️
CHANGELOG.md at the repository root holds the most recent releases inline.
Older releases live one-file-per-version under docs/changelog/, linked from the ## Older Releases section at the bottom of the active changelog.
The active window has a default retention of 7 entries. A higher count is fine for short bursts of releases, but the active changelog SHOULD stay short enough that scrolling it is cheap.
Cutting a Release ✂️
-
Update CHANGELOG.md: prepend a new
## [<version>] - <YYYY-MM-DD>section with the change set, written in the same style as the existing entries. When the release ships package metadata, also update packaging/debian/changelog and the%changelogsection of packaging/fedora/infinito-nexus.spec with the corresponding entries. -
Trim CHANGELOG.md and mirror its kept entries into the package changelogs:
python -m cli.contributing.changelog.archiveThe CLI keeps the most recent 7 entries (override with
--keep N) and processes each of the following files:- CHANGELOG.md is trimmed; every older entry is written to its own file under docs/changelog/ named
<padded-semver>-<release-date>.md, and the## Older Releasesindex at the bottom of the active changelog is rebuilt from the archive directory listing so it never drifts away from what is on disk. - packaging/debian/changelog and the
%changelogsection of packaging/fedora/infinito-nexus.spec are regenerated from the kept CHANGELOG.md entries, with a trailing notice that points at docs.infinito.nexus for further releases and a plain-text list of the archived versions and their dates (no links).
Pass
--dry-runto preview without writing. - CHANGELOG.md is trimmed; every older entry is written to its own file under docs/changelog/ named
-
Run
make testlocally and confirm everything is green. -
Commit the changelog and any archive additions in a single commit with subject
Release version <MAJOR>.<MINOR>.<PATCH>(matching the historical convention). -
Tag the commit:
git tag -a v<MAJOR>.<MINOR>.<PATCH> -m "Release version <MAJOR>.<MINOR>.<PATCH>". -
Push the branch and the tag with git-sign-push, running it outside the sandbox.
CI on Tag Push 🤖
A v* tag push fans out into two CI workflows:
| Workflow | Purpose |
|---|---|
| release-version.yml | Backfills CI images for the version tag if missing, retags them to release tags, and publishes the version-specific image set. |
| release-highest.yml | Decides whether the new version is the highest published version and, if so, advances the latest floating tag. |
Both run from the trusted workflow ref and check out the version tag for the actual build payload, so the release artefacts match the tagged commit byte-for-byte.
Idempotence 🔁
The archive CLI is byte-idempotent. Re-running it on an already-processed tree leaves every file unchanged: CHANGELOG.md is rewritten only when its target content differs from disk, the per-release archive files are never overwritten, and the package changelogs are only rewritten when the regenerated content differs from disk. This makes it safe to run from a pre-commit hook, a release script, or by hand without keeping track of state.
Related Pages 🔗
- docs/changelog/README.md describes the archive directory layout.
- pipeline.md covers the broader CI orchestrator that hands off to the release workflows.