@@ -84,11 +84,42 @@ def main(): # pragma: no cover
8484 # Check to see if repository has a CODEOWNERS file
8585 file_changed = False
8686 codeowners_file_contents , codeowners_filepath = get_codeowners_file (repo )
87+ has_codeowners = codeowners_file_contents is not None
88+ codeowners_size = (
89+ getattr (codeowners_file_contents , "size" , None ) if has_codeowners else None
90+ )
91+ is_empty_codeowners = has_codeowners and codeowners_size == 0
8792
88- if not codeowners_file_contents :
89- print ( f"Skipping { repo .full_name } as it does not have a CODEOWNERS file" )
93+ if not has_codeowners or is_empty_codeowners :
94+ repo_name = repo .full_name
9095 no_codeowners_count += 1
91- repos_missing_codeowners .append (repo )
96+ repos_missing_codeowners .append (repo_name )
97+
98+ if not has_codeowners :
99+ print (f"{ repo_name } does not have a CODEOWNERS file" )
100+ else :
101+ print (f"{ repo_name } has an empty CODEOWNERS file" )
102+
103+ if dry_run :
104+ continue
105+
106+ suggested_codeowners = build_default_codeowners (repo )
107+ target_path = codeowners_filepath or ".github/CODEOWNERS"
108+ eligble_for_pr_count += 1
109+ try :
110+ pull = commit_changes (
111+ title ,
112+ body ,
113+ repo ,
114+ suggested_codeowners ,
115+ commit_message ,
116+ target_path ,
117+ create_new = not has_codeowners ,
118+ )
119+ pull_count += 1
120+ print (f"\t Created pull request { pull .html_url } " )
121+ except github3 .exceptions .NotFoundError :
122+ print ("\t Failed to create pull request. Check write permissions." )
92123 continue
93124
94125 codeowners_count += 1
@@ -179,35 +210,14 @@ def get_codeowners_file(repo):
179210 the file contents and file path or None if it doesn't exist
180211 """
181212 codeowners_file_contents = None
182- codeowners_filepath = None
183- try :
184- if (
185- repo .file_contents (".github/CODEOWNERS" )
186- and repo .file_contents (".github/CODEOWNERS" ).size > 0
187- ):
188- codeowners_file_contents = repo .file_contents (".github/CODEOWNERS" )
189- codeowners_filepath = ".github/CODEOWNERS"
190- except github3 .exceptions .NotFoundError :
191- pass
192- try :
193- if (
194- repo .file_contents ("CODEOWNERS" )
195- and repo .file_contents ("CODEOWNERS" ).size > 0
196- ):
197- codeowners_file_contents = repo .file_contents ("CODEOWNERS" )
198- codeowners_filepath = "CODEOWNERS"
199- except github3 .exceptions .NotFoundError :
200- pass
201- try :
202- if (
203- repo .file_contents ("docs/CODEOWNERS" )
204- and repo .file_contents ("docs/CODEOWNERS" ).size > 0
205- ):
206- codeowners_file_contents = repo .file_contents ("docs/CODEOWNERS" )
207- codeowners_filepath = "docs/CODEOWNERS"
208- except github3 .exceptions .NotFoundError :
209- pass
210- return codeowners_file_contents , codeowners_filepath
213+ for path in (".github/CODEOWNERS" , "CODEOWNERS" , "docs/CODEOWNERS" ):
214+ try :
215+ codeowners_file_contents = repo .file_contents (path )
216+ if codeowners_file_contents :
217+ return codeowners_file_contents , path
218+ except github3 .exceptions .NotFoundError :
219+ continue
220+ return None , None
211221
212222
213223def print_stats (
@@ -216,7 +226,7 @@ def print_stats(
216226 """Print the statistics from this run to the terminal output"""
217227 print (f"Found { users_count } users to remove" )
218228 print (f"Created { pull_count } pull requests successfully" )
219- print (f"Skipped { no_codeowners_count } repositories without a CODEOWNERS file " )
229+ print (f"Found { no_codeowners_count } repositories missing or empty CODEOWNERS files " )
220230 print (f"Processed { codeowners_count } repositories with a CODEOWNERS file" )
221231 if eligble_for_pr_count == 0 :
222232 print ("No pull requests were needed" )
@@ -273,13 +283,31 @@ def get_usernames_from_codeowners(codeowners_file_contents, ignore_teams=True):
273283 return usernames
274284
275285
286+ def build_default_codeowners (repo ):
287+ """Build a placeholder CODEOWNERS file for repositories without one."""
288+ owner_login = repo .owner .login
289+ owner_type = getattr (repo .owner , "type" , "" )
290+ if owner_type == "Organization" :
291+ owner_handle = f"{ owner_login } /REPLACE_WITH_TEAM"
292+ else :
293+ owner_handle = owner_login
294+
295+ contents = (
296+ "# CODEOWNERS\n "
297+ "# Replace the placeholder with the appropriate owner(s) for this repository.\n "
298+ f"* @{ owner_handle } \n "
299+ )
300+ return contents .encode ("ASCII" )
301+
302+
276303def commit_changes (
277304 title ,
278305 body ,
279306 repo ,
280307 codeowners_file_contents_new ,
281308 commit_message ,
282309 codeowners_filepath ,
310+ create_new = False ,
283311):
284312 """Commit the changes to the repo and open a pull request and return the pull request object"""
285313 default_branch = repo .default_branch
@@ -288,11 +316,19 @@ def commit_changes(
288316 front_matter = "refs/heads/"
289317 branch_name = f"codeowners-{ str (uuid .uuid4 ())} "
290318 repo .create_ref (front_matter + branch_name , default_branch_commit )
291- repo .file_contents (codeowners_filepath ).update (
292- message = commit_message ,
293- content = codeowners_file_contents_new ,
294- branch = branch_name ,
295- )
319+ if create_new :
320+ repo .create_file (
321+ codeowners_filepath ,
322+ commit_message ,
323+ codeowners_file_contents_new ,
324+ branch = branch_name ,
325+ )
326+ else :
327+ repo .file_contents (codeowners_filepath ).update (
328+ message = commit_message ,
329+ content = codeowners_file_contents_new ,
330+ branch = branch_name ,
331+ )
296332
297333 pull = repo .create_pull (
298334 title = title , body = body , head = branch_name , base = repo .default_branch
0 commit comments