Skip to content

Commit 55f6387

Browse files
committed
feat(vscode): diagnostic for bad config.yaml error
- return error - filter diagnostics so it doesn't show in wrong place
1 parent 466968e commit 55f6387

File tree

5 files changed

+82
-8
lines changed

5 files changed

+82
-8
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqlmesh/core/config/loader.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ def load_config_from_paths(
9191
"SQLMesh project config could not be found. Point the cli to the project path with `sqlmesh -p`. If you haven't set up the SQLMesh project, run `sqlmesh init`."
9292
)
9393

94+
yaml_config_path: t.Optional[Path] = None
9495
for path in [*project_paths, *personal_paths]:
9596
if not path.exists():
9697
continue
@@ -107,8 +108,9 @@ def load_config_from_paths(
107108
if extension in ("yml", "yaml"):
108109
if config_name != "config" and not python_config:
109110
raise ConfigError(
110-
"YAML configs do not support multiple configs. Use Python instead."
111+
"YAML configs do not support multiple configs. Use Python instead.",
111112
)
113+
yaml_config_path = path.resolve()
112114
non_python_configs.append(load_config_from_yaml(path))
113115
elif extension == "py":
114116
try:
@@ -149,7 +151,8 @@ def load_config_from_paths(
149151
except ValidationError as e:
150152
raise ConfigError(
151153
validation_error_message(e, "Invalid project config:")
152-
+ "\n\nVerify your config.yaml and environment variables."
154+
+ "\n\nVerify your config.yaml and environment variables.",
155+
location=yaml_config_path,
153156
)
154157

155158
no_dialect_err_msg = "Default model SQL dialect is a required configuration parameter. Set it in the `model_defaults` `dialect` key in your config file."

sqlmesh/lsp/main.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ def completion(
767767
get_sql_completions(None, URI(params.text_document.uri))
768768
return None
769769

770-
def _get_diagnostics_for_uri(self, uri: URI) -> t.Tuple[t.List[types.Diagnostic], int]:
770+
def _get_diagnostics_for_uri(self, uri: URI) -> t.Tuple[t.List[types.Diagnostic], str]:
771771
"""Get diagnostics for a specific URI, returning (diagnostics, result_id).
772772
773773
Since we no longer track version numbers, we always return 0 as the result_id.
@@ -776,12 +776,16 @@ def _get_diagnostics_for_uri(self, uri: URI) -> t.Tuple[t.List[types.Diagnostic]
776776
try:
777777
context = self._context_get_or_load(uri)
778778
diagnostics = context.lint_model(uri)
779-
return LSPContext.diagnostics_to_lsp_diagnostics(diagnostics), 0
779+
return LSPContext.diagnostics_to_lsp_diagnostics(
780+
diagnostics
781+
), self.context_state.version_id
780782
except ConfigError as config_error:
781783
diagnostic, error = context_error_to_diagnostic(config_error, uri_filter=uri)
782784
if diagnostic:
783-
return [diagnostic[1]], 0
784-
return [], 0
785+
location, diag = diagnostic
786+
if location == uri.value:
787+
return [diag], self.context_state.version_id
788+
return [], self.context_state.version_id
785789

786790
def _context_get_or_load(self, document_uri: t.Optional[URI] = None) -> LSPContext:
787791
state = self.context_state

vscode/extension/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@
155155
"ts-loader": "^9.5.2",
156156
"typescript": "^5.8.3",
157157
"typescript-eslint": "^8.35.1",
158-
"vitest": "^3.2.4"
158+
"vitest": "^3.2.4",
159+
"yaml": "^2.8.0"
159160
}
160161
}

vscode/extension/tests/broken_project.spec.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ import { test, expect } from './fixtures'
22
import fs from 'fs-extra'
33
import os from 'os'
44
import path from 'path'
5-
import { openLineageView, saveFile, SUSHI_SOURCE_PATH } from './utils'
5+
import {
6+
openLineageView,
7+
runCommand,
8+
saveFile,
9+
SUSHI_SOURCE_PATH,
10+
} from './utils'
611
import { createPythonInterpreterSettingsSpecifier } from './utils_code_server'
12+
import { execAsync } from '../src/utilities/exec'
13+
import yaml from 'yaml'
714

815
test('bad project, double model', async ({ page, sharedCodeServer }) => {
916
const tempDir = await fs.mkdtemp(
@@ -253,3 +260,59 @@ test('bad project, double model, check lineage', async ({
253260

254261
await page.waitForTimeout(500)
255262
})
263+
264+
const setup = async (tempDir: string) => {
265+
// Run the sqlmesh CLI from the root of the repo using the local path
266+
const sqlmeshCliPath = path.resolve(__dirname, '../../../.venv/bin/sqlmesh')
267+
const result = await execAsync(sqlmeshCliPath, ['init', 'duckdb'], {
268+
cwd: tempDir,
269+
})
270+
expect(result.exitCode).toBe(0)
271+
}
272+
273+
test.describe('Bad config.py/config.yaml file issues', () => {
274+
test('sqlmesh init, then corrupted config.yaml, bad parameters', async ({
275+
page,
276+
sharedCodeServer,
277+
}) => {
278+
const tempDir = await fs.mkdtemp(
279+
path.join(os.tmpdir(), 'vscode-test-tcloud-'),
280+
)
281+
await setup(tempDir)
282+
await createPythonInterpreterSettingsSpecifier(tempDir)
283+
284+
const configYamlPath = path.join(tempDir, 'config.yaml')
285+
// Write an invalid YAML to config.yaml
286+
const config = {
287+
gateway: 'test',
288+
}
289+
// Write config to the yaml file
290+
await fs.writeFile(configYamlPath, yaml.stringify(config))
291+
292+
await page.goto(
293+
`http://127.0.0.1:${sharedCodeServer.codeServerPort}/?folder=${tempDir}`,
294+
)
295+
await page.waitForLoadState('networkidle')
296+
297+
// Open full_model.sql model
298+
await page
299+
.getByRole('treeitem', { name: 'models', exact: true })
300+
.locator('a')
301+
.click()
302+
await page
303+
.getByRole('treeitem', { name: 'full_model.sql', exact: true })
304+
.locator('a')
305+
.click()
306+
307+
// Wait for the error to appear
308+
await page.waitForSelector('text=Error creating context')
309+
310+
// Open the problems view
311+
await runCommand(page, 'View: Focus Problems')
312+
313+
// Asser that the error is present in the problems view
314+
await page
315+
.getByText('Invalid project config:', { exact: true })
316+
.isVisible({ timeout: 5_000 })
317+
})
318+
})

0 commit comments

Comments
 (0)