Skip to content

Commit 7d6c77e

Browse files
authored
Merge pull request #142 from OpenRailAssociation/parent-team-checks
Check relationship between child and parent teams
2 parents c36fa9c + 8b10a09 commit 7d6c77e

File tree

4 files changed

+62
-11
lines changed

4 files changed

+62
-11
lines changed

gh_org_mgr/_gh_org.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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)

gh_org_mgr/_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def log_progress(message: str) -> None:
3434
sys.stderr.write("\r\033[K")
3535
sys.stderr.flush()
3636
else:
37-
sys.stderr.write(f"\r\033[K⏳ {message}")
37+
sys.stderr.write(f"\r\033[K⏳ {message}\n")
3838
sys.stderr.flush()
3939

4040

gh_org_mgr/manage.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ def main():
134134
# Synchronise organisation owners
135135
log_progress("Synchronising organisation owners...")
136136
org.sync_org_owners(dry=args.dry, force=args.force)
137+
# Validate parent/child team relationships
138+
log_progress("Validating team hierarchy...")
139+
org.ensure_team_hierarchy()
137140
# Create teams that aren't present at Github yet
138141
log_progress("Creating missing teams...")
139142
org.create_missing_teams(dry=args.dry)
@@ -164,7 +167,6 @@ def main():
164167
org.sync_repo_collaborator_permissions(dry=args.dry)
165168

166169
# Debug output
167-
log_progress("") # clear progress
168170
logging.debug("Final dataclass:\n%s", org.pretty_print_dataclass())
169171
org.ratelimit()
170172

tests/data/config/teams_files/teams_changes.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@ Test2:
1414
maintainer:
1515
- TEST_USER
1616

17-
Test3:
18-
notification_setting: notifications_disabled
19-
privacy: closed
20-
2117
Test3-child:
2218
parent: Test3
2319
description: Child of Test3
2420
privacy: closed
2521
member:
2622
- TEST_USER
2723

24+
Test3:
25+
notification_setting: notifications_disabled
26+
privacy: closed
27+
2828
Test4:
2929
member:

0 commit comments

Comments
 (0)