Skip to content

Commit b3bd132

Browse files
authored
Feat(cicd_bot): Document and enable the min_intervals plan option (#4901)
1 parent 7e983f8 commit b3bd132

File tree

6 files changed

+79
-0
lines changed

6 files changed

+79
-0
lines changed

docs/concepts/plans.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,63 @@ Models needing backfill (missing dates):
246246
Enter the backfill end date (eg. '1 month ago', '2020-01-01') or blank to backfill up until '2024-09-27 00:00:00':
247247
```
248248
249+
#### Minimum intervals
250+
251+
When you run a plan with a fixed `--start` or `--end` date, you create a virtual data environment with a limited subset of data. However, if the time range specified is less than the size of an interval on one of your models, that model will be skipped by default.
252+
253+
For example, if you have a model like so:
254+
255+
```sql
256+
MODEL(
257+
name sqlmesh_example.monthly_model,
258+
kind INCREMENTAL_BY_TIME_RANGE (
259+
time_column month
260+
),
261+
cron '@monthly'
262+
);
263+
264+
SELECT SUM(a) AS sum_a, MONTH(day) AS month
265+
FROM sqlmesh_example.upstream_model
266+
WHERE day BETWEEN @start_ds AND @end_ds
267+
```
268+
269+
make a change to it and run the following:
270+
271+
```bash linenums="1" hl_lines="8"
272+
$ sqlmesh plan dev --start '1 day ago'
273+
274+
Models:
275+
└── Added:
276+
└── sqlmesh_example__dev.monthly_model
277+
Apply - Virtual Update [y/n]: y
278+
279+
SKIP: No model batches to execute
280+
```
281+
282+
No data will be backfilled because `1 day ago` does not contain a complete month. However, you can use the `--min-intervals` option to override this behaviour like so:
283+
284+
```bash linenums="1" hl_lines="11"
285+
$ sqlmesh plan dev --start '1 day ago' --min-intervals 1
286+
287+
Models:
288+
└── Added:
289+
└── sqlmesh_example__dev.monthly_model
290+
Apply - Virtual Update [y/n]: y
291+
292+
[1/1] sqlmesh_example__dev.monthly_model [insert 2025-06-01 - 2025-06-30] 0.08s
293+
Executing model batches ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 1/1 • 0:00:00
294+
295+
✔ Model batches executed
296+
```
297+
298+
This will ensure that regardless of the plan `--start` date, all added or modified models will have at least `--min-intervals` intervals considered for backfill.
299+
300+
!!! info
301+
302+
If you are running plans manually you can just adjust the `--start` date to be wide enough to cover the models in question.
303+
304+
The `--min-intervals` option is primarily intended for [automation scenarios](../integrations/github.md) where the plan is always run with a default relative start date and you always want (for example) "2 weeks worth of data" in the target environment.
305+
249306
### Data preview for forward-only changes
250307
As mentioned earlier, the data output produced by [forward-only changes](#forward-only-change) in a development environment can only be used for preview and will not be reused in production.
251308

docs/integrations/github.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ Below is an example of how to define the default config for the bot in either YA
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 |
296296
| `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 |
297+
| `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 |
297298
| `skip_pr_backfill` | Indicates if the bot should skip backfilling models in the PR environment. Default: `True` | bool | N |
298299
| `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 |
299300
| `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 |

sqlmesh/integrations/github/cicd/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class GithubCICDBotConfig(BaseConfig):
2828
pr_include_unmodified: t.Optional[bool] = None
2929
run_on_deploy_to_prod: bool = False
3030
pr_environment_name: t.Optional[str] = None
31+
pr_min_intervals: t.Optional[int] = None
3132
prod_branch_names_: t.Optional[str] = Field(default=None, alias="prod_branch_name")
3233

3334
@model_validator(mode="before")

sqlmesh/integrations/github/cicd/controller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ def pr_plan(self) -> Plan:
402402
skip_linter=True,
403403
categorizer_config=self.bot_config.auto_categorize_changes,
404404
start=self.bot_config.default_pr_start,
405+
min_intervals=self.bot_config.pr_min_intervals,
405406
skip_backfill=self.bot_config.skip_pr_backfill,
406407
include_unmodified=self.bot_config.pr_include_unmodified,
407408
)

tests/integrations/github/cicd/test_config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def test_load_yaml_config_default(tmp_path):
4141
assert config.cicd_bot.pr_include_unmodified is None
4242
assert config.cicd_bot.pr_environment_name is None
4343
assert config.cicd_bot.prod_branch_names == ["main", "master"]
44+
assert not config.cicd_bot.pr_min_intervals
4445

4546

4647
def test_load_yaml_config(tmp_path):
@@ -64,6 +65,7 @@ def test_load_yaml_config(tmp_path):
6465
pr_include_unmodified: true
6566
pr_environment_name: "MyOverride"
6667
prod_branch_name: testing
68+
pr_min_intervals: 1
6769
model_defaults:
6870
dialect: duckdb
6971
""",
@@ -88,6 +90,7 @@ def test_load_yaml_config(tmp_path):
8890
assert config.cicd_bot.pr_include_unmodified
8991
assert config.cicd_bot.pr_environment_name == "MyOverride"
9092
assert config.cicd_bot.prod_branch_names == ["testing"]
93+
assert config.cicd_bot.pr_min_intervals == 1
9194

9295

9396
def test_load_python_config_defaults(tmp_path):
@@ -119,6 +122,7 @@ def test_load_python_config_defaults(tmp_path):
119122
assert config.cicd_bot.pr_include_unmodified is None
120123
assert config.cicd_bot.pr_environment_name is None
121124
assert config.cicd_bot.prod_branch_names == ["main", "master"]
125+
assert not config.cicd_bot.pr_min_intervals
122126

123127

124128
def test_load_python_config(tmp_path):
@@ -141,6 +145,7 @@ def test_load_python_config(tmp_path):
141145
seed=AutoCategorizationMode.FULL,
142146
),
143147
default_pr_start="1 week ago",
148+
pr_min_intervals=1,
144149
enable_deploy_command=True,
145150
skip_pr_backfill=False,
146151
pr_include_unmodified=True,
@@ -172,6 +177,7 @@ def test_load_python_config(tmp_path):
172177
assert config.cicd_bot.pr_include_unmodified
173178
assert config.cicd_bot.pr_environment_name == "MyOverride"
174179
assert config.cicd_bot.prod_branch_names == ["testing"]
180+
assert config.cicd_bot.pr_min_intervals == 1
175181

176182

177183
def test_validation(tmp_path):

tests/integrations/github/cicd/test_github_controller.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from sqlmesh.core.dialect import parse_one
1414
from sqlmesh.core.model import SqlModel
1515
from sqlmesh.core.user import User, UserRole
16+
from sqlmesh.core.plan.definition import Plan
1617
from sqlmesh.integrations.github.cicd.config import GithubCICDBotConfig, MergeMethod
1718
from sqlmesh.integrations.github.cicd.controller import (
1819
BotCommand,
@@ -253,6 +254,18 @@ def test_pr_plan_auto_categorization(github_client, make_controller):
253254
assert controller._context._run_plan_tests.call_args == call(skip_tests=True)
254255
assert controller._pr_plan_builder._categorizer_config == custom_categorizer_config
255256
assert controller.pr_plan.start == default_start_absolute
257+
assert not controller.pr_plan.start_override_per_model
258+
259+
260+
def test_pr_plan_min_intervals(github_client, make_controller):
261+
controller = make_controller(
262+
"tests/fixtures/github/pull_request_synchronized.json",
263+
github_client,
264+
bot_config=GithubCICDBotConfig(default_pr_start="1 day ago", pr_min_intervals=1),
265+
)
266+
assert controller.pr_plan.environment.name == "hello_world_2"
267+
assert isinstance(controller.pr_plan, Plan)
268+
assert controller.pr_plan.start_override_per_model
256269

257270

258271
def test_prod_plan(github_client, make_controller):

0 commit comments

Comments
 (0)