Skip to content

Commit 0ed86c9

Browse files
committed
fix: finalize release note generator and update goldens and Spanner changelog
- Implements inclusive lower boundary and other refinements in script. - Updates golden files to match the final output. - Updates Spanner changelog with the final backfilled notes.
1 parent 7eb4135 commit 0ed86c9

4 files changed

Lines changed: 111 additions & 92 deletions

File tree

.github/release-note-generation/generate_module_notes.py

Lines changed: 105 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,109 @@ def find_version_boundaries(file_path, pattern, target_version, module=None):
5454
found_ver = match.group(1)
5555

5656
if found_ver:
57-
if found_ver == target_version and not target_release_commit:
57+
if found_ver == target_version:
5858
target_release_commit = commit
5959
break # Stop as soon as we find the target release!
6060

61-
if found_ver != target_version and "-SNAPSHOT" not in found_ver:
62-
if not prev_version:
63-
prev_version = found_ver
64-
first_prev_commit = commit
65-
elif found_ver != prev_version:
66-
# Found a newer stable version before hitting target!
67-
prev_version = found_ver
68-
first_prev_commit = commit
61+
# Track the first occurrence of the latest stable version before target
62+
if found_ver != target_version and "-SNAPSHOT" not in found_ver and (not prev_version or found_ver != prev_version):
63+
prev_version = found_ver
64+
first_prev_commit = commit
6965

7066
return first_prev_commit, target_release_commit, prev_version
7167
except SystemExit:
7268
return None, None, None
7369

7470

71+
def verify_commit(commit_hash, directory, module, allowed_versions):
72+
"""Verifies if a commit belongs to the release based on file state."""
73+
if directory == ".":
74+
pom_path = "gapic-libraries-bom/pom.xml"
75+
else:
76+
pom_path = f"{directory}/pom.xml"
77+
78+
# Check if file exists at that commit to avoid noisy errors
79+
check_cmd = ["git", "cat-file", "-e", f"{commit_hash}:{pom_path}"]
80+
check_result = subprocess.run(check_cmd, stderr=subprocess.PIPE)
81+
if check_result.returncode != 0:
82+
return False
83+
84+
try:
85+
content = run_cmd(["git", "show", f"{commit_hash}:{pom_path}"])
86+
# Allow optional <packaging> tag in between artifactId and version
87+
pattern = re.compile(rf"<artifactId>{re.escape(module)}</artifactId>\s*(?:<packaging>[^<]+</packaging>\s*)?<version>([^<]+)</version>", re.DOTALL)
88+
89+
match = pattern.search(content)
90+
if match and match.group(1) in allowed_versions:
91+
return True
92+
except SystemExit:
93+
pass
94+
95+
return False
96+
97+
98+
def parse_commit_overrides(commit_data, short_name, prefix_regex, commit_hash, categorize_callback):
99+
"""Parses commit overrides and calls callback for each item."""
100+
match = re.search(r"BEGIN_COMMIT_OVERRIDE(.*?)END_COMMIT_OVERRIDE", commit_data, re.DOTALL)
101+
if not match:
102+
return False
103+
104+
override_content = match.group(1)
105+
current_item = []
106+
in_module_item = False
107+
108+
for line in override_content.splitlines():
109+
line_stripped = line.strip()
110+
if not line_stripped:
111+
continue
112+
113+
is_new_item = prefix_regex.match(line_stripped)
114+
115+
if is_new_item:
116+
if in_module_item and current_item:
117+
categorize_callback(commit_hash, " ".join(current_item))
118+
current_item = []
119+
in_module_item = False
120+
121+
should_include = False
122+
if short_name:
123+
if f"[{short_name}]" in line_stripped:
124+
should_include = True
125+
else:
126+
should_include = True
127+
128+
if should_include:
129+
in_module_item = True
130+
current_item.append(line_stripped)
131+
elif in_module_item:
132+
if line_stripped.startswith(("PiperOrigin-RevId:", "Source Link:")):
133+
continue
134+
if line_stripped in ("END_NESTED_COMMIT", "BEGIN_NESTED_COMMIT"):
135+
continue
136+
current_item.append(line_stripped)
137+
138+
if in_module_item and current_item:
139+
categorize_callback(commit_hash, " ".join(current_item))
140+
141+
return True
142+
143+
144+
def get_tag_or_commit(commit_hash):
145+
"""Returns the tag pointing at the commit if there is exactly one, else the commit hash."""
146+
if not commit_hash:
147+
return None
148+
try:
149+
# Remove ~1 if present to find the actual tag pointing at the commit
150+
clean_hash = commit_hash.split("~")[0]
151+
tags_output = run_cmd(["git", "tag", "--points-at", clean_hash])
152+
tags = [line.strip() for line in tags_output.splitlines() if line.strip()]
153+
if len(tags) == 1:
154+
return tags[0]
155+
except SystemExit:
156+
pass
157+
return commit_hash
158+
159+
75160
def main():
76161
parser = argparse.ArgumentParser(
77162
description="Generate release notes based on commit history for a specific module."
@@ -104,7 +189,7 @@ def main():
104189
target_commit = None
105190
if target_release_commit:
106191
target_commit = target_release_commit
107-
print(f"Found target release commit at {target_release_commit}. Using exclusive upper boundary {target_commit}", file=sys.stderr)
192+
print(f"Found target release commit at {target_release_commit}. Using inclusive upper boundary {target_commit}", file=sys.stderr)
108193

109194
if not target_commit:
110195
print(f"Target version {target_version} not found in history of {pom_path}.", file=sys.stderr)
@@ -121,7 +206,7 @@ def main():
121206
"git",
122207
"log",
123208
"--format=%H %s%n%b%n--END_OF_COMMIT--",
124-
f"{prev_commit}..{target_commit}" if prev_commit else target_commit,
209+
f"{prev_commit}~1..{target_commit}" if prev_commit else target_commit,
125210
]
126211
if directory != ".":
127212
notes_cmd.extend(["--", directory])
@@ -179,95 +264,30 @@ def categorize_and_append(commit_hash, text):
179264
body = "\n".join(lines[1:])
180265

181266
# Verify if commit belongs to this release based on file state
182-
should_include = False
183267
target_snapshot = f"{target_version}-SNAPSHOT"
184268
allowed_versions = (prev_version, target_snapshot) if prev_version else (target_snapshot,)
185-
try:
186-
if directory == ".":
187-
pom_path = "gapic-libraries-bom/pom.xml"
188-
else:
189-
pom_path = f"{directory}/pom.xml"
190-
# Check if file exists at that commit to avoid noisy errors
191-
check_cmd = ["git", "cat-file", "-e", f"{commit_hash}:{pom_path}"]
192-
check_result = subprocess.run(check_cmd, stderr=subprocess.PIPE)
193-
if check_result.returncode == 0:
194-
content = run_cmd(["git", "show", f"{commit_hash}:{pom_path}"])
195-
if directory == ".":
196-
pattern = re.compile(r"<artifactId>gapic-libraries-bom</artifactId>\s*<packaging>pom</packaging>\s*<version>([^<]+)</version>", re.DOTALL)
197-
else:
198-
pattern = re.compile(rf"<artifactId>{re.escape(module)}</artifactId>\s*<version>([^<]+)</version>", re.DOTALL)
199-
200-
match = pattern.search(content)
201-
202-
203-
if match and match.group(1) in allowed_versions:
204-
should_include = True
205-
except SystemExit:
206-
pass
207-
208-
if not should_include:
269+
270+
target_module = "gapic-libraries-bom" if directory == "." else module
271+
if not verify_commit(commit_hash, directory, target_module, allowed_versions):
209272
continue
210273

211274
# Check for override in the entire message
212275
if "BEGIN_COMMIT_OVERRIDE" in body or "BEGIN_COMMIT_OVERRIDE" in subject:
213-
match = re.search(r"BEGIN_COMMIT_OVERRIDE(.*?)END_COMMIT_OVERRIDE", commit_data, re.DOTALL)
214-
if match:
215-
override_content = match.group(1)
216-
current_item = []
217-
in_module_item = False
218-
219-
for line in override_content.splitlines():
220-
line_stripped = line.strip()
221-
if not line_stripped:
222-
continue
223-
224-
# Check if it's a new item using regex
225-
is_new_item = prefix_regex.match(line_stripped)
226-
227-
if is_new_item:
228-
# If we were in an item, save it
229-
if in_module_item and current_item:
230-
categorize_and_append(commit_hash, " ".join(current_item))
231-
current_item = []
232-
in_module_item = False
233-
234-
# Check if this new item is for our module or if we want all
235-
should_include = False
236-
if args.short_name:
237-
if f"[{args.short_name}]" in line_stripped:
238-
should_include = True
239-
else:
240-
should_include = True
241-
242-
if should_include:
243-
in_module_item = True
244-
current_item.append(line_stripped)
245-
elif in_module_item:
246-
# Continuation line
247-
if line_stripped.startswith(("PiperOrigin-RevId:", "Source Link:")):
248-
continue
249-
if line_stripped in ("END_NESTED_COMMIT", "BEGIN_NESTED_COMMIT"):
250-
continue
251-
current_item.append(line_stripped)
252-
253-
# Save the last item if we were in one
254-
if in_module_item and current_item:
255-
categorize_and_append(commit_hash, " ".join(current_item))
256-
257-
# Ignore the title since there was an override
258-
continue
276+
if parse_commit_overrides(commit_data, args.short_name, prefix_regex, commit_hash, categorize_and_append):
277+
continue
259278

260279
# Fallback to title check if no override
261280
if prefix_regex.match(subject):
262281
categorize_and_append(commit_hash, subject)
263282

264283
# Get dates and build header
265-
# We use ~1 to be exclusive of the boundary as requested earlier, but let's be careful.
266-
# If prev_commit is None, we don't set a range.
267284
target_date = run_cmd(["git", "log", "-1", "--format=%cI", target_commit]).strip()
268285
date_str = target_date.split("T")[0] # Get YYYY-MM-DD
269286

270-
compare_url = f"https://github.com/googleapis/google-cloud-java/compare/{prev_commit}...{target_commit}" if prev_commit else f"https://github.com/googleapis/google-cloud-java/commit/{target_commit}"
287+
prev_ref = get_tag_or_commit(prev_commit)
288+
target_ref = get_tag_or_commit(target_commit)
289+
290+
compare_url = f"https://github.com/googleapis/google-cloud-java/compare/{prev_ref}...{target_ref}" if prev_ref else f"https://github.com/googleapis/google-cloud-java/commit/{target_ref}"
271291

272292
print(f"## [{target_version}]({compare_url}) ({date_str})")
273293
print()

.github/release-note-generation/testdata/golden_java-run_0.71.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## [0.71.0](https://github.com/googleapis/google-cloud-java/compare/12e01b2413f...bb7de963ded) (2025-08-08)
1+
## [0.71.0](https://github.com/googleapis/google-cloud-java/compare/v1.64.0...v1.65.0) (2025-08-08)
22

33
### ⚠ BREAKING CHANGES
44

.github/release-note-generation/testdata/golden_root_1.85.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## [1.85.0](https://github.com/googleapis/google-cloud-java/compare/6bef068b4d8...fabe31c2a9d) (2026-04-13)
1+
## [1.85.0](https://github.com/googleapis/google-cloud-java/compare/6bef068b4d8...v1.85.0) (2026-04-13)
22

33
### Features
44

java-spanner/CHANGELOG.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
* feat(spanner): add shared endpoint cooldowns for location-aware rerouting (#12845) ([f5f273b](https://github.com/googleapis/google-cloud-java/commit/f5f273ba0bd6b7ca9a8be7d1b5a89211ef5ff9fc))
88

99

10-
## [6.116.0](https://github.com/googleapis/google-cloud-java/compare/6bef068b4d8...fabe31c2a9d) (2026-04-13)
10+
## [6.116.0](https://github.com/googleapis/google-cloud-java/compare/6bef068b4d8...v1.85.0) (2026-04-13)
1111

1212
* No change
1313

14-
## [6.115.0](https://github.com/googleapis/google-cloud-java/compare/c2147fc9ab7...6bef068b4d8) (2026-04-10)
14+
## [6.115.0](https://github.com/googleapis/google-cloud-java/compare/v1.83.0-sdk-platform-java...6bef068b4d8) (2026-04-10)
1515

1616
### Bug Fixes
1717

@@ -20,9 +20,10 @@
2020
* fix(spanner): fix grpc-gcp affinity cleanup and multiplexed channel usage leaks (#12726) ([55c9857](https://github.com/googleapis/google-cloud-java/commit/55c985776700b1219ece39a519021030eb18d927))
2121
* fix(spanner): ensure executeQueryAsync is non-blocking (#12715) ([b7e34d2](https://github.com/googleapis/google-cloud-java/commit/b7e34d22191df6bf7fc2aa30bd83234312dd89c6))
2222
* fix(spanner): honor built-in metrics opt-out for gRPC metrics exporter (#12711) ([57baaea](https://github.com/googleapis/google-cloud-java/commit/57baaeaae5ef1f819e04b253dfcf570499ccc110))
23+
* fix: update Version.java and correct spanner version for 1.83.0 release (#12712) ([c2147fc](https://github.com/googleapis/google-cloud-java/commit/c2147fc9ab767b0546e6f71483e3f0af1a99740c))
2324

2425

25-
## [6.114.0](https://github.com/googleapis/google-cloud-java/compare/55c4e0f125c...c2147fc9ab7) (2026-04-08)
26+
## [6.114.0](https://github.com/googleapis/google-cloud-java/compare/55c4e0f125c...v1.83.0-sdk-platform-java) (2026-04-08)
2627

2728
* No change
2829

@@ -31,8 +32,6 @@
3132
* No change
3233

3334

34-
35-
3635
## [6.112.0](https://github.com/googleapis/java-spanner/compare/v6.111.1...v6.112.0) (2026-03-17)
3736

3837

0 commit comments

Comments
 (0)