11from  os  import  getenv 
2+ from  typing  import  Optional 
23
34import  requests 
45
56
67def  get_github_prs (token : str , owner : str , repo : str , label : str  =  "" , state : str  =  "all" ) ->  list [dict ]:
78    """ 
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 . 
910
1011    Args: 
1112        token (str): GitHub token. 
@@ -23,39 +24,10 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st
2324        "Accept" : "application/vnd.github.v3+json" ,
2425    }
2526
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. 
5528    prs_url  =  f"https://api.github.com/repos/{ owner }  /{ repo }  /issues" 
5629    params  =  {
5730        "state" : state ,
58-         "milestone" : milestone_id ,
5931        "labels" : label ,
6032        "per_page" : 100 ,
6133    }
@@ -83,14 +55,18 @@ def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: st
8355    return  all_prs 
8456
8557
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 ]:
8761    """ 
8862    Returns a list of pull requests after applying the label and state filters. 
8963
9064    Args: 
9165        pull_request_items (list[dict]): List of PR items. 
9266        label (str): The label name. Filter is not applied when empty string. 
9367        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. 
9470
9571    Returns: 
9672        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")
9975    pr_list  =  []
10076    count  =  0 
10177    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 
10583
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+     )
10794
10895    return  pr_list 
10996
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 ]:
11199    """ 
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. 
113101
114102    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. 
118104
119105    Returns: 
120106        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
123109    """ 
124110    assignee_list  =  []
125111    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" ]
128113
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" )
130115
131116    return  assignee_list 
132117
118+ 
133119def  get_pr_descriptions (pull_request_items : list [dict ]) ->  str :
134120    """ 
135121    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
207193
208194    print (f"Fetching { state }   PRs for { repository_owner }  /{ repository_name }   ..." )
209195
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 )
211198
212-     if  not  pull_requests :
213-         print ("No matching  pull requests found" )
199+     if  not  all_pull_requests :
200+         print ("No pull requests found" )
214201        exit (1 )
215202
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" )
217204
218-     release_pr  =  get_prs (pull_requests , "release" , "open" )
205+     release_pr  =  get_prs (all_pull_requests , "release" , "open" )
219206
220207    if  len (release_pr ) !=  1 :
221208        print (f"Unable to find the exact release PR. Returned result: { release_pr }  " )
222209        exit (1 )
223210
224211    print (f"Found release PR: { release_pr [0 ]['title' ]}  " )
225212
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" )
228226
229227    description_content  =  "# Release notes\n " 
230228    description_content  +=  f"## Features\n { get_pr_descriptions (enhancement_prs )}  "  if  enhancement_prs  else  "" 
231229    description_content  +=  f"## Bug fixes\n { get_pr_descriptions (bug_fix_prs )}  "  if  bug_fix_prs  else  "" 
232230
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 )))
234232    assignees .sort (key = str .lower )
235233
236234    description_content  +=  f"### Authors:\n { ', ' .join (assignees )}  " 
0 commit comments