Skip to content

Commit c12af30

Browse files
committed
Narrow local-only state skip to format command
In multi-repo projects, lint results depend on models merged from the prod environment in state during Context.load(), so forcing lint to never load state changed lint outcomes when upstream models live in another repo. Per review discussion on PR #5804, lint reverts to loading state (guarded by a new CLI test) and only format remains local-only. The lint case stays open in issue #5182. Signed-off-by: Joe Hartshorn <8881940+j-hartshorn@users.noreply.github.com>
1 parent fadfe36 commit c12af30

3 files changed

Lines changed: 17 additions & 54 deletions

File tree

sqlmesh/cli/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"table_name",
4242
)
4343
SKIP_CONTEXT_COMMANDS = ("init", "ui")
44-
LOCAL_ONLY_COMMANDS = ("format", "lint")
44+
LOCAL_ONLY_COMMANDS = ("format",)
4545

4646

4747
def _sqlmesh_version() -> str:

tests/cli/test_cli.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,14 +2271,25 @@ def test_format_runs_without_state(runner: CliRunner, tmp_path: Path, mocker):
22712271
mock.assert_not_called()
22722272

22732273

2274-
def test_lint_runs_without_state(runner: CliRunner, tmp_path: Path, mocker):
2274+
def test_lint_still_loads_state(runner: CliRunner, tmp_path: Path, mocker):
2275+
"""Guard that `lint` explicitly passes `load_state=True` and still reaches state sync."""
22752276
mock = _setup_local_only_project(tmp_path, mocker)
2276-
result = runner.invoke(cli, ["--paths", str(tmp_path), "lint"])
2277-
assert result.exit_code == 0, f"Lint failed: {result.output}\nException: {result.exception}"
2278-
mock.assert_not_called()
2277+
init_spy = mocker.spy(Context, "__init__")
2278+
2279+
runner.invoke(cli, ["--paths", str(tmp_path), "lint"])
2280+
2281+
assert init_spy.called, "Context was never constructed"
2282+
for call in init_spy.call_args_list:
2283+
assert "load_state" in call.kwargs, (
2284+
"CLI didn't pass load_state= explicitly; missing kwarg defaults to True silently"
2285+
)
2286+
assert call.kwargs["load_state"] is True, (
2287+
f"Context was constructed with load_state={call.kwargs['load_state']} for `lint`"
2288+
)
2289+
assert mock.called, "state-sync was never accessed during `lint`"
22792290

22802291

2281-
@pytest.mark.parametrize("command", ["format", "lint"])
2292+
@pytest.mark.parametrize("command", ["format"])
22822293
def test_local_only_commands_skip_state_multiple_paths(
22832294
runner: CliRunner, tmp_path: Path, mocker, command: str
22842295
):

tests/core/linter/test_builtin.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -232,51 +232,3 @@ def test_no_missing_unit_tests(tmp_path, copy_to_temp_path):
232232
assert len(model_violations) == 0, (
233233
f"Model {model_name} should not have a violation since it has a test"
234234
)
235-
236-
237-
def test_lint_without_state_load(tmp_path, copy_to_temp_path, mocker) -> None:
238-
sushi_paths = copy_to_temp_path("examples/sushi")
239-
sushi_path = sushi_paths[0]
240-
241-
with open(sushi_path / "config.py", "r") as f:
242-
read_file = f.read()
243-
244-
# Set a project name so state-merge code reaches the `self._load_state` guard.
245-
project_anchor = "config = Config(\n gateways="
246-
assert project_anchor in read_file, (
247-
"sushi config.py shape drifted; update project_anchor in test"
248-
)
249-
read_file = read_file.replace(
250-
project_anchor,
251-
'config = Config(\n project="sushi",\n gateways=',
252-
)
253-
254-
# Enable one built-in rule so `lint_models` doesn't take the empty-rule-set path.
255-
before = """ linter=LinterConfig(
256-
enabled=False,
257-
rules=[
258-
"ambiguousorinvalidcolumn",
259-
"invalidselectstarexpansion",
260-
"noselectstar",
261-
"nomissingaudits",
262-
"nomissingowner",
263-
"nomissingexternalmodels",
264-
],
265-
),"""
266-
after = 'linter=LinterConfig(enabled=True, rules=["nomissingexternalmodels"]),'
267-
assert before in read_file, (
268-
"sushi config.py LinterConfig block shape drifted; update `before` in test"
269-
)
270-
read_file = read_file.replace(before, after)
271-
272-
with open(sushi_path / "config.py", "w") as f:
273-
f.write(read_file)
274-
275-
mock = mocker.patch(
276-
"sqlmesh.core.state_sync.db.facade.EngineAdapterStateSync.get_versions",
277-
side_effect=RuntimeError("state should not be accessed"),
278-
)
279-
280-
context = Context(paths=[sushi_path], load_state=False)
281-
context.lint_models(raise_on_error=False)
282-
mock.assert_not_called()

0 commit comments

Comments
 (0)