github-org-manager 0.5.7__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: github-org-manager
3
- Version: 0.5.7
3
+ Version: 0.6.0
4
4
  Summary: Manage a GitHub Organization, its teams, repository permissions, and more
5
5
  License: Apache-2.0
6
6
  Keywords: github,github-management,permissions,access-control
@@ -76,7 +76,7 @@ Afterwards, the tool is executable with the command `gh-org-mgr`. The `--help` f
76
76
 
77
77
  Inside [`config/example`](./config/example), you can find an example configuration that shall help you to understand the structure:
78
78
 
79
- * `app.yaml`: Configuration necessary to run this tool
79
+ * `app.yaml`: Configuration necessary to run this tool and controlling some behaviour
80
80
  * `org.yaml`: Organization-wide configuration
81
81
  * `teams/*.yaml`: Configuration concerning the teams of your organization.
82
82
 
@@ -48,7 +48,7 @@ Afterwards, the tool is executable with the command `gh-org-mgr`. The `--help` f
48
48
 
49
49
  Inside [`config/example`](./config/example), you can find an example configuration that shall help you to understand the structure:
50
50
 
51
- * `app.yaml`: Configuration necessary to run this tool
51
+ * `app.yaml`: Configuration necessary to run this tool and controlling some behaviour
52
52
  * `org.yaml`: Organization-wide configuration
53
53
  * `teams/*.yaml`: Configuration concerning the teams of your organization.
54
54
 
@@ -550,6 +550,10 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
550
550
  open_invitations = [user.login.lower() for user in self.org.invitations()]
551
551
 
552
552
  for team, team_attrs in self.current_teams.items():
553
+ # Ignore any team not being configured locally, will be handled later
554
+ if team.name not in self.configured_teams:
555
+ continue
556
+
553
557
  # Update current team members with dict[NamedUser, str (role)]
554
558
  team_attrs["members"] = self._get_current_team_members(team)
555
559
 
@@ -559,15 +563,6 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
559
563
  user.login.lower(): role for user, role in team_attrs["members"].items()
560
564
  }
561
565
 
562
- # Handle the team not being configured locally
563
- if team.name not in self.configured_teams:
564
- logging.warning(
565
- "Team '%s' does not seem to be configured locally. "
566
- "Taking no action about this team at all",
567
- team.name,
568
- )
569
- continue
570
-
571
566
  # Get configuration from current team
572
567
  if team_configuration := self.configured_teams.get(team.name):
573
568
  pass
@@ -666,8 +661,35 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
666
661
  team.name,
667
662
  )
668
663
 
669
- def get_members_without_team(self) -> None:
670
- """Get all organisation members without any team membership"""
664
+ def get_unconfigured_teams(
665
+ self, dry: bool = False, delete_unconfigured_teams: bool = False
666
+ ) -> None:
667
+ """Get all teams that are not configured locally and optionally remove them"""
668
+ # Get all teams that are not configured locally
669
+ unconfigured_teams: list[Team] = []
670
+ for team in self.current_teams:
671
+ if team.name not in self.configured_teams:
672
+ unconfigured_teams.append(team)
673
+
674
+ if unconfigured_teams:
675
+ if delete_unconfigured_teams:
676
+ for team in unconfigured_teams:
677
+ logging.info("Deleting team '%s' as it is not configured locally", team.name)
678
+ if not dry:
679
+ team.delete()
680
+ else:
681
+ unconfigured_teams_str = [team.name for team in unconfigured_teams]
682
+ logging.warning(
683
+ "The following teams of your GitHub organisation are not "
684
+ "configured locally: %s. Taking no action about these teams.",
685
+ ", ".join(unconfigured_teams_str),
686
+ )
687
+
688
+ def get_members_without_team(
689
+ self, dry: bool = False, remove_members_without_team: bool = False
690
+ ) -> None:
691
+ """Get all organisation members without any team membership, and
692
+ optionally remove them"""
671
693
  # Combine org owners and org members
672
694
  all_org_members = set(self.org_members + self.current_org_owners)
673
695
 
@@ -676,18 +698,30 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
676
698
  for _, team_attrs in self.current_teams.items():
677
699
  for member in team_attrs.get("members", {}):
678
700
  all_team_members_lst.append(member)
679
- # Also add users that have just been added to a team, and unify them
680
- all_team_members: set[NamedUser] = set(all_team_members_lst + self.newly_added_users)
701
+ # Also add org owners and users that have just been added to a team, and unify them
702
+ all_team_members: set[NamedUser] = set(
703
+ all_team_members_lst + self.newly_added_users + self.current_org_owners
704
+ )
681
705
 
682
706
  # Find members that are in org_members but not team_members
683
707
  members_without_team = all_org_members.difference(all_team_members)
684
708
 
685
709
  if members_without_team:
686
- members_without_team_str = [user.login for user in members_without_team]
687
- logging.warning(
688
- "The following members of your GitHub organisation are not member of any team: %s",
689
- ", ".join(members_without_team_str),
690
- )
710
+ if remove_members_without_team:
711
+ for user in members_without_team:
712
+ logging.info(
713
+ "Removing user '%s' from organisation as they are not member of any team",
714
+ user.login,
715
+ )
716
+ if not dry:
717
+ self.org.remove_from_membership(user)
718
+ else:
719
+ members_without_team_str = [user.login for user in members_without_team]
720
+ logging.warning(
721
+ "The following members of your GitHub organisation are not "
722
+ "member of any team: %s",
723
+ ", ".join(members_without_team_str),
724
+ )
691
725
 
692
726
  # --------------------------------------------------------------------------
693
727
  # Repos
@@ -129,8 +129,16 @@ def main():
129
129
  org.sync_current_teams_settings(dry=args.dry)
130
130
  # Synchronise the team memberships
131
131
  org.sync_teams_members(dry=args.dry)
132
- # Report about organisation members that do not belong to any team
133
- org.get_members_without_team()
132
+ # Report and act on teams that are not configured locally
133
+ org.get_unconfigured_teams(
134
+ dry=args.dry,
135
+ delete_unconfigured_teams=cfg_app.get("delete_unconfigured_teams", False),
136
+ )
137
+ # Report and act on organisation members that do not belong to any team
138
+ org.get_members_without_team(
139
+ dry=args.dry,
140
+ remove_members_without_team=cfg_app.get("remove_members_without_team", False),
141
+ )
134
142
  # Synchronise the permissions of teams for all repositories
135
143
  org.sync_repo_permissions(dry=args.dry, ignore_archived=args.ignore_archived)
136
144
  # Remove individual collaborator permissions if they are higher than the one
@@ -4,7 +4,7 @@
4
4
 
5
5
  [tool.poetry]
6
6
  name = "github-org-manager"
7
- version = "0.5.7"
7
+ version = "0.6.0"
8
8
  description = "Manage a GitHub Organization, its teams, repository permissions, and more"
9
9
  authors = ["Max Mehl <max.mehl@deutschebahn.com>"]
10
10
  readme = "README.md"
@@ -39,7 +39,7 @@ mypy = "^1.9.0"
39
39
  pylint = "^3.1.0"
40
40
  types-pyyaml = "^6.0.12.20240311"
41
41
  types-requests = "^2.32.0.20240712"
42
- bump-my-version = "^0.30.0"
42
+ bump-my-version = "^0.32.0"
43
43
 
44
44
  [build-system]
45
45
  requires = ["poetry-core"]