Skip to content

Commit 933a765

Browse files
Merge branch 'main' into feat/add-fabric-engine
2 parents 173e0ac + 079efc8 commit 933a765

File tree

12 files changed

+471
-136
lines changed

12 files changed

+471
-136
lines changed

docs/concepts/macros/sqlmesh_macros.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,35 @@ MODEL (
216216
name @customer.some_table,
217217
kind FULL,
218218
blueprints (
219-
(customer := customer1, field_a := x, field_b := y),
220-
(customer := customer2, field_a := z, field_b := w)
219+
(customer := customer1, field_a := x, field_b := y, field_c := 'foo'),
220+
(customer := customer2, field_a := z, field_b := w, field_c := 'bar')
221221
)
222222
);
223223

224224
SELECT
225225
@field_a,
226-
@{field_b} AS field_b
226+
@{field_b} AS field_b,
227+
@field_c AS @{field_c}
227228
FROM @customer.some_source
229+
230+
/*
231+
When rendered for customer1.some_table:
232+
SELECT
233+
x,
234+
y AS field_b,
235+
'foo' AS foo
236+
FROM customer1.some_source
237+
238+
When rendered for customer2.some_table:
239+
SELECT
240+
z,
241+
w AS field_b,
242+
'bar' AS bar
243+
FROM customer2.some_source
244+
*/
228245
```
229246

230-
Note the use of both regular `@field_a` and curly brace syntax `@{field_b}` macro variable references in the model query. Learn more [above](#embedding-variables-in-strings)
247+
Note the use of both regular `@field_a` and curly brace syntax `@{field_b}` macro variable references in the model query. Both of these will be rendered as identifiers. In the case of `field_c`, which in the blueprints is a string, it would be rendered as a string literal when used with the regular macro syntax `@field_c` and if we want to use the string as an identifier then we use the curly braces `@{field_c}`. Learn more [above](#embedding-variables-in-strings)
231248

232249
Blueprint variables can be accessed using the syntax shown above, or through the `@BLUEPRINT_VAR()` macro function, which also supports specifying default values in case the variable is undefined (similar to `@VAR()`).
233250

docs/integrations/github.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,13 @@ Below is an example of how to define the default config for the bot in either YA
293293
| `enable_deploy_command` | Indicates if the `/deploy` command should be enabled in order to allowed synchronized deploys to production. Default: `False` | bool | N |
294294
| `command_namespace` | The namespace to use for SQLMesh commands. For example if you provide `#SQLMesh` as a value then commands will be expected in the format of `#SQLMesh/<command>`. Default: `None` meaning no namespace is used. | string | N |
295295
| `auto_categorize_changes` | Auto categorization behavior to use for the bot. If not provided then the project-wide categorization behavior is used. See [Auto-categorize model changes](https://sqlmesh.readthedocs.io/en/stable/guides/configuration/#auto-categorize-model-changes) for details. | dict | N |
296-
| `default_pr_start` | Default start when creating PR environment plans. If running in a mode where the bot automatically backfills models (based on `auto_categorize_changes` behavior) then this can be used to limit the amount of data backfilled. Defaults to `None` meaning the start date is set to the earliest model's start or to 1 day ago if [data previews](../concepts/plans.md#data-preview) need to be computed. | str | N |
296+
| `default_pr_start` | Default start when creating PR environment plans. If running in a mode where the bot automatically backfills models (based on `auto_categorize_changes` behavior) then this can be used to limit the amount of data backfilled. Defaults to `None` meaning the start date is set to the earliest model's start or to 1 day ago if [data previews](../concepts/plans.md#data-preview) need to be computed.| str | N |
297297
| `pr_min_intervals` | Intended for use when `default_pr_start` is set to a relative time, eg `1 week ago`. This ensures that at least this many intervals across every model are included for backfill in the PR environment. Without this, models with an interval unit wider than `default_pr_start` (such as `@monthly` models if `default_pr_start` was set to `1 week ago`) will be excluded from backfill entirely. | int | N |
298298
| `skip_pr_backfill` | Indicates if the bot should skip backfilling models in the PR environment. Default: `True` | bool | N |
299299
| `pr_include_unmodified` | Indicates whether to include unmodified models in the PR environment. Default to the project's config value (which defaults to `False`) | bool | N |
300300
| `run_on_deploy_to_prod` | Indicates whether to run latest intervals when deploying to prod. If set to false, the deployment will backfill only the changed models up to the existing latest interval in production, ignoring any missing intervals beyond this point. Default: `False` | bool | N |
301301
| `pr_environment_name` | The name of the PR environment to create for which a PR number will be appended to. Defaults to the repo name if not provided. Note: The name will be normalized to alphanumeric + underscore and lowercase. | str | N |
302-
| `prod_branch_name` | The name of the git branch associated with production. Ex: `prod`. Default: `main` or `master` is considered prod | str | N |
302+
| `prod_branch_name` | The name of the git branch associated with production. Ex: `prod`. Default: `main` or `master` is considered prod | str | N |
303303

304304
Example with all properties defined:
305305

sqlmesh/core/config/root.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,18 @@ def _normalize_identifiers(key: str) -> None:
260260

261261
return self
262262

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+
263275
def get_default_test_connection(
264276
self,
265277
default_catalog: t.Optional[str] = None,

sqlmesh/core/console.py

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,34 +3049,47 @@ class CaptureTerminalConsole(TerminalConsole):
30493049
def __init__(self, console: t.Optional[RichConsole] = None, **kwargs: t.Any) -> None:
30503050
super().__init__(console=console, **kwargs)
30513051
self._captured_outputs: t.List[str] = []
3052+
self._warnings: t.List[str] = []
30523053
self._errors: t.List[str] = []
30533054

30543055
@property
30553056
def captured_output(self) -> str:
30563057
return "".join(self._captured_outputs)
30573058

3059+
@property
3060+
def captured_warnings(self) -> str:
3061+
return "".join(self._warnings)
3062+
30583063
@property
30593064
def captured_errors(self) -> str:
30603065
return "".join(self._errors)
30613066

30623067
def consume_captured_output(self) -> str:
3063-
output = self.captured_output
3064-
self.clear_captured_outputs()
3065-
return output
3068+
try:
3069+
return self.captured_output
3070+
finally:
3071+
self._captured_outputs = []
30663072

3067-
def consume_captured_errors(self) -> str:
3068-
errors = self.captured_errors
3069-
self.clear_captured_errors()
3070-
return errors
3073+
def consume_captured_warnings(self) -> str:
3074+
try:
3075+
return self.captured_warnings
3076+
finally:
3077+
self._warnings = []
30713078

3072-
def clear_captured_outputs(self) -> None:
3073-
self._captured_outputs = []
3079+
def consume_captured_errors(self) -> str:
3080+
try:
3081+
return self.captured_errors
3082+
finally:
3083+
self._errors = []
30743084

3075-
def clear_captured_errors(self) -> None:
3076-
self._errors = []
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)
30773089

30783090
def log_error(self, message: str) -> None:
3079-
self._errors.append(message)
3091+
if message not in self._errors:
3092+
self._errors.append(message)
30803093
super().log_error(message)
30813094

30823095
def log_skipped_models(self, snapshot_names: t.Set[str]) -> None:
@@ -3087,9 +3100,8 @@ def log_skipped_models(self, snapshot_names: t.Set[str]) -> None:
30873100
super().log_skipped_models(snapshot_names)
30883101

30893102
def log_failed_models(self, errors: t.List[NodeExecutionFailedError]) -> None:
3090-
if errors:
3091-
self._errors.append("\n".join(str(ex) for ex in errors))
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)
30933105

30943106
def _print(self, value: t.Any, **kwargs: t.Any) -> None:
30953107
with self.console.capture() as capture:
@@ -3110,6 +3122,11 @@ class MarkdownConsole(CaptureTerminalConsole):
31103122
AUDIT_PADDING = 7
31113123

31123124
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+
31133130
super().__init__(
31143131
**{**kwargs, "console": RichConsole(no_color=True, width=kwargs.pop("width", None))}
31153132
)
@@ -3434,18 +3451,40 @@ def show_linter_violations(
34343451
self._print(msg)
34353452
self._errors.append(msg)
34363453

3437-
def log_error(self, message: str) -> None:
3438-
super().log_error(f"```\n\\[ERROR] {message}```\n\n")
3454+
@property
3455+
def captured_warnings(self) -> str:
3456+
return self._render_alert_block("WARNING", self._warnings)
34393457

3440-
def log_warning(self, short_message: str, long_message: t.Optional[str] = None) -> None:
3441-
logger.warning(long_message or short_message)
3458+
@property
3459+
def captured_errors(self) -> str:
3460+
return self._render_alert_block("CAUTION", self._errors)
34423461

3443-
if not short_message.endswith("\n"):
3444-
short_message += (
3445-
"\n" # so that the closing ``` ends up on a newline which is important for GitHub
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"
34473486

3448-
self._print(f"```\n\\[WARNING] {short_message}```\n\n")
3487+
return ""
34493488

34503489
def _print(self, value: t.Any, **kwargs: t.Any) -> None:
34513490
self.console.print(value, **kwargs)
@@ -3472,7 +3511,7 @@ def _print(self, value: t.Any, **kwargs: t.Any) -> None:
34723511
super()._print(value, **kwargs)
34733512
for captured_output in self._captured_outputs:
34743513
print(captured_output)
3475-
self.clear_captured_outputs()
3514+
self.consume_captured_output()
34763515

34773516
def _prompt(self, message: str, **kwargs: t.Any) -> t.Any:
34783517
self._print(message)

sqlmesh/integrations/github/cicd/command.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,7 @@ def _update_pr_environment(controller: GithubController) -> bool:
119119
except Exception as e:
120120
logger.exception("Error occurred when updating PR environment")
121121
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
126123
)
127124
return (
128125
conclusion is not None

sqlmesh/integrations/github/cicd/config.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sqlmesh.core.config.base import BaseConfig
88
from sqlmesh.utils.date import TimeLike
99
from sqlmesh.utils.pydantic import model_validator
10+
from sqlmesh.core.console import get_console
1011

1112

1213
class MergeMethod(str, Enum):
@@ -22,10 +23,12 @@ class GithubCICDBotConfig(BaseConfig):
2223
enable_deploy_command: bool = False
2324
merge_method: t.Optional[MergeMethod] = None
2425
command_namespace: t.Optional[str] = None
25-
auto_categorize_changes: CategorizerConfig = CategorizerConfig.all_off()
26+
auto_categorize_changes_: t.Optional[CategorizerConfig] = Field(
27+
default=None, alias="auto_categorize_changes"
28+
)
2629
default_pr_start: t.Optional[TimeLike] = None
27-
skip_pr_backfill: bool = True
28-
pr_include_unmodified: t.Optional[bool] = None
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")
2932
run_on_deploy_to_prod: bool = False
3033
pr_environment_name: t.Optional[str] = None
3134
pr_min_intervals: t.Optional[int] = None
@@ -50,6 +53,26 @@ def prod_branch_names(self) -> t.List[str]:
5053
return [self.prod_branch_names_]
5154
return ["main", "master"]
5255

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+
5376
FIELDS_FOR_ANALYTICS: t.ClassVar[t.Set[str]] = {
5477
"invalidate_environment_after_deploy",
5578
"enable_deploy_command",

0 commit comments

Comments
 (0)