1
1
from os import getenv
2
+ from typing import Optional
2
3
3
4
import requests
4
5
5
6
6
7
def get_github_prs (token : str , owner : str , repo : str , label : str = "" , state : str = "all" ) -> list [dict ]:
7
8
"""
8
- Fetches pull requests from a GitHub repository that match a given milestone and label .
9
+ Fetches pull requests from a GitHub repository that match a given label and state .
9
10
10
11
Args:
11
12
token (str): GitHub token.
@@ -23,39 +24,10 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st
23
24
"Accept" : "application/vnd.github.v3+json" ,
24
25
}
25
26
26
- milestone_id = None
27
- milestone_url = f"https://api.github.com/repos/{ owner } /{ repo } /milestones"
28
- params = {"state" : "open" }
29
-
30
- try :
31
- response = requests .get (milestone_url , headers = headers , params = params )
32
- response .raise_for_status ()
33
- milestones = response .json ()
34
-
35
- if len (milestones ) > 2 :
36
- print ("More than two milestones found, unable to determine the milestone required." )
37
- exit (1 )
38
-
39
- # milestones.pop()
40
- for ms in milestones :
41
- if ms ["title" ] != "Future" :
42
- milestone_id = ms ["number" ]
43
- print (f"Gathering PRs with milestone { ms ['title' ]} ..." )
44
- break
45
-
46
- if not milestone_id :
47
- print (f"No suitable milestone found in repository '{ owner } /{ repo } '." )
48
- exit (1 )
49
-
50
- except requests .exceptions .RequestException as e :
51
- print (f"Error fetching milestones: { e } " )
52
- exit (1 )
53
-
54
- # This endpoint allows filtering by milestone and label. A PR in GH's perspective is a type of issue.
27
+ # This endpoint allows filtering by label(and milestone). A PR in GH's perspective is a type of issue.
55
28
prs_url = f"https://api.github.com/repos/{ owner } /{ repo } /issues"
56
29
params = {
57
30
"state" : state ,
58
- "milestone" : milestone_id ,
59
31
"labels" : label ,
60
32
"per_page" : 100 ,
61
33
}
@@ -83,14 +55,18 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st
83
55
return all_prs
84
56
85
57
86
- def get_prs (pull_request_items : list [dict ], label : str = "" , state : str = "all" ) -> list [dict ]:
58
+ def get_prs (
59
+ pull_request_items : list [dict ], label : str = "" , state : str = "all" , milestone_title : Optional [str ] = None
60
+ ) -> list [dict ]:
87
61
"""
88
62
Returns a list of pull requests after applying the label and state filters.
89
63
90
64
Args:
91
65
pull_request_items (list[dict]): List of PR items.
92
66
label (str): The label name. Filter is not applied when empty string.
93
67
state (str): State of PR, e.g. open, closed, all
68
+ milestone_title (Optional[str]): The milestone title to filter by. This is the milestone number you created
69
+ in GitHub, e.g. '1.20.0'. If None, no milestone filtering is applied.
94
70
95
71
Returns:
96
72
list: A list of dictionaries, where each dictionary represents a pull request.
@@ -99,22 +75,32 @@ def get_prs(pull_request_items: list[dict], label: str = "", state: str = "all")
99
75
pr_list = []
100
76
count = 0
101
77
for pr in pull_request_items :
102
- if state in [pr ["state" ], "all" ] and (not label or [item for item in pr ["labels" ] if item ["name" ] == label ]):
103
- pr_list .append (pr )
104
- count += 1
78
+ if state not in [pr ["state" ], "all" ]:
79
+ continue
80
+
81
+ if label and not [item for item in pr ["labels" ] if item ["name" ] == label ]:
82
+ continue
105
83
106
- print (f"Found { count } PRs with { label if label else 'no filter on' } label and state as { state } " )
84
+ if milestone_title :
85
+ if pr ["milestone" ] is None or pr ["milestone" ]["title" ] != milestone_title :
86
+ continue
87
+
88
+ pr_list .append (pr )
89
+ count += 1
90
+
91
+ print (
92
+ f"Found { count } PRs with { label if label else 'no filter on' } label, state as { state } , and milestone { pr ["milestone" ] if pr ["milestone" ] is not None else "None" } "
93
+ )
107
94
108
95
return pr_list
109
96
110
- def get_prs_assignees (pull_request_items : list [dict ], label : str = "" , state : str = "all" ) -> list [str ]:
97
+
98
+ def get_prs_assignees (pull_request_items : list [dict ]) -> list [str ]:
111
99
"""
112
- Returns a list of pull request assignees after applying the label and state filters , excludes jjw24.
100
+ Returns a list of pull request assignees, excludes jjw24.
113
101
114
102
Args:
115
- pull_request_items (list[dict]): List of PR items.
116
- label (str): The label name. Filter is not applied when empty string.
117
- state (str): State of PR, e.g. open, closed, all
103
+ pull_request_items (list[dict]): List of PR items to get the assignees from.
118
104
119
105
Returns:
120
106
list: A list of strs, where each string is an assignee name. List is not distinct, so can contain
@@ -123,13 +109,13 @@ def get_prs_assignees(pull_request_items: list[dict], label: str = "", state: st
123
109
"""
124
110
assignee_list = []
125
111
for pr in pull_request_items :
126
- if state in [pr ["state" ], "all" ] and (not label or [item for item in pr ["labels" ] if item ["name" ] == label ]):
127
- [assignee_list .append (assignee ["login" ]) for assignee in pr ["assignees" ] if assignee ["login" ] != "jjw24" ]
112
+ [assignee_list .append (assignee ["login" ]) for assignee in pr ["assignees" ] if assignee ["login" ] != "jjw24" ]
128
113
129
- print (f"Found { len (assignee_list )} assignees with { label if label else 'no filter on' } label and state as { state } " )
114
+ print (f"Found { len (assignee_list )} assignees" )
130
115
131
116
return assignee_list
132
117
118
+
133
119
def get_pr_descriptions (pull_request_items : list [dict ]) -> str :
134
120
"""
135
121
Returns the concatenated string of pr title and number in the format of
@@ -207,30 +193,42 @@ def update_pull_request_description(token: str, owner: str, repo: str, pr_number
207
193
208
194
print (f"Fetching { state } PRs for { repository_owner } /{ repository_name } ..." )
209
195
210
- pull_requests = get_github_prs (github_token , repository_owner , repository_name )
196
+ # First, get all PRs to find the release PR and determine the milestone
197
+ all_pull_requests = get_github_prs (github_token , repository_owner , repository_name )
211
198
212
- if not pull_requests :
213
- print ("No matching pull requests found" )
199
+ if not all_pull_requests :
200
+ print ("No pull requests found" )
214
201
exit (1 )
215
202
216
- print (f"\n Found total of { len (pull_requests )} pull requests" )
203
+ print (f"\n Found total of { len (all_pull_requests )} pull requests" )
217
204
218
- release_pr = get_prs (pull_requests , "release" , "open" )
205
+ release_pr = get_prs (all_pull_requests , "release" , "open" )
219
206
220
207
if len (release_pr ) != 1 :
221
208
print (f"Unable to find the exact release PR. Returned result: { release_pr } " )
222
209
exit (1 )
223
210
224
211
print (f"Found release PR: { release_pr [0 ]['title' ]} " )
225
212
226
- enhancement_prs = get_prs (pull_requests , "enhancement" , "closed" )
227
- bug_fix_prs = get_prs (pull_requests , "bug" , "closed" )
213
+ release_milestone_title = release_pr [0 ].get ("milestone" , {}).get ("title" , None )
214
+
215
+ if not release_milestone_title :
216
+ print ("Release PR does not have a milestone assigned." )
217
+ exit (1 )
218
+
219
+ print (f"Using milestone number: { release_milestone_title } " )
220
+
221
+ enhancement_prs = get_prs (all_pull_requests , "enhancement" , "closed" , release_milestone_title )
222
+ bug_fix_prs = get_prs (all_pull_requests , "bug" , "closed" , release_milestone_title )
223
+
224
+ if len (enhancement_prs ) == 0 and len (bug_fix_prs ) == 0 :
225
+ print (f"No PRs with { release_milestone_title } milestone were found" )
228
226
229
227
description_content = "# Release notes\n "
230
228
description_content += f"## Features\n { get_pr_descriptions (enhancement_prs )} " if enhancement_prs else ""
231
229
description_content += f"## Bug fixes\n { get_pr_descriptions (bug_fix_prs )} " if bug_fix_prs else ""
232
230
233
- assignees = list (set (get_prs_assignees (pull_requests , "enhancement" , "closed" ) + get_prs_assignees (pull_requests , "bug" , "closed" )))
231
+ assignees = list (set (get_prs_assignees (enhancement_prs ) + get_prs_assignees (bug_fix_prs )))
234
232
assignees .sort (key = str .lower )
235
233
236
234
description_content += f"### Authors:\n { ', ' .join (assignees )} "
0 commit comments