|
7 | 7 |
|
8 | 8 | import yaml
|
9 | 9 | from dotenv import load_dotenv
|
10 |
| -from pydantic import BaseModel, field_validator |
| 10 | +from pydantic import BaseModel, field_validator, model_validator |
11 | 11 | from requests.auth import AuthBase
|
12 | 12 |
|
13 | 13 | load_dotenv()
|
14 |
| -_ENVVAR_CHECK_DONE_GITHUB_PERSONAL_ACCESS_TOKEN = "CHECK_DONE_GITHUB_PERSONAL_ACCESS_TOKEN" |
15 |
| -CHECK_DONE_GITHUB_PERSONAL_ACCESS_TOKEN = os.environ.get(_ENVVAR_CHECK_DONE_GITHUB_PERSONAL_ACCESS_TOKEN) |
16 | 14 |
|
17 |
| -_CONFIG_PATH = Path(__file__).parent.parent / "data" / ".check_done.yaml" |
| 15 | +_CONFIGURATION_PATH = Path(__file__).parent.parent / "configuration" / ".check_done.yaml" |
18 | 16 | _GITHUB_ORGANIZATION_NAME_AND_PROJECT_NUMBER_URL_REGEX = re.compile(
|
19 | 17 | r"https://github\.com/orgs/(?P<organization_name>[a-zA-Z0-9\-]+)/projects/(?P<project_number>[0-9]+).*"
|
20 | 18 | )
|
|
23 | 21 | )
|
24 | 22 |
|
25 | 23 |
|
26 |
| -class _ConfigInfo(BaseModel): |
| 24 | +class YamlInfo(BaseModel): |
| 25 | + check_done_github_app_id: str | None = None |
| 26 | + check_done_github_app_private_key: str | None = None |
| 27 | + personal_access_token: str | None = None |
| 28 | + project_status_name_to_check: str | None = None |
27 | 29 | project_url: str
|
28 |
| - check_done_github_app_id: str |
29 |
| - check_done_github_app_private_key: str |
30 | 30 |
|
31 |
| - @field_validator("project_url", "check_done_github_app_id", "check_done_github_app_private_key", mode="before") |
| 31 | + @field_validator( |
| 32 | + "project_url", |
| 33 | + "project_status_name_to_check", |
| 34 | + "personal_access_token", |
| 35 | + "check_done_github_app_id", |
| 36 | + "check_done_github_app_private_key", |
| 37 | + mode="before", |
| 38 | + ) |
32 | 39 | def value_from_env(cls, value: Any | None):
|
33 |
| - if isinstance(value, str): |
34 |
| - stripped_value = value.strip() |
35 |
| - result = ( |
36 |
| - resolved_environment_variables(value) |
37 |
| - if stripped_value.startswith("${") and stripped_value.endswith("}") |
38 |
| - else stripped_value |
39 |
| - ) |
40 |
| - else: |
41 |
| - result = value |
| 40 | + stripped_value = value.strip() |
| 41 | + result = ( |
| 42 | + resolved_environment_variables(value) |
| 43 | + if stripped_value.startswith("${") and stripped_value.endswith("}") |
| 44 | + else stripped_value |
| 45 | + ) |
42 | 46 | return result
|
43 | 47 |
|
44 | 48 |
|
45 |
| -def config_info() -> _ConfigInfo: |
46 |
| - config_map = config_map_from_yaml_file(_CONFIG_PATH) |
47 |
| - return _ConfigInfo(**config_map) |
| 49 | +class ConfigurationInfo(BaseModel): |
| 50 | + check_done_github_app_id: str | None = None |
| 51 | + check_done_github_app_private_key: str | None = None |
| 52 | + personal_access_token: str | None = None |
| 53 | + project_number: int |
| 54 | + project_owner_name: str |
| 55 | + project_owner_type: str |
| 56 | + project_status_name_to_check: str | None = None |
| 57 | + |
| 58 | + @model_validator(mode="after") |
| 59 | + def validate_at_least_one_authentication_method_in_configuration(self): |
| 60 | + has_user_authentication = ( |
| 61 | + self.personal_access_token is not None and self.project_owner_type == ProjectOwnerType.User.value |
| 62 | + ) |
| 63 | + has_organizational_authentication = ( |
| 64 | + self.check_done_github_app_id is not None |
| 65 | + and self.check_done_github_app_private_key is not None |
| 66 | + and self.project_owner_type == ProjectOwnerType.Organization.value |
| 67 | + ) |
| 68 | + if not (has_user_authentication or has_organizational_authentication): |
| 69 | + raise ValueError("At least one authentication method must be configured.") |
| 70 | + return self |
| 71 | + |
| 72 | + |
| 73 | +def configuration_info() -> ConfigurationInfo: |
| 74 | + yaml_map = configuration_map_from_yaml_file(_CONFIGURATION_PATH) |
| 75 | + yaml_info = YamlInfo(**yaml_map) |
| 76 | + project_url = yaml_info.project_url |
| 77 | + project_owner_type, project_owner_name, project_number = ( |
| 78 | + github_project_owner_type_and_project_owner_name_and_project_number_from_url_if_matches(project_url) |
| 79 | + ) |
| 80 | + return ConfigurationInfo( |
| 81 | + check_done_github_app_id=yaml_info.check_done_github_app_id, |
| 82 | + check_done_github_app_private_key=yaml_info.check_done_github_app_private_key, |
| 83 | + personal_access_token=yaml_info.personal_access_token, |
| 84 | + project_number=project_number, |
| 85 | + project_owner_name=project_owner_name, |
| 86 | + project_owner_type=project_owner_type, |
| 87 | + project_status_name_to_check=yaml_info.project_status_name_to_check, |
| 88 | + ) |
48 | 89 |
|
49 | 90 |
|
50 | 91 | class ProjectOwnerType(StrEnum):
|
@@ -82,14 +123,14 @@ def resolved_environment_variables(value: str, fail_on_missing_envvar=True) -> s
|
82 | 123 | return result
|
83 | 124 |
|
84 | 125 |
|
85 |
| -def config_map_from_yaml_file(config_path: Path) -> dict: |
| 126 | +def configuration_map_from_yaml_file(configuration_path: Path) -> dict: |
86 | 127 | try:
|
87 |
| - with open(config_path) as config_file: |
88 |
| - result = yaml.safe_load(config_file) |
| 128 | + with open(configuration_path) as configuration_file: |
| 129 | + result = yaml.safe_load(configuration_file) |
89 | 130 | if result is None:
|
90 |
| - raise ValueError(f"The check_done configuration is empty. Path: {config_path}") |
| 131 | + raise ValueError(f"The check_done configuration is empty. Path: {configuration_path}") |
91 | 132 | except FileNotFoundError as error:
|
92 |
| - raise FileNotFoundError(f"Cannot find check_done configuration: {config_path}") from error |
| 133 | + raise FileNotFoundError(f"Cannot find check_done configuration: {configuration_path}") from error |
93 | 134 | return result
|
94 | 135 |
|
95 | 136 |
|
|
0 commit comments