Skip to content

Commit f34ab9c

Browse files
authored
Support ChangeLog for the new branch model [skip ci] (#13687)
1, Retrieve commit hashes for a release after enabling the new branch model. 2, Create a query to fetch pull request (PR) information from GitHub using commit hashes. 3, Support retrieving PR ChangeLogs for both the old and new branch models. --------- Signed-off-by: timl <timl@nvidia.com>
1 parent b7538f9 commit f34ab9c

1 file changed

Lines changed: 135 additions & 2 deletions

File tree

scripts/generate-changelog

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Usage:
5555
"""
5656
import os
5757
import sys
58+
import subprocess
5859
from argparse import ArgumentParser
5960
from collections import OrderedDict
6061
from datetime import date, datetime
@@ -77,6 +78,8 @@ LABEL_WONTFIX, LABEL_INVALID, LABEL_DUPLICATE = 'wontfix', 'invalid', 'duplicate
7778
LABEL_BUG = 'bug'
7879
LABEL_PERFORMANCE, LABEL_SHUFFLE = 'performance', 'shuffle'
7980
LABEL_FEATURE, LABEL_SQL = 'feature request', 'SQL'
81+
# The release version from which the release branch changes (e.g., branch-YY.MM --> release/YY.MM)
82+
FROM_RELEASE = '25.12'
8083
# Queries
8184
query_pr = """
8285
query ($baseRefName: String!, $after: String) {
@@ -148,7 +151,111 @@ query ($after: String, $since: DateTime) {
148151
}
149152
}
150153
"""
154+
query_pr_by_commit = """
155+
query ($sha: String!) {
156+
repository(name: "spark-rapids", owner: "NVIDIA") {
157+
commit: object(expression: $sha) {
158+
... on Commit {
159+
associatedPullRequests(first: 10) {
160+
edges {
161+
node {
162+
title
163+
number
164+
state
165+
url
166+
baseRefName
167+
labels(first: 10) {
168+
nodes {
169+
name
170+
}
171+
}
172+
mergedAt
173+
projectItems(first: 10) {
174+
nodes {
175+
roadmap: fieldValueByName(name: "Roadmap") {
176+
... on ProjectV2ItemFieldSingleSelectValue {
177+
name
178+
}
179+
}
180+
}
181+
}
182+
}
183+
}
184+
}
185+
}
186+
}
187+
}
188+
}
189+
"""
151190

191+
# Get the previous release version string(YY.MM format, 2 months before the current version)
192+
# from the current version, e.g. YY.MM2[current] --> YY.MM1[previous]
193+
# param current_ver: the current version, e.g. YY.MM2
194+
# return: the previous version, e.g. YY.MM1,
195+
def get_prev_release_version(current_ver: str):
196+
year, month = map(int, current_ver.split("."))
197+
if month > 2:
198+
new_year = year
199+
new_month = month - 2
200+
else:
201+
new_year = year - 1
202+
new_month = month + 10
203+
prev_ver = f"{new_year:02d}.{new_month:02d}"
204+
return prev_ver
205+
206+
# Get the commit hashes between two branches or release tags.
207+
# param releases: set of release versions, e.g. {'YY.MM2', 'YY.MM1'}
208+
# return: dict of commit hashes, e.g. {YY.MM2: [sha1, sha2, ...], YY.MM1: [shaX, shaY, ...]}
209+
def get_commits(releases: set):
210+
rel_list = list(releases)
211+
ver_commits = {}
212+
count = len(rel_list) # descending version order assured
213+
for i, to_rel in enumerate(rel_list):
214+
to_branch = f"origin/release/{to_rel}"
215+
# commits of releases[YY.MM2, YY.MM1] --> git log "YY.MM2..YY.MM1" for YY.MM2, "YY.MM1..YY.MM0" for YY.MM1
216+
if i + 1 < count:
217+
from_rel = rel_list[i + 1]
218+
else:
219+
from_rel = get_prev_release_version(to_rel)
220+
based_rel = float(from_rel)
221+
if based_rel < float(FROM_RELEASE):
222+
from_branch = f"origin/branch-{from_rel}"
223+
else:
224+
from_branch = f"origin/release/{from_rel}"
225+
226+
# Get all the commit hashes, excluding those commits whose title contains '[bot]'
227+
git_log_args = [
228+
"git", "--no-pager", "log",
229+
f"{from_branch}..{to_branch}", "--pretty=format:%h",
230+
"--grep=[bot]", "-F", "--invert-grep"
231+
]
232+
233+
# Use check=True to raise exception if git fails, making errors explicit
234+
result = subprocess.run(git_log_args, capture_output=True, text=True, check=True)
235+
236+
commits = result.stdout.splitlines()
237+
ver_commits[to_rel] = commits
238+
return ver_commits
239+
240+
# Get the PR list from commit hashes
241+
# param ver_commits, e.g. {v1: [sha1, sha2, ...], v2: [shaX, shaY, ...]}
242+
# param token: the token for the API
243+
# return: list of PRs associated with the commit hashes, e.g. [{PR1 info}, {PR2 info}, ...]
244+
def get_pr_via_commits(ver_commits: set, token: str):
245+
pr_list = []
246+
for version, commits in ver_commits.items():
247+
for sha in commits:
248+
res = post(query=query_pr_by_commit, token=token, variable={'sha': sha})
249+
try:
250+
pr_item = res.json()['data']['repository']['commit']['associatedPullRequests']['edges'][0]['node']
251+
pr_item['ver'] = version
252+
# Handle the case of multiple commits being associated with the same PR
253+
if pr_item not in pr_list and pr_item['mergedAt'] is not None:
254+
pr_list.append(pr_item)
255+
except Exception as e:
256+
print(f"Exception: {e}, commit sha '{sha}' does not have the associated Pull Request")
257+
continue
258+
return pr_list
152259

153260
def process_changelog(resource_type: str, changelog: dict, releases: set, projects: set, token: str):
154261
if resource_type == PULL_REQUESTS:
@@ -175,6 +282,11 @@ def process_changelog(resource_type: str, changelog: dict, releases: set, projec
175282
ver = item["projectItems"]["nodes"][0]['roadmap']['name']
176283
project = f"{RELEASE} {ver}"
177284

285+
# Overwrite project version after the {FROM_RELEASE} if provided
286+
if item.get('ver') is not None:
287+
ver = item['ver']
288+
project = f"{RELEASE} {ver}"
289+
178290
if not release_project(project, projects):
179291
continue
180292

@@ -207,11 +319,30 @@ def process_changelog(resource_type: str, changelog: dict, releases: set, projec
207319
})
208320

209321

322+
# Get the PRs based on the release versions
210323
def process_pr(releases: set, token: str):
211324
pr = []
212-
for rel in releases:
325+
current_ver = list(releases)[0]
326+
current_ver_float = float(current_ver)
327+
based_rel = float(FROM_RELEASE)
328+
329+
# Note: only the last 2 releases are supported/included in the changelog
330+
# Both releases are after {FROM_RELEASE}
331+
if current_ver_float > based_rel:
332+
ver_commits = get_commits(releases)
333+
pr = get_pr_via_commits(ver_commits, token)
334+
# One release is the {FROM_RELEASE}, the other is before the {FROM_RELEASE}
335+
elif current_ver_float == based_rel:
336+
ver_commits = get_commits({FROM_RELEASE})
337+
pr = get_pr_via_commits(ver_commits, token)
338+
prev_ver = get_prev_release_version(current_ver=FROM_RELEASE)
213339
pr.extend(fetch(resource_type=PULL_REQUESTS, token=token,
214-
variables={'baseRefName': f"branch-{rel}"}))
340+
variables={'baseRefName': f"branch-{prev_ver}"}))
341+
# Both releases are before the {FROM_RELEASE}
342+
else:
343+
for rel in releases:
344+
pr.extend(fetch(resource_type=PULL_REQUESTS, token=token,
345+
variables={'baseRefName': f"branch-{rel}"}))
215346
return pr
216347

217348

@@ -301,6 +432,8 @@ def main(rels: str, path: str, token: str):
301432
try:
302433
changelog = {} # changelog dict
303434
releases = {x.strip() for x in rels.split(',')}
435+
# Sort releases in descending order for the follow-up operations
436+
releases = sorted(releases, reverse=True)
304437
projects = {f"{RELEASE} {rel}" for rel in releases}
305438

306439
print('Processing pull requests ...')

0 commit comments

Comments
 (0)