sqlmesh 0.200.1.dev10__py3-none-any.whl → 0.200.1.dev12__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.
- sqlmesh/_version.py +2 -2
- sqlmesh/core/config/root.py +12 -0
- sqlmesh/core/console.py +64 -25
- sqlmesh/integrations/github/cicd/command.py +1 -4
- sqlmesh/integrations/github/cicd/config.py +26 -3
- sqlmesh/integrations/github/cicd/controller.py +123 -92
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/METADATA +1 -1
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/RECORD +12 -12
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/WHEEL +0 -0
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/entry_points.txt +0 -0
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/licenses/LICENSE +0 -0
- {sqlmesh-0.200.1.dev10.dist-info → sqlmesh-0.200.1.dev12.dist-info}/top_level.txt +0 -0
sqlmesh/_version.py
CHANGED
|
@@ -17,5 +17,5 @@ __version__: str
|
|
|
17
17
|
__version_tuple__: VERSION_TUPLE
|
|
18
18
|
version_tuple: VERSION_TUPLE
|
|
19
19
|
|
|
20
|
-
__version__ = version = '0.200.1.
|
|
21
|
-
__version_tuple__ = version_tuple = (0, 200, 1, '
|
|
20
|
+
__version__ = version = '0.200.1.dev12'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 200, 1, 'dev12')
|
sqlmesh/core/config/root.py
CHANGED
|
@@ -260,6 +260,18 @@ class Config(BaseConfig):
|
|
|
260
260
|
|
|
261
261
|
return self
|
|
262
262
|
|
|
263
|
+
@model_validator(mode="after")
|
|
264
|
+
def _inherit_project_config_in_cicd_bot(self) -> Self:
|
|
265
|
+
if self.cicd_bot:
|
|
266
|
+
# inherit the project-level settings into the CICD bot if they have not been explicitly overridden
|
|
267
|
+
if self.cicd_bot.auto_categorize_changes_ is None:
|
|
268
|
+
self.cicd_bot.auto_categorize_changes_ = self.plan.auto_categorize_changes
|
|
269
|
+
|
|
270
|
+
if self.cicd_bot.pr_include_unmodified_ is None:
|
|
271
|
+
self.cicd_bot.pr_include_unmodified_ = self.plan.include_unmodified
|
|
272
|
+
|
|
273
|
+
return self
|
|
274
|
+
|
|
263
275
|
def get_default_test_connection(
|
|
264
276
|
self,
|
|
265
277
|
default_catalog: t.Optional[str] = None,
|
sqlmesh/core/console.py
CHANGED
|
@@ -3049,34 +3049,47 @@ class CaptureTerminalConsole(TerminalConsole):
|
|
|
3049
3049
|
def __init__(self, console: t.Optional[RichConsole] = None, **kwargs: t.Any) -> None:
|
|
3050
3050
|
super().__init__(console=console, **kwargs)
|
|
3051
3051
|
self._captured_outputs: t.List[str] = []
|
|
3052
|
+
self._warnings: t.List[str] = []
|
|
3052
3053
|
self._errors: t.List[str] = []
|
|
3053
3054
|
|
|
3054
3055
|
@property
|
|
3055
3056
|
def captured_output(self) -> str:
|
|
3056
3057
|
return "".join(self._captured_outputs)
|
|
3057
3058
|
|
|
3059
|
+
@property
|
|
3060
|
+
def captured_warnings(self) -> str:
|
|
3061
|
+
return "".join(self._warnings)
|
|
3062
|
+
|
|
3058
3063
|
@property
|
|
3059
3064
|
def captured_errors(self) -> str:
|
|
3060
3065
|
return "".join(self._errors)
|
|
3061
3066
|
|
|
3062
3067
|
def consume_captured_output(self) -> str:
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3068
|
+
try:
|
|
3069
|
+
return self.captured_output
|
|
3070
|
+
finally:
|
|
3071
|
+
self._captured_outputs = []
|
|
3066
3072
|
|
|
3067
|
-
def
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3073
|
+
def consume_captured_warnings(self) -> str:
|
|
3074
|
+
try:
|
|
3075
|
+
return self.captured_warnings
|
|
3076
|
+
finally:
|
|
3077
|
+
self._warnings = []
|
|
3071
3078
|
|
|
3072
|
-
def
|
|
3073
|
-
|
|
3079
|
+
def consume_captured_errors(self) -> str:
|
|
3080
|
+
try:
|
|
3081
|
+
return self.captured_errors
|
|
3082
|
+
finally:
|
|
3083
|
+
self._errors = []
|
|
3074
3084
|
|
|
3075
|
-
def
|
|
3076
|
-
self.
|
|
3085
|
+
def log_warning(self, short_message: str, long_message: t.Optional[str] = None) -> None:
|
|
3086
|
+
if short_message not in self._warnings:
|
|
3087
|
+
self._warnings.append(short_message)
|
|
3088
|
+
super().log_warning(short_message, long_message)
|
|
3077
3089
|
|
|
3078
3090
|
def log_error(self, message: str) -> None:
|
|
3079
|
-
self._errors
|
|
3091
|
+
if message not in self._errors:
|
|
3092
|
+
self._errors.append(message)
|
|
3080
3093
|
super().log_error(message)
|
|
3081
3094
|
|
|
3082
3095
|
def log_skipped_models(self, snapshot_names: t.Set[str]) -> None:
|
|
@@ -3087,9 +3100,8 @@ class CaptureTerminalConsole(TerminalConsole):
|
|
|
3087
3100
|
super().log_skipped_models(snapshot_names)
|
|
3088
3101
|
|
|
3089
3102
|
def log_failed_models(self, errors: t.List[NodeExecutionFailedError]) -> None:
|
|
3090
|
-
if
|
|
3091
|
-
|
|
3092
|
-
super().log_failed_models(errors)
|
|
3103
|
+
self._errors.extend([str(ex) for ex in errors if str(ex) not in self._errors])
|
|
3104
|
+
super().log_failed_models(errors)
|
|
3093
3105
|
|
|
3094
3106
|
def _print(self, value: t.Any, **kwargs: t.Any) -> None:
|
|
3095
3107
|
with self.console.capture() as capture:
|
|
@@ -3110,6 +3122,11 @@ class MarkdownConsole(CaptureTerminalConsole):
|
|
|
3110
3122
|
AUDIT_PADDING = 7
|
|
3111
3123
|
|
|
3112
3124
|
def __init__(self, **kwargs: t.Any) -> None:
|
|
3125
|
+
self.alert_block_max_content_length = int(kwargs.pop("alert_block_max_content_length", 500))
|
|
3126
|
+
self.alert_block_collapsible_threshold = int(
|
|
3127
|
+
kwargs.pop("alert_block_collapsible_threshold", 200)
|
|
3128
|
+
)
|
|
3129
|
+
|
|
3113
3130
|
super().__init__(
|
|
3114
3131
|
**{**kwargs, "console": RichConsole(no_color=True, width=kwargs.pop("width", None))}
|
|
3115
3132
|
)
|
|
@@ -3434,18 +3451,40 @@ class MarkdownConsole(CaptureTerminalConsole):
|
|
|
3434
3451
|
self._print(msg)
|
|
3435
3452
|
self._errors.append(msg)
|
|
3436
3453
|
|
|
3437
|
-
|
|
3438
|
-
|
|
3454
|
+
@property
|
|
3455
|
+
def captured_warnings(self) -> str:
|
|
3456
|
+
return self._render_alert_block("WARNING", self._warnings)
|
|
3439
3457
|
|
|
3440
|
-
|
|
3441
|
-
|
|
3458
|
+
@property
|
|
3459
|
+
def captured_errors(self) -> str:
|
|
3460
|
+
return self._render_alert_block("CAUTION", self._errors)
|
|
3442
3461
|
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3462
|
+
def _render_alert_block(self, block_type: str, items: t.List[str]) -> str:
|
|
3463
|
+
# GitHub Markdown alert syntax, https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
|
|
3464
|
+
if items:
|
|
3465
|
+
item_contents = ""
|
|
3466
|
+
list_indicator = "- " if len(items) > 1 else ""
|
|
3467
|
+
|
|
3468
|
+
for item in items:
|
|
3469
|
+
item = item.replace("\n", "\n> ")
|
|
3470
|
+
item_contents += f">\n> {list_indicator}{item}\n"
|
|
3471
|
+
|
|
3472
|
+
if len(item_contents) > self.alert_block_max_content_length:
|
|
3473
|
+
truncation_msg = (
|
|
3474
|
+
"...\n>\n> Truncated. Please check the console for full information.\n"
|
|
3475
|
+
)
|
|
3476
|
+
item_contents = item_contents[
|
|
3477
|
+
0 : self.alert_block_max_content_length - len(truncation_msg)
|
|
3478
|
+
]
|
|
3479
|
+
item_contents += truncation_msg
|
|
3480
|
+
break
|
|
3481
|
+
|
|
3482
|
+
if len(item_contents) > self.alert_block_collapsible_threshold:
|
|
3483
|
+
item_contents = f"> <details>\n{item_contents}> </details>"
|
|
3484
|
+
|
|
3485
|
+
return f"> [!{block_type}]\n{item_contents}\n"
|
|
3447
3486
|
|
|
3448
|
-
|
|
3487
|
+
return ""
|
|
3449
3488
|
|
|
3450
3489
|
def _print(self, value: t.Any, **kwargs: t.Any) -> None:
|
|
3451
3490
|
self.console.print(value, **kwargs)
|
|
@@ -3472,7 +3511,7 @@ class DatabricksMagicConsole(CaptureTerminalConsole):
|
|
|
3472
3511
|
super()._print(value, **kwargs)
|
|
3473
3512
|
for captured_output in self._captured_outputs:
|
|
3474
3513
|
print(captured_output)
|
|
3475
|
-
self.
|
|
3514
|
+
self.consume_captured_output()
|
|
3476
3515
|
|
|
3477
3516
|
def _prompt(self, message: str, **kwargs: t.Any) -> t.Any:
|
|
3478
3517
|
self._print(message)
|
|
@@ -119,10 +119,7 @@ def _update_pr_environment(controller: GithubController) -> bool:
|
|
|
119
119
|
except Exception as e:
|
|
120
120
|
logger.exception("Error occurred when updating PR environment")
|
|
121
121
|
conclusion = controller.update_pr_environment_check(
|
|
122
|
-
status=GithubCheckStatus.COMPLETED,
|
|
123
|
-
exception=e,
|
|
124
|
-
plan=controller.pr_plan_or_none,
|
|
125
|
-
plan_flags=controller.pr_plan_flags,
|
|
122
|
+
status=GithubCheckStatus.COMPLETED, exception=e
|
|
126
123
|
)
|
|
127
124
|
return (
|
|
128
125
|
conclusion is not None
|
|
@@ -7,6 +7,7 @@ from sqlmesh.core.config import CategorizerConfig
|
|
|
7
7
|
from sqlmesh.core.config.base import BaseConfig
|
|
8
8
|
from sqlmesh.utils.date import TimeLike
|
|
9
9
|
from sqlmesh.utils.pydantic import model_validator
|
|
10
|
+
from sqlmesh.core.console import get_console
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class MergeMethod(str, Enum):
|
|
@@ -22,10 +23,12 @@ class GithubCICDBotConfig(BaseConfig):
|
|
|
22
23
|
enable_deploy_command: bool = False
|
|
23
24
|
merge_method: t.Optional[MergeMethod] = None
|
|
24
25
|
command_namespace: t.Optional[str] = None
|
|
25
|
-
|
|
26
|
+
auto_categorize_changes_: t.Optional[CategorizerConfig] = Field(
|
|
27
|
+
default=None, alias="auto_categorize_changes"
|
|
28
|
+
)
|
|
26
29
|
default_pr_start: t.Optional[TimeLike] = None
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
skip_pr_backfill_: t.Optional[bool] = Field(default=None, alias="skip_pr_backfill")
|
|
31
|
+
pr_include_unmodified_: t.Optional[bool] = Field(default=None, alias="pr_include_unmodified")
|
|
29
32
|
run_on_deploy_to_prod: bool = False
|
|
30
33
|
pr_environment_name: t.Optional[str] = None
|
|
31
34
|
pr_min_intervals: t.Optional[int] = None
|
|
@@ -50,6 +53,26 @@ class GithubCICDBotConfig(BaseConfig):
|
|
|
50
53
|
return [self.prod_branch_names_]
|
|
51
54
|
return ["main", "master"]
|
|
52
55
|
|
|
56
|
+
@property
|
|
57
|
+
def auto_categorize_changes(self) -> CategorizerConfig:
|
|
58
|
+
return self.auto_categorize_changes_ or CategorizerConfig.all_off()
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def pr_include_unmodified(self) -> bool:
|
|
62
|
+
return self.pr_include_unmodified_ or False
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def skip_pr_backfill(self) -> bool:
|
|
66
|
+
if self.skip_pr_backfill_ is None:
|
|
67
|
+
get_console().log_warning(
|
|
68
|
+
"`skip_pr_backfill` is unset, defaulting it to `true` (no data will be backfilled).\n"
|
|
69
|
+
"Future versions of SQLMesh will default to `skip_pr_backfill: false` to align with the CLI default behaviour.\n"
|
|
70
|
+
"If you would like to preserve the current behaviour and remove this warning, please explicitly set `skip_pr_backfill: true` in the bot config.\n\n"
|
|
71
|
+
"For more information on configuring the bot, see: https://sqlmesh.readthedocs.io/en/stable/integrations/github/"
|
|
72
|
+
)
|
|
73
|
+
return True
|
|
74
|
+
return self.skip_pr_backfill_
|
|
75
|
+
|
|
53
76
|
FIELDS_FOR_ANALYTICS: t.ClassVar[t.Set[str]] = {
|
|
54
77
|
"invalidate_environment_after_deploy",
|
|
55
78
|
"enable_deploy_command",
|
|
@@ -494,7 +494,7 @@ class GithubController:
|
|
|
494
494
|
|
|
495
495
|
try:
|
|
496
496
|
# Clear out any output that might exist from prior steps
|
|
497
|
-
self._console.
|
|
497
|
+
self._console.consume_captured_output()
|
|
498
498
|
if plan.restatements:
|
|
499
499
|
self._console._print("\n**Restating models**\n")
|
|
500
500
|
else:
|
|
@@ -522,13 +522,115 @@ class GithubController:
|
|
|
522
522
|
if not difference_summary and not missing_dates:
|
|
523
523
|
return f"No changes to apply.{plan_flags_section}"
|
|
524
524
|
|
|
525
|
-
|
|
525
|
+
warnings_block = self._console.consume_captured_warnings()
|
|
526
|
+
errors_block = self._console.consume_captured_errors()
|
|
527
|
+
|
|
528
|
+
return f"{warnings_block}{errors_block}{difference_summary}\n{missing_dates}{plan_flags_section}"
|
|
526
529
|
except PlanError as e:
|
|
527
530
|
logger.exception("Plan failed to generate")
|
|
528
531
|
return f"Plan failed to generate. Check for pending or unresolved changes. Error: {e}"
|
|
529
532
|
finally:
|
|
530
533
|
self._console.verbosity = orig_verbosity
|
|
531
534
|
|
|
535
|
+
def get_pr_environment_summary(
|
|
536
|
+
self, conclusion: GithubCheckConclusion, exception: t.Optional[Exception] = None
|
|
537
|
+
) -> str:
|
|
538
|
+
heading = ""
|
|
539
|
+
summary = ""
|
|
540
|
+
|
|
541
|
+
if conclusion.is_success:
|
|
542
|
+
summary = self._get_pr_environment_summary_success()
|
|
543
|
+
elif conclusion.is_action_required:
|
|
544
|
+
heading = f":warning: Action Required to create or update PR Environment `{self.pr_environment_name}` :warning:"
|
|
545
|
+
summary = self._get_pr_environment_summary_action_required(exception)
|
|
546
|
+
elif conclusion.is_failure:
|
|
547
|
+
heading = (
|
|
548
|
+
f":x: Failed to create or update PR Environment `{self.pr_environment_name}` :x:"
|
|
549
|
+
)
|
|
550
|
+
summary = self._get_pr_environment_summary_failure(exception)
|
|
551
|
+
elif conclusion.is_skipped:
|
|
552
|
+
heading = f":next_track_button: Skipped creating or updating PR Environment `{self.pr_environment_name}` :next_track_button:"
|
|
553
|
+
summary = self._get_pr_environment_summary_skipped(exception)
|
|
554
|
+
else:
|
|
555
|
+
heading = f":interrobang: Got an unexpected conclusion: {conclusion.value}"
|
|
556
|
+
|
|
557
|
+
# note: we just add warnings here, errors will be covered by the "failure" conclusion
|
|
558
|
+
if warnings := self._console.consume_captured_warnings():
|
|
559
|
+
summary = f"{warnings}\n{summary}"
|
|
560
|
+
|
|
561
|
+
return f"{heading}\n\n{summary}".strip()
|
|
562
|
+
|
|
563
|
+
def _get_pr_environment_summary_success(self) -> str:
|
|
564
|
+
prod_plan = self.prod_plan_with_gaps
|
|
565
|
+
|
|
566
|
+
if not prod_plan.has_changes:
|
|
567
|
+
summary = "No models were modified in this PR.\n"
|
|
568
|
+
else:
|
|
569
|
+
intro = self._generate_pr_environment_summary_intro()
|
|
570
|
+
summary = intro + self._generate_pr_environment_summary_list(prod_plan)
|
|
571
|
+
|
|
572
|
+
if prod_plan.user_provided_flags:
|
|
573
|
+
summary += self._generate_plan_flags_section(prod_plan.user_provided_flags)
|
|
574
|
+
|
|
575
|
+
return summary
|
|
576
|
+
|
|
577
|
+
def _get_pr_environment_summary_skipped(self, exception: t.Optional[Exception] = None) -> str:
|
|
578
|
+
if isinstance(exception, NoChangesPlanError):
|
|
579
|
+
skip_reason = "No changes were detected compared to the prod environment."
|
|
580
|
+
elif isinstance(exception, TestFailure):
|
|
581
|
+
skip_reason = "Unit Test(s) Failed so skipping PR creation"
|
|
582
|
+
else:
|
|
583
|
+
skip_reason = "A prior stage failed resulting in skipping PR creation."
|
|
584
|
+
|
|
585
|
+
return skip_reason
|
|
586
|
+
|
|
587
|
+
def _get_pr_environment_summary_action_required(
|
|
588
|
+
self, exception: t.Optional[Exception] = None
|
|
589
|
+
) -> str:
|
|
590
|
+
plan = self.pr_plan_or_none
|
|
591
|
+
if isinstance(exception, UncategorizedPlanError) and plan:
|
|
592
|
+
failure_msg = f"The following models could not be categorized automatically:\n"
|
|
593
|
+
for snapshot in plan.uncategorized:
|
|
594
|
+
failure_msg += f"- {snapshot.name}\n"
|
|
595
|
+
failure_msg += (
|
|
596
|
+
f"\nRun `sqlmesh plan {self.pr_environment_name}` locally to apply these changes.\n\n"
|
|
597
|
+
"If you would like the bot to automatically categorize changes, check the [documentation](https://sqlmesh.readthedocs.io/en/stable/integrations/github/) for more information."
|
|
598
|
+
)
|
|
599
|
+
else:
|
|
600
|
+
failure_msg = "Please check the Actions Workflow logs for more information."
|
|
601
|
+
|
|
602
|
+
return failure_msg
|
|
603
|
+
|
|
604
|
+
def _get_pr_environment_summary_failure(self, exception: t.Optional[Exception] = None) -> str:
|
|
605
|
+
console_output = self._console.consume_captured_output()
|
|
606
|
+
|
|
607
|
+
if isinstance(exception, PlanError):
|
|
608
|
+
failure_msg = f"Plan application failed.\n"
|
|
609
|
+
if exception.args and (msg := exception.args[0]) and isinstance(msg, str):
|
|
610
|
+
failure_msg += f"\n{msg}\n"
|
|
611
|
+
if console_output:
|
|
612
|
+
failure_msg += f"\n{console_output}"
|
|
613
|
+
elif isinstance(exception, (SQLMeshError, SqlglotError, ValueError)):
|
|
614
|
+
# this logic is taken from the global error handler attached to the CLI, which uses `click.echo()` to output the message
|
|
615
|
+
# so cant be re-used here because it bypasses the Console
|
|
616
|
+
failure_msg = f"**Error:** {str(exception)}"
|
|
617
|
+
elif exception:
|
|
618
|
+
logger.debug(
|
|
619
|
+
"Got unexpected error. Error Type: "
|
|
620
|
+
+ str(type(exception))
|
|
621
|
+
+ " Stack trace: "
|
|
622
|
+
+ traceback.format_exc()
|
|
623
|
+
)
|
|
624
|
+
failure_msg = f"This is an unexpected error.\n\n**Exception:**\n```\n{traceback.format_exc()}\n```"
|
|
625
|
+
|
|
626
|
+
if captured_errors := self._console.consume_captured_errors():
|
|
627
|
+
failure_msg = f"{captured_errors}\n{failure_msg}"
|
|
628
|
+
|
|
629
|
+
if plan_flags := self.pr_plan_flags:
|
|
630
|
+
failure_msg += f"\n\n{self._generate_plan_flags_section(plan_flags)}"
|
|
631
|
+
|
|
632
|
+
return failure_msg
|
|
633
|
+
|
|
532
634
|
def run_tests(self) -> t.Tuple[ModelTextTestResult, str]:
|
|
533
635
|
"""
|
|
534
636
|
Run tests for the PR
|
|
@@ -539,7 +641,7 @@ class GithubController:
|
|
|
539
641
|
"""
|
|
540
642
|
Run linter for the PR
|
|
541
643
|
"""
|
|
542
|
-
self._console.
|
|
644
|
+
self._console.consume_captured_output()
|
|
543
645
|
self._context.lint_models()
|
|
544
646
|
|
|
545
647
|
def _get_or_create_comment(self, header: str = BOT_HEADER_MSG) -> IssueComment:
|
|
@@ -611,7 +713,22 @@ class GithubController:
|
|
|
611
713
|
Creates a PR environment from the logic present in the PR. If the PR contains changes that are
|
|
612
714
|
uncategorized, then an error will be raised.
|
|
613
715
|
"""
|
|
614
|
-
self._context.apply(self.pr_plan)
|
|
716
|
+
self._context.apply(self.pr_plan) # will raise if PR environment creation fails
|
|
717
|
+
|
|
718
|
+
# update PR info comment
|
|
719
|
+
vde_title = "- :eyes: To **review** this PR's changes, use virtual data environment:"
|
|
720
|
+
comment_value = f"{vde_title}\n - `{self.pr_environment_name}`"
|
|
721
|
+
if self.bot_config.enable_deploy_command:
|
|
722
|
+
comment_value += (
|
|
723
|
+
"\n- :arrow_forward: To **apply** this PR's plan to prod, comment:\n - `/deploy`"
|
|
724
|
+
)
|
|
725
|
+
dedup_regex = vde_title.replace("*", r"\*") + r".*"
|
|
726
|
+
updated_comment, _ = self.update_sqlmesh_comment_info(
|
|
727
|
+
value=comment_value,
|
|
728
|
+
dedup_regex=dedup_regex,
|
|
729
|
+
)
|
|
730
|
+
if updated_comment:
|
|
731
|
+
self._append_output("created_pr_environment", "true")
|
|
615
732
|
|
|
616
733
|
def deploy_to_prod(self) -> None:
|
|
617
734
|
"""
|
|
@@ -855,13 +972,7 @@ class GithubController:
|
|
|
855
972
|
)
|
|
856
973
|
|
|
857
974
|
def update_pr_environment_check(
|
|
858
|
-
self,
|
|
859
|
-
status: GithubCheckStatus,
|
|
860
|
-
exception: t.Optional[Exception] = None,
|
|
861
|
-
plan: t.Optional[Plan] = None,
|
|
862
|
-
plan_flags: t.Optional[
|
|
863
|
-
t.Dict[str, UserProvidedFlags]
|
|
864
|
-
] = None, # note: the plan flags are passed separately in case the plan fails to build
|
|
975
|
+
self, status: GithubCheckStatus, exception: t.Optional[Exception] = None
|
|
865
976
|
) -> t.Optional[GithubCheckConclusion]:
|
|
866
977
|
"""
|
|
867
978
|
Updates the status of the merge commit for the PR environment.
|
|
@@ -882,87 +993,7 @@ class GithubController:
|
|
|
882
993
|
def conclusion_handler(
|
|
883
994
|
conclusion: GithubCheckConclusion, exception: t.Optional[Exception]
|
|
884
995
|
) -> t.Tuple[GithubCheckConclusion, str, t.Optional[str]]:
|
|
885
|
-
|
|
886
|
-
prod_plan = self.prod_plan_with_gaps
|
|
887
|
-
|
|
888
|
-
if not prod_plan.has_changes:
|
|
889
|
-
summary = "No models were modified in this PR.\n"
|
|
890
|
-
else:
|
|
891
|
-
intro = self._generate_pr_environment_summary_intro()
|
|
892
|
-
summary = intro + self._generate_pr_environment_summary_list(prod_plan)
|
|
893
|
-
if prod_plan.user_provided_flags:
|
|
894
|
-
summary += self._generate_plan_flags_section(prod_plan.user_provided_flags)
|
|
895
|
-
|
|
896
|
-
vde_title = (
|
|
897
|
-
"- :eyes: To **review** this PR's changes, use virtual data environment:"
|
|
898
|
-
)
|
|
899
|
-
comment_value = f"{vde_title}\n - `{self.pr_environment_name}`"
|
|
900
|
-
if self.bot_config.enable_deploy_command:
|
|
901
|
-
comment_value += "\n- :arrow_forward: To **apply** this PR's plan to prod, comment:\n - `/deploy`"
|
|
902
|
-
dedup_regex = vde_title.replace("*", r"\*") + r".*"
|
|
903
|
-
updated_comment, _ = self.update_sqlmesh_comment_info(
|
|
904
|
-
value=comment_value,
|
|
905
|
-
dedup_regex=dedup_regex,
|
|
906
|
-
)
|
|
907
|
-
if updated_comment:
|
|
908
|
-
self._append_output("created_pr_environment", "true")
|
|
909
|
-
else:
|
|
910
|
-
if isinstance(exception, NoChangesPlanError):
|
|
911
|
-
skip_reason = "No changes were detected compared to the prod environment."
|
|
912
|
-
elif isinstance(exception, TestFailure):
|
|
913
|
-
skip_reason = "Unit Test(s) Failed so skipping PR creation"
|
|
914
|
-
else:
|
|
915
|
-
skip_reason = "A prior stage failed resulting in skipping PR creation."
|
|
916
|
-
|
|
917
|
-
if not skip_reason and exception:
|
|
918
|
-
logger.debug(
|
|
919
|
-
f"Got {type(exception).__name__}. Stack trace: " + traceback.format_exc()
|
|
920
|
-
)
|
|
921
|
-
|
|
922
|
-
captured_errors = self._console.consume_captured_errors()
|
|
923
|
-
if captured_errors:
|
|
924
|
-
logger.debug(f"Captured errors: {captured_errors}")
|
|
925
|
-
failure_msg = f"**Errors:**\n{captured_errors}\n"
|
|
926
|
-
elif isinstance(exception, UncategorizedPlanError) and plan:
|
|
927
|
-
failure_msg = f"The following models could not be categorized automatically:\n"
|
|
928
|
-
for snapshot in plan.uncategorized:
|
|
929
|
-
failure_msg += f"- {snapshot.name}\n"
|
|
930
|
-
failure_msg += (
|
|
931
|
-
f"\nRun `sqlmesh plan {self.pr_environment_name}` locally to apply these changes.\n\n"
|
|
932
|
-
"If you would like the bot to automatically categorize changes, check the [documentation](https://sqlmesh.readthedocs.io/en/stable/integrations/github/) for more information."
|
|
933
|
-
)
|
|
934
|
-
elif isinstance(exception, PlanError):
|
|
935
|
-
failure_msg = f"Plan application failed.\n"
|
|
936
|
-
if exception.args and (msg := exception.args[0]) and isinstance(msg, str):
|
|
937
|
-
failure_msg += f"\n{msg}\n"
|
|
938
|
-
if self._console.captured_output:
|
|
939
|
-
failure_msg += f"\n{self._console.captured_output}"
|
|
940
|
-
elif isinstance(exception, (SQLMeshError, SqlglotError, ValueError)):
|
|
941
|
-
# this logic is taken from the global error handler attached to the CLI, which uses `click.echo()` to output the message
|
|
942
|
-
# so cant be re-used here because it bypasses the Console
|
|
943
|
-
failure_msg = f"**Error:** {str(exception)}"
|
|
944
|
-
else:
|
|
945
|
-
logger.debug(
|
|
946
|
-
"Got unexpected error. Error Type: "
|
|
947
|
-
+ str(type(exception))
|
|
948
|
-
+ " Stack trace: "
|
|
949
|
-
+ traceback.format_exc()
|
|
950
|
-
)
|
|
951
|
-
failure_msg = f"This is an unexpected error.\n\n**Exception:**\n```\n{traceback.format_exc()}\n```"
|
|
952
|
-
|
|
953
|
-
conclusion_to_summary = {
|
|
954
|
-
GithubCheckConclusion.SKIPPED: f":next_track_button: Skipped creating or updating PR Environment `{self.pr_environment_name}`. {skip_reason}",
|
|
955
|
-
GithubCheckConclusion.FAILURE: f":x: Failed to create or update PR Environment `{self.pr_environment_name}` :x:\n\n{failure_msg}",
|
|
956
|
-
GithubCheckConclusion.CANCELLED: f":stop_sign: Cancelled creating or updating PR Environment `{self.pr_environment_name}`",
|
|
957
|
-
GithubCheckConclusion.ACTION_REQUIRED: f":warning: Action Required to create or update PR Environment `{self.pr_environment_name}` :warning:\n\n{failure_msg}",
|
|
958
|
-
}
|
|
959
|
-
summary = conclusion_to_summary.get(
|
|
960
|
-
conclusion, f":interrobang: Got an unexpected conclusion: {conclusion.value}"
|
|
961
|
-
)
|
|
962
|
-
if plan_flags:
|
|
963
|
-
plan_flags_section = self._generate_plan_flags_section(plan_flags)
|
|
964
|
-
summary += f"\n\n{plan_flags_section}"
|
|
965
|
-
|
|
996
|
+
summary = self.get_pr_environment_summary(conclusion, exception)
|
|
966
997
|
self._append_output("pr_environment_name", self.pr_environment_name)
|
|
967
998
|
return conclusion, check_title, summary
|
|
968
999
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
|
|
2
|
-
sqlmesh/_version.py,sha256=
|
|
2
|
+
sqlmesh/_version.py,sha256=f89faFM_DxHFkvinUsuJF9n9u3sFNxAX1D6IFxS9yaQ,530
|
|
3
3
|
sqlmesh/magics.py,sha256=bEzYqXHcM0P3CjyUZ1O9arlmXAdLAhoglKbQ29fD9AE,41842
|
|
4
4
|
sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -11,7 +11,7 @@ sqlmesh/cli/options.py,sha256=-4ZclMIeIg8tghLowtmQNmpEFlm0QtOVEh40NqRdFHA,2499
|
|
|
11
11
|
sqlmesh/cli/project_init.py,sha256=5JmDmUK0WJs39hM-p76uzHYdo1Hnx6fKuJg_pR3Hn5A,16419
|
|
12
12
|
sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
|
|
14
|
-
sqlmesh/core/console.py,sha256=
|
|
14
|
+
sqlmesh/core/console.py,sha256=wQ4r6R6FeCrJ2DNoE0uFSsJwqQ4n4fB7xN0bCI2q7gY,162572
|
|
15
15
|
sqlmesh/core/constants.py,sha256=gUABXWYgH_XOSbaZAJsR5o1dmA61JDCvBDyxqqXX3LM,2630
|
|
16
16
|
sqlmesh/core/context.py,sha256=9a8RqBBHQXjH7nMYiTcQRtp5_NYpk_9EFpPoBfDx94o,124321
|
|
17
17
|
sqlmesh/core/context_diff.py,sha256=CeUmMBJzN__98YohrtAiIYPIZI9mrGBk6bVADWBLUw8,21692
|
|
@@ -52,7 +52,7 @@ sqlmesh/core/config/migration.py,sha256=3lZIlmIcQH3_hIEm3Y3jFtvpw0YXzRzAnk4Odikl
|
|
|
52
52
|
sqlmesh/core/config/model.py,sha256=GIz9kAfKD2NEXNzCZCQ17-CK2OtqN6WgeG1fvibcChM,3650
|
|
53
53
|
sqlmesh/core/config/naming.py,sha256=KVGaxUgswoQajXR_cHFJvgkmEFcU1V182GkOH1SJxuE,326
|
|
54
54
|
sqlmesh/core/config/plan.py,sha256=wJEDfocMcKSMN0-5gVojjIcmJZ0376LJU3tkGMTm-Zc,1558
|
|
55
|
-
sqlmesh/core/config/root.py,sha256
|
|
55
|
+
sqlmesh/core/config/root.py,sha256=-ev6H4Qi0rlwiO8NyvgGXsunkvpVpaRZD6ZCuTDF4V4,16223
|
|
56
56
|
sqlmesh/core/config/run.py,sha256=rIK0QnN_vArrwps2TXGVEbvao2AnGZgzXo7EliJa42E,852
|
|
57
57
|
sqlmesh/core/config/scheduler.py,sha256=LVEHIln7dqv8ygo4uniji4j9-gfuJlk-WpQU9FAqILs,7031
|
|
58
58
|
sqlmesh/core/config/ui.py,sha256=jsO-S6_d9NkLZGG5pT4mgKgxMF34KzkDociZAMvCX3U,278
|
|
@@ -160,9 +160,9 @@ sqlmesh/integrations/llm.py,sha256=URgp3JKr06HYv6hGlJ7DQ9m3QCc41xRWK23HzmGpbDI,1
|
|
|
160
160
|
sqlmesh/integrations/slack.py,sha256=nxLxu5WztGbZH3JdqnzyPqmJUMfRdJ_49LQ7zR-u39Q,6801
|
|
161
161
|
sqlmesh/integrations/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
162
162
|
sqlmesh/integrations/github/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
163
|
-
sqlmesh/integrations/github/cicd/command.py,sha256=
|
|
164
|
-
sqlmesh/integrations/github/cicd/config.py,sha256=
|
|
165
|
-
sqlmesh/integrations/github/cicd/controller.py,sha256=
|
|
163
|
+
sqlmesh/integrations/github/cicd/command.py,sha256=yW0dmsnL-gFTzUvTX1RvCOXDcsOdi0M7IVCpsLR9IN8,12194
|
|
164
|
+
sqlmesh/integrations/github/cicd/config.py,sha256=Cnk1xClYZpYuyCjj1cJnAdlqe0RObd6bg75dnmStEBA,3351
|
|
165
|
+
sqlmesh/integrations/github/cicd/controller.py,sha256=8tFGVmPxdOl6rMj6X-50ZbzfgaQHVl2fa4QbVhr3WY4,53248
|
|
166
166
|
sqlmesh/lsp/api.py,sha256=_lA_buSPZFIoLJuINbS53LbNOOaHp3orb0uFTE3aCqI,2328
|
|
167
167
|
sqlmesh/lsp/completions.py,sha256=Nt1XpFL_-QAIRM8BBBC6Ia6YG0hMZriiNJra7nGfD4Q,6676
|
|
168
168
|
sqlmesh/lsp/context.py,sha256=szHCrmKAjg4wDRnSbjRU158wSGx9HBRnNswSNw9N0Qc,12439
|
|
@@ -284,7 +284,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
|
|
|
284
284
|
sqlmesh/utils/rich.py,sha256=BpB47G0-JcU41LkGkeGKroexj4NOV5fox4-GOIXPgIc,3545
|
|
285
285
|
sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
|
|
286
286
|
sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
|
|
287
|
-
sqlmesh-0.200.1.
|
|
287
|
+
sqlmesh-0.200.1.dev12.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
|
|
288
288
|
web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
289
289
|
web/client/dist/index.html,sha256=DAv6ABGogw4xRLb0T_92WP_S7XWyqVhwBgGKWtuQwU4,5472
|
|
290
290
|
web/client/dist/assets/Audits-BuHAmFO8.js,sha256=zbYUF6WdUGMbnMm01GMNPb7xAJwj-o5wMdL56vPo2qE,1041
|
|
@@ -402,8 +402,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
|
|
|
402
402
|
web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
|
|
403
403
|
web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
|
|
404
404
|
web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
|
|
405
|
-
sqlmesh-0.200.1.
|
|
406
|
-
sqlmesh-0.200.1.
|
|
407
|
-
sqlmesh-0.200.1.
|
|
408
|
-
sqlmesh-0.200.1.
|
|
409
|
-
sqlmesh-0.200.1.
|
|
405
|
+
sqlmesh-0.200.1.dev12.dist-info/METADATA,sha256=2tmGq4Rr66n8cWCVZXhBZ3mjb3ESOhtWioDmKsKFcbI,26679
|
|
406
|
+
sqlmesh-0.200.1.dev12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
407
|
+
sqlmesh-0.200.1.dev12.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
|
|
408
|
+
sqlmesh-0.200.1.dev12.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
|
|
409
|
+
sqlmesh-0.200.1.dev12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|