88import json
99import os
1010import re
11- from typing import Any , Optional
11+ from typing import Any , cast , Dict , List , Optional
1212
1313from urllib .error import HTTPError
1414
15- from github_utils import gh_fetch_url , gh_post_pr_comment
15+ from github_utils import gh_fetch_url , gh_post_pr_comment , gh_query_issues_by_labels
1616
1717from gitutils import get_git_remote_name , get_git_repo_dir , GitRepo
1818from trymerge import get_pr_commit_sha , GitHubPR
2424 "critical" ,
2525 "fixnewfeature" ,
2626}
27+ RELEASE_BRANCH_REGEX = re .compile (r"release/(?P<version>.+)" )
2728
2829
2930def parse_args () -> Any :
@@ -34,7 +35,7 @@ def parse_args() -> Any:
3435 "--onto-branch" , type = str , required = True , help = "the target release branch"
3536 )
3637 parser .add_argument (
37- "--github-actor" , type = str , required = True , help = "all the world’ s a stage"
38+ "--github-actor" , type = str , required = True , help = "all the world' s a stage"
3839 )
3940 parser .add_argument (
4041 "--classification" ,
@@ -63,6 +64,33 @@ def get_merge_commit_sha(repo: GitRepo, pr: GitHubPR) -> Optional[str]:
6364 return commit_sha if pr .is_closed () else None
6465
6566
67+ def get_release_version (onto_branch : str ) -> Optional [str ]:
68+ """
69+ Return the release version if the target branch is a release branch
70+ """
71+ m = re .match (RELEASE_BRANCH_REGEX , onto_branch )
72+ return m .group ("version" ) if m else ""
73+
74+
75+ def get_tracker_issues (
76+ org : str , project : str , onto_branch : str
77+ ) -> List [Dict [str , Any ]]:
78+ """
79+ Find the tracker issue from the repo. The tracker issue needs to have the title
80+ like [VERSION] Release Tracker following the convention on PyTorch
81+ """
82+ version = get_release_version (onto_branch )
83+ if not version :
84+ return []
85+
86+ tracker_issues = gh_query_issues_by_labels (org , project , labels = ["release tracker" ])
87+ if not tracker_issues :
88+ return []
89+
90+ # Figure out the tracker issue from the list by looking at the title
91+ return [issue for issue in tracker_issues if version in issue .get ("title" , "" )]
92+
93+
6694def cherry_pick (
6795 github_actor : str ,
6896 repo : GitRepo ,
@@ -82,17 +110,49 @@ def cherry_pick(
82110 )
83111
84112 try :
113+ org , project = repo .gh_owner_and_name ()
114+
115+ cherry_pick_pr = ""
85116 if not dry_run :
86- org , project = repo .gh_owner_and_name ()
87117 cherry_pick_pr = submit_pr (repo , pr , cherry_pick_branch , onto_branch )
88118
89- msg = f"The cherry pick PR is at { cherry_pick_pr } "
90- if fixes :
91- msg += f" and it is linked with issue { fixes } "
92- elif classification in REQUIRES_ISSUE :
93- msg += f" and it is recommended to link a { classification } cherry pick PR with an issue"
119+ tracker_issues_comments = []
120+ tracker_issues = get_tracker_issues (org , project , onto_branch )
121+ for issue in tracker_issues :
122+ issue_number = int (str (issue .get ("number" , "0" )))
123+ if not issue_number :
124+ continue
125+
126+ res = cast (
127+ Dict [str , Any ],
128+ post_tracker_issue_comment (
129+ org ,
130+ project ,
131+ issue_number ,
132+ pr .pr_num ,
133+ cherry_pick_pr ,
134+ classification ,
135+ fixes ,
136+ dry_run ,
137+ ),
138+ )
139+
140+ comment_url = res .get ("html_url" , "" )
141+ if comment_url :
142+ tracker_issues_comments .append (comment_url )
94143
95- post_comment (org , project , pr .pr_num , msg )
144+ msg = f"The cherry pick PR is at { cherry_pick_pr } "
145+ if fixes :
146+ msg += f" and it is linked with issue { fixes } ."
147+ elif classification in REQUIRES_ISSUE :
148+ msg += f" and it is recommended to link a { classification } cherry pick PR with an issue."
149+
150+ if tracker_issues_comments :
151+ msg += " The following tracker issues are updated:\n "
152+ for tracker_issues_comment in tracker_issues_comments :
153+ msg += f"* { tracker_issues_comment } \n "
154+
155+ post_pr_comment (org , project , pr .pr_num , msg , dry_run )
96156
97157 finally :
98158 if current_branch :
@@ -164,7 +224,9 @@ def submit_pr(
164224 raise RuntimeError (msg ) from error
165225
166226
167- def post_comment (org : str , project : str , pr_num : int , msg : str ) -> None :
227+ def post_pr_comment (
228+ org : str , project : str , pr_num : int , msg : str , dry_run : bool = False
229+ ) -> List [Dict [str , Any ]]:
168230 """
169231 Post a comment on the PR itself to point to the cherry picking PR when success
170232 or print the error when failure
@@ -187,7 +249,35 @@ def post_comment(org: str, project: str, pr_num: int, msg: str) -> None:
187249 comment = "\n " .join (
188250 (f"### Cherry picking #{ pr_num } " , f"{ msg } " , "" , f"{ internal_debugging } " )
189251 )
190- gh_post_pr_comment (org , project , pr_num , comment )
252+ return gh_post_pr_comment (org , project , pr_num , comment , dry_run )
253+
254+
255+ def post_tracker_issue_comment (
256+ org : str ,
257+ project : str ,
258+ issue_num : int ,
259+ pr_num : int ,
260+ cherry_pick_pr : str ,
261+ classification : str ,
262+ fixes : str ,
263+ dry_run : bool = False ,
264+ ) -> List [Dict [str , Any ]]:
265+ """
266+ Post a comment on the tracker issue (if any) to record the cherry pick
267+ """
268+ comment = "\n " .join (
269+ (
270+ "Link to landed trunk PR (if applicable):" ,
271+ f"* https://github.com/{ org } /{ project } /pull/{ pr_num } " ,
272+ "" ,
273+ "Link to release branch PR:" ,
274+ f"* { cherry_pick_pr } " ,
275+ "" ,
276+ "Criteria Category:" ,
277+ " - " .join ((classification .capitalize (), fixes .capitalize ())),
278+ )
279+ )
280+ return gh_post_pr_comment (org , project , issue_num , comment , dry_run )
191281
192282
193283def main () -> None :
@@ -219,7 +309,7 @@ def main() -> None:
219309
220310 except RuntimeError as error :
221311 if not args .dry_run :
222- post_comment (org , project , pr_num , str (error ))
312+ post_pr_comment (org , project , pr_num , str (error ))
223313 else :
224314 raise error
225315
0 commit comments