Skip to content

Commit c0ea814

Browse files
committed
refactor: replace get_env_vars() 29-element tuple with frozen EvergreenConfig dataclass
## What Replace the 29-element tuple returned by get_env_vars() with a frozen dataclass EvergreenConfig. Updated evergreen.py main() to use config.attribute access instead of tuple destructuring, and converted all 20 expected_result tuples in test_env.py to use named EvergreenConfig construction. ## Why The 29-element tuple was fragile — adding/removing fields required counting positions across 20+ tests, and positional access like result[8] was unreadable. Named attribute access eliminates these problems and makes future field additions trivial. ## Notes - This is a pure refactor with no behavior change. All 155 tests pass with identical coverage. - The dataclass uses frozen=True for immutability, matching the previous tuple's semantics. - pylint disable for too-many-instance-attributes is necessary since the config genuinely has 29 fields. This is inherent to the problem domain, not a design issue. - The main() function in evergreen.py is pragma: no cover, so switching from destructuring to config.attr access is verified by the type checker (mypy) rather than unit tests. Signed-off-by: jmeridth <jmeridth@gmail.com>
1 parent f1a2991 commit c0ea814

File tree

3 files changed

+820
-814
lines changed

3 files changed

+820
-814
lines changed

env.py

Lines changed: 69 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import os
66
import re
7+
from dataclasses import dataclass
78
from os.path import dirname, join
89

910
from dotenv import load_dotenv
@@ -45,6 +46,41 @@
4546
]
4647

4748

49+
@dataclass(frozen=True)
50+
class EvergreenConfig: # pylint: disable=too-many-instance-attributes
51+
"""Configuration for the Evergreen action, parsed from environment variables."""
52+
53+
organization: str | None
54+
repository_list: list[str]
55+
search_query: str | None
56+
gh_app_id: int | None
57+
gh_app_installation_id: int | None
58+
gh_app_private_key_bytes: bytes
59+
gh_app_enterprise_only: bool
60+
token: str
61+
ghe: str
62+
exempt_repositories_list: list[str]
63+
follow_up_type: str
64+
title: str
65+
body: str
66+
created_after_date: str
67+
dry_run: bool
68+
commit_message: str
69+
project_id: str | None
70+
group_dependencies: bool | None
71+
filter_visibility: list[str]
72+
batch_size: int | None
73+
enable_security_updates: bool | None
74+
exempt_ecosystems: list[str]
75+
update_existing: bool | None
76+
repo_specific_exemptions: dict
77+
schedule: str
78+
schedule_day: str
79+
team_name: str | None
80+
labels: list[str]
81+
dependabot_config_file: str | None
82+
83+
4884
def get_bool_env_var(env_var_name: str, default: bool = False) -> bool:
4985
"""Get a boolean environment variable.
5086
@@ -117,72 +153,15 @@ def parse_repo_specific_exemptions(repo_specific_exemptions_str: str) -> dict:
117153

118154
def get_env_vars(
119155
test: bool = False,
120-
) -> tuple[
121-
str | None,
122-
list[str],
123-
str | None,
124-
int | None,
125-
int | None,
126-
bytes,
127-
bool,
128-
str,
129-
str,
130-
list[str],
131-
str,
132-
str,
133-
str,
134-
str,
135-
bool,
136-
str,
137-
str | None,
138-
bool | None,
139-
list[str] | None,
140-
int | None,
141-
bool | None,
142-
list[str],
143-
bool | None,
144-
dict,
145-
str,
146-
str,
147-
str | None,
148-
list[str],
149-
str | None,
150-
]:
156+
) -> EvergreenConfig:
151157
"""
152158
Get the environment variables for use in the action.
153159
154160
Args:
155-
None
161+
test: If True, skip loading from .env file.
156162
157163
Returns:
158-
organization (str): The organization to search for repositories in
159-
repository_list (list[str]): A list of repositories to search for
160-
search_query (str): A search query string to filter repositories by
161-
gh_app_id (int | None): The GitHub App ID to use for authentication
162-
gh_app_installation_id (int | None): The GitHub App Installation ID to use for authentication
163-
gh_app_private_key_bytes (bytes): The GitHub App Private Key as bytes to use for authentication
164-
gh_app_enterprise_only (bool): Set this to true if the GH APP is created on GHE and needs to communicate with GHE api only
165-
token (str): The GitHub token to use for authentication
166-
ghe (str): The GitHub Enterprise URL to use for authentication
167-
exempt_repositories_list (list[str]): A list of repositories to exempt from the action
168-
follow_up_type (str): The type of follow up to open (issue or pull)
169-
title (str): The title of the follow up
170-
body (str): The body of the follow up
171-
created_after_date (str): The date to filter repositories by
172-
dry_run (bool): Whether or not to actually open issues/pull requests
173-
commit_message (str): The commit message of the follow up
174-
group_dependencies (bool): Whether to group dependencies in the dependabot.yml file
175-
filter_visibility (list[str]): Run the action only on repositories with the specified listed visibility
176-
batch_size (int): The max number of repositories in scope
177-
enable_security_updates (bool): Whether to enable security updates in target repositories
178-
exempt_ecosystems_list (list[str]): A list of package ecosystems to exempt from the action
179-
update_existing (bool): Whether to update existing dependabot configuration files
180-
repo_specific_exemptions (dict): A dictionary of per repository ecosystem exemptions
181-
schedule (str): The schedule to run the action on
182-
schedule_day (str): The day of the week to run the action on if schedule is daily
183-
team_name (str): The team to search for repositories in
184-
labels (list[str]): A list of labels to be added to dependabot configuration
185-
dependabot_config_file (str): Dependabot extra configuration file location path
164+
EvergreenConfig: A frozen dataclass containing all configuration values.
186165
"""
187166

188167
if not test: # pragma: no cover
@@ -379,34 +358,34 @@ def get_env_vars(
379358
f"No dependabot extra configuration found. Please create one in {dependabot_config_file}"
380359
)
381360

382-
return (
383-
organization,
384-
repositories_list,
385-
search_query,
386-
gh_app_id,
387-
gh_app_installation_id,
388-
gh_app_private_key_bytes,
389-
gh_app_enterprise_only,
390-
token,
391-
ghe,
392-
exempt_repositories_list,
393-
follow_up_type,
394-
title,
395-
body,
396-
created_after_date,
397-
dry_run_bool,
398-
commit_message,
399-
project_id,
400-
group_dependencies_bool,
401-
filter_visibility_list,
402-
batch_size,
403-
enable_security_updates_bool,
404-
exempt_ecosystems_list,
405-
update_existing,
406-
repo_specific_exemptions,
407-
schedule,
408-
schedule_day,
409-
team_name,
410-
labels_list,
411-
dependabot_config_file,
361+
return EvergreenConfig(
362+
organization=organization,
363+
repository_list=repositories_list,
364+
search_query=search_query,
365+
gh_app_id=gh_app_id,
366+
gh_app_installation_id=gh_app_installation_id,
367+
gh_app_private_key_bytes=gh_app_private_key_bytes,
368+
gh_app_enterprise_only=gh_app_enterprise_only,
369+
token=token,
370+
ghe=ghe,
371+
exempt_repositories_list=exempt_repositories_list,
372+
follow_up_type=follow_up_type,
373+
title=title,
374+
body=body,
375+
created_after_date=created_after_date,
376+
dry_run=dry_run_bool,
377+
commit_message=commit_message,
378+
project_id=project_id,
379+
group_dependencies=group_dependencies_bool,
380+
filter_visibility=filter_visibility_list,
381+
batch_size=batch_size,
382+
enable_security_updates=enable_security_updates_bool,
383+
exempt_ecosystems=exempt_ecosystems_list,
384+
update_existing=update_existing,
385+
repo_specific_exemptions=repo_specific_exemptions,
386+
schedule=schedule,
387+
schedule_day=schedule_day,
388+
team_name=team_name,
389+
labels=labels_list,
390+
dependabot_config_file=dependabot_config_file,
412391
)

0 commit comments

Comments
 (0)