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.
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/PKG-INFO +2 -2
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/README.md +1 -1
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/_gh_org.py +52 -18
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/manage.py +10 -2
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/pyproject.toml +2 -2
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/LICENSE.txt +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/LICENSES/Apache-2.0.txt +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/LICENSES/CC0-1.0.txt +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/LICENSES/MIT.txt +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/__init__.py +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/_config.py +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/_gh_api.py +0 -0
- {github_org_manager-0.5.7 → github_org_manager-0.6.0}/gh_org_mgr/_setup_team.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: github-org-manager
|
|
3
|
-
Version: 0.
|
|
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
|
|
670
|
-
|
|
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(
|
|
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
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
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
|
|
133
|
-
org.
|
|
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.
|
|
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.
|
|
42
|
+
bump-my-version = "^0.32.0"
|
|
43
43
|
|
|
44
44
|
[build-system]
|
|
45
45
|
requires = ["poetry-core"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|