diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ab9e26..48a8540 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -150,7 +150,8 @@ jobs: # Promoting the changelog is part of cutting the release, not a chore to # remember afterwards: the script fails when `## [Unreleased]` is empty, # so a release with nothing written down stops here instead of shipping - # release notes that say nothing. + # release notes that say nothing. It also fails when an entry has no bold + # one-line summary, because the release notes are those summaries. - name: Promote the changelog env: VERSION: ${{ steps.version.outputs.version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index a960843..afeadfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). This file records what changed for users of the library, in particular -behaviour that changed under an unchanged API. The `## [Unreleased]` section is -what the Release workflow publishes as the GitHub release notes, and a release -with an empty one fails — so entries are added by hand, in the pull request -that earns them. See [Releasing](https://fastapi-cachex.readthedocs.io/en/latest/DEVELOPMENT/#releasing). +behaviour that changed under an unchanged API. Entries are added by hand, in +the pull request that earns them, and each one opens with a bold one-line +summary: `- **What changed.** The details...`. The GitHub release notes list +those summaries and link back here, and a release fails if `## [Unreleased]` +is empty or an entry has no summary. Entries before 0.3.8 predate this format. +See [Releasing](https://fastapi-cachex.readthedocs.io/en/latest/DEVELOPMENT/#releasing). Note that 0.3.3 was never released; 0.3.4 follows 0.3.2. ## [Unreleased] +### Changed + +- **GitHub release notes list one line per change.** Each changelog entry now + opens with a bold one-line summary. The release page shows only those + summaries with their issue links, grouped as in the changelog, and links to + the full entries on the documentation site. The changelog itself keeps the + details. + ## [0.3.7] - 2026-09-25 ### Added diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 1e7da2d..cefe2f6 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -34,10 +34,11 @@ Please refer to our [Development Guide](DEVELOPMENT.md) for detailed instruction is allowed to lag behind them 2. Add an entry to the `## [Unreleased]` section of [CHANGELOG.md](https://github.com/allen0099/FastAPI-CacheX/blob/master/CHANGELOG.md) if your change alters behaviour, adds public - API, or fixes something a user could have hit. That section is what the - release notes are built from, and a release refuses to run on an empty one, - so an omission surfaces — but only at release time, and only as "somebody - forgot", never as which PR it was + API, or fixes something a user could have hit. Open the entry with a bold + one-line summary, `- **What changed.** The details...`: the release notes + list only those summaries, and CI fails on an entry without one. A release + also refuses to run on an empty section, so an omission surfaces — but only + at release time, and only as "somebody forgot", never as which PR it was 3. Update the documentation with any new dependencies, features, or changes. New public API needs a docstring and, if it lives in a module not yet covered, an entry under `docs/api/`; check the site with diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index f3c45df..0d43f68 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -262,8 +262,9 @@ The workflow runs in this order: `vX.Y.Z` is already tagged, locally or on the remote, the run stops here. 3. **The changelog.** `scripts/changelog_release.py` renames `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`, opens a fresh empty `## [Unreleased]` above - it, rewrites the compare links at the bottom, and writes the promoted - section out to be used as the release body. + it, rewrites the compare links at the bottom, and writes the release body: + each entry's bold summary and issue links, and a link to the full entries + on the documentation site. 4. **The permanent part**, kept together at the end: commit the version bump and the promoted changelog, push it, tag, push the tag by refspec, create the GitHub release from the promoted section, publish to PyPI. @@ -295,10 +296,32 @@ different path, not of this one. `CHANGELOG.md` used to be maintained entirely by hand and nothing enforced it: the release notes came from `git log --pretty=format:"- %s (%h)"`, so a release happened whether or not anyone had written down what it meant. That is no -longer true. The release body **is** the `## [Unreleased]` section, and an -empty one fails the run — for a hand-maintained file, "nobody wrote it down" is -far more likely than "nothing changed". The commit list has not been lost: the -release body ends with a compare link against the previous tag. +longer true. The release body is built from the `## [Unreleased]` section, and +an empty one fails the run — for a hand-maintained file, "nobody wrote it down" +is far more likely than "nothing changed". The commit list has not been lost: +the release body ends with a compare link against the previous tag. + +The changelog and the release page serve different readers. The changelog +explains each change in full: what behaviour moved, why, and how to adapt. The +release page is scanned, so it gets one line per change. Every entry therefore +opens with a bold summary, and the release body is just those summaries: + +```markdown +- **Add `CacheManager.add()` for store-if-absent writes.** It uses the same + key prefix, JSON encoding and `default_ttl` as `set()`, and runs on the + backend's atomic `set_if_absent` ... ([#65](https://github.com/allen0099/FastAPI-CacheX/issues/65)) +``` + +becomes ``- Add `CacheManager.add()` for store-if-absent writes. ([#65](...))`` +under the same `### Added` heading. The issue links are carried over from +anywhere in the entry, and the body ends with a link to the version's section +on the documentation site's changelog page. Write the summary for someone +deciding whether this release matters to them: what changed, in the imperative +or as a plain statement, not how. An entry without one fails the run and is +named in the error, and so does a line in the section that is neither a `###` +heading nor a `- ` entry. `tests/test_changelog_release.py` runs the same check +on the real `CHANGELOG.md`, so the pull request that adds an entry without a +summary fails CI instead of the release. Two details of the promotion are worth knowing, because both have bitten this project: diff --git a/scripts/changelog_release.py b/scripts/changelog_release.py index e182267..9e80795 100644 --- a/scripts/changelog_release.py +++ b/scripts/changelog_release.py @@ -2,10 +2,20 @@ `release.yml` calls this so that one manual dispatch does the whole cut: the heading is renamed, a fresh empty `## [Unreleased]` is opened above it, the -compare links at the bottom are rewritten, and the promoted section is written -out to be used verbatim as the GitHub release body. +compare links at the bottom are rewritten, and a short version of the promoted +section is written out as the GitHub release body. -Two rules drive the implementation: +The changelog keeps the full story of each change; the release page lists one +line per change. Every entry therefore opens with a bold one-line summary:: + + - **Add `CacheManager.add()` for store-if-absent writes.** It uses the + same key prefix ... ([#65](https://github.com/.../issues/65)) + +and the release body is those summaries with their issue links, grouped under +the same `###` headings, followed by a link to the full entries on the +documentation site. + +Three rules drive the implementation: * The previous version is **read from the existing headings**, never derived from the new one. 0.3.3 was never released, so `[0.3.4]` has to compare @@ -14,6 +24,8 @@ * An empty `## [Unreleased]` is an error, not an empty release note. The changelog is maintained by hand, so "nothing was written down" and "nothing changed" look identical from here, and only the former is likely. +* An entry without a bold summary is an error too, for the same reason: the + release would otherwise publish a line nobody chose. Run it directly to see what a release would produce:: @@ -36,6 +48,12 @@ ) _LINK = re.compile(r"^\[(?P