2424from jwt .exceptions import InvalidKeyError
2525
2626from ._gh_api import get_github_secrets_from_env , run_graphql_query
27+ from ._helpers import (
28+ compare_two_dicts ,
29+ compare_two_lists ,
30+ pretty_print_dict ,
31+ sluggify_teamname ,
32+ )
2733from ._stats import OrgChanges
2834
2935
@@ -64,12 +70,6 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
6470 # --------------------------------------------------------------------------
6571 # Helper functions
6672 # --------------------------------------------------------------------------
67- def _sluggify_teamname (self , team : str ) -> str :
68- """Slugify a GitHub team name"""
69- # TODO: this is very naive, no other special chars are
70- # supported, or multiple spaces etc.
71- return team .replace (" " , "-" )
72-
7373 # amazonq-ignore-next-line
7474 def login (
7575 self , orgname : str , token : str = "" , app_id : str | int = "" , app_private_key : str = ""
@@ -120,88 +120,9 @@ def ratelimit(self):
120120 "Current rate limit: %s/%s (reset: %s)" , core .remaining , core .limit , core .reset
121121 )
122122
123- def pretty_print_dict (self , dictionary : dict ) -> str :
124- """Convert a dict to a pretty-printed output"""
125-
126- # Censor sensible fields
127- def censor_half_string (string : str ) -> str :
128- """Censor 50% of a string (rounded up)"""
129- half1 = int (len (string ) / 2 )
130- half2 = len (string ) - half1
131- return string [:half1 ] + "*" * (half2 )
132-
133- sensible_keys = ["gh_token" , "gh_app_private_key" ]
134- for key in sensible_keys :
135- if value := dictionary .get (key , "" ):
136- dictionary [key ] = censor_half_string (value )
137-
138- # Print dict nicely
139- def pretty (d , indent = 0 ):
140- string = ""
141- for key , value in d .items ():
142- string += " " * indent + str (key ) + ":\n "
143- if isinstance (value , dict ):
144- string += pretty (value , indent + 1 )
145- else :
146- string += " " * (indent + 1 ) + str (value ) + "\n "
147-
148- return string
149-
150- return pretty (dictionary )
151-
152123 def pretty_print_dataclass (self ) -> str :
153124 """Convert this dataclass to a pretty-printed output"""
154- return self .pretty_print_dict (asdict (self ))
155-
156- def compare_two_lists (self , list1 : list [str ], list2 : list [str ]):
157- """
158- Compares two lists of strings and returns a tuple containing elements
159- missing in each list and common elements.
160-
161- Args:
162- list1 (list of str): The first list of strings.
163- list2 (list of str): The second list of strings.
164-
165- Returns:
166- tuple: A tuple containing three lists:
167- 1. The first list contains elements in `list2` that are missing in `list1`.
168- 2. The second list contains elements that are present in both `list1` and `list2`.
169- 3. The third list contains elements in `list1` that are missing in `list2`.
170-
171- Example:
172- >>> list1 = ["apple", "banana", "cherry"]
173- >>> list2 = ["banana", "cherry", "date", "fig"]
174- >>> compare_lists(list1, list2)
175- (['date', 'fig'], ['banana', 'cherry'], ['apple'])
176- """
177- # Convert lists to sets for easier comparison
178- set1 , set2 = set (list1 ), set (list2 )
179-
180- # Elements in list2 that are missing in list1
181- missing_in_list1 = list (set2 - set1 )
182-
183- # Elements present in both lists
184- common_elements = list (set1 & set2 )
185-
186- # Elements in list1 that are missing in list2
187- missing_in_list2 = list (set1 - set2 )
188-
189- # Return the result as a tuple
190- return (missing_in_list1 , common_elements , missing_in_list2 )
191-
192- def compare_two_dicts (self , dict1 : dict , dict2 : dict ) -> dict [str , dict [str , str | int | None ]]:
193- """Compares two dictionaries. Assume that the keys are the same. Output
194- a dict with keys that have differing values"""
195- # Create an empty dictionary to store differences
196- differences = {}
197-
198- # Iterate through the keys (assuming both dictionaries have the same keys)
199- for key in dict1 :
200- # Compare the values for each key
201- if dict1 [key ] != dict2 [key ]:
202- differences [key ] = {"dict1" : dict1 [key ], "dict2" : dict2 [key ]}
203-
204- return differences
125+ return pretty_print_dict (asdict (self ), sensible_keys = ["gh_token" , "gh_app_private_key" ])
205126
206127 def _resolve_gh_username (self , username : str , teamname : str ) -> NamedUser | None :
207128 """Turn a username into a proper GitHub user object"""
@@ -296,7 +217,7 @@ def sync_org_owners(self, dry: bool = False, force: bool = False) -> None:
296217 return
297218
298219 # Get differences between the current and configured owners
299- owners_remove , owners_ok , owners_add = self . compare_two_lists (
220+ owners_remove , owners_ok , owners_add = compare_two_lists (
300221 self .configured_org_owners , [user .login for user in self .current_org_owners ]
301222 )
302223 # Compare configured (lower-cased) owners with lower-cased list of current owners
@@ -375,7 +296,7 @@ def create_missing_teams(self, dry: bool = False):
375296 for team , attributes in self .configured_teams .items ():
376297 if team not in existent_team_names :
377298 if parent := attributes .get ("parent" ): # type: ignore
378- parent_id = self .org .get_team_by_slug (self . _sluggify_teamname (parent )).id
299+ parent_id = self .org .get_team_by_slug (sluggify_teamname (parent )).id
379300
380301 logging .info ("Creating team '%s' with parent ID '%s'" , team , parent_id )
381302 self .stats .create_team (team )
@@ -417,7 +338,7 @@ def _prepare_team_config_for_sync(
417338 # team coming from config, and valid string
418339 elif isinstance (parent , str ) and parent :
419340 team_config ["parent_team_id" ] = self .org .get_team_by_slug (
420- self . _sluggify_teamname (parent )
341+ sluggify_teamname (parent )
421342 ).id
422343 # empty from string, so probably default value
423344 elif isinstance (parent , str ) and not parent :
@@ -478,7 +399,7 @@ def sync_current_teams_settings(self, dry: bool = False) -> None:
478399 )
479400
480401 # Compare settings and update if necessary
481- if differences := self . compare_two_dicts (configured_team_configs , current_team_configs ):
402+ if differences := compare_two_dicts (configured_team_configs , current_team_configs ):
482403 # Log differences
483404 logging .info (
484405 "Team settings for '%s' differ from the configuration. Updating them:" ,
@@ -497,7 +418,7 @@ def sync_current_teams_settings(self, dry: bool = False) -> None:
497418 logging .critical (
498419 "Team '%s' settings could not be edited. Error: \n %s" ,
499420 team .name ,
500- self . pretty_print_dict (exc .data ),
421+ pretty_print_dict (exc .data ),
501422 )
502423 sys .exit (1 )
503424 else :
@@ -772,7 +693,7 @@ def _create_perms_changelist_for_teams(
772693
773694 # Convert team name to Team object
774695 try :
775- team = self .org .get_team_by_slug (self . _sluggify_teamname (team_name ))
696+ team = self .org .get_team_by_slug (sluggify_teamname (team_name ))
776697 # Team not found, probably because a new team should be created, but it's a dry-run
777698 except UnknownObjectException :
778699 logging .debug (
@@ -795,7 +716,7 @@ def _create_perms_changelist_for_teams(
795716 attributes = {
796717 "id" : 0 ,
797718 "name" : team_name ,
798- "slug" : self . _sluggify_teamname (team_name ),
719+ "slug" : sluggify_teamname (team_name ),
799720 },
800721 completed = True , # Mark as fully initialized
801722 )
0 commit comments