dbt-platform-helper 13.1.2__py3-none-any.whl → 13.3.0__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.

Potentially problematic release.


This version of dbt-platform-helper might be problematic. Click here for more details.

Files changed (57) hide show
  1. dbt_platform_helper/commands/application.py +2 -4
  2. dbt_platform_helper/commands/codebase.py +11 -7
  3. dbt_platform_helper/commands/conduit.py +1 -3
  4. dbt_platform_helper/commands/config.py +12 -314
  5. dbt_platform_helper/commands/copilot.py +14 -8
  6. dbt_platform_helper/commands/database.py +17 -9
  7. dbt_platform_helper/commands/environment.py +5 -6
  8. dbt_platform_helper/commands/generate.py +2 -3
  9. dbt_platform_helper/commands/notify.py +1 -3
  10. dbt_platform_helper/commands/pipeline.py +1 -3
  11. dbt_platform_helper/commands/secrets.py +1 -3
  12. dbt_platform_helper/commands/version.py +2 -2
  13. dbt_platform_helper/domain/codebase.py +17 -9
  14. dbt_platform_helper/domain/config.py +345 -0
  15. dbt_platform_helper/domain/copilot.py +158 -157
  16. dbt_platform_helper/domain/maintenance_page.py +42 -15
  17. dbt_platform_helper/domain/pipelines.py +1 -1
  18. dbt_platform_helper/domain/terraform_environment.py +1 -1
  19. dbt_platform_helper/domain/versioning.py +161 -30
  20. dbt_platform_helper/providers/aws/__init__.py +0 -0
  21. dbt_platform_helper/providers/{aws.py → aws/exceptions.py} +10 -0
  22. dbt_platform_helper/providers/aws/interfaces.py +13 -0
  23. dbt_platform_helper/providers/aws/opensearch.py +23 -0
  24. dbt_platform_helper/providers/aws/redis.py +21 -0
  25. dbt_platform_helper/providers/aws/sso_auth.py +61 -0
  26. dbt_platform_helper/providers/cache.py +40 -4
  27. dbt_platform_helper/providers/config.py +2 -1
  28. dbt_platform_helper/providers/config_validator.py +28 -25
  29. dbt_platform_helper/providers/copilot.py +1 -1
  30. dbt_platform_helper/providers/io.py +5 -2
  31. dbt_platform_helper/providers/kms.py +22 -0
  32. dbt_platform_helper/providers/load_balancers.py +26 -15
  33. dbt_platform_helper/providers/parameter_store.py +47 -0
  34. dbt_platform_helper/providers/platform_config_schema.py +17 -0
  35. dbt_platform_helper/providers/semantic_version.py +18 -88
  36. dbt_platform_helper/providers/terraform_manifest.py +1 -0
  37. dbt_platform_helper/providers/version.py +102 -26
  38. dbt_platform_helper/providers/version_status.py +80 -0
  39. dbt_platform_helper/providers/yaml_file.py +0 -1
  40. dbt_platform_helper/utils/aws.py +24 -142
  41. dbt_platform_helper/utils/git.py +3 -1
  42. dbt_platform_helper/utils/tool_versioning.py +12 -0
  43. {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/METADATA +2 -2
  44. {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/RECORD +48 -47
  45. {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/WHEEL +1 -1
  46. platform_helper.py +1 -1
  47. dbt_platform_helper/providers/opensearch.py +0 -36
  48. dbt_platform_helper/providers/platform_helper_versioning.py +0 -107
  49. dbt_platform_helper/providers/redis.py +0 -34
  50. dbt_platform_helper/templates/svc/manifest-backend.yml +0 -69
  51. dbt_platform_helper/templates/svc/manifest-public.yml +0 -109
  52. dbt_platform_helper/utils/cloudfoundry.py +0 -14
  53. dbt_platform_helper/utils/files.py +0 -59
  54. dbt_platform_helper/utils/manifests.py +0 -18
  55. dbt_platform_helper/utils/versioning.py +0 -91
  56. {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/LICENSE +0 -0
  57. {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/entry_points.txt +0 -0
@@ -192,15 +192,14 @@ class LoadBalancerProvider:
192
192
  rule_name: str,
193
193
  priority: int,
194
194
  conditions: list,
195
+ additional_tags: list = [],
195
196
  ):
196
197
  return self.create_rule(
197
198
  listener_arn=listener_arn,
198
199
  priority=priority,
199
200
  conditions=conditions,
200
201
  actions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
201
- tags=[
202
- {"Key": "name", "Value": rule_name},
203
- ],
202
+ tags=[{"Key": "name", "Value": rule_name}, *additional_tags],
204
203
  )
205
204
 
206
205
  def create_header_rule(
@@ -212,6 +211,7 @@ class LoadBalancerProvider:
212
211
  rule_name: str,
213
212
  priority: int,
214
213
  conditions: list,
214
+ additional_tags: list = [],
215
215
  ):
216
216
 
217
217
  combined_conditions = [
@@ -222,11 +222,16 @@ class LoadBalancerProvider:
222
222
  ] + conditions
223
223
 
224
224
  self.create_forward_rule(
225
- listener_arn, target_group_arn, rule_name, priority, combined_conditions
225
+ listener_arn,
226
+ target_group_arn,
227
+ rule_name,
228
+ priority,
229
+ combined_conditions,
230
+ additional_tags,
226
231
  )
227
232
 
228
- self.io.info(
229
- f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\n\nIf request header {header_name} contains one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.",
233
+ self.io.debug(
234
+ f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\nIf request header {header_name} contains one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.\n\n",
230
235
  )
231
236
 
232
237
  def create_source_ip_rule(
@@ -237,6 +242,7 @@ class LoadBalancerProvider:
237
242
  rule_name: str,
238
243
  priority: int,
239
244
  conditions: list,
245
+ additional_tags: list = [],
240
246
  ):
241
247
  combined_conditions = [
242
248
  {
@@ -246,24 +252,29 @@ class LoadBalancerProvider:
246
252
  ] + conditions
247
253
 
248
254
  self.create_forward_rule(
249
- listener_arn, target_group_arn, rule_name, priority, combined_conditions
255
+ listener_arn,
256
+ target_group_arn,
257
+ rule_name,
258
+ priority,
259
+ combined_conditions,
260
+ additional_tags,
250
261
  )
251
262
 
252
- self.io.info(
253
- f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\n\nIf request source ip matches one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.",
263
+ self.io.debug(
264
+ f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\nIf request source ip matches one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.\n\n",
254
265
  )
255
266
 
256
- def delete_listener_rule_by_tags(self, tag_descriptions: list, tag_name: str) -> str:
257
- current_rule_arn = None
267
+ def delete_listener_rule_by_tags(self, tag_descriptions: list, tag_name: str) -> list:
268
+ deleted_rules = []
258
269
 
259
270
  for description in tag_descriptions:
260
271
  tags = {t["Key"]: t["Value"] for t in description["Tags"]}
261
272
  if tags.get("name") == tag_name:
262
- current_rule_arn = description["ResourceArn"]
263
- if current_rule_arn:
264
- self.evlb_client.delete_rule(RuleArn=current_rule_arn)
273
+ if description["ResourceArn"]:
274
+ self.evlb_client.delete_rule(RuleArn=description["ResourceArn"])
275
+ deleted_rules.append(description)
265
276
 
266
- return current_rule_arn
277
+ return deleted_rules
267
278
 
268
279
 
269
280
  class LoadBalancerException(PlatformException):
@@ -0,0 +1,47 @@
1
+ import boto3
2
+
3
+ from dbt_platform_helper.platform_exception import PlatformException
4
+
5
+
6
+ class ParameterStore:
7
+ def __init__(self, ssm_client: boto3.client):
8
+ self.ssm_client = ssm_client
9
+
10
+ def get_ssm_parameter_by_name(self, parameter_name: str) -> dict:
11
+ """
12
+ Retrieves the latest version of a parameter from parameter store for a
13
+ given name/arn.
14
+
15
+ Args:
16
+ path (str): The parameter name to retrieve the parameter value for.
17
+ Returns:
18
+ dict: A dictionary representation of your ssm parameter
19
+ """
20
+
21
+ return self.ssm_client.get_parameter(Name=parameter_name)["Parameter"]
22
+
23
+ def get_ssm_parameters_by_path(self, path: str) -> list:
24
+ """
25
+ Retrieves all SSM parameters for a given path from parameter store.
26
+
27
+ Args:
28
+ path (str): The parameter path to retrieve the parameters for. e.g. /copilot/applications/
29
+ Returns:
30
+ list: A list of dictionaries containing all SSM parameters under the provided path.
31
+ """
32
+
33
+ parameters = []
34
+ paginator = self.ssm_client.get_paginator("get_parameters_by_path")
35
+ page_iterator = paginator.paginate(Path=path, Recursive=True)
36
+
37
+ for page in page_iterator:
38
+ parameters.extend(page.get("Parameters", []))
39
+
40
+ if parameters:
41
+ return parameters
42
+ else:
43
+ raise ParameterNotFoundForPathException()
44
+
45
+
46
+ class ParameterNotFoundForPathException(PlatformException):
47
+ """Exception raised when no parameters are found for a given path."""
@@ -28,6 +28,7 @@ class PlatformConfigSchema:
28
28
  PlatformConfigSchema.__monitoring_schema(),
29
29
  PlatformConfigSchema.__opensearch_schema(),
30
30
  PlatformConfigSchema.__postgres_schema(),
31
+ PlatformConfigSchema.__datadog_schema(),
31
32
  PlatformConfigSchema.__prometheus_policy_schema(),
32
33
  PlatformConfigSchema.__redis_schema(),
33
34
  PlatformConfigSchema.__s3_bucket_schema(),
@@ -48,6 +49,7 @@ class PlatformConfigSchema:
48
49
  "postgres": Schema(PlatformConfigSchema.__postgres_schema()),
49
50
  "prometheus-policy": Schema(PlatformConfigSchema.__prometheus_policy_schema()),
50
51
  "redis": Schema(PlatformConfigSchema.__redis_schema()),
52
+ "datadog": Schema(PlatformConfigSchema.__datadog_schema()),
51
53
  "s3": Schema(PlatformConfigSchema.__s3_bucket_schema()),
52
54
  "s3-policy": Schema(PlatformConfigSchema.__s3_bucket_policy_schema()),
53
55
  "subscription-filter": PlatformConfigSchema.__no_configuration_required_schema(
@@ -434,6 +436,21 @@ class PlatformConfigSchema:
434
436
 
435
437
  return True
436
438
 
439
+ @staticmethod
440
+ def __datadog_schema() -> dict:
441
+ return {
442
+ "type": "datadog",
443
+ Optional("environments"): {
444
+ Optional(PlatformConfigSchema.__valid_environment_name()): {
445
+ "team_name": str,
446
+ "contact_name": str,
447
+ "contact_email": str,
448
+ "documentation_url": str,
449
+ "services_to_monitor": list,
450
+ }
451
+ },
452
+ }
453
+
437
454
  @staticmethod
438
455
  def __s3_bucket_schema() -> dict:
439
456
  def _valid_s3_bucket_arn(key):
@@ -1,12 +1,6 @@
1
1
  import re
2
- from dataclasses import dataclass
3
- from dataclasses import field
4
- from typing import Dict
5
- from typing import Optional
6
2
  from typing import Union
7
3
 
8
- from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
9
- from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
10
4
  from dbt_platform_helper.providers.validation import ValidationException
11
5
 
12
6
 
@@ -35,13 +29,20 @@ class SemanticVersion:
35
29
  return "unknown"
36
30
  return ".".join([str(s) for s in [self.major, self.minor, self.patch]])
37
31
 
32
+ def __repr__(self) -> str:
33
+ return str(self)
34
+
38
35
  def __lt__(self, other) -> bool:
39
36
  return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
40
37
 
41
38
  def __eq__(self, other) -> bool:
39
+ if other is None:
40
+ return False
42
41
  return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
43
42
 
44
43
  def validate_compatibility_with(self, other):
44
+ if other is None:
45
+ raise ValidationException("Cannot compare NoneType")
45
46
  if (self.major == 0 and other.major == 0) and (
46
47
  self.minor != other.minor or self.patch != other.patch
47
48
  ):
@@ -54,93 +55,22 @@ class SemanticVersion:
54
55
  raise IncompatibleMinorVersionException(str(self), str(other))
55
56
 
56
57
  @staticmethod
57
- def from_string(version_string: Union[str, None]):
58
+ def _cast_to_int_with_fallback(input, fallback=-1):
59
+ try:
60
+ return int(input)
61
+ except ValueError:
62
+ return fallback
63
+
64
+ @classmethod
65
+ def from_string(self, version_string: Union[str, None]):
58
66
  if version_string is None:
59
67
  return None
60
68
 
61
- version_plain = version_string.replace("v", "")
62
- version_segments = re.split(r"[.\-]", version_plain)
69
+ version_segments = re.split(r"[.\-]", version_string.replace("v", ""))
63
70
 
64
71
  if len(version_segments) != 3:
65
72
  return None
66
73
 
67
- output_version = [0, 0, 0]
68
- for index, segment in enumerate(version_segments):
69
- try:
70
- output_version[index] = int(segment)
71
- except ValueError:
72
- output_version[index] = -1
73
-
74
- return SemanticVersion(output_version[0], output_version[1], output_version[2])
75
-
76
-
77
- @dataclass
78
- class VersionStatus:
79
- local: SemanticVersion = None
80
- latest: SemanticVersion = None
81
-
82
- def __str__(self):
83
- attrs = {
84
- key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
85
- }
86
- attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
87
- return f"{self.__class__.__name__}: {attrs_str}"
88
-
89
- def is_outdated(self):
90
- return self.local != self.latest
91
-
92
- def validate(self):
93
- pass
94
-
95
-
96
- @dataclass
97
- class PlatformHelperVersionStatus(VersionStatus):
98
- local: Optional[SemanticVersion] = None
99
- latest: Optional[SemanticVersion] = None
100
- deprecated_version_file: Optional[SemanticVersion] = None
101
- platform_config_default: Optional[SemanticVersion] = None
102
- pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
103
-
104
- def __str__(self):
105
- semantic_version_attrs = {
106
- key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
107
- }
108
-
109
- class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
110
-
111
- if self.pipeline_overrides.items():
112
- pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
113
- f"{key}: {value}" for key, value in self.pipeline_overrides.items()
114
- )
115
- class_str = ", ".join([class_str, pipeline_overrides_str])
116
-
117
- return f"{self.__class__.__name__}: {class_str}"
118
-
119
- def validate(self) -> dict:
120
- if self.platform_config_default and not self.deprecated_version_file:
121
- return {}
122
-
123
- warnings = []
124
- errors = []
125
-
126
- missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
127
- deprecation_message = (
128
- f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
129
- )
130
-
131
- if self.platform_config_default and self.deprecated_version_file:
132
- warnings.append(deprecation_message)
133
-
134
- if not self.platform_config_default and self.deprecated_version_file:
135
- warnings.append(deprecation_message)
136
- warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
137
-
138
- if not self.platform_config_default and not self.deprecated_version_file:
139
- message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
140
- message += f"{missing_default_version_message}{self.local}\n"
141
- errors.append(message)
74
+ major, minor, patch = [self._cast_to_int_with_fallback(s) for s in version_segments]
142
75
 
143
- return {
144
- "warnings": warnings,
145
- "errors": errors,
146
- }
76
+ return SemanticVersion(major, minor, patch)
@@ -128,6 +128,7 @@ class TerraformManifestProvider:
128
128
  "codebase": "${each.key}",
129
129
  "repository": "${each.value.repository}",
130
130
  "deploy_repository": f"{deploy_repository}",
131
+ "deploy_repository_branch": '${lookup(each.value, "deploy_repository_branch", "main")}',
131
132
  "additional_ecr_repository": '${lookup(each.value, "additional_ecr_repository", null)}',
132
133
  "pipelines": '${lookup(each.value, "pipelines", [])}',
133
134
  "services": "${each.value.services}",
@@ -1,18 +1,36 @@
1
+ import re
2
+ import subprocess
1
3
  from abc import ABC
4
+ from abc import abstractmethod
2
5
  from importlib.metadata import PackageNotFoundError
3
6
  from importlib.metadata import version
7
+ from pathlib import Path
8
+ from typing import Union
4
9
 
5
- import requests
10
+ from requests import Session
11
+ from requests.adapters import HTTPAdapter
12
+ from urllib3.util import Retry
6
13
 
14
+ from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
7
15
  from dbt_platform_helper.platform_exception import PlatformException
16
+ from dbt_platform_helper.providers.io import ClickIOProvider
8
17
  from dbt_platform_helper.providers.semantic_version import SemanticVersion
18
+ from dbt_platform_helper.providers.yaml_file import FileProviderException
19
+ from dbt_platform_helper.providers.yaml_file import YamlFileProvider
9
20
 
10
21
 
11
- class LocalVersionProviderException(PlatformException):
22
+ def set_up_retry():
23
+ session = Session()
24
+ retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[403, 500, 502, 503, 504])
25
+ session.mount("https://", HTTPAdapter(max_retries=retries))
26
+ return session
27
+
28
+
29
+ class InstalledVersionProviderException(PlatformException):
12
30
  pass
13
31
 
14
32
 
15
- class InstalledToolNotFoundException(LocalVersionProviderException):
33
+ class InstalledToolNotFoundException(InstalledVersionProviderException):
16
34
  def __init__(
17
35
  self,
18
36
  tool_name: str,
@@ -21,40 +39,98 @@ class InstalledToolNotFoundException(LocalVersionProviderException):
21
39
 
22
40
 
23
41
  class VersionProvider(ABC):
24
- pass
42
+ @abstractmethod
43
+ def get_semantic_version() -> SemanticVersion:
44
+ raise NotImplementedError("Must be implemented in subclasses")
25
45
 
26
46
 
27
- class LocalVersionProvider:
47
+ class InstalledVersionProvider:
28
48
  @staticmethod
29
- def get_installed_tool_version(tool_name: str) -> SemanticVersion:
49
+ def get_semantic_version(tool_name: str) -> SemanticVersion:
30
50
  try:
31
51
  return SemanticVersion.from_string(version(tool_name))
32
52
  except PackageNotFoundError:
33
53
  raise InstalledToolNotFoundException(tool_name)
34
54
 
35
55
 
36
- # TODO add timeouts and exception handling for requests
37
- # TODO Alternatively use the gitpython package?
38
- class GithubVersionProvider(VersionProvider):
56
+ class GithubLatestVersionProvider(VersionProvider):
39
57
  @staticmethod
40
- def get_latest_version(repo_name: str, tags: bool = False) -> SemanticVersion:
41
- if tags:
42
- tags_list = requests.get(f"https://api.github.com/repos/{repo_name}/tags").json()
43
- versions = [SemanticVersion.from_string(v["name"]) for v in tags_list]
44
- versions.sort(reverse=True)
45
- return versions[0]
58
+ def get_semantic_version(
59
+ repo_name: str, tags: bool = False, request_session=set_up_retry(), io=ClickIOProvider()
60
+ ) -> Union[SemanticVersion, None]:
61
+
62
+ semantic_version = None
63
+ try:
64
+ if tags:
65
+ response = request_session.get(f"https://api.github.com/repos/{repo_name}/tags")
66
+
67
+ versions = [SemanticVersion.from_string(v["name"]) for v in response.json()]
68
+ versions.sort(reverse=True)
69
+ semantic_version = versions[0]
70
+ else:
71
+ package_info = request_session.get(
72
+ f"https://api.github.com/repos/{repo_name}/releases/latest"
73
+ ).json()
74
+ semantic_version = SemanticVersion.from_string(package_info["tag_name"])
75
+ except Exception as e:
76
+ io.error(f"Exception occured when calling Github with:\n{str(e)}")
46
77
 
47
- package_info = requests.get(
48
- f"https://api.github.com/repos/{repo_name}/releases/latest"
49
- ).json()
50
- return SemanticVersion.from_string(package_info["tag_name"])
78
+ return semantic_version
51
79
 
52
80
 
53
- class PyPiVersionProvider(VersionProvider):
81
+ class PyPiLatestVersionProvider(VersionProvider):
54
82
  @staticmethod
55
- def get_latest_version(project_name: str) -> SemanticVersion:
56
- package_info = requests.get(f"https://pypi.org/pypi/{project_name}/json").json()
57
- released_versions = package_info["releases"].keys()
58
- parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
59
- parsed_released_versions.sort(reverse=True)
60
- return parsed_released_versions[0]
83
+ def get_semantic_version(
84
+ project_name: str, request_session=set_up_retry(), io=ClickIOProvider()
85
+ ) -> Union[SemanticVersion, None]:
86
+ semantic_version = None
87
+ try:
88
+ package_info = request_session.get(f"https://pypi.org/pypi/{project_name}/json").json()
89
+ released_versions = package_info["releases"].keys()
90
+ parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
91
+ parsed_released_versions.sort(reverse=True)
92
+ semantic_version = parsed_released_versions[0]
93
+ except Exception as e:
94
+ io.error(f"Exception occured when calling PyPi with:\n{str(e)}")
95
+ return semantic_version
96
+
97
+
98
+ class DeprecatedVersionFileVersionProvider(VersionProvider):
99
+ def __init__(self, file_provider: YamlFileProvider):
100
+ self.file_provider = file_provider or YamlFileProvider
101
+
102
+ def get_semantic_version(self) -> Union[SemanticVersion, None]:
103
+ deprecated_version_file = Path(PLATFORM_HELPER_VERSION_FILE)
104
+ try:
105
+ loaded_version = self.file_provider.load(deprecated_version_file)
106
+ version_from_file = SemanticVersion.from_string(loaded_version)
107
+ except FileProviderException:
108
+ version_from_file = None
109
+ return version_from_file
110
+
111
+
112
+ class AWSCLIInstalledVersionProvider(VersionProvider):
113
+ @staticmethod
114
+ def get_semantic_version() -> Union[SemanticVersion, None]:
115
+ installed_aws_version = None
116
+ try:
117
+ response = subprocess.run("aws --version", capture_output=True, shell=True)
118
+ matched = re.match(r"aws-cli/([0-9.]+)", response.stdout.decode("utf8"))
119
+ installed_aws_version = matched.group(1)
120
+ except (ValueError, AttributeError):
121
+ pass
122
+ return SemanticVersion.from_string(installed_aws_version)
123
+
124
+
125
+ class CopilotInstalledVersionProvider(VersionProvider):
126
+ @staticmethod
127
+ def get_semantic_version() -> Union[SemanticVersion, None]:
128
+ copilot_version = None
129
+
130
+ try:
131
+ response = subprocess.run("copilot --version", capture_output=True, shell=True)
132
+ [copilot_version] = re.findall(r"[0-9.]+", response.stdout.decode("utf8"))
133
+ except ValueError:
134
+ pass
135
+
136
+ return SemanticVersion.from_string(copilot_version)
@@ -0,0 +1,80 @@
1
+ from dataclasses import dataclass
2
+ from dataclasses import field
3
+ from typing import Dict
4
+ from typing import Optional
5
+
6
+ from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
7
+ from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
8
+ from dbt_platform_helper.providers.semantic_version import SemanticVersion
9
+
10
+
11
+ @dataclass
12
+ class VersionStatus:
13
+ installed: SemanticVersion = None
14
+ latest: SemanticVersion = None
15
+
16
+ def __str__(self):
17
+ attrs = {
18
+ key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
19
+ }
20
+ attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
21
+ return f"{self.__class__.__name__}: {attrs_str}"
22
+
23
+ def is_outdated(self):
24
+ return self.installed != self.latest
25
+
26
+ def validate(self):
27
+ pass
28
+
29
+
30
+ @dataclass
31
+ class PlatformHelperVersionStatus(VersionStatus):
32
+ installed: Optional[SemanticVersion] = None
33
+ latest: Optional[SemanticVersion] = None
34
+ deprecated_version_file: Optional[SemanticVersion] = None
35
+ platform_config_default: Optional[SemanticVersion] = None
36
+ pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
37
+
38
+ def __str__(self):
39
+ semantic_version_attrs = {
40
+ key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
41
+ }
42
+
43
+ class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
44
+
45
+ if self.pipeline_overrides.items():
46
+ pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
47
+ f"{key}: {value}" for key, value in self.pipeline_overrides.items()
48
+ )
49
+ class_str = ", ".join([class_str, pipeline_overrides_str])
50
+
51
+ return f"{self.__class__.__name__}: {class_str}"
52
+
53
+ def validate(self) -> dict:
54
+ if self.platform_config_default and not self.deprecated_version_file:
55
+ return {}
56
+
57
+ warnings = []
58
+ errors = []
59
+
60
+ missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
61
+ deprecation_message = (
62
+ f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
63
+ )
64
+
65
+ if self.platform_config_default and self.deprecated_version_file:
66
+ warnings.append(deprecation_message)
67
+
68
+ if not self.platform_config_default and self.deprecated_version_file:
69
+ warnings.append(deprecation_message)
70
+ warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
71
+
72
+ if not self.platform_config_default and not self.deprecated_version_file:
73
+ message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
74
+ message += f"{missing_default_version_message}{self.installed}\n"
75
+ errors.append(message)
76
+
77
+ return {
78
+ "warnings": warnings,
79
+ "errors": errors,
80
+ }
@@ -45,7 +45,6 @@ class YamlFileProvider:
45
45
 
46
46
  if not yaml_content:
47
47
  return {}
48
-
49
48
  YamlFileProvider.lint_yaml_for_duplicate_keys(path)
50
49
 
51
50
  return yaml_content