Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/guides/linter.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ Error: Linter detected errors in the code. Please fix them before proceeding.

Use `sqlmesh lint --help` for more information.

You can pass `--local` to run lint without loading state from the configured state connection:

``` bash
$ sqlmesh lint --local
```

This can make linting faster in repositories where all referenced models are loaded from local files. In multi-repository setups, or when linting only a subset of projects, `--local` may cause additional linting errors because SQLMesh will not resolve references or schemas from models that exist only in remote state.


## Applying linting rules

Expand Down Expand Up @@ -258,4 +266,4 @@ You may specify that a rule's violation should not error and only log a warning
)
```

SQLMesh will raise an error if the same rule is included in more than one of the `rules`, `warn_rules`, and `ignored_rules` keys since they should be mutually exclusive.
SQLMesh will raise an error if the same rule is included in more than one of the `rules`, `warn_rules`, and `ignored_rules` keys since they should be mutually exclusive.
5 changes: 4 additions & 1 deletion docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@ Usage: sqlmesh lint [OPTIONS]
Options:
--model TEXT A model to lint. Multiple models can be linted. If no models are specified, every model will be linted.
--local Lint using only locally loaded project files without loading state. In multi-repository setups, or when
linting only a subset of projects, this may cause additional linting errors because SQLMesh will not resolve
references or schemas from models that exist only in remote state.
--help Show this message and exit.
```
```
8 changes: 8 additions & 0 deletions sqlmesh/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ def cli(
load = True
# Local-only gating must hold for any number of --paths, so it stays outside the block below.
load_state = ctx.invoked_subcommand not in LOCAL_ONLY_COMMANDS
if ctx.invoked_subcommand == "lint" and "--local" in sys.argv:
load_state = False

if len(paths) == 1:
path = os.path.abspath(paths[0])
Expand Down Expand Up @@ -1194,6 +1196,12 @@ def environments(obj: Context) -> None:
multiple=True,
help="A model to lint. Multiple models can be linted. If no models are specified, every model will be linted.",
)
@click.option(
"--local",
is_flag=True,
expose_value=False,
help="Lint using only locally loaded project files without loading state.",
)
@click.pass_obj
@error_handler
@cli_analytics
Expand Down
19 changes: 19 additions & 0 deletions tests/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,25 @@ def test_lint_still_loads_state(runner: CliRunner, tmp_path: Path, mocker):
assert mock.called, "state-sync was never accessed during `lint`"


def test_lint_local_runs_without_state(runner: CliRunner, tmp_path: Path, mocker, monkeypatch):
mock = _setup_local_only_project(tmp_path, mocker)
init_spy = mocker.spy(Context, "__init__")
monkeypatch.setattr("sys.argv", ["sqlmesh", "--paths", str(tmp_path), "lint", "--local"])

result = runner.invoke(cli, ["--paths", str(tmp_path), "lint", "--local"])

assert result.exit_code == 0, f"Lint failed: {result.output}\nException: {result.exception}"
assert init_spy.called, "Context was never constructed"
for call in init_spy.call_args_list:
assert "load_state" in call.kwargs, (
"CLI didn't pass load_state= explicitly; missing kwarg defaults to True silently"
)
assert call.kwargs["load_state"] is False, (
f"Context was constructed with load_state={call.kwargs['load_state']} for `lint --local`"
)
mock.assert_not_called()


@pytest.mark.parametrize("command", ["format"])
def test_local_only_commands_skip_state_multiple_paths(
runner: CliRunner, tmp_path: Path, mocker, command: str
Expand Down