github-org-manager 0.7.2__tar.gz → 0.7.4__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.7.2
3
+ Version: 0.7.4
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
@@ -19,10 +19,10 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Topic :: Software Development :: Version Control :: Git
21
21
  Classifier: Topic :: Utilities
22
- Requires-Dist: pygithub (>=2.3.0,<3.0.0)
22
+ Requires-Dist: pygithub (>=2.7.0,<3.0.0)
23
23
  Requires-Dist: python-slugify (>=8.0.4,<9.0.0)
24
- Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
25
- Requires-Dist: requests (>=2.32.3,<3.0.0)
24
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
25
+ Requires-Dist: requests (>=2.32.4,<3.0.0)
26
26
  Project-URL: Repository, https://github.com/OpenRailAssociation/github-org-manager
27
27
  Description-Content-Type: text/markdown
28
28
 
@@ -8,14 +8,12 @@ import logging
8
8
  import sys
9
9
  from dataclasses import asdict, dataclass, field
10
10
 
11
- from github import (
12
- Auth,
13
- Github,
11
+ from github import Auth, Github, GithubIntegration
12
+ from github.GithubException import (
13
+ BadCredentialsException,
14
14
  GithubException,
15
- GithubIntegration,
16
15
  UnknownObjectException,
17
16
  )
18
- from github.GithubException import BadCredentialsException
19
17
  from github.NamedUser import NamedUser
20
18
  from github.Organization import Organization
21
19
  from github.Repository import Repository
@@ -54,6 +52,7 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
54
52
  current_repos_collaborators: dict[Repository, dict[str, str]] = field(default_factory=dict)
55
53
  configured_repos_collaborators: dict[str, dict[str, str]] = field(default_factory=dict)
56
54
  archived_repos: list[Repository] = field(default_factory=list)
55
+ unconfigured_teams: list[Team] = field(default_factory=list)
57
56
  unconfigured_team_repo_permissions: dict[str, dict[str, str]] = field(default_factory=dict)
58
57
  stats: OrgChanges = field(default_factory=OrgChanges)
59
58
 
@@ -84,8 +83,9 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
84
83
 
85
84
  # Decide how to login. If app set, prefer this
86
85
  if self.gh_app_id and self.gh_app_private_key:
87
- logging.debug("Logging in via app %s", self.gh_app_id)
88
- auth = Auth.AppAuth(app_id=self.gh_app_id, private_key=self.gh_app_private_key)
86
+ gh_app_id_sanitized = str(self.gh_app_id).strip("'").strip('"')
87
+ logging.debug("Logging in via app %s", gh_app_id_sanitized)
88
+ auth = Auth.AppAuth(app_id=gh_app_id_sanitized, private_key=self.gh_app_private_key)
89
89
  app = GithubIntegration(auth=auth)
90
90
  try:
91
91
  installation = app.get_org_installation(org=orgname)
@@ -115,7 +115,7 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
115
115
 
116
116
  def ratelimit(self):
117
117
  """Print current rate limit"""
118
- core = self.gh.get_rate_limit().core
118
+ core = self.gh.get_rate_limit().resources.core
119
119
  logging.info(
120
120
  "Current rate limit: %s/%s (reset: %s)", core.remaining, core.limit, core.reset
121
121
  )
@@ -202,9 +202,16 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
202
202
  return True
203
203
 
204
204
  def _is_user_authenticated_user(self, user: NamedUser) -> bool:
205
- """Check if a given NamedUser is the authenticated user"""
206
- if user.login == self.gh.get_user().login:
207
- return True
205
+ """Check if a given NamedUser is the authenticated user. If logging in via App, this will
206
+ always return False, as the authenticated user is the App itself"""
207
+ try:
208
+ if user.login == self.gh.get_user().login:
209
+ return True
210
+ except GithubException as e:
211
+ if e.status == 403 and "Resource not accessible by integration" in str(e):
212
+ logging.debug("Cannot check if user is authenticated, as this is an App login")
213
+ return False
214
+ raise
208
215
  return False
209
216
 
210
217
  def sync_org_owners(self, dry: bool = False, force: bool = False) -> None:
@@ -296,7 +303,18 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
296
303
  for team, attributes in self.configured_teams.items():
297
304
  if team not in existent_team_names:
298
305
  if parent := attributes.get("parent"): # type: ignore
299
- parent_id = self.org.get_team_by_slug(sluggify_teamname(parent)).id
306
+ try:
307
+ parent_id = self.org.get_team_by_slug(sluggify_teamname(parent)).id
308
+ except UnknownObjectException:
309
+ if dry:
310
+ logging.debug(
311
+ "For team %s, the configured parent team's ('%s') ID wasn't found, "
312
+ "probably because it should be created but it's a dry-run. "
313
+ "We set a default ID of 424242",
314
+ team,
315
+ parent,
316
+ )
317
+ parent_id = 424242
300
318
 
301
319
  logging.info("Creating team '%s' with parent ID '%s'", team, parent_id)
302
320
  self.stats.create_team(team)
@@ -594,31 +612,38 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
594
612
  team.name,
595
613
  )
596
614
 
597
- def get_unconfigured_teams(
615
+ def get_and_delete_unconfigured_teams(
598
616
  self, dry: bool = False, delete_unconfigured_teams: bool = False
599
617
  ) -> None:
600
618
  """Get all teams that are not configured locally and optionally remove them"""
601
619
  # Get all teams that are not configured locally
602
- unconfigured_teams: list[Team] = []
603
620
  for team in self.current_teams:
604
621
  if team.name not in self.configured_teams:
605
- unconfigured_teams.append(team)
622
+ self.unconfigured_teams.append(team)
606
623
 
607
- if unconfigured_teams:
624
+ if self.unconfigured_teams:
608
625
  if delete_unconfigured_teams:
609
- for team in unconfigured_teams:
626
+ for team in self.unconfigured_teams:
610
627
  logging.info("Deleting team '%s' as it is not configured locally", team.name)
611
628
  self.stats.delete_team(team=team.name, deleted=True)
612
629
  if not dry:
613
- team.delete()
630
+ try:
631
+ team.delete()
632
+ except UnknownObjectException as e:
633
+ logging.info(
634
+ "Team '%s' could not be deleted, probably because it was already "
635
+ "deleted as part of a parent team. "
636
+ "Error: %s",
637
+ team.name,
638
+ e,
639
+ )
614
640
  else:
615
- unconfigured_teams_str = [team.name for team in unconfigured_teams]
616
641
  logging.warning(
617
642
  "The following teams of your GitHub organisation are not "
618
643
  "configured locally: %s. Taking no action about these teams.",
619
- ", ".join(unconfigured_teams_str),
644
+ ", ".join([team.name for team in self.unconfigured_teams]),
620
645
  )
621
- for team in unconfigured_teams:
646
+ for team in self.unconfigured_teams:
622
647
  self.stats.delete_team(team=team.name, deleted=False)
623
648
 
624
649
  def get_members_without_team(
@@ -145,7 +145,7 @@ def main():
145
145
  org.sync_teams_members(dry=args.dry)
146
146
  # Report and act on teams that are not configured locally
147
147
  log_progress("Checking for unconfigured teams...")
148
- org.get_unconfigured_teams(
148
+ org.get_and_delete_unconfigured_teams(
149
149
  dry=args.dry,
150
150
  delete_unconfigured_teams=cfg_app.get("delete_unconfigured_teams", False),
151
151
  )
@@ -4,7 +4,7 @@
4
4
 
5
5
  [tool.poetry]
6
6
  name = "github-org-manager"
7
- version = "0.7.2"
7
+ version = "0.7.4"
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"
@@ -27,9 +27,9 @@ gh-org-mgr = 'gh_org_mgr.manage:main'
27
27
 
28
28
  [tool.poetry.dependencies]
29
29
  python = "^3.10"
30
- pygithub = "^2.3.0"
31
- pyyaml = "^6.0.1"
32
- requests = "^2.32.3"
30
+ pygithub = "^2.7.0"
31
+ pyyaml = "^6.0.2"
32
+ requests = "^2.32.4"
33
33
  python-slugify = "^8.0.4"
34
34
 
35
35
  [tool.poetry.group.dev.dependencies]