Skip to content

Commit 7e983f8

Browse files
authored
Fix(cicd_bot): Don't truncate backfill model list (#4915)
1 parent e3daa8f commit 7e983f8

File tree

5 files changed

+84
-33
lines changed

5 files changed

+84
-33
lines changed

sqlmesh/core/console.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3107,7 +3107,9 @@ class MarkdownConsole(CaptureTerminalConsole):
31073107
AUDIT_PADDING = 7
31083108

31093109
def __init__(self, **kwargs: t.Any) -> None:
3110-
super().__init__(**{**kwargs, "console": RichConsole(no_color=True)})
3110+
super().__init__(
3111+
**{**kwargs, "console": RichConsole(no_color=True, width=kwargs.pop("width", None))}
3112+
)
31113113

31123114
def show_environment_difference_summary(
31133115
self,

sqlmesh/integrations/github/cicd/command.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
@click.pass_context
2929
def github(ctx: click.Context, token: str) -> None:
3030
"""Github Action CI/CD Bot. See https://sqlmesh.readthedocs.io/en/stable/integrations/github/ for details"""
31-
set_console(MarkdownConsole())
31+
# set a larger width because if none is specified, it auto-detects 80 characters when running in GitHub Actions
32+
# which can result in surprise newlines when outputting dates to backfill
33+
set_console(MarkdownConsole(width=1000))
3234
ctx.obj["github"] = GithubController(
3335
paths=ctx.obj["paths"],
3436
token=token,

sqlmesh/integrations/github/cicd/controller.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ def _append_output(cls, key: str, value: str) -> None:
485485
print(f"{key}={value}", file=fh)
486486

487487
def get_plan_summary(self, plan: Plan) -> str:
488+
# use Verbosity.VERY_VERBOSE to prevent the list of models from being truncated
489+
# this is particularly important for the "Models needing backfill" list because
490+
# there is no easy way to tell this otherwise
491+
orig_verbosity = self._console.verbosity
492+
self._console.verbosity = Verbosity.VERY_VERBOSE
493+
488494
try:
489495
# Clear out any output that might exist from prior steps
490496
self._console.clear_captured_outputs()
@@ -517,7 +523,10 @@ def get_plan_summary(self, plan: Plan) -> str:
517523

518524
return f"{difference_summary}\n{missing_dates}{plan_flags_section}"
519525
except PlanError as e:
526+
logger.exception("Plan failed to generate")
520527
return f"Plan failed to generate. Check for pending or unresolved changes. Error: {e}"
528+
finally:
529+
self._console.verbosity = orig_verbosity
521530

522531
def run_tests(self) -> t.Tuple[ModelTextTestResult, str]:
523532
"""

tests/integrations/github/cicd/test_github_controller.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# type: ignore
2+
import typing as t
23
import os
34
import pathlib
45
from unittest import mock
@@ -17,6 +18,7 @@
1718
BotCommand,
1819
MergeStateStatus,
1920
)
21+
from sqlmesh.integrations.github.cicd.controller import GithubController
2022
from sqlmesh.integrations.github.cicd.command import _update_pr_environment
2123
from sqlmesh.utils.date import to_datetime, now
2224
from tests.integrations.github.cicd.conftest import MockIssueComment
@@ -591,3 +593,39 @@ def test_uncategorized(
591593
assert "The following models could not be categorized automatically" in summary
592594
assert '- "b"' in summary
593595
assert "Run `sqlmesh plan hello_world_2` locally to apply these changes" in summary
596+
597+
598+
def test_get_plan_summary_doesnt_truncate_backfill_list(
599+
github_client, make_controller: t.Callable[..., GithubController]
600+
):
601+
controller = make_controller(
602+
"tests/fixtures/github/pull_request_synchronized.json",
603+
github_client,
604+
mock_out_context=False,
605+
)
606+
607+
summary = controller.get_plan_summary(controller.prod_plan)
608+
609+
assert "more ...." not in summary
610+
611+
assert (
612+
"""**Models needing backfill:**
613+
* `memory.raw.demographics`: [full refresh]
614+
* `memory.sushi.active_customers`: [full refresh]
615+
* `memory.sushi.count_customers_active`: [full refresh]
616+
* `memory.sushi.count_customers_inactive`: [full refresh]
617+
* `memory.sushi.customer_revenue_by_day`: [2025-06-30 - 2025-07-06]
618+
* `memory.sushi.customer_revenue_lifetime`: [2025-06-30 - 2025-07-06]
619+
* `memory.sushi.customers`: [full refresh]
620+
* `memory.sushi.items`: [2025-06-30 - 2025-07-06]
621+
* `memory.sushi.latest_order`: [full refresh]
622+
* `memory.sushi.marketing`: [2025-06-30 - 2025-07-06]
623+
* `memory.sushi.order_items`: [2025-06-30 - 2025-07-06]
624+
* `memory.sushi.orders`: [2025-06-30 - 2025-07-06]
625+
* `memory.sushi.raw_marketing`: [full refresh]
626+
* `memory.sushi.top_waiters`: [recreate view]
627+
* `memory.sushi.waiter_as_customer_by_day`: [2025-06-30 - 2025-07-06]
628+
* `memory.sushi.waiter_names`: [full refresh]
629+
* `memory.sushi.waiter_revenue_by_day`: [2025-06-30 - 2025-07-06]"""
630+
in summary
631+
)

tests/integrations/github/cicd/test_integration.py

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ def test_merge_pr_has_non_breaking_change(
298298
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
299299

300300
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
301-
* `sushi.waiter_revenue_by_day` (Non-breaking)
301+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
302302
303303
```diff
304304
---
@@ -318,10 +318,10 @@ def test_merge_pr_has_non_breaking_change(
318318
ON o.id = oi.order_id AND o.event_date = oi.event_date
319319
```
320320
Indirectly Modified Children:
321-
- `sushi.top_waiters` (Indirect Non-breaking)
321+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
322322
"""
323323
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
324-
- `sushi.top_waiters` (Indirect Non-breaking)
324+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
325325
"""
326326

327327
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
@@ -509,7 +509,7 @@ def test_merge_pr_has_non_breaking_change_diff_start(
509509
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
510510

511511
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
512-
* `sushi.waiter_revenue_by_day` (Non-breaking)
512+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
513513
514514
```diff
515515
---
@@ -529,10 +529,10 @@ def test_merge_pr_has_non_breaking_change_diff_start(
529529
ON o.id = oi.order_id AND o.event_date = oi.event_date
530530
```
531531
Indirectly Modified Children:
532-
- `sushi.top_waiters` (Indirect Non-breaking)
532+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
533533
"""
534534
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
535-
- `sushi.top_waiters` (Indirect Non-breaking)
535+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
536536
"""
537537

538538
prod_plan_preview_summary = prod_plan_preview_checks_runs[2]["output"]["summary"]
@@ -1032,7 +1032,7 @@ def test_no_merge_since_no_deploy_signal(
10321032
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
10331033

10341034
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
1035-
* `sushi.waiter_revenue_by_day` (Non-breaking)
1035+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
10361036
10371037
```diff
10381038
---
@@ -1052,10 +1052,10 @@ def test_no_merge_since_no_deploy_signal(
10521052
ON o.id = oi.order_id AND o.event_date = oi.event_date
10531053
```
10541054
Indirectly Modified Children:
1055-
- `sushi.top_waiters` (Indirect Non-breaking)"""
1055+
- `memory.sushi.top_waiters` (Indirect Non-breaking)"""
10561056

10571057
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
1058-
- `sushi.top_waiters` (Indirect Non-breaking)
1058+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
10591059
"""
10601060

10611061
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
@@ -1232,7 +1232,7 @@ def test_no_merge_since_no_deploy_signal_no_approvers_defined(
12321232
assert GithubCheckStatus(prod_plan_preview_checks_runs[2]["status"]).is_completed
12331233
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
12341234
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
1235-
* `sushi.waiter_revenue_by_day` (Non-breaking)
1235+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
12361236
12371237
```diff
12381238
---
@@ -1252,10 +1252,10 @@ def test_no_merge_since_no_deploy_signal_no_approvers_defined(
12521252
ON o.id = oi.order_id AND o.event_date = oi.event_date
12531253
```
12541254
Indirectly Modified Children:
1255-
- `sushi.top_waiters` (Indirect Non-breaking)
1255+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
12561256
"""
12571257
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
1258-
- `sushi.top_waiters` (Indirect Non-breaking)
1258+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
12591259
"""
12601260
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
12611261
prod_plan_preview_summary = prod_plan_preview_checks_runs[2]["output"]["summary"]
@@ -1414,7 +1414,7 @@ def test_deploy_comment_pre_categorized(
14141414
assert GithubCheckStatus(prod_plan_preview_checks_runs[2]["status"]).is_completed
14151415
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
14161416
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
1417-
* `sushi.waiter_revenue_by_day` (Non-breaking)
1417+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
14181418
14191419
```diff
14201420
---
@@ -1434,10 +1434,10 @@ def test_deploy_comment_pre_categorized(
14341434
ON o.id = oi.order_id AND o.event_date = oi.event_date
14351435
```
14361436
Indirectly Modified Children:
1437-
- `sushi.top_waiters` (Indirect Non-breaking)
1437+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
14381438
"""
14391439
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
1440-
- `sushi.top_waiters` (Indirect Non-breaking)
1440+
- `memory.sushi.top_waiters` (Indirect Non-breaking)
14411441
"""
14421442
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
14431443
prod_plan_preview_summary = prod_plan_preview_checks_runs[2]["output"]["summary"]
@@ -1781,7 +1781,7 @@ def test_overlapping_changes_models(
17811781
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
17821782

17831783
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
1784-
* `sushi.customers` (Non-breaking)
1784+
* `memory.sushi.customers` (Non-breaking)
17851785
17861786
```diff
17871787
---
@@ -1801,23 +1801,23 @@ def test_overlapping_changes_models(
18011801
WITH current_marketing AS (
18021802
```
18031803
Indirectly Modified Children:
1804-
- `sushi.active_customers` (Indirect Non-breaking)
1805-
- `sushi.count_customers_active` (Indirect Non-breaking)
1806-
- `sushi.count_customers_inactive` (Indirect Non-breaking)
1807-
- `sushi.waiter_as_customer_by_day` (Indirect Breaking)
1804+
- `memory.sushi.active_customers` (Indirect Non-breaking)
1805+
- `memory.sushi.count_customers_active` (Indirect Non-breaking)
1806+
- `memory.sushi.count_customers_inactive` (Indirect Non-breaking)
1807+
- `memory.sushi.waiter_as_customer_by_day` (Indirect Breaking)
18081808
18091809
1810-
* `sushi.waiter_names` (Breaking)
1810+
* `memory.sushi.waiter_names` (Breaking)
18111811
18121812
18131813
Indirectly Modified Children:
1814-
- `sushi.waiter_as_customer_by_day` (Indirect Breaking)"""
1814+
- `memory.sushi.waiter_as_customer_by_day` (Indirect Breaking)"""
18151815

18161816
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
1817-
- `sushi.active_customers` (Indirect Non-breaking)
1818-
- `sushi.count_customers_active` (Indirect Non-breaking)
1819-
- `sushi.count_customers_inactive` (Indirect Non-breaking)
1820-
- `sushi.waiter_as_customer_by_day` (Indirect Breaking)"""
1817+
- `memory.sushi.active_customers` (Indirect Non-breaking)
1818+
- `memory.sushi.count_customers_active` (Indirect Non-breaking)
1819+
- `memory.sushi.count_customers_inactive` (Indirect Non-breaking)
1820+
- `memory.sushi.waiter_as_customer_by_day` (Indirect Breaking)"""
18211821

18221822
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
18231823
prod_plan_preview_summary = prod_plan_preview_checks_runs[2]["output"]["summary"]
@@ -1993,7 +1993,7 @@ def test_pr_add_model(
19931993
)
19941994

19951995
expected_prod_plan_summary = """**Added Models:**
1996-
- `sushi.cicd_test_model` (Breaking)"""
1996+
- `memory.sushi.cicd_test_model` (Breaking)"""
19971997

19981998
assert "SQLMesh - Prod Plan Preview" in controller._check_run_mapping
19991999
prod_plan_preview_checks_runs = controller._check_run_mapping[
@@ -2144,7 +2144,7 @@ def test_pr_delete_model(
21442144
)
21452145

21462146
expected_prod_plan_summary = """**Removed Models:**
2147-
- `sushi.top_waiters` (Breaking)"""
2147+
- `memory.sushi.top_waiters` (Breaking)"""
21482148

21492149
assert "SQLMesh - Prod Plan Preview" in controller._check_run_mapping
21502150
prod_plan_preview_checks_runs = controller._check_run_mapping[
@@ -2330,7 +2330,7 @@ def test_has_required_approval_but_not_base_branch(
23302330
assert GithubCheckStatus(prod_plan_preview_checks_runs[2]["status"]).is_completed
23312331
assert GithubCheckConclusion(prod_plan_preview_checks_runs[2]["conclusion"]).is_success
23322332
expected_prod_plan_directly_modified_summary = """**Directly Modified:**
2333-
* `sushi.waiter_revenue_by_day` (Non-breaking)
2333+
* `memory.sushi.waiter_revenue_by_day` (Non-breaking)
23342334
23352335
```diff
23362336
---
@@ -2350,10 +2350,10 @@ def test_has_required_approval_but_not_base_branch(
23502350
ON o.id = oi.order_id AND o.event_date = oi.event_date
23512351
```
23522352
Indirectly Modified Children:
2353-
- `sushi.top_waiters` (Indirect Non-breaking)"""
2353+
- `memory.sushi.top_waiters` (Indirect Non-breaking)"""
23542354

23552355
expected_prod_plan_indirectly_modified_summary = """**Indirectly Modified:**
2356-
- `sushi.top_waiters` (Indirect Non-breaking)"""
2356+
- `memory.sushi.top_waiters` (Indirect Non-breaking)"""
23572357

23582358
assert prod_plan_preview_checks_runs[2]["output"]["title"] == "Prod Plan Preview"
23592359
prod_plan_preview_summary = prod_plan_preview_checks_runs[2]["output"]["summary"]

0 commit comments

Comments
 (0)