99import sys
1010import time
1111from collections import defaultdict
12+ from pathlib import Path
1213
14+ import yaml
1315from github import Auth , Github , GithubException
1416from github .GithubException import UnknownObjectException
1517from west .manifest import Manifest , ManifestProject
1618
1719TOP_DIR = os .path .join (os .path .dirname (__file__ ))
18- sys .path .insert (0 , os . path . join ( TOP_DIR , "scripts" ))
20+ sys .path .insert (0 , str ( Path ( __file__ ). resolve (). parents [ 1 ] ))
1921from get_maintainer import Maintainers # noqa: E402
2022
21- zephyr_base = os .getenv ('ZEPHYR_BASE' , os .path .join (TOP_DIR , '..' ))
23+ ZEPHYR_BASE = os .environ .get ('ZEPHYR_BASE' )
24+ if ZEPHYR_BASE :
25+ ZEPHYR_BASE = Path (ZEPHYR_BASE )
26+ else :
27+ ZEPHYR_BASE = Path (__file__ ).resolve ().parents [2 ]
28+ # Propagate this decision to child processes.
29+ os .environ ['ZEPHYR_BASE' ] = str (ZEPHYR_BASE )
2230
2331
2432def log (s ):
@@ -71,10 +79,22 @@ def parse_args():
7179 help = "Updated manifest file to compare against current west.yml" ,
7280 )
7381
82+ parser .add_argument (
83+ "--updated-maintainer-file" ,
84+ default = None ,
85+ help = "Updated maintainer file to compare against current MAINTAINERS.yml" ,
86+ )
87+
7488 parser .add_argument ("-v" , "--verbose" , action = "count" , default = 0 , help = "Verbose Output" )
7589
7690 args = parser .parse_args ()
7791
92+ def load_areas (filename : str ):
93+ with open (filename ) as f :
94+ doc = yaml .safe_load (f )
95+ return {
96+ k : v for k , v in doc .items () if isinstance (v , dict ) and ("files" in v or "files-regex" in v )
97+ }
7898
7999def process_manifest (old_manifest_file ):
80100 log ("Processing manifest changes" )
@@ -104,6 +124,93 @@ def process_manifest(old_manifest_file):
104124 log (f'manifest areas: { areas } ' )
105125 return areas
106126
127+ def set_or_empty (d , key ):
128+ return set (d .get (key , []) or [])
129+
130+ def compare_areas (old , new , repo_fullname = None , token = None ):
131+ old_areas = set (old .keys ())
132+ new_areas = set (new .keys ())
133+
134+ changed_areas = set ()
135+ added_areas = new_areas - old_areas
136+ removed_areas = old_areas - new_areas
137+ common_areas = old_areas & new_areas
138+
139+ print ("=== Areas Added ===" )
140+ for area in sorted (added_areas ):
141+ print (f"+ { area } " )
142+
143+ print ("\n === Areas Removed ===" )
144+ for area in sorted (removed_areas ):
145+ print (f"- { area } " )
146+
147+ print ("\n === Area Changes ===" )
148+ for area in sorted (common_areas ):
149+ changes = []
150+ old_entry = old [area ]
151+ new_entry = new [area ]
152+
153+ # Compare maintainers
154+ old_maint = set_or_empty (old_entry , "maintainers" )
155+ new_maint = set_or_empty (new_entry , "maintainers" )
156+ added_maint = new_maint - old_maint
157+ removed_maint = old_maint - new_maint
158+ if added_maint :
159+ changes .append (f" Maintainers added: { ', ' .join (sorted (added_maint ))} " )
160+ if removed_maint :
161+ changes .append (f" Maintainers removed: { ', ' .join (sorted (removed_maint ))} " )
162+
163+ # Compare collaborators
164+ old_collab = set_or_empty (old_entry , "collaborators" )
165+ new_collab = set_or_empty (new_entry , "collaborators" )
166+ added_collab = new_collab - old_collab
167+ removed_collab = old_collab - new_collab
168+ if added_collab :
169+ changes .append (f" Collaborators added: { ', ' .join (sorted (added_collab ))} " )
170+ if removed_collab :
171+ changes .append (f" Collaborators removed: { ', ' .join (sorted (removed_collab ))} " )
172+
173+ # Compare status
174+ old_status = old_entry .get ("status" )
175+ new_status = new_entry .get ("status" )
176+ if old_status != new_status :
177+ changes .append (f" Status changed: { old_status } -> { new_status } " )
178+
179+ # Compare labels
180+ old_labels = set_or_empty (old_entry , "labels" )
181+ new_labels = set_or_empty (new_entry , "labels" )
182+ added_labels = new_labels - old_labels
183+ removed_labels = old_labels - new_labels
184+ if added_labels :
185+ changes .append (f" Labels added: { ', ' .join (sorted (added_labels ))} " )
186+ if removed_labels :
187+ changes .append (f" Labels removed: { ', ' .join (sorted (removed_labels ))} " )
188+
189+ # Compare files
190+ old_files = set_or_empty (old_entry , "files" )
191+ new_files = set_or_empty (new_entry , "files" )
192+ added_files = new_files - old_files
193+ removed_files = old_files - new_files
194+ if added_files :
195+ changes .append (f" Files added: { ', ' .join (sorted (added_files ))} " )
196+ if removed_files :
197+ changes .append (f" Files removed: { ', ' .join (sorted (removed_files ))} " )
198+
199+ # Compare files-regex
200+ old_regex = set_or_empty (old_entry , "files-regex" )
201+ new_regex = set_or_empty (new_entry , "files-regex" )
202+ added_regex = new_regex - old_regex
203+ removed_regex = old_regex - new_regex
204+ if added_regex :
205+ changes .append (f" files-regex added: { ', ' .join (sorted (added_regex ))} " )
206+ if removed_regex :
207+ changes .append (f" files-regex removed: { ', ' .join (sorted (removed_regex ))} " )
208+
209+ if changes :
210+ changed_areas .add (area )
211+ print (f"area changed: { area } " )
212+
213+ return added_areas | removed_areas | changed_areas
107214
108215def process_pr (gh , maintainer_file , number ):
109216 gh_repo = gh .get_repo (f"{ args .org } /{ args .repo } " )
@@ -129,6 +236,7 @@ def process_pr(gh, maintainer_file, number):
129236 meta_areas = ['Release Notes' , 'Documentation' , 'Samples' , 'Tests' ]
130237
131238 collab_per_path = []
239+ additional_reviews = set ()
132240 for changed_file in fn :
133241 num_files += 1
134242 log (f"file: { changed_file .filename } " )
@@ -144,6 +252,22 @@ def process_pr(gh, maintainer_file, number):
144252 area_match = maintainer_file .name2areas (_area )
145253 if area_match :
146254 areas .extend (area_match )
255+ elif changed_file .filename in ['MAINTAINERS.yml' ]:
256+ areas = maintainer_file .path2areas (changed_file .filename )
257+ if args .updated_maintainer_file :
258+ log (
259+ "No updated maintainer file, cannot process MAINTAINERS.yml changes, skipping..."
260+ )
261+
262+ old_areas = load_areas (args .updated_maintainer_file )
263+ new_areas = load_areas ('MAINTAINERS.yml' )
264+ changed_areas = compare_areas (old_areas , new_areas )
265+ for _area in changed_areas :
266+ area_match = maintainer_file .name2areas (_area )
267+ if area_match :
268+ # get list of maintainers for changed area
269+ additional_reviews .update (maintainer_file .areas [_area ].maintainers )
270+ log (f"MAINTAINERS.yml changed, adding reviewrs: { additional_reviews } " )
147271 else :
148272 areas = maintainer_file .path2areas (changed_file .filename )
149273 for _area in areas :
@@ -192,6 +316,8 @@ def process_pr(gh, maintainer_file, number):
192316 collab += collab_per_path
193317
194318 collab = list (dict .fromkeys (collab ))
319+ # add more reviewers based on maintainer file changes.
320+ collab += list (additional_reviews )
195321 log (f"collab: { collab } " )
196322
197323 _all_maintainers = dict (
0 commit comments