Skip to content

feat(sync): bucket the release-gap report into security, fixes, and chores - #73

Merged
aclark4life merged 1 commit into
mainfrom
sync-backport-report-buckets
Aug 26, 2026
Merged

feat(sync): bucket the release-gap report into security, fixes, and chores#73
aclark4life merged 1 commit into
mainfrom
sync-backport-report-buckets

Conversation

@aclark4life

Copy link
Copy Markdown
Collaborator

Summary

The release-gap report added in #71 listed every upstream commit after the matching downstream release as one flat run. On the first real run that meant reading 25 commits on stable/6.0.x to notice four of them were CVE fixes. This does that triage in dbx.

Commits are bucketed by subject and printed most- to least-urgent:

  • 🔴 securityFixed CVE-YYYY-NNNNN
  • 🔧 fixesFixed #NNNNN
  • ❓ unclassified — no known convention; shown by default, on purpose
  • 🧹 chores — version bumps, stub release notes, translation updates, Refs #NNNNN follow-ups; hidden behind a count

New flags: --all (list everything) and --security-only (CVE fixes only). --all wins when both are passed. Security is a sort order, not a filter, by default — a plain Fixed #NNNNN can be release-worthy too.

Live run of dbx sync django --all-branches --dry-run:

🌿 mongodb-6.0.x → upstream/stable/6.0.x
   django-mongodb-backend 6.0.4 (2026-07-14) … upstream tip
   25 new commit(s): 4 security, 10 fixes, 5 unclassified, 6 chores
   🔴 security
     [6.2 cycle] 13debb622a 2026-08-04 [6.0.x] Fixed CVE-2026-15920 -- Made display_for_field() validate URLs before rendering admin links.
     ...
   (6 more hidden — pass --all)

Being wrong in the safe direction

Added CVE-x, CVE-y to security archive. names CVEs but only edits a docs page, so it is matched as a chore before anything keying off "CVE" — otherwise it inflates the count that is supposed to mean "cut a release now".

The chore patterns stay narrow. Anything unrecognised lands in unclassified and stays visible, so a convention change upstream surfaces as unclassified commits rather than quietly filing real fixes as noise. Expect real commits there — "Fixed minor typos in docs and docstrings" is unclassified today. The summary line counts len(commits) independently of the buckets, so the total still moves if the patterns stop matching.

Presentation only: this does not make the report more correct. "New" is still committer date against the release tag.

Testing

  • 10-case parametrized classifier test, including the Added CVE-… to security archive false positive.
  • Five report-rendering tests: default hiding, --all, --security-only, --all overriding --security-only, and unclassified staying visible.
  • Full suite: 489 passed. Docs build at the same 2 pre-existing warnings.

…hores

The report added in #71 listed every upstream commit that landed after the
matching downstream release as one flat run of lines, which on the first real
run meant reading 25 commits on stable/6.0.x to notice that four of them were
CVE fixes. That is triage work the tool can do: Django's stable branches follow
a tight commit-subject convention, so the commits sort themselves.

Each commit is now bucketed by subject -- security (`Fixed CVE-YYYY-NNNNN`),
fixes (`Fixed #NNNNN`), chores (version bumps, stub release notes, translation
updates, `Refs #NNNNN` follow-ups), and unclassified -- and printed in that
order under a summary line. Chores are hidden behind a count; `--all` lists
them, `--security-only` narrows to the CVE fixes, and `--all` wins when both
are passed. Security is a sort order rather than a filter by default, since a
plain `Fixed #NNNNN` can be release-worthy too.

Two deliberate choices about being wrong. `Added CVE-x, CVE-y to security
archive.` names CVEs but only edits a docs page, so it is matched as a chore
before anything keying off "CVE" -- otherwise it inflates the count that is
supposed to mean "cut a release now". And the chore patterns stay narrow:
anything unrecognised lands in `unclassified` and stays visible, so a change in
upstream's conventions surfaces as unclassified commits rather than quietly
filing real fixes as noise. The summary line counts len(commits) independently
of the buckets, so the total still moves if the patterns stop matching.

This is presentation only -- it does not make the report more correct. "New" is
still committer date against the release tag.
@aclark4life
aclark4life merged commit 134dca2 into main Aug 26, 2026
12 checks passed
@aclark4life
aclark4life deleted the sync-backport-report-buckets branch August 26, 2026 17:20
aclark4life added a commit that referenced this pull request Aug 26, 2026
… branch (#74)

Assembling the next "Backports for 5.2.5 LTS" PR -- the successor to
INTPYTHON-528 Backports for 5.2.4 LTS (#607) -- means finding commits on
django-mongodb-backend's main that are not yet on 5.2.x. Nothing in dbx
answered that. The release-gap report added in #71/#73 looks like it should,
but it compares the *Django fork* against a released *backend* tag, so it never
opens the backend's own branches: wrong repos, wrong pair.

`dbx backports <repo>` compares a repo's main against each of its X.Y.x release
branches and lists what has not made it across, bounded by that branch's latest
release tag.

The window is the whole design. Since the 5.2.x branch point, 164 commits are
missing from 5.2.x -- 82 after filtering release chores, and nearly all of those
are feature work that the supported-versions policy forbids on an LTS branch.
Since the 5.2.4 tag there is one. The older commits are not candidates: they
were already triaged when #607 was assembled, and were either taken or
deliberately skipped.

The window is also load-bearing for correctness. Backports get squashed into a
single commit, so the main commits they carried keep their own patch-ids and
`git log --cherry-mark` recognises only 6 of the 164 as already present.
Bounding by the tag sidesteps that, because everything the squash carried
predates it. The cost is that a fix missed before the last release stays hidden,
so --since and --all widen the window for that hunt.

Branches with no release tag (5.1.x, 5.0.x) are skipped rather than falling back
to the branch point, which dumped 262 and 290 lines of commits that were never
candidates.

Eligibility is left to the reader. Only security fixes and data-loss bugs belong
on LTS branches per the backend's release-process.rst, and no commit subject
marks those. The documented signal is the PR description, but roughly 4 of the
last 60 merged PRs follow it and there is no backport label, so keying off it
would report an empty list and look authoritative. Release chores are filtered
(version bumps including ticket-prefixed ones, dependency bumps, SBOM, stub
notes, release prep, and "Update to Django X.Y", which is the opposite of a
backport); everything else is shown.

Moves latest_release_tag/classify_commit/git_out into utils/release.py so the
two commands share them instead of importing across command modules. sync.py is
refactor-only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant