feat(@deepnote/cli): support linting integrations env yaml file directly - #337
Conversation
Allow `deepnote lint .deepnote.env.yaml` to validate an integrations file without requiring a .deepnote notebook. Validates YAML structure, integration schemas, and env var references, displaying human-readable errors consistent with the existing configuration issues output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe PR extends the Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches⚔️ Resolve merge conflicts
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/cli/src/commands/lint.test.ts`:
- Around line 774-793: The test for createLintAction in lint.test.ts only
asserts integrationsFile.issues and exit behavior and misses verifying the
top-level JSON contract on failures; update the failing-case tests (e.g., the
'outputs JSON with issues for an invalid integrations yaml file' case that calls
action(intFile, { output: 'json' })) to also assert parsed.success is false and
parsed.issueCount.errors > 0 and parsed.issueCount.total > 0 (and add a similar
assertion in one notebook+integrations failure test) so configuration failures
cannot serialize as success: true or zero issue counts; locate and modify the
tests that call createLintAction/action and parse JSON output to include these
additional assertions.
- Around line 1242-1257: The test currently assumes a missing user-specified
integrations file silently yields an empty config; instead, update the CLI's
lint handling so that when the integrationsFile option is provided but the file
does not exist you surface an error and exit with invalid-usage semantics;
locate the lint option parsing / handler (createLintAction and the lint command
logic in packages/cli/src/commands/lint.ts) and change the branch that reads
integrationsFile to validate existence and throw or call the existing error/exit
path (same style as the direct YAML path handling around the existing error
block) rather than falling back to an empty integrations result.
In `@packages/cli/src/commands/lint.ts`:
- Around line 112-120: The returned lint status currently spreads the result of
lintFile() and embeds a hardcoded integrationsFile object from
lintIntegrationsFile() which sets zero issue counts, so invalid integrations
produce inconsistent success/issueCount; update
lintIntegrationsFile(integrationsFilePath, parsedIntegrations) to compute and
return true issue counts (issueCount.total and per-category counts) and a
success boolean based on parsedIntegrations.issues and
parsedIntegrations.integrations parsing results, and then merge those counts
into the top-level lint object returned by the function that builds the final
result (the block returning { path: absolutePath, ...lint, integrationsFile: {
... } }) so that overall issueCount and success reflect both lintFile() and
lintIntegrationsFile() outcomes. Ensure you reference lintFile(),
lintIntegrationsFile(), parsedIntegrations and the returned integrationsFile
fields when making the change.
- Around line 82-103: The lint command currently mutates process.env by calling
dotenv.config(...) and injecting envVars from
getEnvironmentVariablesForIntegrations(parsedIntegrations.integrations, ...)
without restoring originals, causing subsequent lint runs in the same process to
see stale/overwritten values; fix by saving the original environment state (for
each env var you will set and the result of dotenv.config), apply the dotenv and
injected envVars only for the duration of the lint run (the logic around
parseIntegrationsFile / getEnvironmentVariablesForIntegrations and subsequent
checks), and restore process.env to its prior values after the lint completes or
on error (ensure restoration covers both newly added vars and overwritten values
so caller-provided envs are preserved); use the function/variable names
parseIntegrationsFile, getEnvironmentVariablesForIntegrations, dotenv.config,
and the envVars loop to locate where to capture and later restore the original
environment.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d0f233c3-5bbc-433a-9b95-4b488f62d7e7
📒 Files selected for processing (3)
packages/cli/src/cli.tspackages/cli/src/commands/lint.test.tspackages/cli/src/commands/lint.ts
…le errors Address two review findings on the lint-integrations feature: - JSON output now reports `success: false` when the integrations file has validation issues. Previously the `...lint` spread in `lintFile` left `success: true` even though the command exits 1, so `-o json` consumers saw a passing result for a failing run. - An explicitly specified `--integrations-file` that is missing now fails with `ExitCode.InvalidUsage` (mirroring the direct-YAML path) instead of being silently ignored. The implicit default file remains optional. Tests updated accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion ids `getIntegrationEnvVarName` sanitized the id before prepending `SQL_`, yielding `SQL__100ABC`, while the generator (`getSqlEnvVarName` in @deepnote/database-integrations — the name `lintFile` injects into process.env and that generated SQL Python reads) prepends `SQL_` first and produces `SQL_100ABC`. For integration ids starting with a digit the lookup never matched the injected var, so an integration configured in the integrations file was falsely reported as `missing-integration` and lint exited 1 (the shipped examples/3_integrations.deepnote triggers this). Prepend `SQL_` before sanitizing so the checker matches the injected name. Add a unit test for the digit-prefixed case and rewrite the missing-integration test to use the example's real digit-prefixed id (it previously used a non-matching id and passed regardless of the bug). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/cli/src/commands/lint.ts (2)
165-177:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
lintIntegrationsFilereturns zero counts despite errors.
success: !hasErrorsbutissueCountis always zeros. Same fix pattern needed here.Proposed fix
const hasErrors = parsedIntegrations.issues.length > 0 + const configurationErrorCount = parsedIntegrations.issues.length return { path: absolutePath, success: !hasErrors, - issueCount: { errors: 0, warnings: 0, total: 0 }, + issueCount: { + errors: configurationErrorCount, + warnings: 0, + total: configurationErrorCount, + }, issues: [],🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/lint.ts` around lines 165 - 177, The returned object from lintIntegrationsFile currently sets issueCount to zeros and issues to an empty array despite parsedIntegrations containing real issues; update the return to compute issueCount from parsedIntegrations.issues (set errors/warnings/total appropriately—e.g. count by severity and sum for total), set issues to parsedIntegrations.issues, and keep success as !hasErrors; reference lintIntegrationsFile, parsedIntegrations, issueCount, and integrationsFile in your changes.
126-135:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
issueCountdoesn't reflect configuration errors.
successnow incorporatesparsedIntegrations.issues.length === 0, butissueCountis just spread fromlint. Consumers checkingissueCount.errorswill see zero even whensuccessis false due to config issues.Proposed fix
+ const configurationErrorCount = parsedIntegrations.issues.length return { path: absolutePath, ...lint, - success: lint.success && parsedIntegrations.issues.length === 0, + success: lint.success && configurationErrorCount === 0, + issueCount: { + errors: lint.issueCount.errors + configurationErrorCount, + warnings: lint.issueCount.warnings, + total: lint.issueCount.total + configurationErrorCount, + }, integrationsFile: {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/lint.ts` around lines 126 - 135, The returned lint result currently spreads lint and sets success using parsedIntegrations.issues but leaves issueCount unchanged, so issueCount.errors remains zero when config errors exist; update the returned object to compute a combined issueCount by merging lint.issueCount with parsedIntegrations.issues (e.g., increment errors and total counts or otherwise aggregate parsedIntegrations.issues.length into lint.issueCount.errors and total) before returning, referencing the variables absolutePath, lint, parsedIntegrations, success, and integrationsFile so consumers relying on issueCount.errors see config-related errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/cli/src/commands/lint.ts`:
- Around line 165-177: The returned object from lintIntegrationsFile currently
sets issueCount to zeros and issues to an empty array despite parsedIntegrations
containing real issues; update the return to compute issueCount from
parsedIntegrations.issues (set errors/warnings/total appropriately—e.g. count by
severity and sum for total), set issues to parsedIntegrations.issues, and keep
success as !hasErrors; reference lintIntegrationsFile, parsedIntegrations,
issueCount, and integrationsFile in your changes.
- Around line 126-135: The returned lint result currently spreads lint and sets
success using parsedIntegrations.issues but leaves issueCount unchanged, so
issueCount.errors remains zero when config errors exist; update the returned
object to compute a combined issueCount by merging lint.issueCount with
parsedIntegrations.issues (e.g., increment errors and total counts or otherwise
aggregate parsedIntegrations.issues.length into lint.issueCount.errors and
total) before returning, referencing the variables absolutePath, lint,
parsedIntegrations, success, and integrationsFile so consumers relying on
issueCount.errors see config-related errors.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a4c186e0-aca4-41b3-b689-5afcbfd9d539
📒 Files selected for processing (4)
packages/cli/src/commands/lint.test.tspackages/cli/src/commands/lint.tspackages/cli/src/utils/analysis.test.tspackages/cli/src/utils/analysis.ts
…SqlEnvVarName Address PR review feedback: - analysis: getIntegrationEnvVarName now delegates to getSqlEnvVarName from @deepnote/database-integrations (newly exported from the package index) instead of re-deriving the SQL_ env var name, so the missing-integration checker and the env-var injection share a single source of truth. - lint tests: new text-output tests assert the whole output via toMatchInlineSnapshot (color disabled + per-run temp paths normalized); new JSON-output tests assert the whole parsed object via toEqual. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/cli/src/commands/lint.test.ts (2)
934-949:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRestore
process.envin this suite.These tests call
lintFile, andpackages/cli/src/commands/lint.tsinjectsSQL_*vars intoprocess.env.vi.unstubAllEnvs()only revertsvi.stubEnv, so this suite leaks config into later tests and can hide missing-integration failures.🛠 Suggested fix
describe('lint command - integrations file loading', () => { let program: Command let consoleSpy: Mock<typeof console.log> let consoleErrorSpy: Mock<typeof console.error> let exitSpy: Mock<typeof process.exit> let tempDir: string + let originalEnv: NodeJS.ProcessEnv ... beforeEach(() => { + originalEnv = { ...process.env } program = new Command() consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}) consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => { throw new Error('process.exit called') @@ afterEach(() => { consoleSpy.mockRestore() consoleErrorSpy.mockRestore() exitSpy.mockRestore() vi.unstubAllEnvs() + process.env = originalEnv })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/lint.test.ts` around lines 934 - 949, The test suite is leaking environment variables set by lintFile (from packages/cli/src/commands/lint.ts), so capture process.env in beforeEach (e.g., const originalEnv = { ...process.env }) and restore it in afterEach (assign process.env = originalEnv) in addition to calling vi.unstubAllEnvs(); update the existing beforeEach/afterEach that set up program, console spies, exitSpy and call resetOutputConfig() to include the env snapshot/restore to ensure SQL_* vars injected by lintFile are cleared between tests.
661-672: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winWrap the new YAML fixtures with
dedent.These multiline literals are manually aligned. Using
ts-dedenthere matches the repo rule and prevents indentation drift from changing fixture contents.🛠 Suggested fix
+import dedent from 'ts-dedent' ... - await writeFile( - intFile, - `integrations: - - id: my-postgres - name: My PostgreSQL - type: pgsql - metadata: - host: localhost - port: "5432" - database: mydb - user: root - password: secret` - ) + await writeFile( + intFile, + dedent` + integrations: + - id: my-postgres + name: My PostgreSQL + type: pgsql + metadata: + host: localhost + port: '5432' + database: mydb + user: root + password: secret + ` + )As per coding guidelines,
**/*.ts: Usets-dedentfor clean multiline template strings.Also applies to: 808-812, 987-996, 1336-1345
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/cli/src/commands/lint.test.ts` around lines 661 - 672, The multiline YAML fixtures written with writeFile (e.g., the call that writes to intFile in packages/cli/src/commands/lint.test.ts) are raw indented template strings; wrap them with ts-dedent to normalize indentation. Import dedent from "ts-dedent" if not already, and replace the literal template strings passed to writeFile with dedent(`...`) so the fixture content is stable; apply the same change to the other similar fixtures mentioned (lines around 808-812, 987-996, 1336-1345).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/cli/src/commands/lint.test.ts`:
- Line 3: Tests produce OS-dependent snapshots because file paths returned by
join/relative/resolve use backslashes on Windows; update the snapshot helper
used in lint.test.ts (and any assertions around the calls to relative() / join()
/ resolve at the lines referenced) to normalize path separators before
snapshotting by converting platform separators to '/' (e.g., replace all
backslashes with '/'), or use path.posix equivalents so snapshots are stable
across OSes; ensure the normalization runs on the strings produced by relative()
/ join() / resolve prior to creating the snapshot.
---
Outside diff comments:
In `@packages/cli/src/commands/lint.test.ts`:
- Around line 934-949: The test suite is leaking environment variables set by
lintFile (from packages/cli/src/commands/lint.ts), so capture process.env in
beforeEach (e.g., const originalEnv = { ...process.env }) and restore it in
afterEach (assign process.env = originalEnv) in addition to calling
vi.unstubAllEnvs(); update the existing beforeEach/afterEach that set up
program, console spies, exitSpy and call resetOutputConfig() to include the env
snapshot/restore to ensure SQL_* vars injected by lintFile are cleared between
tests.
- Around line 661-672: The multiline YAML fixtures written with writeFile (e.g.,
the call that writes to intFile in packages/cli/src/commands/lint.test.ts) are
raw indented template strings; wrap them with ts-dedent to normalize
indentation. Import dedent from "ts-dedent" if not already, and replace the
literal template strings passed to writeFile with dedent(`...`) so the fixture
content is stable; apply the same change to the other similar fixtures mentioned
(lines around 808-812, 987-996, 1336-1345).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 64f27378-b6a8-4076-91ce-ce1fb5212556
📒 Files selected for processing (3)
packages/cli/src/commands/lint.test.tspackages/cli/src/utils/analysis.tspackages/database-integrations/src/index.ts
Resolve import-line conflicts in packages/cli/src/commands/lint.ts and lint.test.ts by keeping both sides' imports: getEnvironmentVariablesForIntegrations + resolvePythonExecutable, and afterAll/beforeAll + BUILTIN_INTEGRATIONS. All other changes (lint refactor, --python wiring, --integrations-file option, analysis.ts) auto-merged. Verified: typecheck passes, lint.test.ts 55/55 green, biome clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #337 +/- ##
==========================================
+ Coverage 84.35% 84.49% +0.14%
==========================================
Files 153 153
Lines 7996 8083 +87
Branches 2163 2186 +23
==========================================
+ Hits 6745 6830 +85
- Misses 1250 1252 +2
Partials 1 1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… fix lint accounting - Surface env-var generation errors (e.g. invalid-JSON BigQuery/Spanner service_account) as validation issues in both the .deepnote and direct integrations-YAML lint paths, via a shared per-integration helper, so schema-valid-but-ungeneratable integrations no longer lint green. - Count integration/config failures in the returned issueCount on both paths, so JSON no longer reports success:false with issueCount.total:0; use notebook-only errors in the text summary to avoid double-counting. - Restore process.env after each lint run (withRestoredEnv) so runs are idempotent and don't leak .env / injected integration vars. - Refactor tests to vitest env mocking (vi.stubEnv / vi.unstubAllEnvs) and normalize snapshot path separators for Windows portability. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update skills/deepnote/references/cli-analysis.md and SKILL.md to document the optional [path] argument, the --integrations-file option, direct integrations-YAML linting, and integration config-error checks, mirroring cli.ts. Required by the AGENTS.md skill-sync rule when CLI commands/options change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rning sink runtime-core no longer calls console.error directly (satisfies lint/suspicious/noConsole). The agent handler now emits MCP client cleanup failures through an optional onWarning callback on AgentBlockContext, threaded through ExecutionOptions. The CLI wires onWarning to its debug() logger, which writes to stderr and is debug-gated, so warnings honor the output format and never corrupt JSON/TOON stdout — consistent with how other non-fatal cleanup failures are logged in run.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve import conflicts in analysis.ts and lint.test.ts after main relocated integration constants (BUILTIN_INTEGRATIONS, DEFAULT_ENV_FILE) into @deepnote/database-integrations (PR #394). Also update lint.ts to import DEFAULT_ENV_FILE from the package since it was removed from cli/src/constants.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tegrations The merge removed the local ValidationIssue export from validate.ts (main relocated the type into @deepnote/database-integrations). Update lint.ts to import it from the package so typecheck passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Main added the cloud-sql integration type, so the lint integrations-yaml inline snapshots must list 'cloud-sql' among the valid discriminator values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback on the direct integrations-yaml lint path: - Emit empty integrations/inputs stubs so `lint <file>.yaml -o json` has the same shape as the .deepnote path (machine consumers needn't special-case it). - Warn on stderr when --integrations-file is passed alongside a direct YAML path, instead of silently ignoring the flag. - Key integration env-var generation-error paths by integration id rather than a positional index, which could drift from the file position when earlier entries failed schema validation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Coderabbit is asleep, only minor changes since approval so admin merging. |
Allow
deepnote lint .deepnote.env.yamlto validate an integrationsfile without requiring a .deepnote notebook. Validates YAML structure,
integration schemas, and env var references, displaying human-readable
errors consistent with the existing configuration issues output.
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com
Summary by CodeRabbit
Release Notes
New Features
Improvements