atlas-init 0.4.3__py3-none-any.whl → 0.4.4__py3-none-any.whl

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.
atlas_init/__init__.py CHANGED
@@ -1,9 +1,8 @@
1
1
  from pathlib import Path
2
2
 
3
- VERSION = "0.4.3"
3
+ VERSION = "0.4.4"
4
4
 
5
5
 
6
6
  def running_in_repo() -> bool:
7
- maybe_git_directory = Path(__file__).parent.parent / ".git"
8
- git_config = maybe_git_directory / "config"
7
+ git_config = Path(__file__).parent.parent / ".git/config"
9
8
  return git_config.exists() and "atlas-init" in git_config.read_text()
atlas_init/cli.py CHANGED
@@ -45,6 +45,7 @@ from atlas_init.settings.env_vars import (
45
45
  active_suites,
46
46
  init_settings,
47
47
  )
48
+ from atlas_init.settings.env_vars_generated import AWSSettings, AtlasSettings
48
49
  from atlas_init.settings.path import (
49
50
  CwdIsNoRepoPathError,
50
51
  dump_vscode_dotenv,
@@ -79,7 +80,7 @@ def apply(context: typer.Context, *, skip_outputs: bool = False):
79
80
 
80
81
 
81
82
  def _plan_or_apply(extra_args: list[str], command: Literal["plan", "apply"], *, skip_outputs: bool) -> list[TestSuite]: # type: ignore
82
- settings = init_settings()
83
+ settings = init_settings(AtlasSettings, AWSSettings)
83
84
  logger.info(f"using the '{command}' command, extra args: {extra_args}")
84
85
  try:
85
86
  suites = active_suites(settings)
atlas_init/cli_cfn/app.py CHANGED
@@ -14,9 +14,6 @@ from atlas_init.cli_cfn.aws import (
14
14
  from atlas_init.cli_cfn.aws import (
15
15
  delete_stack as delete_stack_aws,
16
16
  )
17
- from atlas_init.cli_cfn.cfn_parameter_finder import (
18
- read_execution_role,
19
- )
20
17
  from atlas_init.cli_cfn.contract import contract_test_cmd
21
18
  from atlas_init.cli_cfn.example import example_cmd
22
19
  from atlas_init.cli_cfn.files import (
@@ -31,6 +28,7 @@ from atlas_init.repos.cfn import (
31
28
  )
32
29
  from atlas_init.repos.path import Repo, current_dir, find_paths, resource_root
33
30
  from atlas_init.settings.env_vars import active_suites, init_settings
31
+ from atlas_init.settings.env_vars_modules import TFModuleCfn
34
32
 
35
33
  app = typer.Typer(no_args_is_help=True)
36
34
  app.command(name="example")(example_cmd)
@@ -55,8 +53,8 @@ def reg(
55
53
  if local:
56
54
  deregister_cfn_resource_type(type_name, deregister=not dry_run, region_filter=region)
57
55
  logger.info(f"ready to activate {type_name}")
58
- settings = init_settings()
59
- cfn_execution_role = read_execution_role(settings.load_env_vars_full())
56
+ settings = init_settings(TFModuleCfn)
57
+ cfn_execution_role = settings.env_vars_cls(TFModuleCfn).CFN_EXAMPLE_EXECUTION_ROLE
60
58
  last_third_party = get_last_cfn_type(type_name, region, is_third_party=True)
61
59
  assert last_third_party, f"no 3rd party extension found for {type_name} in {region}"
62
60
  if dry_run:
@@ -2,46 +2,19 @@ import logging
2
2
  from pathlib import Path
3
3
  from typing import Any
4
4
 
5
- from model_lib import Entity, dump, parse_model, parse_payload
5
+ from model_lib import Entity, dump, parse_model
6
6
  from mypy_boto3_cloudformation.type_defs import ParameterTypeDef
7
7
  from pydantic import ConfigDict, Field
8
8
  from rich import prompt
9
- from zero_3rdparty.dict_nested import read_nested
10
9
  from zero_3rdparty.file_utils import clean_dir
11
10
 
12
11
  from atlas_init.cli_cfn.files import create_sample_file, default_log_group_name
13
12
  from atlas_init.cloud.aws import PascalAlias
14
13
  from atlas_init.repos.cfn import CfnType, cfn_examples_dir, cfn_type_normalized
15
- from atlas_init.settings.path import DEFAULT_TF_PATH
16
14
 
17
15
  logger = logging.getLogger(__name__)
18
16
 
19
17
 
20
- def read_execution_role(loaded_env_vars: dict[str, str]) -> str:
21
- return loaded_env_vars["CFN_EXAMPLE_EXECUTION_ROLE"]
22
-
23
-
24
- def check_execution_role(repo_path: Path, loaded_env_vars: dict[str, str]) -> str:
25
- execution_role = cfn_examples_dir(repo_path) / "execution-role.yaml"
26
- execution_raw = parse_payload(execution_role)
27
- actions_expected = read_nested(
28
- execution_raw,
29
- "Resources.ExecutionRole.Properties.Policies.[0].PolicyDocument.Statement.[0].Action",
30
- )
31
- actions_found = parse_payload(DEFAULT_TF_PATH / "modules/cfn/resource_actions.yaml")
32
- if diff := set(actions_expected) ^ set(actions_found):
33
- raise ValueError(f"non-matching execution role actions: {sorted(diff)}")
34
- services_found = parse_payload(DEFAULT_TF_PATH / "modules/cfn/assume_role_services.yaml")
35
- services_expected = read_nested(
36
- execution_raw,
37
- "Resources.ExecutionRole.Properties.AssumeRolePolicyDocument.Statement.[0].Principal.Service",
38
- )
39
- if diff := set(services_found) ^ set(services_expected):
40
- raise ValueError(f"non-matching execution role services: {sorted(diff)}")
41
- logger.info(f"execution role is up to date with {execution_role}")
42
- return read_execution_role(loaded_env_vars)
43
-
44
-
45
18
  class TemplatePathNotFoundError(Exception):
46
19
  def __init__(self, type_name: str, examples_dir: Path) -> None:
47
20
  self.type_name = type_name
@@ -16,6 +16,7 @@ from atlas_init.cli_helper.run_manager import RunManager
16
16
  from atlas_init.cli_root import is_dry_run
17
17
  from atlas_init.repos.path import Repo, ResourcePaths, find_paths
18
18
  from atlas_init.settings.env_vars import AtlasInitSettings, init_settings
19
+ from atlas_init.settings.env_vars_generated import AWSSettings
19
20
 
20
21
  logger = logging.getLogger(__name__)
21
22
 
@@ -111,7 +112,7 @@ def contract_test(
111
112
  resource_paths: ResourcePaths | None = None,
112
113
  only_names: list[str] | None = None,
113
114
  ):
114
- settings = settings or init_settings()
115
+ settings = settings or init_settings(AWSSettings)
115
116
  resource_paths = resource_paths or find_paths(Repo.CFN)
116
117
  resource_name = resource_paths.resource_name
117
118
  generated_env_vars = settings.load_env_vars_full()
@@ -122,11 +123,12 @@ def contract_test(
122
123
  )
123
124
  create_response = create_contract_test_inputs(create_inputs)
124
125
  create_response.log_input_files(logger)
126
+ aws_settings = AWSSettings.from_env()
125
127
  run_contract_test = RunContractTest(
126
128
  resource_path=resource_paths.resource_path,
127
129
  repo_path=resource_paths.repo_path,
128
- aws_profile=settings.AWS_PROFILE,
129
- cfn_region=settings.cfn_region,
130
+ aws_profile=aws_settings.AWS_PROFILE,
131
+ cfn_region=settings.cfn_region(aws_settings.AWS_REGION),
130
132
  only_names=only_names,
131
133
  )
132
134
  if run_contract_test.skip_build:
@@ -15,7 +15,6 @@ from atlas_init.cli_cfn.aws import (
15
15
  from atlas_init.cli_cfn.aws import delete_stack as delete_stack_aws
16
16
  from atlas_init.cli_cfn.cfn_parameter_finder import (
17
17
  CfnTemplate,
18
- check_execution_role,
19
18
  decode_parameters,
20
19
  dump_resource_to_file,
21
20
  dump_sample_file,
@@ -24,6 +23,8 @@ from atlas_init.cli_cfn.cfn_parameter_finder import (
24
23
  from atlas_init.repos.cfn import CfnType, Operation, infer_cfn_type_name
25
24
  from atlas_init.repos.path import Repo, find_paths
26
25
  from atlas_init.settings.env_vars import AtlasInitSettings, init_settings
26
+ from atlas_init.settings.env_vars_generated import AWSSettings
27
+ from atlas_init.settings.env_vars_modules import TFModuleCfn
27
28
 
28
29
  logger = logging.getLogger(__name__)
29
30
 
@@ -99,23 +100,24 @@ def example_cmd(
99
100
  ),
100
101
  register_all_types_in_example: bool = typer.Option(False, "--reg-all", help="Check all types"),
101
102
  ):
102
- settings = init_settings()
103
+ settings = init_settings(TFModuleCfn, AWSSettings)
104
+ cfn_settings = TFModuleCfn.from_env()
105
+ aws_settings = AWSSettings.from_env()
103
106
  assert settings.tf_vars, "no cfn config found, re-run atlas_init apply with CFN flags"
104
107
  repo_path, resource_path, _ = find_paths(Repo.CFN)
105
- env_vars_generated = settings.load_env_vars_full()
106
108
  inputs = CfnExampleInputs(
107
109
  type_name=type_name or infer_cfn_type_name(),
108
110
  example_name=example_name,
109
111
  delete_stack_first=delete_first,
110
- region_filter=region or settings.cfn_region,
111
- stack_name=stack_name or f"{settings.cfn_profile}-{example_name or 'atlas-init'}",
112
+ region_filter=region or settings.cfn_region(aws_settings.AWS_REGION),
113
+ stack_name=stack_name or f"{cfn_settings.MONGODB_ATLAS_PROFILE}-{example_name or 'atlas-init'}",
112
114
  operation=operation, # type: ignore
113
115
  resource_params=resource_params, # type: ignore
114
116
  stack_timeout_s=stack_timeout_s,
115
117
  force_deregister=force_deregister,
116
118
  reg_version=reg_version,
117
119
  force_keep=force_keep,
118
- execution_role=execution_role or check_execution_role(repo_path, env_vars_generated),
120
+ execution_role=execution_role or cfn_settings.CFN_EXAMPLE_EXECUTION_ROLE,
119
121
  export_example_to_inputs=export_example_to_inputs,
120
122
  export_example_to_samples=export_example_to_samples,
121
123
  register_all_types_in_example=register_all_types_in_example,
@@ -16,7 +16,7 @@ from atlas_init.cli_tf.go_test_run import (
16
16
  )
17
17
  from atlas_init.settings.config import TestSuite
18
18
  from atlas_init.settings.env_vars import AtlasInitSettings
19
- from atlas_init.settings.path import DEFAULT_DOWNLOADS_DIR
19
+ from atlas_init.settings.path import load_dotenv
20
20
 
21
21
  logger = logging.getLogger(__name__)
22
22
 
@@ -51,6 +51,7 @@ def env_vars_for_capture(mode: GoTestCaptureMode) -> dict[str, str]:
51
51
 
52
52
 
53
53
  class GoTestResult(Entity):
54
+ logs_dir: Path
54
55
  runs: dict[str, list[GoTestRun]] = Field(default_factory=dict)
55
56
  failure_names: set[str] = Field(default_factory=set)
56
57
 
@@ -66,7 +67,7 @@ class GoTestResult(Entity):
66
67
  if prev_test_results:
67
68
  logger.warning(f"2nd time test results for {test_name}")
68
69
  for result in test_results:
69
- log_path = _log_path(test_name)
70
+ log_path = _log_path(self.logs_dir, test_name)
70
71
  result.log_path = log_path
71
72
  prev_test_results.extend(test_results)
72
73
  return all(run.is_pass for run in test_results)
@@ -95,7 +96,8 @@ def run_go_tests(
95
96
  )
96
97
  if ci_value := test_env.pop("CI", None):
97
98
  logger.warning(f"popped CI={ci_value}")
98
- results = GoTestResult()
99
+ logs_dir = settings.go_test_logs_dir
100
+ results = GoTestResult(logs_dir=logs_dir)
99
101
  commands_to_run: dict[str, str] = {}
100
102
  for group in groups:
101
103
  if group.sequential_tests:
@@ -118,6 +120,7 @@ def run_go_tests(
118
120
  return _run_tests(
119
121
  results,
120
122
  repo_path,
123
+ logs_dir,
121
124
  commands_to_run,
122
125
  test_env,
123
126
  test_timeout_s=timeout_minutes * 60,
@@ -170,7 +173,7 @@ def resolve_env_vars(
170
173
  if env_vars == GoEnvVars.manual:
171
174
  test_env_vars = settings.load_profile_manual_env_vars(skip_os_update=True)
172
175
  elif env_vars == GoEnvVars.vscode:
173
- test_env_vars = settings.load_env_vars(settings.env_vars_vs_code)
176
+ test_env_vars = load_dotenv(settings.env_vars_vs_code)
174
177
  else:
175
178
  raise NotImplementedError(f"don't know how to load env_vars={env_vars}")
176
179
  test_env_vars |= {
@@ -188,6 +191,7 @@ def resolve_env_vars(
188
191
  def _run_tests(
189
192
  results: GoTestResult,
190
193
  repo_path: Path,
194
+ logs_dir: Path,
191
195
  commands_to_run: dict[str, str],
192
196
  test_env: dict[str, str],
193
197
  test_timeout_s: int = 301 * 60,
@@ -199,11 +203,11 @@ def _run_tests(
199
203
  actual_workers = min(max_workers, len(commands_to_run)) or 1
200
204
  with ThreadPoolExecutor(max_workers=actual_workers) as pool:
201
205
  for name, command in sorted(commands_to_run.items()):
202
- log_path = _log_path(name)
206
+ log_path = _log_path(logs_dir, name)
203
207
  if log_path.exists() and log_path.read_text():
204
208
  if re_run:
205
209
  logger.info(f"moving existing logs of {name} to old dir")
206
- move_logs_to_dir({name}, dir_name="old")
210
+ move_logs_to_dir(logs_dir, {name}, dir_name="old")
207
211
  else:
208
212
  logger.info(f"skipping {name} because log exists")
209
213
  continue
@@ -229,7 +233,7 @@ def _run_tests(
229
233
  continue
230
234
  context = GoTestContext(
231
235
  name=name,
232
- html_url=f"file://{_log_path(name)}",
236
+ html_url=f"file://{_log_path(logs_dir, name)}",
233
237
  steps=[GoTestContextStep(name="local-run")],
234
238
  )
235
239
  try:
@@ -250,14 +254,14 @@ def _run_tests(
250
254
  if not results.add_test_results_all_pass(name, parsed_tests):
251
255
  results.failure_names.add(name)
252
256
  if failure_names := results.failure_names:
253
- move_logs_to_dir(failure_names)
257
+ move_logs_to_dir(logs_dir, failure_names)
254
258
  logger.error(f"failed to run tests: {sorted(failure_names)}")
255
259
  return results
256
260
 
257
261
 
258
- def move_logs_to_dir(names: set[str], dir_name: str = "failures"):
259
- new_dir = DEFAULT_DOWNLOADS_DIR / dir_name
260
- for log in DEFAULT_DOWNLOADS_DIR.glob("*.log"):
262
+ def move_logs_to_dir(logs_dir: Path, names: set[str], dir_name: str = "failures"):
263
+ new_dir = logs_dir / dir_name
264
+ for log in logs_dir.glob("*.log"):
261
265
  if log.stem in names:
262
266
  text = log.read_text()
263
267
  assert "\n" in text
@@ -266,5 +270,5 @@ def move_logs_to_dir(names: set[str], dir_name: str = "failures"):
266
270
  log.rename(new_dir / f"{ts}.{log.name}")
267
271
 
268
272
 
269
- def _log_path(name: str) -> Path:
270
- return DEFAULT_DOWNLOADS_DIR / f"{name}.log"
273
+ def _log_path(logs_dir: Path, name: str) -> Path:
274
+ return logs_dir / f"{name}.log"
@@ -13,6 +13,7 @@ from atlas_init.cli_helper.run import (
13
13
  )
14
14
  from atlas_init.settings.config import TerraformVars, TestSuite
15
15
  from atlas_init.settings.env_vars import AtlasInitSettings
16
+ from atlas_init.settings.env_vars_generated import AWSSettings, AtlasSettings
16
17
 
17
18
  logger = logging.getLogger(__name__)
18
19
 
@@ -20,17 +21,19 @@ logger = logging.getLogger(__name__)
20
21
  def get_tf_vars(settings: AtlasInitSettings, active_groups: list[TestSuite]) -> dict[str, Any]: # type: ignore
21
22
  tf_vars = TerraformVars() # type: ignore
22
23
  tf_vars = sum((group.vars for group in active_groups), start=tf_vars)
24
+ aws_settings = AWSSettings.from_env()
25
+ atlas_settings = AtlasSettings.from_env()
23
26
  return {
24
- "atlas_public_key": settings.MONGODB_ATLAS_PUBLIC_KEY,
25
- "atlas_private_key": settings.MONGODB_ATLAS_PRIVATE_KEY,
26
- "atlas_base_url": settings.MONGODB_ATLAS_BASE_URL,
27
- "is_mongodbgov_cloud": settings.is_mongodbgov_cloud,
28
- "org_id": settings.MONGODB_ATLAS_ORG_ID,
29
- "aws_region": settings.AWS_REGION,
27
+ "atlas_public_key": atlas_settings.MONGODB_ATLAS_PUBLIC_KEY,
28
+ "atlas_private_key": atlas_settings.MONGODB_ATLAS_PRIVATE_KEY,
29
+ "atlas_base_url": atlas_settings.MONGODB_ATLAS_BASE_URL,
30
+ "is_mongodbgov_cloud": atlas_settings.is_mongodbgov_cloud,
31
+ "org_id": atlas_settings.MONGODB_ATLAS_ORG_ID,
32
+ "aws_region": aws_settings.AWS_REGION,
30
33
  "project_name": settings.project_name,
31
34
  "out_dir": settings.profile_dir,
32
35
  "extra_env_vars": settings.manual_env_vars,
33
- **settings.tf_vars(),
36
+ **settings.tf_vars(aws_settings.AWS_REGION),
34
37
  **tf_vars.as_configs(),
35
38
  }
36
39
 
@@ -56,7 +59,7 @@ class state_copier: # noqa: N801
56
59
 
57
60
 
58
61
  def run_terraform(settings: AtlasInitSettings, command: str, extra_args: list[str]):
59
- with state_copier(settings.tf_state_path, settings.tf_path):
62
+ with state_copier(settings.tf_state_path, settings.atlas_init_tf_src_path):
60
63
  _run_terraform(settings, command, extra_args)
61
64
 
62
65
 
@@ -71,7 +74,7 @@ def _run_terraform(settings: AtlasInitSettings, command: str, extra_args: list[s
71
74
  "terraform",
72
75
  " ".join(command_parts),
73
76
  env=os.environ | {"TF_DATA_DIR": settings.tf_data_dir},
74
- cwd=settings.tf_path,
77
+ cwd=settings.atlas_init_tf_src_path,
75
78
  logger=logger,
76
79
  )
77
80
  if not is_ok:
@@ -88,10 +91,10 @@ def dump_tf_vars(settings: AtlasInitSettings, tf_vars: dict[str, Any]):
88
91
 
89
92
 
90
93
  def export_outputs(settings: AtlasInitSettings) -> None:
91
- with state_copier(settings.tf_state_path, settings.tf_path):
94
+ with state_copier(settings.tf_state_path, settings.atlas_init_tf_src_path):
92
95
  result = run_command_receive_result(
93
96
  "terraform output -json",
94
- settings.tf_path,
97
+ settings.atlas_init_tf_src_path,
95
98
  logger,
96
99
  env=os.environ | {"TF_DATA_DIR": settings.tf_data_dir},
97
100
  )
@@ -6,10 +6,11 @@ from zero_3rdparty.id_creator import simple_id
6
6
 
7
7
  from atlas_init.settings.env_vars import init_settings
8
8
  from atlas_init.settings.env_vars_generated import (
9
- EnvVarsGenerated,
9
+ AtlasSettingsWithProject,
10
+ AWSSettings,
10
11
  RealmSettings,
11
- TFModuleCluster,
12
12
  )
13
+ from atlas_init.settings.env_vars_modules import TFModuleCluster
13
14
  from atlas_init.settings.path import dump_dotenv
14
15
  from atlas_init.typer_app import app_command
15
16
 
@@ -23,11 +24,14 @@ def trigger_app():
23
24
 
24
25
  def create_realm_app():
25
26
  settings = init_settings()
26
- base_url = settings.realm_url
27
- project_id = settings.env_vars_cls(EnvVarsGenerated).MONGODB_ATLAS_PROJECT_ID
28
- cluster_name = settings.env_vars_cls(TFModuleCluster).MONGODB_ATLAS_CLUSTER_NAME
27
+ atlas_settings = settings.env_vars_cls(AtlasSettingsWithProject)
28
+ cluster_settings = settings.env_vars_cls(TFModuleCluster)
29
+ project_id = atlas_settings.MONGODB_ATLAS_PROJECT_ID
30
+ base_url = atlas_settings.realm_url
31
+ cluster_name = cluster_settings.MONGODB_ATLAS_CLUSTER_NAME
29
32
  auth_headers = login_to_realm(settings, base_url)
30
33
  realm_settings = settings.env_vars_cls_or_none(RealmSettings, path=settings.env_vars_trigger)
34
+ aws_settings = settings.env_vars_cls(AWSSettings)
31
35
  if realm_settings and function_exists(
32
36
  base_url,
33
37
  auth_headers,
@@ -44,7 +48,7 @@ def create_realm_app():
44
48
  app_id = apps[0]["_id"]
45
49
  else:
46
50
  logger.info("no apps found, creating one")
47
- app = create_app(base_url, auth_headers, project_id, cluster_name, settings.AWS_REGION)
51
+ app = create_app(base_url, auth_headers, project_id, cluster_name, aws_settings.AWS_REGION)
48
52
  logger.info(f"created app: {app}")
49
53
  app_id = app["_id"]
50
54
  logger.info(f"using app_id: {app_id}")
@@ -109,7 +113,11 @@ _cloud_deployment_regions = {
109
113
 
110
114
 
111
115
  def create_app(
112
- base_url: str, auth_headers: dict[str, str], project_id: str, cluster_name: str, aws_region: str
116
+ base_url: str,
117
+ auth_headers: dict[str, str],
118
+ project_id: str,
119
+ cluster_name: str,
120
+ aws_region: str,
113
121
  ) -> dict:
114
122
  provider_region = f"aws-{aws_region}"
115
123
  location = _cloud_deployment_regions.get(provider_region)
@@ -229,7 +237,12 @@ class _RetryPostRequestError(Exception):
229
237
  reraise=True,
230
238
  )
231
239
  def _request_post_call(
232
- url: str, data: dict, headers: dict[str, str], timeout: int, *, log_data_on_failure: bool = False
240
+ url: str,
241
+ data: dict,
242
+ headers: dict[str, str],
243
+ timeout: int,
244
+ *,
245
+ log_data_on_failure: bool = False,
233
246
  ) -> dict:
234
247
  response = requests.post(url, json=data, headers=headers, timeout=timeout)
235
248
  if response.status_code >= 500: # noqa: PLR2004
atlas_init/cli_tf/app.py CHANGED
@@ -63,7 +63,7 @@ def schema(
63
63
  schema_out_path = settings.schema_out_path_computed
64
64
  schema_out_path.mkdir(exist_ok=True)
65
65
 
66
- schema_parsed = parse_py_terraform_schema(settings.tf_schema_config_path)
66
+ schema_parsed = parse_py_terraform_schema(settings.atlas_init_tf_schema_config_path)
67
67
  generator_config = dump_generator_config(schema_parsed)
68
68
  generator_config_path = schema_out_path / "generator_config.yaml"
69
69
  generator_config_path.write_text(generator_config)
@@ -20,17 +20,13 @@ from atlas_init.cli_tf.go_test_run import GoTestRun, parse
20
20
  from atlas_init.repos.path import (
21
21
  GH_OWNER_TERRAFORM_PROVIDER_MONGODBATLAS,
22
22
  )
23
- from atlas_init.settings.path import (
24
- DEFAULT_GITHUB_CI_RUN_LOGS,
25
- DEFAULT_GITHUB_SUMMARY_DIR,
26
- )
23
+ from atlas_init.settings.env_vars import init_settings
27
24
 
28
25
  logger = logging.getLogger(__name__)
29
26
 
30
27
  GH_TOKEN_ENV_NAME = "GH_TOKEN" # noqa: S105 #nosec
31
- GITHUB_CI_RUN_LOGS_ENV_NAME = "GITHUB_CI_RUN_LOGS"
32
28
  GITHUB_CI_SUMMARY_DIR_ENV_NAME = "GITHUB_CI_SUMMARY_DIR_ENV_NAME"
33
- REQUIRED_GH_ENV_VARS = [GH_TOKEN_ENV_NAME, GITHUB_CI_RUN_LOGS_ENV_NAME]
29
+ REQUIRED_GH_ENV_VARS = [GH_TOKEN_ENV_NAME]
34
30
  MAX_DOWNLOADS = 5
35
31
 
36
32
 
@@ -154,19 +150,11 @@ def download_job_safely(workflow_dir: Path, job: WorkflowJob) -> Path | None:
154
150
 
155
151
 
156
152
  def logs_dir() -> Path:
157
- logs_dir_str = os.environ.get(GITHUB_CI_RUN_LOGS_ENV_NAME)
158
- if not logs_dir_str:
159
- logger.info(f"using {DEFAULT_GITHUB_CI_RUN_LOGS} to store github ci logs!")
160
- return DEFAULT_GITHUB_CI_RUN_LOGS
161
- return Path(logs_dir_str)
153
+ return init_settings().github_ci_run_logs
162
154
 
163
155
 
164
156
  def summary_dir(summary_name: str) -> Path:
165
- summary_dir_str = os.environ.get(GITHUB_CI_SUMMARY_DIR_ENV_NAME)
166
- if not summary_dir_str:
167
- logger.info(f"using {DEFAULT_GITHUB_SUMMARY_DIR / summary_name} to store summaries")
168
- return DEFAULT_GITHUB_SUMMARY_DIR / summary_name
169
- return Path(summary_dir_str) / summary_name
157
+ return init_settings().github_ci_summary_dir / summary_name
170
158
 
171
159
 
172
160
  def workflow_logs_dir(workflow: WorkflowRun) -> Path:
@@ -2,6 +2,7 @@ import logging
2
2
  from collections import defaultdict
3
3
  from copy import deepcopy
4
4
  from pathlib import Path
5
+ from typing import Callable
5
6
 
6
7
  import hcl2
7
8
  from lark import Token, Tree, UnexpectedToken
@@ -51,6 +52,8 @@ def update_description(tree: Tree, new_descriptions: dict[str, str], existing_na
51
52
  def token_name(token: Token | Tree) -> str:
52
53
  if isinstance(token, Token):
53
54
  return token.value.strip('"')
55
+ if isinstance(token, Tree) and token.data == "identifier":
56
+ return token.children[0].value.strip('"') # type: ignore
54
57
  err_msg = f"unexpected token type {type(token)} for token name"
55
58
  raise ValueError(err_msg)
56
59
 
@@ -102,31 +105,46 @@ def create_description_attribute(description_value: str) -> Tree:
102
105
  return Tree(Token("RULE", "attribute"), children)
103
106
 
104
107
 
105
- def process_descriptions(
108
+ def process_generic(
106
109
  node: Tree,
107
- name_updates: dict[str, str],
108
- existing_names: dict[str, list[str]],
110
+ tree_match: Callable[[Tree], bool],
111
+ tree_call: Callable[[Tree], Tree],
109
112
  depth=0,
110
- *,
111
- block_type: str,
112
- ) -> Tree:
113
+ ):
113
114
  new_children = []
114
115
  logger.debug(f"[{depth}] (tree)\t|", " " * depth, node.data)
115
116
  for child in node.children:
116
117
  if isinstance(child, Tree):
117
- if is_block_type(child, block_type):
118
- child = update_description( # noqa: PLW2901
119
- child, name_updates, existing_names
120
- )
121
- new_children.append(
122
- process_descriptions(child, name_updates, existing_names, depth + 1, block_type=block_type)
123
- )
118
+ if tree_match(child):
119
+ child = tree_call(child)
120
+ new_children.append(process_generic(child, tree_match, tree_call, depth + 1))
124
121
  else:
125
122
  new_children.append(process_token(child, depth + 1))
126
-
127
123
  return Tree(node.data, new_children)
128
124
 
129
125
 
126
+ def process_descriptions(
127
+ node: Tree,
128
+ name_updates: dict[str, str],
129
+ existing_names: dict[str, list[str]],
130
+ depth=0,
131
+ *,
132
+ block_type: str,
133
+ ) -> Tree:
134
+ def tree_match(tree: Tree) -> bool:
135
+ return is_block_type(tree, block_type)
136
+
137
+ def tree_call(tree: Tree) -> Tree:
138
+ return update_description(tree, name_updates, existing_names)
139
+
140
+ return process_generic(
141
+ node,
142
+ tree_match,
143
+ tree_call,
144
+ depth=depth,
145
+ )
146
+
147
+
130
148
  def update_descriptions(tf_path: Path, new_names: dict[str, str], block_type: str) -> tuple[str, dict[str, list[str]]]:
131
149
  try:
132
150
  tree = hcl2.parses(tf_path.read_text()) # type: ignore
@@ -142,3 +160,86 @@ def update_descriptions(tf_path: Path, new_names: dict[str, str], block_type: st
142
160
  )
143
161
  new_tf = hcl2.writes(new_tree) # type: ignore
144
162
  return new_tf, existing_descriptions
163
+
164
+
165
+ def _block_name_body(tree: Tree) -> tuple[str, Tree]:
166
+ try:
167
+ _, name_token, body = tree.children
168
+ name = token_name(name_token)
169
+ except (IndexError, AttributeError) as e:
170
+ raise ValueError("unexpected block structure") from e
171
+ return name, body
172
+
173
+
174
+ def _read_attribute(tree_body: Tree, attribute_name: str) -> Tree | None:
175
+ for attribute in tree_body.children:
176
+ if not isinstance(attribute, Tree):
177
+ continue
178
+ if attribute.data != "attribute":
179
+ continue
180
+ attr_identifier, _, attr_value = attribute.children
181
+ if token_name(attr_identifier.children[0]) != attribute_name:
182
+ continue
183
+ return attr_value
184
+
185
+
186
+ def _is_object(tree_body: Tree) -> bool:
187
+ if not isinstance(tree_body, Tree):
188
+ return False
189
+ if len(tree_body.children) != 1:
190
+ return False
191
+ if not isinstance(tree_body.children[0], Tree):
192
+ return False
193
+ return tree_body.children[0].data == "object"
194
+
195
+
196
+ def _read_object_elems(tree_body: Tree) -> list[Tree]:
197
+ object_elements = []
198
+ for obj_child in tree_body.children[0].children:
199
+ if not isinstance(obj_child, Tree):
200
+ continue
201
+ if obj_child.data != "object_elem":
202
+ continue
203
+ object_elements.append(obj_child)
204
+ return object_elements
205
+
206
+
207
+ def _read_object_elem_key(tree_body: Tree) -> str:
208
+ name_tree, _, _ = tree_body.children
209
+ return token_name(name_tree.children[0])
210
+
211
+
212
+ def read_block_attribute_object_keys(tf_path: Path, block_type: str, block_name: str, block_key: str) -> list[str]:
213
+ try:
214
+ tree = hcl2.parses(tf_path.read_text()) # type: ignore
215
+ except UnexpectedToken as e:
216
+ logger.warning(f"failed to parse {tf_path}: {e}")
217
+ return []
218
+ env_vars = []
219
+
220
+ def extract_env_vars(tree: Tree) -> bool:
221
+ if not is_block_type(tree, block_type):
222
+ return False
223
+ name, body = _block_name_body(tree)
224
+ if name != block_name:
225
+ return False
226
+ attribute_value = _read_attribute(body, block_key)
227
+ if not attribute_value:
228
+ return False
229
+ if not _is_object(attribute_value):
230
+ return False
231
+ object_elements = _read_object_elems(attribute_value)
232
+ for obj_elem in object_elements:
233
+ key = _read_object_elem_key(obj_elem)
234
+ env_vars.append(key)
235
+ return False
236
+
237
+ def tree_call(tree: Tree) -> Tree:
238
+ return tree
239
+
240
+ process_generic(
241
+ tree,
242
+ extract_env_vars,
243
+ tree_call,
244
+ )
245
+ return env_vars
@@ -33,7 +33,7 @@ from atlas_init.repos.go_sdk import (
33
33
  download_admin_api,
34
34
  parse_api_spec_paths,
35
35
  )
36
- from atlas_init.settings.path import DEFAULT_DOWNLOADS_DIR
36
+ from atlas_init.settings.env_vars import init_settings
37
37
 
38
38
  logger = logging.getLogger(__name__)
39
39
 
@@ -164,7 +164,8 @@ def resolve_admin_api_path(sdk_repo_path_str: str, sdk_branch: str, admin_api_pa
164
164
  assert sdk_repo_path.exists(), f"not found sdk_repo_path={sdk_repo_path}"
165
165
  resolved_admin_api_path = api_spec_path_transformed(sdk_repo_path)
166
166
  else:
167
- resolved_admin_api_path = DEFAULT_DOWNLOADS_DIR / "atlas-api-transformed.yaml"
167
+ settings = init_settings()
168
+ resolved_admin_api_path = settings.atlas_atlas_api_transformed_yaml
168
169
  if not is_cache_up_to_date(resolved_admin_api_path, 3600):
169
170
  download_admin_api(resolved_admin_api_path, sdk_branch)
170
171
  assert resolved_admin_api_path.exists(), f"unable to resolve admin_api_path={resolved_admin_api_path}"