Skip to content

Commit 11697a1

Browse files
authored
fix: action bot properly output all changes (#2513)
1 parent 22b628a commit 11697a1

5 files changed

Lines changed: 195 additions & 199 deletions

File tree

sqlmesh/core/console.py

Lines changed: 30 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,16 +1406,16 @@ def show_model_difference_summary(
14061406
ignored_snapshot_ids = ignored_snapshot_ids or set()
14071407
if context_diff.is_new_environment:
14081408
self._print(
1409-
f"**New environment `{context_diff.environment}` will be created from `{context_diff.create_from}`**\n\n"
1409+
f"**New environment `{context_diff.environment}` will be created from `{context_diff.create_from}`**\n"
14101410
)
14111411
if not context_diff.has_snapshot_changes:
14121412
return
14131413

14141414
if not context_diff.has_changes:
1415-
self._print(f"**No differences when compared to `{context_diff.environment}`**\n\n")
1415+
self._print(f"**No differences when compared to `{context_diff.environment}`**\n")
14161416
return
14171417

1418-
self._print(f"**Summary of differences against `{context_diff.environment}`:**\n\n")
1418+
self._print(f"**Summary of differences against `{context_diff.environment}`:**\n")
14191419

14201420
added_snapshots = {
14211421
context_diff.snapshots[s_id]
@@ -1424,21 +1424,19 @@ def show_model_difference_summary(
14241424
}
14251425
added_snapshot_models = {s for s in added_snapshots if s.is_model}
14261426
if added_snapshot_models:
1427-
self._print(f"**Added Models:**\n")
1428-
for snapshot in added_snapshot_models:
1427+
self._print(f"\n**Added Models:**")
1428+
for snapshot in sorted(added_snapshot_models):
14291429
self._print(
1430-
f"- {snapshot.display_name(environment_naming_info, default_catalog)}\n"
1430+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`"
14311431
)
1432-
self._print("\n")
14331432

14341433
added_snapshot_audits = {s for s in added_snapshots if s.is_audit}
14351434
if added_snapshot_audits:
1436-
self._print(f"**Added Standalone Audits:**\n")
1437-
for snapshot in added_snapshot_audits:
1435+
self._print(f"\n**Added Standalone Audits:**")
1436+
for snapshot in sorted(added_snapshot_audits):
14381437
self._print(
1439-
f"- {snapshot.display_name(environment_naming_info, default_catalog)}\n"
1438+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`"
14401439
)
1441-
self._print("\n")
14421440

14431441
removed_snapshot_table_infos = {
14441442
snapshot_table_info
@@ -1447,21 +1445,19 @@ def show_model_difference_summary(
14471445
}
14481446
removed_model_snapshot_table_infos = {s for s in removed_snapshot_table_infos if s.is_model}
14491447
if removed_model_snapshot_table_infos:
1450-
self._print(f"**Removed Models:**\n")
1451-
for snapshot_table_info in removed_model_snapshot_table_infos:
1448+
self._print(f"\n**Removed Models:**")
1449+
for snapshot_table_info in sorted(removed_model_snapshot_table_infos):
14521450
self._print(
1453-
f"- {snapshot_table_info.display_name(environment_naming_info, default_catalog)}\n"
1451+
f"- `{snapshot_table_info.display_name(environment_naming_info, default_catalog)}`"
14541452
)
1455-
self._print("\n")
14561453

14571454
removed_audit_snapshot_table_infos = {s for s in removed_snapshot_table_infos if s.is_audit}
14581455
if removed_audit_snapshot_table_infos:
1459-
self._print(f"**Removed Standalone Audits:**\n")
1460-
for snapshot_table_info in removed_audit_snapshot_table_infos:
1456+
self._print(f"\n**Removed Standalone Audits:**")
1457+
for snapshot_table_info in sorted(removed_audit_snapshot_table_infos):
14611458
self._print(
1462-
f"- {snapshot_table_info.display_name(environment_naming_info, default_catalog)}\n"
1459+
f"- `{snapshot_table_info.display_name(environment_naming_info, default_catalog)}`"
14631460
)
1464-
self._print("\n")
14651461

14661462
modified_snapshots = {
14671463
current_snapshot
@@ -1480,43 +1476,39 @@ def show_model_difference_summary(
14801476
elif context_diff.metadata_updated(snapshot.name):
14811477
metadata_modified.append(snapshot)
14821478
if directly_modified:
1483-
self._print(f"**Directly Modified:**\n")
1484-
for snapshot in directly_modified:
1479+
self._print(f"\n**Directly Modified:**")
1480+
for snapshot in sorted(directly_modified):
14851481
self._print(
1486-
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`\n"
1482+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`"
14871483
)
14881484
if not no_diff:
1489-
self._print(f"```diff\n{context_diff.text_diff(snapshot.name)}\n```\n")
1490-
self._print("\n")
1485+
self._print(f"```diff\n{context_diff.text_diff(snapshot.name)}\n```")
14911486
if indirectly_modified:
1492-
self._print(f"**Indirectly Modified:**\n")
1493-
for snapshot in indirectly_modified:
1487+
self._print(f"\n**Indirectly Modified:**")
1488+
for snapshot in sorted(indirectly_modified):
14941489
self._print(
1495-
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`\n"
1490+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`"
14961491
)
1497-
self._print("\n")
14981492
if metadata_modified:
1499-
self._print(f"**Metadata Updated:**\n")
1500-
for snapshot in metadata_modified:
1493+
self._print(f"\n**Metadata Updated:**")
1494+
for snapshot in sorted(metadata_modified):
15011495
self._print(
1502-
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`\n"
1496+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}`"
15031497
)
1504-
self._print("\n")
15051498
if ignored_snapshot_ids:
1506-
self._print(f"**Ignored Models (Expected Plan Start):**\n")
1507-
for s_id in ignored_snapshot_ids:
1499+
self._print(f"\n**Ignored Models (Expected Plan Start):**")
1500+
for s_id in sorted(ignored_snapshot_ids):
15081501
snapshot = context_diff.snapshots[s_id]
15091502
self._print(
1510-
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}` ({snapshot.get_latest(start_date(snapshot, context_diff.snapshots.values()))})\n"
1503+
f"- `{snapshot.display_name(environment_naming_info, default_catalog)}` ({snapshot.get_latest(start_date(snapshot, context_diff.snapshots.values()))})"
15111504
)
1512-
self._print("\n")
15131505

15141506
def _show_missing_dates(self, plan: Plan, default_catalog: t.Optional[str]) -> None:
15151507
"""Displays the models with missing dates."""
15161508
missing_intervals = plan.missing_intervals
15171509
if not missing_intervals:
15181510
return
1519-
self._print("**Models needing backfill (missing dates):**\n\n")
1511+
self._print("\n**Models needing backfill (missing dates):**")
15201512
for missing in missing_intervals:
15211513
snapshot = plan.context_diff.snapshots[missing.snapshot_id]
15221514
if not snapshot.is_model:
@@ -1527,9 +1519,8 @@ def _show_missing_dates(self, plan: Plan, default_catalog: t.Optional[str]) -> N
15271519
preview_modifier = " (**preview**)"
15281520

15291521
self._print(
1530-
f"* `{snapshot.display_name(plan.environment_naming_info, default_catalog)}`: {missing.format_intervals(snapshot.node.interval_unit)}{preview_modifier}\n"
1522+
f"* `{snapshot.display_name(plan.environment_naming_info, default_catalog)}`: {missing.format_intervals(snapshot.node.interval_unit)}{preview_modifier}"
15311523
)
1532-
self._print("\n")
15331524

15341525
def _show_categorized_snapshots(self, plan: Plan, default_catalog: t.Optional[str]) -> None:
15351526
context_diff = plan.context_diff

sqlmesh/core/plan/definition.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717
merge_intervals,
1818
missing_intervals,
1919
)
20-
from sqlmesh.core.snapshot.definition import Interval, SnapshotId, format_intervals
20+
from sqlmesh.core.snapshot.definition import (
21+
Interval,
22+
SnapshotId,
23+
SnapshotTableInfo,
24+
format_intervals,
25+
)
2126
from sqlmesh.utils.date import TimeLike, now, to_datetime, to_timestamp
2227
from sqlmesh.utils.pydantic import PydanticModel
2328

@@ -123,7 +128,7 @@ def snapshots(self) -> t.Dict[SnapshotId, Snapshot]:
123128
}
124129

125130
@cached_property
126-
def modified_snapshots(self) -> t.Dict[SnapshotId, Snapshot]:
131+
def modified_snapshots(self) -> t.Dict[SnapshotId, t.Union[Snapshot, SnapshotTableInfo]]:
127132
"""Returns the modified (either directly or indirectly) snapshots."""
128133
return {
129134
**{s_id: self.context_diff.snapshots[s_id] for s_id in sorted(self.directly_modified)},
@@ -132,6 +137,7 @@ def modified_snapshots(self) -> t.Dict[SnapshotId, Snapshot]:
132137
for downstream_s_ids in self.indirectly_modified.values()
133138
for s_id in sorted(downstream_s_ids)
134139
},
140+
**self.context_diff.removed_snapshots,
135141
}
136142

137143
@property

sqlmesh/integrations/github/cicd/controller.py

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
from sqlmesh.core.context import Context
2323
from sqlmesh.core.environment import Environment
2424
from sqlmesh.core.plan import Plan, PlanBuilder
25-
from sqlmesh.core.snapshot.definition import Snapshot, SnapshotId, format_intervals
25+
from sqlmesh.core.snapshot.definition import (
26+
Snapshot,
27+
SnapshotChangeCategory,
28+
SnapshotId,
29+
SnapshotTableInfo,
30+
format_intervals,
31+
)
2632
from sqlmesh.core.user import User
2733
from sqlmesh.integrations.github.cicd.config import GithubCICDBotConfig
2834
from sqlmesh.utils import word_characters_only
@@ -426,9 +432,13 @@ def bot_config(self) -> GithubCICDBotConfig:
426432
return bot_config
427433

428434
@property
429-
def modified_snapshots(self) -> t.Dict[SnapshotId, Snapshot]:
435+
def modified_snapshots(self) -> t.Dict[SnapshotId, t.Union[Snapshot, SnapshotTableInfo]]:
430436
return self.prod_plan_with_gaps.modified_snapshots
431437

438+
@property
439+
def removed_snapshots(self) -> t.Set[SnapshotId]:
440+
return set(self.prod_plan_with_gaps.context_diff.removed_snapshots)
441+
432442
@classmethod
433443
def _append_output(cls, key: str, value: str) -> None:
434444
"""
@@ -442,13 +452,19 @@ def get_plan_summary(self, plan: Plan) -> str:
442452
try:
443453
# Clear out any output that might exist from prior steps
444454
self._console.clear_captured_outputs()
445-
self._console._show_categorized_snapshots(plan, self._context.default_catalog)
446-
catagorized_snapshots = self._console.consume_captured_output()
455+
self._console.show_model_difference_summary(
456+
context_diff=plan.context_diff,
457+
environment_naming_info=plan.environment_naming_info,
458+
default_catalog=self._context.default_catalog,
459+
no_diff=False,
460+
ignored_snapshot_ids=plan.ignored,
461+
)
462+
difference_summary = self._console.consume_captured_output()
447463
self._console._show_missing_dates(plan, self._context.default_catalog)
448464
missing_dates = self._console.consume_captured_output()
449-
if not catagorized_snapshots and not missing_dates:
465+
if not difference_summary and not missing_dates:
450466
return "No changes to apply."
451-
return f"{catagorized_snapshots}\n{missing_dates}"
467+
return f"{difference_summary}\n{missing_dates}"
452468
except PlanError as e:
453469
return f"Plan failed to generate. Check for pending or unresolved changes. Error: {e}"
454470

@@ -759,22 +775,32 @@ def conclusion_handler(
759775
# We don't want to display indirect non-breaking since to users these are effectively no-op changes
760776
if modified_snapshot.is_indirect_non_breaking:
761777
continue
762-
model_name = modified_snapshot.node.name
763-
change_category = (
764-
"Uncategorized"
765-
if not modified_snapshot.change_category
766-
else SNAPSHOT_CHANGE_CATEGORY_STR[modified_snapshot.change_category]
767-
)
768-
intervals = (
769-
modified_snapshot.dev_intervals
770-
if modified_snapshot.is_forward_only
771-
else modified_snapshot.intervals
772-
)
773-
interval_output = (
774-
format_intervals(intervals, modified_snapshot.node.interval_unit)
775-
if intervals
776-
else "N/A"
777-
)
778+
if modified_snapshot.snapshot_id in self.removed_snapshots:
779+
# This will be an FQN since we don't have access to node name from a snapshot table info
780+
# which is what a removed snapshot is
781+
model_name = modified_snapshot.name
782+
change_category = SNAPSHOT_CHANGE_CATEGORY_STR[
783+
SnapshotChangeCategory.BREAKING
784+
]
785+
interval_output = "REMOVED"
786+
else:
787+
assert isinstance(modified_snapshot, Snapshot)
788+
model_name = modified_snapshot.node.name
789+
change_category = (
790+
"Uncategorized"
791+
if not modified_snapshot.change_category
792+
else SNAPSHOT_CHANGE_CATEGORY_STR[modified_snapshot.change_category]
793+
)
794+
intervals = (
795+
modified_snapshot.dev_intervals
796+
if modified_snapshot.is_forward_only
797+
else modified_snapshot.intervals
798+
)
799+
interval_output = (
800+
format_intervals(intervals, modified_snapshot.node.interval_unit)
801+
if intervals
802+
else "N/A"
803+
)
778804
body_rows.append(
779805
[
780806
h("td", model_name),
@@ -865,8 +891,6 @@ def conclusion_handler(
865891
title = conclusion_to_title.get(
866892
conclusion, f"Got an unexpected conclusion: {conclusion.value}"
867893
)
868-
if conclusion.is_success and summary:
869-
summary = "**Preview of Prod Plan**\n" + summary
870894
return conclusion, title, summary
871895

872896
self._update_check_handler(

tests/integrations/github/cicd/test_github_commands.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ def test_run_all_success_with_approvers_approved(
141141
<details>
142142
<summary>Prod Plan Being Applied</summary>
143143
144-
145-
**Models needing backfill (missing dates):**"""
144+
**New environment `prod` will be created from `prod`**"""
146145
)
147146
with open(github_output_file, "r", encoding="utf-8") as f:
148147
output = f.read()
@@ -268,8 +267,7 @@ def test_run_all_success_with_approvers_approved_merge_delete(
268267
<details>
269268
<summary>Prod Plan Being Applied</summary>
270269
271-
272-
**Models needing backfill (missing dates):**"""
270+
**New environment `prod` will be created from `prod`**"""
273271
)
274272
with open(github_output_file, "r", encoding="utf-8") as f:
275273
output = f.read()
@@ -907,8 +905,7 @@ def raise_on_prod_plan(plan: Plan):
907905
<details>
908906
<summary>Prod Plan Being Applied</summary>
909907
910-
911-
**Models needing backfill (missing dates):**"""
908+
**New environment `prod` will be created from `prod`**"""
912909
)
913910

914911
with open(github_output_file, "r", encoding="utf-8") as f:
@@ -1101,8 +1098,7 @@ def test_comment_command_deploy_prod(
11011098
<details>
11021099
<summary>Prod Plan Being Applied</summary>
11031100
1104-
1105-
**Models needing backfill (missing dates):**"""
1101+
**New environment `prod` will be created from `prod`**"""
11061102
)
11071103

11081104
with open(github_output_file, "r", encoding="utf-8") as f:

0 commit comments

Comments
 (0)