Skip to content

Commit 4e7ca85

Browse files
fix(release): retain upstream refs in highlights
Collect owner/repository#number references from Upstream-Ref, Bug-Ref, Refs, and Follow-up-To trailers and append any that an explicit or fallback highlight omits. Apply the same behavior to stack component commits and keep integration-harness fixes out of automatic user-facing highlights. Document the trailer contract and cover component-derived references plus integration-scope suppression in the release-note tests.
1 parent 94a2b10 commit 4e7ca85

3 files changed

Lines changed: 56 additions & 4 deletions

File tree

BRANCHES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ make repackage-release VERSION=MAJOR.MINOR.PATCH
5454

5555
`make repackage-release VERSION=MAJOR.MINOR.PATCH` repairs an existing stable tag without moving it. It dispatches the stable package workflow again, verifies the release archive, checksum asset, Homebrew formula URL, version, and SHA, then syncs the checked-in source formula template to the verified release asset.
5656

57-
Release notes are rendered by [Tools/release/release-notes.py](Tools/release/release-notes.py). The notes include a raw commit audit list, and they promote user-facing `Release-Note:` or `Release-Highlight:` commit trailers into a `Highlights` section before that list. Use single-line trailers that describe the Docker Compose feature, CLI option, or workflow users now get; internal release, CI, and documentation chores should normally omit the trailer or use `Release-Note: none`.
57+
Release notes are rendered by [Tools/release/release-notes.py](Tools/release/release-notes.py). The notes include a raw commit audit list, and they promote user-facing `Release-Note:` or `Release-Highlight:` commit trailers into a `Highlights` section before that list. Use single-line trailers that describe the Docker Compose feature, CLI option, or workflow users now get; internal release, CI, and documentation chores should normally omit the trailer or use `Release-Note: none`. For upstream-driven work, also record the original `owner/repository#number` under `Upstream-Ref:`, `Bug-Ref:`, `Refs:`, or `Follow-up-To:`. The renderer preserves references already written into the highlight and appends any missing references, including highlights collected from stack component commits.
5858

5959
`VERSION_SELECTOR` accepts:
6060

Tools/release/release-notes.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,21 @@
3232
EXPLICIT_RELEASE_LINE_PATTERN = re.compile(
3333
r"^Release-(?:Note|Highlight):[ \t]*(?P<value>.*)$"
3434
)
35+
REFERENCE_TRAILER_PATTERN = re.compile(
36+
r"^(?:Upstream-Ref|Bug-Ref|Refs|Follow-up-To):[ \t]*(?P<value>.*)$",
37+
re.IGNORECASE,
38+
)
39+
GITHUB_REFERENCE_PATTERN = re.compile(
40+
r"(?<![A-Za-z0-9_.-])(?P<reference>[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+)\b"
41+
)
3542

3643

3744
@dataclass(frozen=True)
3845
class CommitSummary:
3946
short_hash: str
4047
subject: str
4148
highlights: tuple[str, ...]
49+
upstream_references: tuple[str, ...]
4250

4351

4452
@dataclass(frozen=True)
@@ -177,6 +185,7 @@ def commits_for_revision(repo: Path, revision: str) -> list[CommitSummary]:
177185
short_hash=short_hash,
178186
subject=subject,
179187
highlights=tuple(explicit_release_highlights_from_body(body)),
188+
upstream_references=tuple(upstream_references_from_body(body)),
180189
)
181190
)
182191
return commits
@@ -213,6 +222,23 @@ def explicit_release_highlights_from_body(body: str) -> list[str]:
213222
return explicit_release_highlights(*values)
214223

215224

225+
def upstream_references_from_body(body: str) -> list[str]:
226+
references: list[str] = []
227+
seen: set[str] = set()
228+
for line in body.splitlines():
229+
trailer = REFERENCE_TRAILER_PATTERN.match(line.strip())
230+
if trailer is None:
231+
continue
232+
for match in GITHUB_REFERENCE_PATTERN.finditer(trailer.group("value")):
233+
reference = match.group("reference")
234+
key = reference.lower()
235+
if key in seen:
236+
continue
237+
seen.add(key)
238+
references.append(reference)
239+
return references
240+
241+
216242
CONVENTIONAL_SUBJECT_PATTERN = re.compile(
217243
r"^(?P<type>[a-z]+)(?:[(](?P<scope>[^)]+)[)])?(?P<breaking>!)?: (?P<summary>.+)$"
218244
)
@@ -221,6 +247,7 @@ def explicit_release_highlights_from_body(body: str) -> list[str]:
221247
"ci",
222248
"deps",
223249
"docs",
250+
"integration",
224251
"quality",
225252
"release",
226253
"status",
@@ -256,6 +283,25 @@ def release_highlights(commits: list[CommitSummary]) -> list[str]:
256283
if fallback is not None:
257284
commit_highlights = [fallback]
258285

286+
missing_references = [
287+
reference
288+
for reference in commit.upstream_references
289+
if not any(
290+
reference.lower() in highlight.lower()
291+
for highlight in commit_highlights
292+
)
293+
]
294+
if commit_highlights and missing_references:
295+
label = (
296+
"Upstream reference"
297+
if len(missing_references) == 1
298+
else "Upstream references"
299+
)
300+
commit_highlights[-1] = (
301+
f"{ensure_sentence(commit_highlights[-1])} {label}: "
302+
f"{', '.join(missing_references)}."
303+
)
304+
259305
for highlight in commit_highlights:
260306
if highlight in seen:
261307
continue

Tools/release/test_release_notes.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def test_main_validation_tag_rerun_keeps_full_stable_range(self) -> None:
8080
self.git(repo, "tag", "--no-sign", "0.6.0")
8181
self.commit(repo, "ci(release): simplify package publishing")
8282
self.commit(repo, "fix(release): commit new tap formula files")
83+
self.commit(repo, "fix(integration): preserve serial rootfs")
8384
self.git(repo, "tag", "--no-sign", "homebrew-main-123-abcdef123456")
8485

8586
notes = module.render_release_notes(
@@ -95,6 +96,7 @@ def test_main_validation_tag_rerun_keeps_full_stable_range(self) -> None:
9596
self.assertIn("Commits since `0.6.0`", notes)
9697
self.assertIn("ci(release): simplify package publishing", notes)
9798
self.assertIn("fix(release): commit new tap formula files", notes)
99+
self.assertIn("fix(integration): preserve serial rootfs", notes)
98100
self.assertNotIn("## Highlights", notes)
99101
self.assertNotIn("chore: initial import", notes)
100102

@@ -266,7 +268,11 @@ def test_stack_component_changes_render_highlights(self) -> None:
266268
Clean up attached exec sessions when the client disappears
267269
while preserving detached exec process lifetime.
268270
269-
Release-Highlight: Improves container compose exec reliability by killing attached exec processes when the client disconnects, preventing orphaned sessions from blocking later exec or stop operations while preserving detached exec; ports the useful cleanup from apple/container#1926 for apple/container#1916.
271+
Upstream-Ref: apple/container#1926
272+
273+
Bug-Ref: apple/container#1916
274+
275+
Release-Highlight: Improves container compose exec reliability by killing attached exec processes when the client disconnects, preventing orphaned sessions from blocking later exec or stop operations while preserving detached exec.
270276
""",
271277
)
272278
current_component_ref = self.git(component, "rev-parse", "HEAD")
@@ -315,8 +321,8 @@ def test_stack_component_changes_render_highlights(self) -> None:
315321
"- Improves container compose exec reliability by killing attached "
316322
"exec processes when the client disconnects, preventing orphaned "
317323
"sessions from blocking later exec or stop operations while "
318-
"preserving detached exec; ports the useful cleanup from "
319-
"apple/container#1926 for apple/container#1916.",
324+
"preserving detached exec. Upstream references: "
325+
"apple/container#1926, apple/container#1916.",
320326
notes,
321327
)
322328
self.assertIn("## Component Changes", notes)

0 commit comments

Comments
 (0)