23
23
GIT_USER_NAME = "CC creativecommons.github.io Bot"
24
24
GIT_USER_EMAIL = "cc-creativecommons-github-io-bot@creativecommons.org"
25
25
SYNC_BRANCH = "ct_codeowners"
26
+ CODEOWNERS_TEMPLATE = """\
27
+ # https://help.github.com/en/articles/about-code-owners
28
+ # If you want to match two or more code owners with the same pattern, all the
29
+ # code owners must be on the same line. If the code owners are not on the same
30
+ # line, the pattern matches only the last mentioned code owner.
31
+ * @creativecommons/technology
32
+ """
26
33
27
34
log_name = os .path .basename (os .path .splitext (inspect .stack ()[- 1 ].filename )[0 ])
28
35
LOG = logging .getLogger (log_name )
@@ -35,7 +42,7 @@ def create_codeowners_for_data(args, databag):
35
42
organization = get_cc_organization (github_client )
36
43
37
44
LOG .info ("Identifying and fixing CODEOWNER issues..." )
38
- projects = databag ["projects" ]
45
+ projects = sorted ( databag ["projects" ], key = lambda d : d [ "name" ])
39
46
for project in projects :
40
47
project_name = project ["name" ]
41
48
LOG .info (
@@ -51,7 +58,7 @@ def create_codeowners_for_data(args, databag):
51
58
)
52
59
53
60
LOG .info ("Checking all projects..." )
54
- repos = project ["repos" ]
61
+ repos = sorted ( project ["repos" ])
55
62
with TemporaryDirectory () as temp_dir :
56
63
for repo_name in repos :
57
64
check_and_fix_repo (
@@ -113,18 +120,16 @@ def check_and_fix_repo(args, organization, repo_name, teams, temp_dir):
113
120
codeowners_path = Path (os .path .join (repo_dir , ".github" , "CODEOWNERS" ))
114
121
fix_required = False
115
122
116
- if not codeowners_path .exists () and codeowners_path . is_file () :
123
+ if not codeowners_path .exists ():
117
124
fix_required = True
118
125
LOG .info ("CODEOWNERS does not exist, creating..." )
119
126
os .makedirs (codeowners_path .parent , exist_ok = True )
120
- open (codeowners_path , "a" ).close ()
127
+ with open (codeowners_path , "w" ) as codeowners_file :
128
+ codeowners_file .write (CODEOWNERS_TEMPLATE )
121
129
LOG .log (ccos .log .SUCCESS , "Done." )
122
130
123
131
teams = filter_valid_teams (gh_repo , teams )
124
- team_mention_map = get_team_mention_map (codeowners_path , teams )
125
- if not all (team_mention_map .values ()):
126
- fix_required = True
127
- add_missing_teams (codeowners_path , team_mention_map )
132
+ fix_required = add_missing_teams (codeowners_path , teams )
128
133
129
134
if fix_required :
130
135
branch_name = create_branch (local_repo )
@@ -134,8 +139,6 @@ def check_and_fix_repo(args, organization, repo_name, teams, temp_dir):
134
139
135
140
LOG .log (ccos .log .SUCCESS , "Done." )
136
141
137
- LOG .log (ccos .log .SUCCESS , "All is well." )
138
-
139
142
140
143
def filter_valid_teams (gh_repo , teams ):
141
144
"""
@@ -167,10 +170,10 @@ def create_branch(local_repo):
167
170
168
171
169
172
def commit_or_display_changes (args , local_repo , codeowners_path ):
173
+ local_repo .index .add (items = codeowners_path )
170
174
if args .debug :
171
- LOG .debug (local_repo .git .diff ())
175
+ LOG .debug (local_repo .git .diff (staged = True ))
172
176
else :
173
- local_repo .index .add (items = codeowners_path )
174
177
local_repo .index .commit (message = "Sync Community Team(s) to CODEOWNERS" )
175
178
176
179
@@ -223,50 +226,40 @@ def set_up_repo(clone_url, repo_dir):
223
226
return local_repo
224
227
225
228
226
- def get_team_mention_map (codeowners_path , teams ):
227
- """
228
- Map the team slugs to whether they have been mentioned in the CODEOWNERS
229
- file in any capacity.
230
-
231
- @param codeowners_path: the path of the CODEOWNERS file
232
- @param teams: all the GitHub teams for all Community Teams of a project
233
- @return: a dictionary of team slugs and their mentions
234
- """
235
- with open (codeowners_path ) as codeowners_file :
236
- contents = codeowners_file .read ()
237
- return {team .slug : mentionified (team .slug ) in contents for team in teams }
238
-
239
-
240
- def add_missing_teams (codeowners_path , team_mention_map ):
229
+ def add_missing_teams (codeowners_path , teams ):
241
230
"""
242
231
Add the mention forms for all missing teams in a new line.
243
232
244
233
@param codeowners_path: the path of the CODEOWNERS file
245
234
@param team_mention_map: the dictionary of team slugs and their mentions
246
235
"""
247
- LOG .info ("CODEOWNERS is incomplete, populating..." )
248
- missing_team_slugs = [
249
- team_slug
250
- for team_slug in team_mention_map .keys ()
251
- if not team_mention_map [team_slug ]
252
- ]
253
- with open (codeowners_path , "a" ) as codeowners_file :
254
- addendum = generate_ideal_codeowners_rule (missing_team_slugs )
255
- codeowners_file .write (addendum )
256
- codeowners_file .write ("\n " )
257
- LOG .log (ccos .log .SUCCESS , "Done." )
258
-
259
-
260
- def generate_ideal_codeowners_rule (team_slugs ):
261
- """
262
- Generate an ideal CODEOWNERS rule for the given set of roles. Assigns all
263
- files using the wildcard expression '*' to the given roles.
264
-
265
- @param team_slugs: the set of team slugs to be added to the CODEOWNERS file
266
- @return: the line that should be added to the CODEOWNERS file
267
- """
268
- combined_team_slugs = " " .join (map (mentionified , team_slugs ))
269
- return f"* { combined_team_slugs } "
236
+ fix_required = False
237
+ community_teams = []
238
+ for team in teams :
239
+ community_teams .append (f"@{ GITHUB_ORGANIZATION } /{ team .slug } " )
240
+ community_teams .sort ()
241
+ new_codeowners = []
242
+ with open (codeowners_path , "r" ) as codeowners_file :
243
+ new_codeowners = codeowners_file .readlines ()
244
+ for index , line in enumerate (new_codeowners ):
245
+ if not line .startswith ("* " ):
246
+ continue
247
+ staff_teams = []
248
+ teams = line .split ()[1 :]
249
+ for team in teams :
250
+ if not team .startswith ("@creativecommons/ct-" ):
251
+ staff_teams .append (team )
252
+ staff_teams .sort ()
253
+ new_line = f"* { ' ' .join (staff_teams )} { ' ' .join (community_teams )} \n "
254
+ if line .strip () != new_line .strip ():
255
+ new_codeowners [index ] = new_line
256
+ fix_required = True
257
+ if fix_required :
258
+ LOG .info ("CODEOWNERS is incomplete, populating..." )
259
+ with open (codeowners_path , "w" ) as codeowners_file :
260
+ codeowners_file .writelines (new_codeowners )
261
+ LOG .log (ccos .log .SUCCESS , "Done." )
262
+ return fix_required
270
263
271
264
272
265
def get_github_repo_url_with_credentials (repo_name ):
@@ -282,17 +275,3 @@ def get_github_repo_url_with_credentials(repo_name):
282
275
f"https://{ github_username } :{ github_token } "
283
276
f"@github.com/{ GITHUB_ORGANIZATION } /{ repo_name } .git"
284
277
)
285
-
286
-
287
- def mentionified (team_slug ):
288
- """
289
- Get the mention form of the given team. Mention forms are generated by
290
- prefixing the organization to the team slug.
291
-
292
- mention form schema
293
- @<organization>/<team slug>
294
-
295
- @param team_slug: the slug of the team to mention
296
- @return: the mentionable form of the given team slug
297
- """
298
- return f"@{ GITHUB_ORGANIZATION } /{ team_slug } "
0 commit comments