@@ -45,6 +45,7 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
4545 configured_org_owners : list [str ] = field (default_factory = list )
4646 org_members : list [NamedUser ] = field (default_factory = list )
4747 current_teams : dict [Team , dict ] = field (default_factory = dict )
48+ current_teams_str : list [str ] = field (default_factory = list )
4849 configured_teams : dict [str , dict | None ] = field (default_factory = dict )
4950 newly_added_users : list [NamedUser ] = field (default_factory = list )
5051 current_repos_teams : dict [Repository , dict [Team , str ]] = field (default_factory = dict )
@@ -290,18 +291,65 @@ def _get_current_teams(self):
290291 """Get teams of the existing organisation"""
291292 for team in list (self .org .get_teams ()):
292293 self .current_teams [team ] = {"members" : {}, "repos" : {}}
294+ self .current_teams_str = [team .name for team in self .current_teams ]
293295
294- def create_missing_teams (self , dry : bool = False ):
295- """Find out which teams are configured but not part of the org yet"""
296+ def ensure_team_hierarchy (self ) -> None :
297+ """Check if all configured parent teams make sense: either they exist already or will be
298+ created during this run"""
296299
297300 # Get list of current teams
298301 self ._get_current_teams ()
299302
300- # Get the names of the existing teams
301- existent_team_names = [team .name for team in self .current_teams ]
303+ # First, check whether all configured parent teams exist or will be created
304+ for team , attributes in self .configured_teams .items ():
305+ if parent := attributes .get ("parent" ): # type: ignore
306+ if parent not in self .configured_teams :
307+ if parent not in self .current_teams_str :
308+ logging .critical (
309+ "The team '%s' is configured with parent team '%s', but this parent "
310+ "team does not exist and is not configured to be created. "
311+ "Cannot continue." ,
312+ team ,
313+ parent ,
314+ )
315+ sys .exit (1 )
316+ else :
317+ logging .debug (
318+ "The team '%s' is configured with parent team '%s', "
319+ "which already exists" ,
320+ team ,
321+ parent ,
322+ )
323+ else :
324+ logging .debug (
325+ "The team '%s' is configured with parent team '%s', "
326+ "which will be created during this run" ,
327+ team ,
328+ parent ,
329+ )
330+
331+ # Second, order the teams in a way that parent teams are created before child teams
332+ ordered_teams : dict [str , dict | None ] = {}
333+ while len (ordered_teams ) < len (self .configured_teams ):
334+ for team , attributes in self .configured_teams .items ():
335+ # Team already ordered
336+ if team in ordered_teams :
337+ continue
338+ # Team has parent, but parent not ordered yet
339+ if parent := attributes .get ("parent" ): # type: ignore
340+ if parent not in ordered_teams :
341+ continue
342+ # Team has no parent, or parent already ordered
343+ ordered_teams [team ] = attributes
344+ # Overwrite configured teams with ordered ones
345+ self .configured_teams = ordered_teams
346+
347+ def create_missing_teams (self , dry : bool = False ) -> None :
348+ """Find out which teams are configured but not part of the org yet"""
302349
303350 for team , attributes in self .configured_teams .items ():
304- if team not in existent_team_names :
351+ if team not in self .current_teams_str :
352+ # If a parent team is configured, try to get its ID
305353 if parent := attributes .get ("parent" ): # type: ignore
306354 try :
307355 parent_id = self .org .get_team_by_slug (sluggify_teamname (parent )).id
@@ -328,6 +376,7 @@ def create_missing_teams(self, dry: bool = False):
328376 privacy = "closed" ,
329377 )
330378
379+ # No parent team configured
331380 else :
332381 logging .info ("Creating team '%s' without parent" , team )
333382 self .stats .create_team (team )
0 commit comments