github-org-manager 0.7.12__tar.gz → 0.7.14__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.4
2
2
  Name: github-org-manager
3
- Version: 0.7.12
3
+ Version: 0.7.14
4
4
  Summary: Manage a GitHub Organization, its teams, repository permissions, and more
5
5
  Keywords: github,github-management,permissions,access-control
6
6
  Author: Max Mehl
@@ -26,7 +26,7 @@ Classifier: Programming Language :: Python :: 3.14
26
26
  Requires-Dist: pygithub>=2.8.1,<3
27
27
  Requires-Dist: pyyaml>=6.0.3,<7
28
28
  Requires-Dist: requests>=2.32.5,<3
29
- Requires-Dist: python-slugify>=8.0.4,<9
29
+ Requires-Dist: python-slugify>=9,<10
30
30
  Requires-Dist: jsonschema>=4.25.1,<5
31
31
  Requires-Python: >=3.10, <4
32
32
  Project-URL: Repository, https://github.com/OpenRailAssociation/github-org-manager
@@ -128,7 +128,7 @@ class GHorg:
128
128
  def _resolve_gh_username(self, username: str, teamname: str) -> NamedUser | None:
129
129
  """Turn a username into a proper GitHub user object."""
130
130
  try:
131
- gh_user: NamedUser = self.gh.get_user(username) # type: ignore[assignment] # ty:ignore[invalid-assignment]
131
+ gh_user: NamedUser = self.gh.get_user(username)
132
132
  except UnknownObjectException:
133
133
  logging.exception(
134
134
  "The user '%s' configured as member of team '%s' does not "
@@ -304,7 +304,7 @@ class GHorg:
304
304
 
305
305
  # First, check whether all configured parent teams exist or will be created
306
306
  for team, attributes in self.configured_teams.items():
307
- if parent := attributes.get("parent"): # type: ignore[union-attr] # ty:ignore[unresolved-attribute]
307
+ if parent := attributes.get("parent"): # ty:ignore[unresolved-attribute]
308
308
  if parent not in self.configured_teams:
309
309
  if parent not in self.current_teams_str:
310
310
  logging.critical(
@@ -338,7 +338,7 @@ class GHorg:
338
338
  if team in ordered_teams:
339
339
  continue
340
340
  # Team has parent, but parent not ordered yet
341
- if (parent := attributes.get("parent")) and parent not in ordered_teams: # type: ignore[union-attr] # ty:ignore[unresolved-attribute]
341
+ if (parent := attributes.get("parent")) and parent not in ordered_teams: # ty:ignore[unresolved-attribute]
342
342
  continue
343
343
  # Team has no parent, or parent already ordered
344
344
  ordered_teams[team] = attributes
@@ -350,7 +350,7 @@ class GHorg:
350
350
  for team, attributes in self.configured_teams.items():
351
351
  if team not in self.current_teams_str:
352
352
  # If a parent team is configured, try to get its ID
353
- if parent := attributes.get("parent"): # type: ignore[union-attr] # ty:ignore[unresolved-attribute]
353
+ if parent := attributes.get("parent"): # ty:ignore[unresolved-attribute]
354
354
  try:
355
355
  parent_id = self.org.get_team_by_slug(sluggify_teamname(parent)).id
356
356
  except UnknownObjectException:
@@ -436,13 +436,13 @@ class GHorg:
436
436
  # Use dictionary comprehensions to build the dictionaries with the
437
437
  # relevant team settings for comparison
438
438
  configured_team_configs = {
439
- key: self.configured_teams[team.name].get(key) # type: ignore[union-attr] # ty:ignore[unresolved-attribute]
439
+ key: self.configured_teams[team.name].get(key) # ty:ignore[unresolved-attribute]
440
440
  for key in self.TEAM_CONFIG_FIELDS
441
441
  # Only add keys that are actually in the configuration. Deals
442
442
  # with settings that should be changed, as they are neither
443
443
  # defined in the default or team config, and marked as
444
444
  # <keep-current>
445
- if key in self.configured_teams[team.name] # type: ignore[operator] # ty:ignore[unsupported-operator]
445
+ if key in self.configured_teams[team.name] # ty:ignore[unsupported-operator]
446
446
  }
447
447
  current_team_configs = {
448
448
  key: getattr(team, key)
@@ -450,7 +450,7 @@ class GHorg:
450
450
  # Only compare current team settings with keys that are defined
451
451
  # as the configured team settings. Taking out settings that
452
452
  # shall not be changed
453
- if key in self.configured_teams[team.name] # type: ignore[operator] # ty:ignore[unsupported-operator]
453
+ if key in self.configured_teams[team.name] # ty:ignore[unsupported-operator]
454
454
  }
455
455
 
456
456
  # Resolve parent team id from parent Team object or team string, and sort
@@ -480,7 +480,7 @@ class GHorg:
480
480
  # Execute team setting changes
481
481
  if not dry:
482
482
  try:
483
- team.edit(name=team.name, **configured_team_configs) # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
483
+ team.edit(name=team.name, **configured_team_configs) # ty:ignore[invalid-argument-type]
484
484
  except GithubException as exc:
485
485
  logging.critical(
486
486
  "Team '%s' settings could not be edited. Error: \n%s",
@@ -512,14 +512,22 @@ class GHorg:
512
512
  return []
513
513
 
514
514
  def _get_current_team_members(self, team: Team) -> dict[NamedUser, str]:
515
- """Return dict of current users with their respective roles. Also
516
- contains members of child teams.
515
+ """Return dict of current users with their respective roles. Only
516
+ contains direct members of the team, not members of child teams.
517
517
  """
518
518
  current_users: dict[NamedUser, str] = {}
519
519
  for role in ("member", "maintainer"):
520
- # Make a two-step check whether person is actually in team, as
521
- # get_members() also return child-team members
520
+ # Check whether person is actually direct member of team, as
521
+ # get_members() also returns child-team members, marked via the
522
+ # "inherited" flag in the API response
522
523
  for user in list(team.get_members(role=role)):
524
+ if user._rawData.get("inherited"): # noqa: SLF001
525
+ logging.debug(
526
+ "User '%s' is only member of a child team of '%s', ignoring",
527
+ user.login,
528
+ team.name,
529
+ )
530
+ continue
523
531
  current_users.update({user: role})
524
532
 
525
533
  return current_users
@@ -819,7 +827,7 @@ class GHorg:
819
827
  """Create a record of all members of a team and their permissions on a
820
828
  repo due to being member of an unconfigured team.
821
829
  """
822
- users_of_unconfigured_team: dict[NamedUser, str] = self.current_teams[team].get("members") # type: ignore[assignment] # ty:ignore[invalid-assignment]
830
+ users_of_unconfigured_team: dict[NamedUser, str] = self.current_teams[team].get("members") # ty:ignore[invalid-assignment]
823
831
  # Initiate this repo in the dict as dict if not present
824
832
  if repo_name not in self.unconfigured_team_repo_permissions:
825
833
  self.unconfigured_team_repo_permissions[repo_name] = {}
@@ -893,7 +901,7 @@ class GHorg:
893
901
  if self.configured_teams[team.name] is None:
894
902
  remove = True
895
903
  # Handle: Team is configured, contains config
896
- elif repos := self.configured_teams[team.name].get("repos", []): # type: ignore[union-attr] # ty:ignore[unresolved-attribute]
904
+ elif repos := self.configured_teams[team.name].get("repos", []): # ty:ignore[unresolved-attribute]
897
905
  # If this repo has not been found in the configured repos
898
906
  # for the team, remove all permissions
899
907
  if repo.name not in repos:
@@ -1243,6 +1251,13 @@ class GHorg:
1243
1251
  if repo.name in self.graphql_repos_collaborators:
1244
1252
  # Extract each collaborator from the GraphQL response for this repo
1245
1253
  for collaborator in self.graphql_repos_collaborators[repo.name]:
1254
+ if collaborator is None or collaborator.get("node") is None:
1255
+ logging.debug(
1256
+ "Skipping collaborator entry with missing data in repo %s: %s",
1257
+ repo.name,
1258
+ collaborator,
1259
+ )
1260
+ continue
1246
1261
  login: str = collaborator["node"]["login"]
1247
1262
  # Skip entry if collaborator is org owner, which is "admin" anyway
1248
1263
  if login.lower() in [user.login.lower() for user in self.current_org_owners]:
@@ -90,7 +90,7 @@ def setup_team(
90
90
  # Come up with file name based on team name in the given config directory
91
91
  if not file_path:
92
92
  # Combine config dir and file name
93
- file_path = str(Path(config_path) / "teams" / (slugify(team_name) + ".yaml")) # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
93
+ file_path = str(Path(config_path) / "teams" / (slugify(team_name) + ".yaml")) # ty:ignore[invalid-argument-type]
94
94
  logging.debug("Derived file path: %s", file_path)
95
95
 
96
96
  # Fill template
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "github-org-manager"
3
- version = "0.7.12"
3
+ version = "0.7.14"
4
4
  description = "Manage a GitHub Organization, its teams, repository permissions, and more"
5
5
  requires-python = ">=3.10,<4"
6
6
  readme = "README.md"
@@ -33,7 +33,7 @@ dependencies = [
33
33
  "pygithub>=2.8.1,<3",
34
34
  "pyyaml>=6.0.3,<7",
35
35
  "requests>=2.32.5,<3",
36
- "python-slugify>=8.0.4,<9",
36
+ "python-slugify>=9,<10",
37
37
  "jsonschema>=4.25.1,<5",
38
38
  ]
39
39
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  [project]
6
6
  name = "github-org-manager"
7
- version = "0.7.12"
7
+ version = "0.7.14"
8
8
  description = "Manage a GitHub Organization, its teams, repository permissions, and more"
9
9
  authors = [{ name = "Max Mehl", email = "max.mehl@deutschebahn.com" }]
10
10
  requires-python = ">=3.10,<4"
@@ -35,7 +35,7 @@ dependencies = [
35
35
  "pygithub>=2.8.1,<3",
36
36
  "pyyaml>=6.0.3,<7",
37
37
  "requests>=2.32.5,<3",
38
- "python-slugify>=8.0.4,<9",
38
+ "python-slugify>=9,<10",
39
39
  "jsonschema>=4.25.1,<5",
40
40
  ]
41
41