@ddt-tools/cli 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cli.js +49 -11
- package/dist/cli.js.map +1 -1
- package/dist/{compare-P7JOV76O.js → compare-IOEATL6G.js} +69 -13
- package/dist/compare-IOEATL6G.js.map +1 -0
- package/dist/{errorReporting-3LPE2IJY.js → errorReporting-LX6WT4JH.js} +2 -2
- package/dist/{errorReporting-3LPE2IJY.js.map → errorReporting-LX6WT4JH.js.map} +1 -1
- package/dist/import-EGOVKTLX.js +29 -0
- package/dist/import-EGOVKTLX.js.map +1 -0
- package/dist/{import-2RNYDL4E.js → import-script-R5RXPDH6.js} +5 -5
- package/dist/import-script-R5RXPDH6.js.map +1 -0
- package/dist/{mcp-F7FND5X7.js → mcp-6ZXOAF7S.js} +2 -2
- package/dist/{mcp-F7FND5X7.js.map → mcp-6ZXOAF7S.js.map} +1 -1
- package/dist/{publish-AYCRMCE2.js → publish-HLP3XHM5.js} +35 -8
- package/dist/publish-HLP3XHM5.js.map +1 -0
- package/package.json +13 -13
- package/dist/compare-P7JOV76O.js.map +0 -1
- package/dist/import-2RNYDL4E.js.map +0 -1
- package/dist/publish-AYCRMCE2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport {\n CompareEngine,\n DatabricksExecutor,\n LiveSource,\n PacSource,\n ProjectSource,\n ScriptGenerator,\n ai,\n aiPreflight,\n approval,\n buildDeployManifest,\n buildExecutionPlan,\n colorizeMigrationScript,\n compileSlice,\n createConnection,\n defaultSafePlan,\n executePlan,\n getProfile,\n lintSql,\n loadProject,\n mergeDeployOptions,\n pac as pacNs,\n pacFreshness,\n protectedObjects,\n queryExecution,\n recordDeployChangelog,\n renderHtmlReport,\n resolveProfile,\n safety,\n testFramework,\n validatePlan,\n type CompareSource,\n type StepResult,\n} from '@ddt-tools/core';\nimport { addMappingFlags, buildMappingFromOptions, describeMapping } from '../util/mapping.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\n\n/**\n * `ddt publish` — produce a migration script that drives a target toward a\n * source's desired state. `--dry-run` (default) generates the script and\n * prints the safety assessment + default-safe plan summary.\n * `--apply` executes the plan against the named connection profile via\n * `DatabricksExecutor` + `executePlan`, with automatic rollback on\n * partial failure.\n */\nexport function publishCommand(): Command {\n const cmd = new Command('publish');\n cmd\n .description(\n 'Generate (and optionally apply) a migration script from source → target. ' +\n 'Shared form with `sdt publish`: `--source <desired> --connection <live-target>` ' +\n 'compares the desired state against the live workspace. Or pass `--target <offline>` ' +\n 'to diff against a `.ddtproj` / `.ddtpac` instead.',\n )\n .option(\n '--source <path>',\n 'Source: .ddtproj or .ddtpac (the desired state). Required for a normal publish; omit only with --restore-from-snapshot.',\n )\n .option(\n '--target <path>',\n 'Target / current state: .ddtproj or .ddtpac (offline). Omit to compare the source against the live workspace at --connection (the `sdt publish` model). One of --target or --connection is required for a normal publish.',\n )\n .option(\n '--restore-from-snapshot <batchId>',\n 'Recovery mode: skip the compare step and emit DROP/CREATE … SHALLOW CLONE against the snapshot batch <batchId> from the registry. ' +\n 'Dry-run by default; pass --apply --yes to execute. ' +\n \"This is the command printed by TRUST.4's post-deploy-smoke + TRUST.8's restore-hint when a deploy fails — they tell the operator to run `ddt publish --restore-from-snapshot <id>`.\",\n )\n .option(\n '--snapshot-dir <path>',\n 'Snapshot registry directory used with --restore-from-snapshot.',\n '.ddt/snapshots',\n )\n .option(\n '--out <path>',\n 'Write the generated SQL to this path (currently honored on the --restore-from-snapshot recovery path).',\n )\n .option('--dry-run', 'Print the migration script and safety assessment without applying.', true)\n .option(\n '--apply',\n 'Apply the migration against --connection. Requires --connection. Default false.',\n false,\n )\n .option(\n '--connection <name>',\n 'Connection profile. Doubles as the live compare target when --target is omitted, and as the apply executor (required with --apply).',\n )\n .option(\n '--yes',\n 'Skip interactive confirmation when applying. Required with --apply (no TTY today).',\n false,\n )\n .option('--variables <kv>', 'Comma-separated KEY=VALUE pairs for $(VAR) substitution.')\n .option('--ignore-case', 'Compare object FQNs case-insensitively.', false)\n .option(\n '--no-rollback',\n 'Skip rollback on partial failure (leaves DIRTY state). Default rollback ON.',\n )\n .option(\n '--require-reversible',\n 'Refuse before any DDL runs if any step lacks reverse SQL.',\n false,\n )\n .option(\n '--manifest <path>',\n 'Write a JSON deploy manifest (every step + status + duration + reverse SQL) to <path>. Audit-friendly.',\n )\n .option('--report-html <path>', 'Write a self-contained HTML compare-report to <path>.')\n .option(\n '--no-lint',\n 'Skip the lint gate. Default: lint the generated script and refuse --apply on ERROR-severity findings.',\n )\n .option(\n '--webhook <url>',\n 'POST a deploy-summary JSON to this URL after --apply completes. Or set $DDT_NOTIFY_WEBHOOK.',\n )\n .option(\n '--profile <name>',\n 'Use the deploymentProfiles[<name>] overlay from --source (.ddtproj). ' +\n 'Pulls profile.connection, profile.variables, profile.deployOptions on top of project defaults.',\n )\n .option(\n '--changelog [catalog]',\n 'After --apply, append a row to <catalog>.__ddt.deploy_log on the workspace. ' +\n \"Defaults catalog to the connection profile's default catalog. Liquibase-style audit table.\",\n )\n .option(\n '--no-slice',\n \"Disable the source's Project Slice (if it has one). Default: a sliced source partitions the diff automatically and target objects outside the slice are left untouched.\",\n )\n .option(\n '--query-tag <tag>',\n 'Tag attached to every executed statement so system.query.history can attribute the deploy. ' +\n 'Prepended as `-- DDT-TAG: <tag>` comment. ' +\n \"Defaults to 'ddt:<projectName>@<projectVersion>:<unix-ms>' when omitted.\",\n )\n .option(\n '--color <mode>',\n 'Color stdout: auto | always | never. Honors NO_COLOR / DDT_NO_COLOR env vars in auto mode.',\n 'auto',\n )\n .option(\n '--ai-preflight',\n \"Before --apply, ask AI to grade the operator's plain-language deploy description against the actual diff + safety findings. Blocks on 'mismatch' unless --confirm-after-preflight-mismatch is also passed.\",\n false,\n )\n .option(\n '--ai-preflight-text <narrative>',\n 'Non-interactive form of --ai-preflight. Supplies the narrative directly instead of reading from stdin.',\n )\n .option(\n '--strict-ai-preflight',\n \"Also block on 'partial' (not just 'mismatch'). Useful for production deploys.\",\n false,\n )\n .option(\n '--confirm-after-preflight-mismatch',\n 'Proceed even when --ai-preflight returns a mismatch verdict. Acknowledges that the AI grader flagged a discrepancy.',\n false,\n )\n .option(\n '--require-approvals <n>',\n 'Multi-approver gate: refuse --apply until <n> distinct approvers have signed off via `ddt approval add <deploy-id> --as <user>`. Team-tier.',\n )\n .option(\n '--approver <ids>',\n 'Comma-separated allow-list of approver IDs honoured by --require-approvals. Empty = any approver counts.',\n )\n .option(\n '--deploy-id <id>',\n 'Stable deploy identifier paired with --require-approvals (default: <connection>:<catalog>.<schema>).',\n )\n .option(\n '--approvals-root <path>',\n 'Approvals directory (default: .ddt/approvals).',\n path.join('.ddt', 'approvals'),\n )\n .option(\n '--allow-protected <fqns>',\n 'Comma-separated FQNs to exempt from the .ddt-protection.json gate. Repeatable.',\n (val: string, prev: string[]) => [...prev, val],\n [] as string[],\n )\n .option(\n '--allow-all-protected',\n 'Bypass all protection gates defined in .ddt-protection.json. Use with care.',\n false,\n )\n .option(\n '--freshness <mode>',\n 'Pac age check: warn (log if stale, continue), strict (block if stale), skip (no check). Default warn.',\n 'warn',\n )\n .option(\n '--post-deploy-tests <project>',\n 'After a successful --apply, discover YAML tests under <project>/tests/ and run them against the same connection. Exits 2 on any blocking (error-severity) DQ failure. Emits POST_DEPLOY_DQ_FAILED hint with --manifest recipe when tests fail.',\n );\n addMappingFlags(cmd);\n cmd.action(async (opts) => {\n // Recovery mode — branch out of the normal compare/apply flow and emit\n // DROP/CREATE … SHALLOW CLONE from a recorded snapshot batch.\n if (opts.restoreFromSnapshot) {\n await runRestoreFromSnapshot(opts);\n return;\n }\n if (!opts.source) {\n throw new Error('--source <path> is required (unless --restore-from-snapshot is given).');\n }\n if (!opts.target && !opts.connection) {\n throw new Error(\n 'Provide a current state to diff the source against: --target <.ddtproj|.ddtpac> (offline) ' +\n 'or --connection <profile> (the live workspace, mirroring `sdt publish`). With neither there is nothing to compare.',\n );\n }\n const nameMapping = await buildMappingFromOptions(opts);\n const sourcePath = String(opts.source);\n const engine = new CompareEngine();\n const src: CompareSource = sourcePath.endsWith('.ddtpac')\n ? new PacSource(sourcePath, 'source')\n : new ProjectSource(sourcePath, 'source');\n\n // Target: an offline artifact (--target) or, when omitted, the live\n // workspace at --connection. The live path mirrors `sdt publish`, which\n // always diffs the desired-state pac against the live --connection target.\n // The live target's scope (catalog / schema) is derived from the source.\n let tgt: CompareSource;\n if (opts.target) {\n const targetPath = String(opts.target);\n tgt = targetPath.endsWith('.ddtpac')\n ? new PacSource(targetPath, 'target')\n : new ProjectSource(targetPath, 'target');\n } else {\n const scope = await resolveSourceScope(sourcePath);\n const liveProfile = await getProfile(String(opts.connection));\n tgt = new LiveSource(\n createConnection(liveProfile),\n scope,\n `${String(opts.connection)} (live)`,\n );\n }\n\n // Freshness gate — only fires when source is a .ddtpac, before any network call.\n let loadedSrcPac: Awaited<ReturnType<typeof pacNs.readPac>> | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n loadedSrcPac = await pacNs.readPac(sourcePath);\n const freshnessMode = (opts.freshness as pacFreshness.FreshnessMode | undefined) ?? 'warn';\n if (freshnessMode !== 'skip') {\n const fr = pacFreshness.checkPacFreshness(loadedSrcPac.manifest.builtAt);\n if (fr.status !== 'fresh') {\n const msg = pacFreshness.formatFreshnessWarning(fr, sourcePath);\n if (freshnessMode === 'strict') {\n console.error(msg);\n process.exitCode = 1;\n return;\n } else {\n console.warn(msg);\n }\n }\n }\n }\n\n // Auto-load slice from the source. .ddtpac carries it in manifest;\n // .ddtproj has it as a top-level field. --no-slice bypasses.\n let slice: ReturnType<typeof compileSlice> | undefined;\n if (opts.slice !== false) {\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n if (srcPac.manifest.slice) slice = compileSlice(srcPac.manifest.slice);\n } else {\n const loaded = await loadProject(sourcePath);\n if (loaded.project.slice) slice = compileSlice(loaded.project.slice);\n }\n }\n\n const result = await engine.compare(src, tgt, {\n ignoreCase: !!opts.ignoreCase,\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n const mappingSummary = describeMapping(nameMapping);\n if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n console.log(\n `Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n\n if (opts.reportHtml) {\n const html = renderHtmlReport(result, {\n title: `Publish ${result.source.label} → ${result.target.label}`,\n safety: safety.assess(result),\n });\n const htmlPath = path.resolve(String(opts.reportHtml));\n await fs.mkdir(path.dirname(htmlPath), { recursive: true });\n await fs.writeFile(htmlPath, html, 'utf8');\n console.log(`Wrote HTML report → ${htmlPath}`);\n }\n\n // --profile overlay (project-level deployOptions + variables + connection).\n // VARSYNTAX.2 — also reads from `.ddtpac` manifest's deploymentProfiles\n // snapshot when --source is a built pac (writable since the pac builder\n // snapshots loaded.project.deploymentProfiles into the manifest).\n let profileDeployment: ReturnType<typeof mergeDeployOptions>['deployment'] | undefined;\n let profileVariables: Record<string, string> = {};\n let profileConnection: string | undefined;\n if (opts.profile) {\n const profileName = String(opts.profile);\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n const block = srcPac.manifest.deploymentProfiles?.[profileName];\n if (!block) {\n const available = Object.keys(srcPac.manifest.deploymentProfiles ?? {});\n throw new Error(\n `--profile ${profileName}: no deploymentProfile by that name in the pac manifest. ` +\n (available.length === 0\n ? 'The pac carries no deploymentProfiles (was it built before VARSYNTAX.2, or does the .ddtproj declare any?).'\n : `Available: ${available.join(', ')}.`),\n );\n }\n if (block.variables) profileVariables = { ...block.variables };\n if (block.connection) profileConnection = block.connection;\n // pac-snapshot path does NOT overlay profile-level deployOptions —\n // those are already merged into manifest.deployOptions at build\n // time. Leave profileDeployment undefined; the caller falls back\n // to `mergeDeployOptions().deployment`.\n console.log(\n `Profile: ${profileName}${profileConnection ? ` (connection=${profileConnection})` : ''}`,\n );\n } else {\n const loaded = await loadProject(sourcePath);\n const resolved = resolveProfile(loaded, profileName);\n profileDeployment = resolved.deployment;\n profileVariables = resolved.variables;\n profileConnection = resolved.connection;\n console.log(`Profile: ${resolved.name} (connection=${resolved.connection})`);\n }\n }\n\n const assessment = safety.assess(result);\n const deployment = profileDeployment ?? mergeDeployOptions().deployment;\n const variables = { ...profileVariables, ...(parseVariables(opts.variables) ?? {}) };\n\n // VARSYNTAX.3 — assemble the deploy context so `$(DDT_PROFILE)`,\n // `$(DDT_PROJECT_NAME)`, `$(DDT_PROJECT_VERSION)`, `$(DDT_PLATFORM)`,\n // `$(DDT_TIMESTAMP)`, `$(DDT_USER)` substitute without the operator\n // having to pass them via --variables. projectName / projectVersion\n // come from whichever source was loaded (.ddtpac manifest or\n // .ddtproj); profile defaults to undefined unless --profile was used.\n let ctxProjectName: string | undefined;\n let ctxProjectVersion: string | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n ctxProjectName = srcPac.manifest.projectName;\n ctxProjectVersion = srcPac.manifest.projectVersion;\n } else {\n try {\n const loaded = await loadProject(sourcePath);\n ctxProjectName = loaded.project.name;\n ctxProjectVersion = loaded.project.version;\n } catch {\n // best-effort; built-in vars stay defaulted\n }\n }\n const generateContext: {\n profile?: string;\n projectName?: string;\n projectVersion?: string;\n user?: string;\n } = {\n ...(opts.profile ? { profile: String(opts.profile) } : {}),\n ...(ctxProjectName ? { projectName: ctxProjectName } : {}),\n ...(ctxProjectVersion ? { projectVersion: ctxProjectVersion } : {}),\n };\n\n const generator = new ScriptGenerator();\n const script = generator.generate(result, {\n variables,\n deployment,\n context: generateContext,\n });\n\n const plan = defaultSafePlan(result);\n const validated = validatePlan(result, plan);\n const steps = buildExecutionPlan(result, validated, { deployment });\n\n // Protection gate — block operations on objects declared in .ddt-protection.json.\n const protConfig = await protectedObjects.loadProtectionConfig(path.dirname(sourcePath));\n if (protConfig) {\n const ops = result.objects\n .filter((o) => o.kind !== 'unchanged' && o.kind !== 'added')\n .map((o) => ({\n fqn: o.identity.fqn,\n objectType: String(o.identity.objectType),\n kind: o.kind,\n }));\n const allowedFqns = ((opts.allowProtected as string[]) ?? []).flatMap((s: string) =>\n s.split(',').map((f: string) => f.trim()),\n );\n const violations = protectedObjects.checkProtectedObjects(ops, protConfig, {\n allowedFqns,\n allowAll: Boolean(opts.allowAllProtected),\n });\n if (violations.length > 0) {\n console.error(protectedObjects.formatProtectionViolations(violations));\n process.exitCode = 1;\n return;\n }\n }\n\n printPlanReport(result.summary, script.summary, assessment, steps);\n\n // ── apply path ──────────────────────────────────────────────────────\n if (opts.apply) {\n const connectionName = opts.connection ?? profileConnection;\n if (!connectionName) {\n throw new Error(\n '--apply requires --connection <profile-name> or --profile <env> (supplies connection).',\n );\n }\n if (!opts.yes) {\n throw new Error(\n '--apply requires --yes. The CLI does not currently prompt interactively. Pass --yes to confirm.',\n );\n }\n if (assessment.blocked) {\n console.error(\n `Refusing to apply: safety classifier blocked the deploy (${assessment.blockReason}).`,\n );\n process.exitCode = 2;\n return;\n }\n\n // Approval gate (Team-tier): refuse to apply until N distinct\n // approvers have recorded approval tokens for this deploy id.\n if (opts.requireApprovals !== undefined && opts.requireApprovals !== false) {\n const required = Number(opts.requireApprovals) || 0;\n const allowed = opts.approver\n ? String(opts.approver)\n .split(',')\n .map((s: string) => s.trim())\n .filter(Boolean)\n : [];\n const deployId = String(opts.deployId ?? `${connectionName}:default`);\n const approvalsRoot = String(opts.approvalsRoot);\n const file = await approval.readApprovalFile(approvalsRoot, deployId);\n const outcome = approval.evaluateApprovalGate(\n { deployId, required, allowedApprovers: allowed },\n file.approvals,\n );\n if (!outcome.satisfied) {\n console.error(\n 'Approval gate blocked deploy: ' +\n (outcome.blockReason ?? 'gate not satisfied') +\n `. Record approvals with \\`ddt approval add ${deployId} --as <user>\\`.`,\n );\n process.exitCode = 2;\n return;\n }\n console.error(\n ` approvals: ${outcome.satisfiedBy.length}/${required} ` +\n `(${outcome.satisfiedBy.join(', ')})`,\n );\n }\n\n // AI preflight grader: ask the operator to type what they expect\n // this deploy to do, then let an AI compare that narrative to the\n // actual SQL + safety findings. Blocks deploy on `mismatch`.\n if (opts.aiPreflight) {\n const narrative = await readPreflightNarrative(opts.aiPreflightText);\n if (!narrative) {\n console.error(\n '--ai-preflight needs a narrative. Provide it inline via --ai-preflight-text or via stdin.',\n );\n process.exitCode = 2;\n return;\n }\n console.log('Grading deploy narrative with AI...');\n try {\n const findings = [\n ...assessment.unrecoverable.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.destructive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.expensive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ];\n const compareSummary = `added ${result.summary.added}, modified ${result.summary.modified}, removed ${result.summary.removed}, unchanged ${result.summary.unchanged}`;\n const verdict = await aiPreflight.gradePreflight(\n {\n narrative,\n compareSummary,\n safetyFindings: findings,\n diffSampleDdl: script.sql,\n target: `${result.target.label}@${connectionName ?? 'unknown'}`,\n },\n {\n completeFn: async (prompt) => {\n const r = await ai.complete([{ role: 'user', content: prompt }], {\n feature: 'ai-preflight',\n });\n return r.text;\n },\n },\n );\n console.log(` preflight verdict: ${verdict.verdict}`);\n if (verdict.reasoning) console.log(` preflight reasoning: ${verdict.reasoning}`);\n for (const d of verdict.discrepancies) console.log(` • ${d}`);\n\n const isBlocking =\n verdict.verdict === 'mismatch' ||\n (opts.strictAiPreflight && verdict.verdict === 'partial');\n if (isBlocking && !opts.confirmAfterPreflightMismatch) {\n console.error(\n `Refusing to apply: AI preflight returned \"${verdict.verdict}\". Re-run with --confirm-after-preflight-mismatch to override, or revise the narrative.`,\n );\n process.exitCode = 2;\n return;\n }\n if (verdict.parseFailed) {\n console.warn(\n ' preflight verdict could not be parsed — continuing without a clean alignment signal.',\n );\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(\n `--ai-preflight failed: ${msg}. Configure an AI provider via \\`ddt ai status\\` or omit --ai-preflight.`,\n );\n process.exitCode = 2;\n return;\n }\n }\n\n // Lint gate — refuse --apply when the generated script trips an\n // ERROR-severity lint rule (L003 ADD NOT NULL no DEFAULT, L007\n // REFUSED placeholder). Opt out with --no-lint when you've\n // reviewed the script and want to proceed.\n if (opts.lint !== false) {\n const lint = lintSql(script.sql);\n if (lint.errorCount > 0) {\n console.error('');\n console.error(\n `Refusing to apply: lint gate caught ${lint.errorCount} ERROR-severity finding(s).`,\n );\n for (const f of lint.findings) {\n if (f.severity !== 'ERROR') continue;\n console.error(` ${f.rule} line ${f.line}: ${f.message}`);\n if (f.suggestion) console.error(` → ${f.suggestion}`);\n }\n console.error('');\n console.error('Resolve the findings or re-run with --no-lint to override.');\n process.exitCode = 2;\n return;\n }\n if (lint.warningCount > 0) {\n console.log(`Lint: ${lint.warningCount} warning(s) — proceeding.`);\n }\n }\n\n const profile = await getProfile(String(connectionName));\n const conn = createConnection(profile);\n const queryTag = resolveQueryTag(\n opts.queryTag,\n sourcePath,\n result.source.label,\n ctxProjectName,\n ctxProjectVersion,\n );\n if (queryTag) console.log(`Query tag: ${queryTag}`);\n try {\n await conn.connect();\n const executor = new DatabricksExecutor(conn, {\n ...(queryTag ? { queryTag } : {}),\n onStatement: (sql) => {\n const head = sql.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(` → ${head}${sql.length > 100 ? ' …' : ''}`);\n },\n });\n console.log('');\n console.log('--- APPLYING ---');\n const report = await executePlan(steps, executor, {\n rollbackOnFailure: opts.rollback !== false,\n requireReversible: !!opts.requireReversible,\n onStepStart: (step) => {\n console.log(`▶ ${step.objectType} ${step.fqn} — ${step.description}`);\n },\n onStepEnd: (r: StepResult) => {\n const tag =\n r.status === 'SUCCESS'\n ? '✓'\n : r.status === 'ROLLED_BACK'\n ? '↩'\n : r.status === 'FAILED'\n ? '✗'\n : r.status === 'SKIPPED'\n ? '-'\n : '?';\n console.log(\n ` ${tag} ${r.status}${r.durationMs ? ` (${r.durationMs}ms)` : ''}${r.error ? `: ${r.error}` : ''}`,\n );\n },\n });\n console.log('');\n console.log('--- DEPLOYMENT REPORT ---');\n console.log(`finalState: ${report.finalState}`);\n if (report.failedStepId) console.log(`failedStepId: ${report.failedStepId}`);\n if (report.preflightErrors?.length) {\n console.log('preflightErrors:');\n for (const e of report.preflightErrors) console.log(` ${e}`);\n }\n const manifestJson = JSON.stringify(\n buildDeployManifest(report, profile.auth.host),\n null,\n 2,\n );\n if (opts.manifest) {\n const mPath = path.resolve(String(opts.manifest));\n await fs.mkdir(path.dirname(mPath), { recursive: true });\n await fs.writeFile(mPath, manifestJson + '\\n', 'utf8');\n console.log(`Wrote deploy manifest → ${mPath}`);\n }\n\n // Target-side changelog table — best-effort. Failures are reported\n // (the deploy already succeeded) but never thrown.\n if (opts.changelog) {\n const catalog = opts.changelog === true ? undefined : String(opts.changelog);\n const r = await recordDeployChangelog(\n conn,\n report,\n result.source,\n result.target,\n manifestJson,\n '0.3.0',\n catalog ? { catalog } : {},\n );\n if (r.ok) {\n console.log(`Changelog: deploy_id=${r.entry.deployId} appended.`);\n } else {\n console.error(`Changelog write failed (deploy still completed): ${r.error}`);\n }\n }\n\n // Webhook notification — fire-and-forget POST with a small JSON\n // summary. Slack / Teams / generic JSON receivers all work.\n const webhookUrl = opts.webhook ?? process.env['DDT_NOTIFY_WEBHOOK'];\n if (webhookUrl) {\n try {\n await postWebhook(String(webhookUrl), {\n workspaceHost: profile.auth.host,\n source: result.source.label,\n target: result.target.label,\n finalState: report.finalState,\n failedStepId: report.failedStepId,\n stepsTotal: report.steps.length,\n stepsSucceeded: report.steps.filter((s) => s.status === 'SUCCESS').length,\n stepsFailed: report.steps.filter((s) => s.status === 'FAILED').length,\n });\n console.log(`Webhook notified → ${webhookUrl}`);\n } catch (err) {\n console.error(\n `Webhook POST failed (deploy still completed): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (report.finalState === 'DIRTY') {\n console.error(\n 'DIRTY: rollback could not complete. Manual intervention required — see report above.',\n );\n process.exitCode = 1;\n } else if (report.finalState !== 'CLEAN_SUCCESS') {\n process.exitCode = 1;\n }\n\n if (opts.postDeployTests && report.finalState === 'CLEAN_SUCCESS') {\n const projectRoot = path.resolve(String(opts.postDeployTests));\n console.log('Running post-deploy DQ tests from ' + projectRoot + '...');\n const testFiles = await testFramework.discoverTests(projectRoot);\n if (testFiles.length === 0) {\n console.warn(\n ' POST_DEPLOY_DQ: no tests found under ' + projectRoot + '/tests/ — skipping.',\n );\n } else {\n const dqReport = await testFramework.runTestFiles(testFiles, conn);\n process.stdout.write(testFramework.renderTextReport(dqReport));\n if (dqReport.summary.blocking) {\n console.error(\n 'POST_DEPLOY_DQ_FAILED: ' +\n dqReport.summary.fail +\n ' fail / ' +\n dqReport.summary.error +\n ' error.' +\n (opts.manifest\n ? ' Recover: `ddt revert --manifest ' + String(opts.manifest) + '`'\n : ' Inspect tests/ and re-run after fixing data issues.'),\n );\n process.exitCode = 2;\n } else {\n console.log(\n ' POST_DEPLOY_DQ: ' +\n dqReport.summary.pass +\n '/' +\n dqReport.summary.total +\n ' pass.',\n );\n }\n }\n }\n } finally {\n await conn.disconnect();\n }\n return;\n }\n\n // ── dry-run path (default) ──────────────────────────────────────────\n // Colorize the script for terminal output so destructive lines and safety\n // banners are unmissable; ANSI is stripped in auto mode when stdout is\n // not a TTY (e.g. piped to a file or CI log).\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n console.log('');\n console.log('--- MIGRATION SCRIPT ---');\n console.log(colorizeMigrationScript(script.sql, { mode: colorMode }));\n });\n attachRelatedOptions(cmd, [\n 'deployment.allowDropTable',\n 'deployment.allowDropColumn',\n 'deployment.allowUnrecoverableDrop',\n 'deployment.allowNarrowingTypes',\n 'deployment.allowTableRebuild',\n 'deployment.blockOnPossibleDataLoss',\n 'deployment.preserveTargetOnlyObjects',\n ]);\n return cmd;\n}\n\nfunction printPlanReport(\n diffSummary: { added: number; removed: number; modified: number; unchanged: number },\n scriptSummary: { destructive: number; refused: number },\n assessment: ReturnType<typeof safety.assess>,\n steps: ReturnType<typeof buildExecutionPlan>,\n): void {\n console.log('--- COMPARE SUMMARY ---');\n console.log(\n `+${diffSummary.added} -${diffSummary.removed} ~${diffSummary.modified} =${diffSummary.unchanged}` +\n (scriptSummary.destructive ? ` (destructive=${scriptSummary.destructive})` : '') +\n (scriptSummary.refused ? ` (refused=${scriptSummary.refused})` : ''),\n );\n console.log('');\n console.log('--- SAFETY ASSESSMENT ---');\n console.log(`unrecoverable=${assessment.unrecoverable.length}`);\n console.log(`destructive=${assessment.destructive.length}`);\n console.log(`expensive=${assessment.expensive.length}`);\n console.log(`warnings=${assessment.warnings.length}`);\n if (assessment.blocked) console.log(`BLOCKED: ${assessment.blockReason}`);\n console.log('');\n console.log('--- DEFAULT-SAFE PLAN ---');\n const reversible = steps.filter((st) => st.reverseSql).length;\n console.log(`${steps.length} step(s) planned (default-safe selection)`);\n console.log(`${reversible} step(s) reversible · ${steps.length - reversible} irreversible`);\n}\n\n/**\n * Derive the live-target scope (catalog / schema) from the source artifact.\n * Both `.ddtpac` (manifest.scope) and `.ddtproj` (project.scope) carry a\n * `ProjectScope`. Used to scope the live `LiveSource` when `ddt publish` is\n * run without `--target` (the `sdt publish` model — diff against live).\n */\nasync function resolveSourceScope(\n sourcePath: string,\n): Promise<{ catalog?: string; schema?: string }> {\n const scope = sourcePath.endsWith('.ddtpac')\n ? (await pacNs.readPac(sourcePath)).manifest.scope\n : (await loadProject(sourcePath)).project.scope;\n return {\n ...(scope.catalog ? { catalog: scope.catalog } : {}),\n ...(scope.schema ? { schema: scope.schema } : {}),\n };\n}\n\nasync function postWebhook(url: string, payload: unknown): Promise<void> {\n const isSlackOrTeams = /hooks\\.slack\\.com|webhook\\.office\\.com/.test(url);\n let body: string;\n if (isSlackOrTeams) {\n // Both Slack and Teams accept a simple { text: \"...\" } shape.\n const summary = payload as {\n finalState: string;\n target: string;\n stepsTotal: number;\n stepsSucceeded: number;\n stepsFailed: number;\n };\n body = JSON.stringify({\n text: `DDT publish → ${summary.target} · ${summary.finalState} · ${summary.stepsSucceeded}/${summary.stepsTotal} steps ok${summary.stepsFailed ? ` · ${summary.stepsFailed} failed` : ''}`,\n });\n } else {\n body = JSON.stringify(payload);\n }\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} ${res.statusText}`);\n }\n}\n\n// PD7.2 — parse `--variables KEY=VALUE,KEY=VALUE,...` using indexOf split\n// so a `=` inside the value (base64, JWT, connection-string fragments) is\n// preserved instead of being treated as a second delimiter. Mirrors the SDT\n// parsePublishVariables contract.\nexport function parseVariables(raw: unknown): Record<string, string> | undefined {\n if (!raw) return undefined;\n const out: Record<string, string> = {};\n for (const pair of String(raw).split(',')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n const k = pair.slice(0, eq).trim();\n const v = pair.slice(eq + 1).trim();\n if (k.length > 0) out[k] = v;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Resolve the effective query tag. Honors an explicit `--query-tag`\n * flag value verbatim; otherwise auto-generates\n * `ddt:<projectName>@<projectVersion>:<unix-ms>` using the supplied\n * project context (pac manifest or .ddtproj). When projectName is\n * unknown, falls back to the source-path basename / sourceLabel; when\n * projectVersion is unknown the `@<projectVersion>` segment is omitted\n * so the tag remains valid. Returns undefined only when the user\n * explicitly passed `--query-tag ''`.\n */\nexport function resolveQueryTag(\n raw: unknown,\n sourcePath: string,\n sourceLabel: string,\n projectName: string | undefined,\n projectVersion: string | undefined,\n): string | undefined {\n if (raw !== undefined) {\n const s = String(raw).trim();\n return s.length === 0 ? undefined : s;\n }\n const fallback = sourcePath.endsWith('.ddtpac')\n ? path.basename(sourcePath, '.ddtpac')\n : sourceLabel;\n const base = projectName ?? fallback;\n const version = projectVersion ? `@${projectVersion}` : '';\n return `ddt:${base}${version}:${Date.now()}`;\n}\n\n/**\n * `ddt publish --restore-from-snapshot <batchId>` recovery path.\n *\n * Loads the batch from the registry, emits the restore SQL (DROP target +\n * CREATE … SHALLOW CLONE from snapshot, or a MANUAL hint row for\n * materialised / streaming objects), and either prints it (dry-run / no\n * --apply) or executes it against the live target (--apply --yes).\n *\n * Skips: pac freshness, compare, safety classifier, AI preflight, approval\n * gate, lint gate, manifest writing. Recovery is an emergency lever — the\n * gates that protect the forward path don't apply when we're restoring a\n * known-good snapshot the safety classifier itself authored.\n */\nasync function runRestoreFromSnapshot(opts: Record<string, unknown>): Promise<void> {\n const batchId = String(opts.restoreFromSnapshot);\n const registryDir = path.resolve(String(opts.snapshotDir ?? '.ddt/snapshots'));\n\n let batch: safety.SnapshotBatch;\n try {\n batch = await safety.readSnapshotBatch(registryDir, batchId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Snapshot batch \"${batchId}\" not found in ${registryDir}: ${msg}`);\n process.exitCode = 1;\n return;\n }\n\n const sqlLines = safety.emitRestoreFromSnapshotSql(batch);\n const sql = sqlLines.join('\\n');\n\n console.log(`Restoring from batch ${batch.batchId} (createdAt=${batch.createdAt})`);\n console.log(\n ` ${batch.entries.length} object(s) recorded; ${sqlLines.filter((l) => !l.startsWith('--')).length} statement(s) will run.`,\n );\n\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, sql + '\\n', 'utf8');\n console.log(`Wrote restore SQL → ${out}`);\n }\n\n // The DDT publish defaults --dry-run to true; recovery follows the same\n // convention: only --apply executes anything.\n if (!opts.apply) {\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n process.stdout.write(colorizeMigrationScript(sql, { mode: colorMode }) + '\\n');\n return;\n }\n\n if (!opts.yes) {\n console.error(\n '--apply requires --yes (restore replaces target tables via DROP + SHALLOW CLONE).',\n );\n process.exitCode = 1;\n return;\n }\n if (!opts.connection) {\n console.error('--apply --restore-from-snapshot requires --connection <profile>.');\n process.exitCode = 1;\n return;\n }\n\n const profile = await getProfile(String(opts.connection));\n const conn = createConnection(profile);\n let executed = 0;\n let failed = 0;\n try {\n await conn.connect();\n for (const stmt of queryExecution.splitStatements(sql).map((s) => s.sql)) {\n try {\n await conn.executeRows(stmt);\n executed += 1;\n const head = stmt.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(' → ' + head + (stmt.length > 100 ? ' …' : ''));\n } catch (err) {\n failed += 1;\n const msg = err instanceof Error ? err.message : String(err);\n console.error(` [restore ${batch.batchId}] ${msg}\\n ${stmt}`);\n }\n }\n } finally {\n await conn.disconnect();\n }\n\n if (failed === 0) {\n console.log(`Restore complete. ${executed} statement(s) executed.`);\n } else {\n console.error(\n `Restore finished with failures: ${executed} ok, ${failed} failed. ` +\n `Inspect the snapshot tables under ${registryDir} and recover by hand.`,\n );\n process.exitCode = 1;\n }\n}\n\n/**\n * Read the AI-preflight narrative from --ai-preflight-text (preferred for CI)\n * or from stdin (interactive). Returns trimmed text, or undefined when no\n * narrative was supplied.\n */\nasync function readPreflightNarrative(inlineText: unknown): Promise<string | undefined> {\n if (typeof inlineText === 'string' && inlineText.trim().length > 0) {\n return inlineText.trim();\n }\n if (process.stdin.isTTY) {\n process.stdout.write(\n '\\n--ai-preflight is active. In one paragraph, describe what you expect this deploy to do.\\nEnd with an empty line (or Ctrl-D):\\n> ',\n );\n }\n process.stdin.setEncoding('utf8');\n let buffer = '';\n await new Promise<void>((resolve) => {\n const onData = (chunk: string): void => {\n buffer += chunk;\n if (/\\n\\s*\\n/.test(buffer)) {\n process.stdin.removeListener('data', onData);\n process.stdin.removeListener('end', onEnd);\n resolve();\n }\n };\n const onEnd = (): void => {\n process.stdin.removeListener('data', onData);\n resolve();\n };\n process.stdin.on('data', onData);\n process.stdin.on('end', onEnd);\n });\n const trimmed = buffer.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddt-tools/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "DDT command-line interface. `ddt` lets you extract, build, compare, and deploy Databricks Unity Catalog schemas.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Varol Consulting LLC",
|
|
@@ -24,12 +24,21 @@
|
|
|
24
24
|
"README.md",
|
|
25
25
|
"LICENSE"
|
|
26
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"clean": "rimraf dist",
|
|
30
|
+
"dev": "tsup --watch",
|
|
31
|
+
"start": "node ./dist/cli.js",
|
|
32
|
+
"test": "vitest run --passWithNoTests",
|
|
33
|
+
"test:watch": "vitest --passWithNoTests",
|
|
34
|
+
"typecheck": "tsc --noEmit"
|
|
35
|
+
},
|
|
27
36
|
"dependencies": {
|
|
37
|
+
"@ddt-tools/core": "workspace:*",
|
|
28
38
|
"chalk": "^5.3.0",
|
|
29
39
|
"commander": "^14.0.0",
|
|
30
40
|
"ora": "^9.4.0",
|
|
31
|
-
"zod": "^4.4.3"
|
|
32
|
-
"@ddt-tools/core": "0.2.4"
|
|
41
|
+
"zod": "^4.4.3"
|
|
33
42
|
},
|
|
34
43
|
"devDependencies": {
|
|
35
44
|
"@types/node": "^25.9.1",
|
|
@@ -37,14 +46,5 @@
|
|
|
37
46
|
"tsup": "^8.3.5",
|
|
38
47
|
"typescript": "^6.0.0",
|
|
39
48
|
"vitest": "^4.1.8"
|
|
40
|
-
},
|
|
41
|
-
"scripts": {
|
|
42
|
-
"build": "tsup",
|
|
43
|
-
"clean": "rimraf dist",
|
|
44
|
-
"dev": "tsup --watch",
|
|
45
|
-
"start": "node ./dist/cli.js",
|
|
46
|
-
"test": "vitest run --passWithNoTests",
|
|
47
|
-
"test:watch": "vitest --passWithNoTests",
|
|
48
|
-
"typecheck": "tsc --noEmit"
|
|
49
49
|
}
|
|
50
|
-
}
|
|
50
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/compare.ts","../src/util/color.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n CompareEngine,\n PacSource,\n ai,\n compileSlice,\n pac as pacNs,\n renderHtmlReport,\n safety,\n typecheck,\n writeCompareHistory,\n} from '@ddt-tools/core';\nimport { addMappingFlags, buildMappingFromOptions, describeMapping } from '../util/mapping.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\nimport { makeColorizer, resolveColorMode } from '../util/color.js';\n\n/**\n * `ddt compare` — pac↔pac compare today. Project↔pac and project↔live require\n * the SQL parser pass (v0.3 polish) + a live driver session.\n *\n * Supports logical-name mapping (`--map`, `--map-file`) so two pacs whose\n * database / schema names differ can compare semantically. See\n * `docs/LOGICAL_NAME_MAPPING.md`.\n */\nexport function compareCommand(): Command {\n const cmd = new Command('compare');\n cmd\n .description('Compare two .ddtpac files (pac↔pac); project / live compare pending v0.3 polish.')\n .requiredOption('--source <path>', 'Source .ddtpac (the desired state).')\n .requiredOption('--target <path>', 'Target .ddtpac (the current state).')\n .option('--ignore-case', 'Compare object FQNs case-insensitively.', false)\n .option(\n '--json',\n 'Emit a JSON CompareResult instead of human-readable output. (Alias for --format json.)',\n false,\n )\n .option(\n '--format <fmt>',\n 'Output format: summary | json | markdown. Default summary.',\n 'summary',\n )\n .option('--report-html <path>', 'Also write a self-contained HTML compare-report to <path>.')\n .option(\n '--no-slice',\n \"Disable the source pac's Project Slice (if it has one). Default: a sliced pac is partitioned automatically.\",\n )\n .option(\n '--explain',\n 'After the diff, call the configured AI provider to narrate each change in plain English with reasoning. Requires `ddt ai` to be configured.',\n false,\n )\n .option(\n '--color <mode>',\n 'Colorize severity output: always | never | auto. Default auto (color on TTY).',\n 'auto',\n )\n .option(\n '--type-safe',\n 'After the compare, run the TYPECHECK.1 impact analyzer. Exits with code 2 if the configured --break-on threshold is reached (default `error`). CI-friendly gate — pair with `--format json` for machine-readable output.',\n false,\n )\n .option(\n '--break-on <severity>',\n 'TYPECHECK.2 threshold for --type-safe: `error` (exit 2 on any error ripple, default) | `warning` (exit 2 on any error OR warning ripple, strict CI mode).',\n 'error',\n )\n .option(\n '--write-impact [path]',\n 'Write the TYPECHECK.1 impact analysis to a JSON file the VS Code extension can surface as squiggle-underlines. Default path: `.ddt/impact.json` resolved relative to CWD. Composes with `--type-safe` — the file is written before the gate decides exit code.',\n )\n .option(\n '--no-history',\n 'Skip writing the compare-history audit record (AUDITCMP.1). Default: every compare run writes a record to `.ddt/history/compare/`, exportable via `ddt audit-log emit`.',\n );\n addMappingFlags(cmd);\n cmd.action(async (opts) => {\n const nameMapping = await buildMappingFromOptions(opts);\n const engine = new CompareEngine();\n const source = new PacSource(String(opts.source), 'source');\n const target = new PacSource(String(opts.target), 'target');\n\n // Auto-pull slice from the source pac's manifest unless --no-slice was set.\n let slice: ReturnType<typeof compileSlice> | undefined;\n if (opts.slice !== false) {\n const srcPac = await pacNs.readPac(String(opts.source));\n if (srcPac.manifest.slice) slice = compileSlice(srcPac.manifest.slice);\n }\n\n const startedAt = new Date().toISOString();\n const result = await engine.compare(source, target, {\n ignoreCase: !!opts.ignoreCase,\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n\n // AUDITCMP.1 — compare operations leave the same audit trail deploys\n // do. Written before the format-specific early returns so every\n // output mode is covered. Best-effort: a failed write never breaks\n // the compare itself.\n if (opts.history !== false) {\n const finishedAt = new Date().toISOString();\n try {\n const histAssessment = safety.assess(result);\n const changed = result.summary.added + result.summary.removed + result.summary.modified;\n await writeCompareHistory(path.dirname(path.resolve(String(opts.source))), {\n startedAt,\n finishedAt,\n durationMs: Date.parse(finishedAt) - Date.parse(startedAt),\n outcome: histAssessment.blocked\n ? 'BLOCKED'\n : changed === 0\n ? 'NO_CHANGES'\n : 'CHANGES_FOUND',\n source: { kind: result.source.kind, label: result.source.label },\n target: { kind: result.target.kind, label: result.target.label },\n summary: result.summary,\n sliceApplied: Boolean(slice),\n ...(histAssessment.blocked && histAssessment.blockReason\n ? { blockReason: histAssessment.blockReason }\n : {}),\n });\n } catch {\n /* audit trail is best-effort; never fail the compare over it */\n }\n }\n\n if (opts.reportHtml) {\n const html = renderHtmlReport(result, {\n title: `Compare ${result.source.label} → ${result.target.label}`,\n safety: safety.assess(result),\n });\n const htmlPath = path.resolve(String(opts.reportHtml));\n await fs.mkdir(path.dirname(htmlPath), { recursive: true });\n await fs.writeFile(htmlPath, html, 'utf8');\n console.error(`Wrote ${htmlPath} (${html.length} bytes).`);\n }\n\n const fmt = opts.json ? 'json' : String(opts.format ?? 'summary').toLowerCase();\n if (fmt === 'json') {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n if (fmt === 'markdown') {\n const assessment = safety.assess(result);\n console.log(renderCompareMarkdown(result, assessment));\n return;\n }\n const s = result.summary;\n console.log(`Source: ${result.source.kind}:${result.source.label}`);\n console.log(`Target: ${result.target.kind}:${result.target.label}`);\n const mappingSummary = describeMapping(nameMapping);\n if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n console.log(\n `Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n console.log(`Summary: +${s.added} -${s.removed} ~${s.modified} =${s.unchanged}`);\n for (const o of result.objects) {\n if (o.kind === 'unchanged') continue;\n const glyph = o.kind === 'added' ? '+' : o.kind === 'removed' ? '-' : '~';\n console.log(` ${glyph} ${o.identity.objectType} ${o.identity.fqn}`);\n if (o.kind === 'modified' && o.changes) {\n for (const c of o.changes) {\n console.log(\n ` • ${c.path}: ${JSON.stringify(c.target)} → ${JSON.stringify(c.source)}`,\n );\n }\n }\n }\n\n // Reversibility-grouped safety findings — Unrecoverable first (highest\n // urgency), then Data-impacting, then Reversible. Empty buckets are\n // skipped, and an empty assessment is silent.\n const assessment = safety.assess(result);\n const buckets = safety.groupByReversibility(assessment);\n const total =\n buckets.unrecoverable.length + buckets.dataImpacting.length + buckets.reversible.length;\n if (total > 0) {\n const colorize = makeColorizer(resolveColorMode(opts.color));\n console.log('');\n console.log('Safety findings (grouped by reversibility):');\n const block = safety.formatReversibilityBuckets(buckets);\n const colored = colorize.applyToBlock(block);\n for (const line of colored.split('\\n')) console.log(' ' + line);\n if (assessment.blocked && assessment.blockReason) {\n console.error(' ' + colorize.unrecoverable('BLOCKED: ' + assessment.blockReason));\n }\n }\n\n // AI-narrated diff (--explain). Calls the configured provider with a\n // structured prompt containing the diff summary + per-object changes\n // + safety findings.\n if (opts.explain) {\n console.log('');\n console.log('AI explanation:');\n try {\n const userPrompt = buildExplainPrompt(result, assessment);\n const reply = await ai.complete(\n [\n { role: 'system', content: SYSTEM_PROMPT },\n { role: 'user', content: userPrompt },\n ],\n { feature: 'compare.explain' },\n );\n for (const line of reply.text.split('\\n')) console.log(' ' + line);\n } catch (err) {\n console.error(' --explain failed: ' + (err instanceof Error ? err.message : String(err)));\n console.error(\n ' Run `ddt ai status` to verify your AI provider is configured (`ddt ai test` to send a probe).',\n );\n }\n }\n\n // --write-impact: TYPECHECK.4 — write the impact analysis to a\n // JSON file the VS Code provider reads. Always runs the analyzer\n // (even when --type-safe is off) so the editor surface is\n // independent of the CI-gate flag. Composes with --type-safe.\n if (opts.writeImpact !== undefined) {\n await writeImpactJson(\n result,\n opts.writeImpact === true ? undefined : String(opts.writeImpact),\n );\n }\n\n // --type-safe: TYPECHECK.2 CI gate. Runs the TYPECHECK.1 impact\n // analyzer and exits with code 2 if any error-severity ripple\n // (column-drop or table-drop reaching a dependent object) is\n // detected. Stays silent on a clean diff so this composes well\n // with --format json (which already returned above).\n if (opts.typeSafe) {\n const breakOnRaw = String(opts.breakOn ?? 'error').toLowerCase();\n if (breakOnRaw !== 'error' && breakOnRaw !== 'warning') {\n throw new Error(\n `--break-on must be 'error' or 'warning' (got '${opts.breakOn}'). 'error' fails on any error-severity ripple (default); 'warning' fails on any warning or error ripple (strict CI).`,\n );\n }\n const breakOn = breakOnRaw as 'error' | 'warning';\n const impactFindings = typecheck.analyzeImpact(result);\n const impactSummary = typecheck.summarizeImpact(impactFindings);\n if (impactSummary.findingsCount > 0) {\n console.log('');\n console.log(\n `Type-safety gate (--break-on ${breakOn}) — ${impactSummary.findingsCount} breaking change(s); ${impactSummary.errors} error ripple(s), ${impactSummary.warnings} warning ripple(s); ${impactSummary.affectedObjects.length} dependent object(s).`,\n );\n for (const f of impactFindings) {\n for (const r of f.ripples) {\n const tag = r.severity === 'error' ? 'ERROR' : 'WARN ';\n console.log(\n ` ${tag} ${r.fqn} ← ${f.breakingChangeOn.fqn} (${f.breakingChangeOn.kind})`,\n );\n }\n }\n const failOnError = impactSummary.errors > 0;\n const failOnWarning = breakOn === 'warning' && impactSummary.warnings > 0;\n if (failOnError) {\n console.error(\n `Type-safety gate FAILED — ${impactSummary.errors} error ripple(s). Fix the dependents (drop / update / re-bind) before re-running compare, OR rerun with the change scoped out of the diff.`,\n );\n process.exitCode = 2;\n } else if (failOnWarning) {\n console.error(\n `Type-safety gate FAILED (strict --break-on warning) — ${impactSummary.warnings} warning ripple(s). Tighten the dependent SQL to accept the new type, or drop --break-on warning to allow.`,\n );\n process.exitCode = 2;\n }\n } else {\n console.log('Type-safety gate PASSED — no breaking-change ripples detected.');\n }\n }\n });\n attachRelatedOptions(cmd, [\n 'compare.ignoreCase',\n 'compare.ignoreComments',\n 'compare.ignoreFormattingDifferences',\n 'compare.ignoreClusterBy',\n 'compare.ignorePartitioning',\n 'compare.ignoreStorageLocation',\n ]);\n return cmd;\n}\n\n/**\n * TYPECHECK.4 — write the impact analysis JSON to disk so the VS Code\n * provider can surface it as squiggle-underlines. Defaults to\n * `<cwd>/.ddt/impact.json` (DDT compare today is pac↔pac so we don't\n * resolve a project root from the source arg — that wires in when the\n * project-source variant of compare ships).\n */\nasync function writeImpactJson(\n result: Parameters<typeof typecheck.analyzeImpact>[0],\n explicitPath: string | undefined,\n): Promise<void> {\n const findings = typecheck.analyzeImpact(result);\n const summary = typecheck.summarizeImpact(findings);\n const file: typecheck.ImpactFile = {\n version: typecheck.IMPACT_FILE_VERSION,\n generatedAt: new Date().toISOString(),\n source: `${result.source.kind}:${result.source.label}`,\n target: `${result.target.kind}:${result.target.label}`,\n findings,\n summary,\n };\n const outPath = explicitPath ? path.resolve(explicitPath) : path.resolve('.ddt', 'impact.json');\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, typecheck.serializeImpactFile(file));\n console.log(\n `Wrote ${outPath} — ${summary.findingsCount} finding(s), ${summary.errors} error / ${summary.warnings} warning ripple(s).`,\n );\n}\n\nexport const SYSTEM_PROMPT = `You are a senior Databricks Unity Catalog DBA reviewing a schema diff. Your job is to narrate the changes in plain English to a teammate who hasn't seen the underlying SQL. For each change, briefly say:\n - what it is (e.g. \"a new fact table is added\")\n - why a reasonable engineer might do this (the intent)\n - what to watch out for (the risk, if any)\n\nBe concrete; use the FQNs you're given. Keep it to 3-6 sentences per object. Don't repeat the raw diff back — explain it.`;\n\nexport function buildExplainPrompt(\n result: {\n source: { label: string };\n target: { label: string };\n objects: Array<{\n kind: string;\n identity: { fqn: string; objectType: string };\n changes?: unknown[];\n }>;\n summary: { added: number; removed: number; modified: number; unchanged: number };\n },\n assessment: {\n unrecoverable: unknown[];\n destructive: unknown[];\n expensive: unknown[];\n warnings: unknown[];\n },\n): string {\n const lines: string[] = [];\n lines.push(`Source: ${result.source.label}`);\n lines.push(`Target: ${result.target.label}`);\n lines.push(\n `Summary: +${result.summary.added} added, -${result.summary.removed} removed, ~${result.summary.modified} modified.`,\n );\n lines.push(\n `Safety: ${assessment.unrecoverable.length} unrecoverable, ${assessment.destructive.length} destructive, ${assessment.expensive.length} expensive, ${assessment.warnings.length} warnings.`,\n );\n lines.push('');\n lines.push('Changes (up to 40):');\n const changed = result.objects.filter((o) => o.kind !== 'unchanged').slice(0, 40);\n for (const o of changed) {\n lines.push(`- ${o.kind} ${o.identity.objectType} ${o.identity.fqn}`);\n }\n if (result.objects.filter((o) => o.kind !== 'unchanged').length > 40) {\n lines.push(\n ` (… ${result.objects.filter((o) => o.kind !== 'unchanged').length - 40} more truncated)`,\n );\n }\n lines.push('');\n lines.push(\n 'Please narrate this diff in plain English. Group related changes together when it helps.',\n );\n return lines.join('\\n');\n}\n\nfunction renderCompareMarkdown(\n result: {\n source: { kind: string; label: string };\n target: { kind: string; label: string };\n objects: Array<{ kind: string; identity: { fqn: string; objectType: string } }>;\n summary: { added: number; removed: number; modified: number; unchanged: number };\n },\n assessment: {\n unrecoverable: unknown[];\n destructive: unknown[];\n expensive: unknown[];\n warnings: unknown[];\n blocked?: boolean;\n blockReason?: string;\n },\n): string {\n const lines: string[] = [];\n lines.push(\n `# Compare: ${result.source.kind}:${result.source.label} → ${result.target.kind}:${result.target.label}`,\n );\n lines.push('');\n lines.push(\n `**Summary**: +${result.summary.added} added · -${result.summary.removed} removed · ~${result.summary.modified} modified · =${result.summary.unchanged} unchanged.`,\n );\n lines.push('');\n lines.push('**Safety**:');\n lines.push(`- 🛑 Unrecoverable: ${assessment.unrecoverable.length}`);\n lines.push(`- 🔥 Destructive: ${assessment.destructive.length}`);\n lines.push(`- ⏱ Expensive: ${assessment.expensive.length}`);\n lines.push(`- ⚠ Warnings: ${assessment.warnings.length}`);\n if (assessment.blocked) {\n lines.push('');\n lines.push(\n `> **BLOCKED**: ${assessment.blockReason ?? 'safety classifier refuses to proceed'}`,\n );\n }\n lines.push('');\n const changed = result.objects.filter((o) => o.kind !== 'unchanged');\n if (changed.length === 0) {\n lines.push('_No object-level changes._');\n return lines.join('\\n');\n }\n lines.push('| Kind | Type | FQN |');\n lines.push('| --- | --- | --- |');\n for (const o of changed) {\n lines.push(`| ${o.kind} | \\`${o.identity.objectType}\\` | \\`${o.identity.fqn}\\` |`);\n }\n return lines.join('\\n');\n}\n","/**\n * ANSI severity coloring for CLI output, DDT side. Paired with\n * `Snowflake/packages/cli/src/util/color.ts` byte-for-byte at the\n * API level.\n */\nexport type ColorMode = 'always' | 'never' | 'auto';\n\nconst ANSI = {\n reset: '\\x1b[0m',\n bold: '\\x1b[1m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n cyan: '\\x1b[36m',\n green: '\\x1b[32m',\n gray: '\\x1b[90m',\n};\n\nexport function resolveColorMode(flag: string | undefined): boolean {\n const mode = (flag ?? 'auto').toLowerCase() as ColorMode;\n if (mode === 'always') return true;\n if (mode === 'never') return false;\n if (process.env.NO_COLOR) return false;\n return Boolean(process.stdout.isTTY);\n}\n\nexport interface Colorizer {\n unrecoverable(s: string): string;\n destructive(s: string): string;\n expensive(s: string): string;\n warning(s: string): string;\n safe(s: string): string;\n dim(s: string): string;\n applyToBlock(block: string): string;\n}\n\nexport function makeColorizer(enabled: boolean): Colorizer {\n if (!enabled) {\n const identity = (s: string): string => s;\n return {\n unrecoverable: identity,\n destructive: identity,\n expensive: identity,\n warning: identity,\n safe: identity,\n dim: identity,\n applyToBlock: identity,\n };\n }\n const wrap =\n (codes: string) =>\n (s: string): string =>\n `${codes}${s}${ANSI.reset}`;\n return {\n unrecoverable: wrap(ANSI.bold + ANSI.red),\n destructive: wrap(ANSI.red),\n expensive: wrap(ANSI.yellow),\n warning: wrap(ANSI.cyan),\n safe: wrap(ANSI.green),\n dim: wrap(ANSI.gray),\n applyToBlock(block: string): string {\n return block\n .split('\\n')\n .map((line) => {\n if (/UNRECOVERABLE/.test(line) || line.includes('🛑'))\n return ANSI.bold + ANSI.red + line + ANSI.reset;\n if (/DESTRUCTIVE/.test(line)) return ANSI.red + line + ANSI.reset;\n if (/EXPENSIVE/.test(line)) return ANSI.yellow + line + ANSI.reset;\n if (/WARNING/.test(line) || line.includes('⚠')) return ANSI.cyan + line + ANSI.reset;\n if (/\\bSAFE\\b|\\bOK\\b/.test(line) || line.includes('✓'))\n return ANSI.green + line + ANSI.reset;\n return line;\n })\n .join('\\n');\n },\n };\n}\n"],"mappings":"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","names":["assessment"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/import.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { importScript, loadProject, parseScript, type ParsedStatement } from '@ddt-tools/core';\n\n/**\n * `ddt import` — parse a SQL script and apply each DDL statement to a\n * `.ddtproj` tree, writing each into the canonical folder for its object\n * type (`catalogs/<cat>/schemas/<sch>/<type-folder>/<name>.sql`).\n *\n * Default mode is safe:\n * - Parses the whole script first; refuses to write anything if any\n * statement has hard errors. The user reviews the report, fixes the\n * script, and re-runs.\n * - Refuses to overwrite existing files. Pass `--force` to clobber.\n * - `--dry-run` shows the plan without writing.\n */\nexport function importCommand(): Command {\n const cmd = new Command('import');\n cmd\n .description(\n 'Parse a SQL script and write each DDL statement into the .ddtproj tree under its canonical folder.',\n )\n .requiredOption('--script <path>', 'Path to the SQL script to import.')\n .requiredOption('-p, --project <path>', 'Path to the .ddtproj file.')\n .option('--dry-run', 'Report what would be written without touching disk.', false)\n .option('--force', 'Overwrite existing files (default refuses on conflict).', false)\n .option(\n '--ignore-errors',\n 'Import classifiable statements even when others have errors. Default refuses on any error.',\n false,\n )\n .action(async (opts) => {\n const scriptPath = path.resolve(String(opts.script));\n const projectPath = path.resolve(String(opts.project));\n const sql = await fs.readFile(scriptPath, 'utf8');\n const parsed = parseScript(sql);\n\n // Up-front parse report — surfaces every error/warning verbatim.\n printParseReport(scriptPath, parsed);\n\n if (parsed.totalErrors > 0 && !opts.ignoreErrors) {\n console.error(\n `\\nRefusing to import: ${parsed.totalErrors} parse error(s) above. ` +\n `Fix the script and re-run, or pass --ignore-errors to import the clean statements.`,\n );\n process.exitCode = 2;\n return;\n }\n\n const loaded = await loadProject(projectPath);\n const result = await importScript(parsed, loaded, {\n dryRun: !!opts.dryRun,\n force: !!opts.force,\n });\n\n console.log('');\n const verb = opts.dryRun ? 'would write' : 'wrote';\n for (const item of result.imported) {\n const rel = path.relative(loaded.rootDir, item.targetPath);\n console.log(\n ` ${verb} ${item.statement.objectType} ${qualified(item.statement)} → ${rel}`,\n );\n }\n for (const item of result.skipped) {\n const head =\n item.statement.objectType && item.statement.fqn\n ? `${item.statement.objectType} ${qualified(item.statement)}`\n : `<unclassified statement at line ${item.statement.startLine}>`;\n console.log(` skip ${head} — ${item.reason}`);\n }\n console.log('');\n console.log(\n `Summary: ${result.imported.length} ${verb}, ${result.skipped.length} skipped, ` +\n `${parsed.totalErrors} errors, ${parsed.totalWarnings} warnings.`,\n );\n\n if (result.imported.length === 0 && result.skipped.length > 0) {\n process.exitCode = 1;\n }\n });\n return cmd;\n}\n\nfunction qualified(stmt: ParsedStatement): string {\n if (!stmt.fqn) return '<no fqn>';\n return [stmt.fqn.database, stmt.fqn.schema, stmt.fqn.name].filter(Boolean).join('.');\n}\n\nfunction printParseReport(scriptPath: string, parsed: ReturnType<typeof parseScript>): void {\n console.log(`Parsed ${parsed.statements.length} statement(s) from ${scriptPath}.`);\n if (parsed.totalErrors === 0 && parsed.totalWarnings === 0) {\n console.log('All statements classified cleanly.');\n return;\n }\n for (const stmt of parsed.statements) {\n if (stmt.errors.length === 0 && stmt.warnings.length === 0) continue;\n const head =\n stmt.objectType && stmt.fqn ? `${stmt.objectType} ${qualified(stmt)}` : '<unclassified>';\n console.log(`\\n[line ${stmt.startLine}–${stmt.endLine}] ${head}`);\n for (const e of stmt.errors) console.log(` ERROR: ${e}`);\n for (const w of stmt.warnings) console.log(` warning: ${w}`);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport {\n CompareEngine,\n DatabricksExecutor,\n PacSource,\n ProjectSource,\n ScriptGenerator,\n ai,\n aiPreflight,\n approval,\n buildDeployManifest,\n buildExecutionPlan,\n colorizeMigrationScript,\n compileSlice,\n createConnection,\n defaultSafePlan,\n executePlan,\n getProfile,\n lintSql,\n loadProject,\n mergeDeployOptions,\n pac as pacNs,\n pacFreshness,\n protectedObjects,\n queryExecution,\n recordDeployChangelog,\n renderHtmlReport,\n resolveProfile,\n safety,\n testFramework,\n validatePlan,\n type CompareSource,\n type StepResult,\n} from '@ddt-tools/core';\nimport { addMappingFlags, buildMappingFromOptions, describeMapping } from '../util/mapping.js';\nimport { attachRelatedOptions } from '../util/help-catalog.js';\n\n/**\n * `ddt publish` — produce a migration script that drives a target toward a\n * source's desired state. `--dry-run` (default) generates the script and\n * prints the safety assessment + default-safe plan summary.\n * `--apply` executes the plan against the named connection profile via\n * `DatabricksExecutor` + `executePlan`, with automatic rollback on\n * partial failure.\n */\nexport function publishCommand(): Command {\n const cmd = new Command('publish');\n cmd\n .description('Generate (and optionally apply) a migration script from source → target.')\n .option(\n '--source <path>',\n 'Source: .ddtproj or .ddtpac (the desired state). Required for a normal publish; omit only with --restore-from-snapshot.',\n )\n .option(\n '--target <path>',\n 'Target: .ddtproj or .ddtpac (the current state). Required for a normal publish; omit only with --restore-from-snapshot.',\n )\n .option(\n '--restore-from-snapshot <batchId>',\n 'Recovery mode: skip the compare step and emit DROP/CREATE … SHALLOW CLONE against the snapshot batch <batchId> from the registry. ' +\n 'Dry-run by default; pass --apply --yes to execute. ' +\n \"This is the command printed by TRUST.4's post-deploy-smoke + TRUST.8's restore-hint when a deploy fails — they tell the operator to run `ddt publish --restore-from-snapshot <id>`.\",\n )\n .option(\n '--snapshot-dir <path>',\n 'Snapshot registry directory used with --restore-from-snapshot.',\n '.ddt/snapshots',\n )\n .option(\n '--out <path>',\n 'Write the generated SQL to this path (currently honored on the --restore-from-snapshot recovery path).',\n )\n .option('--dry-run', 'Print the migration script and safety assessment without applying.', true)\n .option(\n '--apply',\n 'Apply the migration against --connection. Requires --connection. Default false.',\n false,\n )\n .option('--connection <name>', 'Connection profile to apply against. Required with --apply.')\n .option(\n '--yes',\n 'Skip interactive confirmation when applying. Required with --apply (no TTY today).',\n false,\n )\n .option('--variables <kv>', 'Comma-separated KEY=VALUE pairs for $(VAR) substitution.')\n .option('--ignore-case', 'Compare object FQNs case-insensitively.', false)\n .option(\n '--no-rollback',\n 'Skip rollback on partial failure (leaves DIRTY state). Default rollback ON.',\n )\n .option(\n '--require-reversible',\n 'Refuse before any DDL runs if any step lacks reverse SQL.',\n false,\n )\n .option(\n '--manifest <path>',\n 'Write a JSON deploy manifest (every step + status + duration + reverse SQL) to <path>. Audit-friendly.',\n )\n .option('--report-html <path>', 'Write a self-contained HTML compare-report to <path>.')\n .option(\n '--no-lint',\n 'Skip the lint gate. Default: lint the generated script and refuse --apply on ERROR-severity findings.',\n )\n .option(\n '--webhook <url>',\n 'POST a deploy-summary JSON to this URL after --apply completes. Or set $DDT_NOTIFY_WEBHOOK.',\n )\n .option(\n '--profile <name>',\n 'Use the deploymentProfiles[<name>] overlay from --source (.ddtproj). ' +\n 'Pulls profile.connection, profile.variables, profile.deployOptions on top of project defaults.',\n )\n .option(\n '--changelog [catalog]',\n 'After --apply, append a row to <catalog>.__ddt.deploy_log on the workspace. ' +\n \"Defaults catalog to the connection profile's default catalog. Liquibase-style audit table.\",\n )\n .option(\n '--no-slice',\n \"Disable the source's Project Slice (if it has one). Default: a sliced source partitions the diff automatically and target objects outside the slice are left untouched.\",\n )\n .option(\n '--query-tag <tag>',\n 'Tag attached to every executed statement so system.query.history can attribute the deploy. ' +\n 'Prepended as `-- DDT-TAG: <tag>` comment. ' +\n \"Defaults to 'ddt:<projectName>@<projectVersion>:<unix-ms>' when omitted.\",\n )\n .option(\n '--color <mode>',\n 'Color stdout: auto | always | never. Honors NO_COLOR / DDT_NO_COLOR env vars in auto mode.',\n 'auto',\n )\n .option(\n '--ai-preflight',\n \"Before --apply, ask AI to grade the operator's plain-language deploy description against the actual diff + safety findings. Blocks on 'mismatch' unless --confirm-after-preflight-mismatch is also passed.\",\n false,\n )\n .option(\n '--ai-preflight-text <narrative>',\n 'Non-interactive form of --ai-preflight. Supplies the narrative directly instead of reading from stdin.',\n )\n .option(\n '--strict-ai-preflight',\n \"Also block on 'partial' (not just 'mismatch'). Useful for production deploys.\",\n false,\n )\n .option(\n '--confirm-after-preflight-mismatch',\n 'Proceed even when --ai-preflight returns a mismatch verdict. Acknowledges that the AI grader flagged a discrepancy.',\n false,\n )\n .option(\n '--require-approvals <n>',\n 'Multi-approver gate: refuse --apply until <n> distinct approvers have signed off via `ddt approval add <deploy-id> --as <user>`. Team-tier.',\n )\n .option(\n '--approver <ids>',\n 'Comma-separated allow-list of approver IDs honoured by --require-approvals. Empty = any approver counts.',\n )\n .option(\n '--deploy-id <id>',\n 'Stable deploy identifier paired with --require-approvals (default: <connection>:<catalog>.<schema>).',\n )\n .option(\n '--approvals-root <path>',\n 'Approvals directory (default: .ddt/approvals).',\n path.join('.ddt', 'approvals'),\n )\n .option(\n '--allow-protected <fqns>',\n 'Comma-separated FQNs to exempt from the .ddt-protection.json gate. Repeatable.',\n (val: string, prev: string[]) => [...prev, val],\n [] as string[],\n )\n .option(\n '--allow-all-protected',\n 'Bypass all protection gates defined in .ddt-protection.json. Use with care.',\n false,\n )\n .option(\n '--freshness <mode>',\n 'Pac age check: warn (log if stale, continue), strict (block if stale), skip (no check). Default warn.',\n 'warn',\n )\n .option(\n '--post-deploy-tests <project>',\n 'After a successful --apply, discover YAML tests under <project>/tests/ and run them against the same connection. Exits 2 on any blocking (error-severity) DQ failure. Emits POST_DEPLOY_DQ_FAILED hint with --manifest recipe when tests fail.',\n );\n addMappingFlags(cmd);\n cmd.action(async (opts) => {\n // Recovery mode — branch out of the normal compare/apply flow and emit\n // DROP/CREATE … SHALLOW CLONE from a recorded snapshot batch.\n if (opts.restoreFromSnapshot) {\n await runRestoreFromSnapshot(opts);\n return;\n }\n if (!opts.source || !opts.target) {\n throw new Error(\n '--source <path> and --target <path> are required (unless --restore-from-snapshot is given).',\n );\n }\n const nameMapping = await buildMappingFromOptions(opts);\n const sourcePath = String(opts.source);\n const targetPath = String(opts.target);\n const engine = new CompareEngine();\n const src: CompareSource = sourcePath.endsWith('.ddtpac')\n ? new PacSource(sourcePath, 'source')\n : new ProjectSource(sourcePath, 'source');\n const tgt: CompareSource = targetPath.endsWith('.ddtpac')\n ? new PacSource(targetPath, 'target')\n : new ProjectSource(targetPath, 'target');\n\n // Freshness gate — only fires when source is a .ddtpac, before any network call.\n let loadedSrcPac: Awaited<ReturnType<typeof pacNs.readPac>> | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n loadedSrcPac = await pacNs.readPac(sourcePath);\n const freshnessMode = (opts.freshness as pacFreshness.FreshnessMode | undefined) ?? 'warn';\n if (freshnessMode !== 'skip') {\n const fr = pacFreshness.checkPacFreshness(loadedSrcPac.manifest.builtAt);\n if (fr.status !== 'fresh') {\n const msg = pacFreshness.formatFreshnessWarning(fr, sourcePath);\n if (freshnessMode === 'strict') {\n console.error(msg);\n process.exitCode = 1;\n return;\n } else {\n console.warn(msg);\n }\n }\n }\n }\n\n // Auto-load slice from the source. .ddtpac carries it in manifest;\n // .ddtproj has it as a top-level field. --no-slice bypasses.\n let slice: ReturnType<typeof compileSlice> | undefined;\n if (opts.slice !== false) {\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n if (srcPac.manifest.slice) slice = compileSlice(srcPac.manifest.slice);\n } else {\n const loaded = await loadProject(sourcePath);\n if (loaded.project.slice) slice = compileSlice(loaded.project.slice);\n }\n }\n\n const result = await engine.compare(src, tgt, {\n ignoreCase: !!opts.ignoreCase,\n ...(nameMapping ? { nameMapping } : {}),\n ...(slice ? { sliceFilter: slice } : {}),\n });\n const mappingSummary = describeMapping(nameMapping);\n if (mappingSummary) console.log(`Mapping: ${mappingSummary}`);\n if (slice) {\n const outside = result.outsideScope?.length ?? 0;\n const refs = result.referenced?.length ?? 0;\n console.log(\n `Slice active: ${result.objects.length} owned · ${outside} outside scope (untouched) · ${refs} referenced`,\n );\n }\n\n if (opts.reportHtml) {\n const html = renderHtmlReport(result, {\n title: `Publish ${result.source.label} → ${result.target.label}`,\n safety: safety.assess(result),\n });\n const htmlPath = path.resolve(String(opts.reportHtml));\n await fs.mkdir(path.dirname(htmlPath), { recursive: true });\n await fs.writeFile(htmlPath, html, 'utf8');\n console.log(`Wrote HTML report → ${htmlPath}`);\n }\n\n // --profile overlay (project-level deployOptions + variables + connection).\n // VARSYNTAX.2 — also reads from `.ddtpac` manifest's deploymentProfiles\n // snapshot when --source is a built pac (writable since the pac builder\n // snapshots loaded.project.deploymentProfiles into the manifest).\n let profileDeployment: ReturnType<typeof mergeDeployOptions>['deployment'] | undefined;\n let profileVariables: Record<string, string> = {};\n let profileConnection: string | undefined;\n if (opts.profile) {\n const profileName = String(opts.profile);\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n const block = srcPac.manifest.deploymentProfiles?.[profileName];\n if (!block) {\n const available = Object.keys(srcPac.manifest.deploymentProfiles ?? {});\n throw new Error(\n `--profile ${profileName}: no deploymentProfile by that name in the pac manifest. ` +\n (available.length === 0\n ? 'The pac carries no deploymentProfiles (was it built before VARSYNTAX.2, or does the .ddtproj declare any?).'\n : `Available: ${available.join(', ')}.`),\n );\n }\n if (block.variables) profileVariables = { ...block.variables };\n if (block.connection) profileConnection = block.connection;\n // pac-snapshot path does NOT overlay profile-level deployOptions —\n // those are already merged into manifest.deployOptions at build\n // time. Leave profileDeployment undefined; the caller falls back\n // to `mergeDeployOptions().deployment`.\n console.log(\n `Profile: ${profileName}${profileConnection ? ` (connection=${profileConnection})` : ''}`,\n );\n } else {\n const loaded = await loadProject(sourcePath);\n const resolved = resolveProfile(loaded, profileName);\n profileDeployment = resolved.deployment;\n profileVariables = resolved.variables;\n profileConnection = resolved.connection;\n console.log(`Profile: ${resolved.name} (connection=${resolved.connection})`);\n }\n }\n\n const assessment = safety.assess(result);\n const deployment = profileDeployment ?? mergeDeployOptions().deployment;\n const variables = { ...profileVariables, ...(parseVariables(opts.variables) ?? {}) };\n\n // VARSYNTAX.3 — assemble the deploy context so `$(DDT_PROFILE)`,\n // `$(DDT_PROJECT_NAME)`, `$(DDT_PROJECT_VERSION)`, `$(DDT_PLATFORM)`,\n // `$(DDT_TIMESTAMP)`, `$(DDT_USER)` substitute without the operator\n // having to pass them via --variables. projectName / projectVersion\n // come from whichever source was loaded (.ddtpac manifest or\n // .ddtproj); profile defaults to undefined unless --profile was used.\n let ctxProjectName: string | undefined;\n let ctxProjectVersion: string | undefined;\n if (sourcePath.endsWith('.ddtpac')) {\n const srcPac = loadedSrcPac ?? (await pacNs.readPac(sourcePath));\n ctxProjectName = srcPac.manifest.projectName;\n ctxProjectVersion = srcPac.manifest.projectVersion;\n } else {\n try {\n const loaded = await loadProject(sourcePath);\n ctxProjectName = loaded.project.name;\n ctxProjectVersion = loaded.project.version;\n } catch {\n // best-effort; built-in vars stay defaulted\n }\n }\n const generateContext: {\n profile?: string;\n projectName?: string;\n projectVersion?: string;\n user?: string;\n } = {\n ...(opts.profile ? { profile: String(opts.profile) } : {}),\n ...(ctxProjectName ? { projectName: ctxProjectName } : {}),\n ...(ctxProjectVersion ? { projectVersion: ctxProjectVersion } : {}),\n };\n\n const generator = new ScriptGenerator();\n const script = generator.generate(result, {\n variables,\n deployment,\n context: generateContext,\n });\n\n const plan = defaultSafePlan(result);\n const validated = validatePlan(result, plan);\n const steps = buildExecutionPlan(result, validated, { deployment });\n\n // Protection gate — block operations on objects declared in .ddt-protection.json.\n const protConfig = await protectedObjects.loadProtectionConfig(path.dirname(sourcePath));\n if (protConfig) {\n const ops = result.objects\n .filter((o) => o.kind !== 'unchanged' && o.kind !== 'added')\n .map((o) => ({\n fqn: o.identity.fqn,\n objectType: String(o.identity.objectType),\n kind: o.kind,\n }));\n const allowedFqns = ((opts.allowProtected as string[]) ?? []).flatMap((s: string) =>\n s.split(',').map((f: string) => f.trim()),\n );\n const violations = protectedObjects.checkProtectedObjects(ops, protConfig, {\n allowedFqns,\n allowAll: Boolean(opts.allowAllProtected),\n });\n if (violations.length > 0) {\n console.error(protectedObjects.formatProtectionViolations(violations));\n process.exitCode = 1;\n return;\n }\n }\n\n printPlanReport(result.summary, script.summary, assessment, steps);\n\n // ── apply path ──────────────────────────────────────────────────────\n if (opts.apply) {\n const connectionName = opts.connection ?? profileConnection;\n if (!connectionName) {\n throw new Error(\n '--apply requires --connection <profile-name> or --profile <env> (supplies connection).',\n );\n }\n if (!opts.yes) {\n throw new Error(\n '--apply requires --yes. The CLI does not currently prompt interactively. Pass --yes to confirm.',\n );\n }\n if (assessment.blocked) {\n console.error(\n `Refusing to apply: safety classifier blocked the deploy (${assessment.blockReason}).`,\n );\n process.exitCode = 2;\n return;\n }\n\n // Approval gate (Team-tier): refuse to apply until N distinct\n // approvers have recorded approval tokens for this deploy id.\n if (opts.requireApprovals !== undefined && opts.requireApprovals !== false) {\n const required = Number(opts.requireApprovals) || 0;\n const allowed = opts.approver\n ? String(opts.approver)\n .split(',')\n .map((s: string) => s.trim())\n .filter(Boolean)\n : [];\n const deployId = String(opts.deployId ?? `${connectionName}:default`);\n const approvalsRoot = String(opts.approvalsRoot);\n const file = await approval.readApprovalFile(approvalsRoot, deployId);\n const outcome = approval.evaluateApprovalGate(\n { deployId, required, allowedApprovers: allowed },\n file.approvals,\n );\n if (!outcome.satisfied) {\n console.error(\n 'Approval gate blocked deploy: ' +\n (outcome.blockReason ?? 'gate not satisfied') +\n `. Record approvals with \\`ddt approval add ${deployId} --as <user>\\`.`,\n );\n process.exitCode = 2;\n return;\n }\n console.error(\n ` approvals: ${outcome.satisfiedBy.length}/${required} ` +\n `(${outcome.satisfiedBy.join(', ')})`,\n );\n }\n\n // AI preflight grader: ask the operator to type what they expect\n // this deploy to do, then let an AI compare that narrative to the\n // actual SQL + safety findings. Blocks deploy on `mismatch`.\n if (opts.aiPreflight) {\n const narrative = await readPreflightNarrative(opts.aiPreflightText);\n if (!narrative) {\n console.error(\n '--ai-preflight needs a narrative. Provide it inline via --ai-preflight-text or via stdin.',\n );\n process.exitCode = 2;\n return;\n }\n console.log('Grading deploy narrative with AI...');\n try {\n const findings = [\n ...assessment.unrecoverable.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.destructive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ...assessment.expensive.map((f) => ({\n code: String(f.code),\n fqn: f.fqn,\n reason: f.reason,\n })),\n ];\n const compareSummary = `added ${result.summary.added}, modified ${result.summary.modified}, removed ${result.summary.removed}, unchanged ${result.summary.unchanged}`;\n const verdict = await aiPreflight.gradePreflight(\n {\n narrative,\n compareSummary,\n safetyFindings: findings,\n diffSampleDdl: script.sql,\n target: `${result.target.label}@${connectionName ?? 'unknown'}`,\n },\n {\n completeFn: async (prompt) => {\n const r = await ai.complete([{ role: 'user', content: prompt }], {\n feature: 'ai-preflight',\n });\n return r.text;\n },\n },\n );\n console.log(` preflight verdict: ${verdict.verdict}`);\n if (verdict.reasoning) console.log(` preflight reasoning: ${verdict.reasoning}`);\n for (const d of verdict.discrepancies) console.log(` • ${d}`);\n\n const isBlocking =\n verdict.verdict === 'mismatch' ||\n (opts.strictAiPreflight && verdict.verdict === 'partial');\n if (isBlocking && !opts.confirmAfterPreflightMismatch) {\n console.error(\n `Refusing to apply: AI preflight returned \"${verdict.verdict}\". Re-run with --confirm-after-preflight-mismatch to override, or revise the narrative.`,\n );\n process.exitCode = 2;\n return;\n }\n if (verdict.parseFailed) {\n console.warn(\n ' preflight verdict could not be parsed — continuing without a clean alignment signal.',\n );\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(\n `--ai-preflight failed: ${msg}. Configure an AI provider via \\`ddt ai status\\` or omit --ai-preflight.`,\n );\n process.exitCode = 2;\n return;\n }\n }\n\n // Lint gate — refuse --apply when the generated script trips an\n // ERROR-severity lint rule (L003 ADD NOT NULL no DEFAULT, L007\n // REFUSED placeholder). Opt out with --no-lint when you've\n // reviewed the script and want to proceed.\n if (opts.lint !== false) {\n const lint = lintSql(script.sql);\n if (lint.errorCount > 0) {\n console.error('');\n console.error(\n `Refusing to apply: lint gate caught ${lint.errorCount} ERROR-severity finding(s).`,\n );\n for (const f of lint.findings) {\n if (f.severity !== 'ERROR') continue;\n console.error(` ${f.rule} line ${f.line}: ${f.message}`);\n if (f.suggestion) console.error(` → ${f.suggestion}`);\n }\n console.error('');\n console.error('Resolve the findings or re-run with --no-lint to override.');\n process.exitCode = 2;\n return;\n }\n if (lint.warningCount > 0) {\n console.log(`Lint: ${lint.warningCount} warning(s) — proceeding.`);\n }\n }\n\n const profile = await getProfile(String(connectionName));\n const conn = createConnection(profile);\n const queryTag = resolveQueryTag(\n opts.queryTag,\n sourcePath,\n result.source.label,\n ctxProjectName,\n ctxProjectVersion,\n );\n if (queryTag) console.log(`Query tag: ${queryTag}`);\n try {\n await conn.connect();\n const executor = new DatabricksExecutor(conn, {\n ...(queryTag ? { queryTag } : {}),\n onStatement: (sql) => {\n const head = sql.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(` → ${head}${sql.length > 100 ? ' …' : ''}`);\n },\n });\n console.log('');\n console.log('--- APPLYING ---');\n const report = await executePlan(steps, executor, {\n rollbackOnFailure: opts.rollback !== false,\n requireReversible: !!opts.requireReversible,\n onStepStart: (step) => {\n console.log(`▶ ${step.objectType} ${step.fqn} — ${step.description}`);\n },\n onStepEnd: (r: StepResult) => {\n const tag =\n r.status === 'SUCCESS'\n ? '✓'\n : r.status === 'ROLLED_BACK'\n ? '↩'\n : r.status === 'FAILED'\n ? '✗'\n : r.status === 'SKIPPED'\n ? '-'\n : '?';\n console.log(\n ` ${tag} ${r.status}${r.durationMs ? ` (${r.durationMs}ms)` : ''}${r.error ? `: ${r.error}` : ''}`,\n );\n },\n });\n console.log('');\n console.log('--- DEPLOYMENT REPORT ---');\n console.log(`finalState: ${report.finalState}`);\n if (report.failedStepId) console.log(`failedStepId: ${report.failedStepId}`);\n if (report.preflightErrors?.length) {\n console.log('preflightErrors:');\n for (const e of report.preflightErrors) console.log(` ${e}`);\n }\n const manifestJson = JSON.stringify(\n buildDeployManifest(report, profile.auth.host),\n null,\n 2,\n );\n if (opts.manifest) {\n const mPath = path.resolve(String(opts.manifest));\n await fs.mkdir(path.dirname(mPath), { recursive: true });\n await fs.writeFile(mPath, manifestJson + '\\n', 'utf8');\n console.log(`Wrote deploy manifest → ${mPath}`);\n }\n\n // Target-side changelog table — best-effort. Failures are reported\n // (the deploy already succeeded) but never thrown.\n if (opts.changelog) {\n const catalog = opts.changelog === true ? undefined : String(opts.changelog);\n const r = await recordDeployChangelog(\n conn,\n report,\n result.source,\n result.target,\n manifestJson,\n '0.3.0',\n catalog ? { catalog } : {},\n );\n if (r.ok) {\n console.log(`Changelog: deploy_id=${r.entry.deployId} appended.`);\n } else {\n console.error(`Changelog write failed (deploy still completed): ${r.error}`);\n }\n }\n\n // Webhook notification — fire-and-forget POST with a small JSON\n // summary. Slack / Teams / generic JSON receivers all work.\n const webhookUrl = opts.webhook ?? process.env['DDT_NOTIFY_WEBHOOK'];\n if (webhookUrl) {\n try {\n await postWebhook(String(webhookUrl), {\n workspaceHost: profile.auth.host,\n source: result.source.label,\n target: result.target.label,\n finalState: report.finalState,\n failedStepId: report.failedStepId,\n stepsTotal: report.steps.length,\n stepsSucceeded: report.steps.filter((s) => s.status === 'SUCCESS').length,\n stepsFailed: report.steps.filter((s) => s.status === 'FAILED').length,\n });\n console.log(`Webhook notified → ${webhookUrl}`);\n } catch (err) {\n console.error(\n `Webhook POST failed (deploy still completed): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n if (report.finalState === 'DIRTY') {\n console.error(\n 'DIRTY: rollback could not complete. Manual intervention required — see report above.',\n );\n process.exitCode = 1;\n } else if (report.finalState !== 'CLEAN_SUCCESS') {\n process.exitCode = 1;\n }\n\n if (opts.postDeployTests && report.finalState === 'CLEAN_SUCCESS') {\n const projectRoot = path.resolve(String(opts.postDeployTests));\n console.log('Running post-deploy DQ tests from ' + projectRoot + '...');\n const testFiles = await testFramework.discoverTests(projectRoot);\n if (testFiles.length === 0) {\n console.warn(\n ' POST_DEPLOY_DQ: no tests found under ' + projectRoot + '/tests/ — skipping.',\n );\n } else {\n const dqReport = await testFramework.runTestFiles(testFiles, conn);\n process.stdout.write(testFramework.renderTextReport(dqReport));\n if (dqReport.summary.blocking) {\n console.error(\n 'POST_DEPLOY_DQ_FAILED: ' +\n dqReport.summary.fail +\n ' fail / ' +\n dqReport.summary.error +\n ' error.' +\n (opts.manifest\n ? ' Recover: `ddt revert --manifest ' + String(opts.manifest) + '`'\n : ' Inspect tests/ and re-run after fixing data issues.'),\n );\n process.exitCode = 2;\n } else {\n console.log(\n ' POST_DEPLOY_DQ: ' +\n dqReport.summary.pass +\n '/' +\n dqReport.summary.total +\n ' pass.',\n );\n }\n }\n }\n } finally {\n await conn.disconnect();\n }\n return;\n }\n\n // ── dry-run path (default) ──────────────────────────────────────────\n // Colorize the script for terminal output so destructive lines and safety\n // banners are unmissable; ANSI is stripped in auto mode when stdout is\n // not a TTY (e.g. piped to a file or CI log).\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n console.log('');\n console.log('--- MIGRATION SCRIPT ---');\n console.log(colorizeMigrationScript(script.sql, { mode: colorMode }));\n });\n attachRelatedOptions(cmd, [\n 'deployment.allowDropTable',\n 'deployment.allowDropColumn',\n 'deployment.allowUnrecoverableDrop',\n 'deployment.allowNarrowingTypes',\n 'deployment.allowTableRebuild',\n 'deployment.blockOnPossibleDataLoss',\n 'deployment.preserveTargetOnlyObjects',\n ]);\n return cmd;\n}\n\nfunction printPlanReport(\n diffSummary: { added: number; removed: number; modified: number; unchanged: number },\n scriptSummary: { destructive: number; refused: number },\n assessment: ReturnType<typeof safety.assess>,\n steps: ReturnType<typeof buildExecutionPlan>,\n): void {\n console.log('--- COMPARE SUMMARY ---');\n console.log(\n `+${diffSummary.added} -${diffSummary.removed} ~${diffSummary.modified} =${diffSummary.unchanged}` +\n (scriptSummary.destructive ? ` (destructive=${scriptSummary.destructive})` : '') +\n (scriptSummary.refused ? ` (refused=${scriptSummary.refused})` : ''),\n );\n console.log('');\n console.log('--- SAFETY ASSESSMENT ---');\n console.log(`unrecoverable=${assessment.unrecoverable.length}`);\n console.log(`destructive=${assessment.destructive.length}`);\n console.log(`expensive=${assessment.expensive.length}`);\n console.log(`warnings=${assessment.warnings.length}`);\n if (assessment.blocked) console.log(`BLOCKED: ${assessment.blockReason}`);\n console.log('');\n console.log('--- DEFAULT-SAFE PLAN ---');\n const reversible = steps.filter((st) => st.reverseSql).length;\n console.log(`${steps.length} step(s) planned (default-safe selection)`);\n console.log(`${reversible} step(s) reversible · ${steps.length - reversible} irreversible`);\n}\n\nasync function postWebhook(url: string, payload: unknown): Promise<void> {\n const isSlackOrTeams = /hooks\\.slack\\.com|webhook\\.office\\.com/.test(url);\n let body: string;\n if (isSlackOrTeams) {\n // Both Slack and Teams accept a simple { text: \"...\" } shape.\n const summary = payload as {\n finalState: string;\n target: string;\n stepsTotal: number;\n stepsSucceeded: number;\n stepsFailed: number;\n };\n body = JSON.stringify({\n text: `DDT publish → ${summary.target} · ${summary.finalState} · ${summary.stepsSucceeded}/${summary.stepsTotal} steps ok${summary.stepsFailed ? ` · ${summary.stepsFailed} failed` : ''}`,\n });\n } else {\n body = JSON.stringify(payload);\n }\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n if (!res.ok) {\n throw new Error(`HTTP ${res.status} ${res.statusText}`);\n }\n}\n\n// PD7.2 — parse `--variables KEY=VALUE,KEY=VALUE,...` using indexOf split\n// so a `=` inside the value (base64, JWT, connection-string fragments) is\n// preserved instead of being treated as a second delimiter. Mirrors the SDT\n// parsePublishVariables contract.\nexport function parseVariables(raw: unknown): Record<string, string> | undefined {\n if (!raw) return undefined;\n const out: Record<string, string> = {};\n for (const pair of String(raw).split(',')) {\n const eq = pair.indexOf('=');\n if (eq <= 0) continue;\n const k = pair.slice(0, eq).trim();\n const v = pair.slice(eq + 1).trim();\n if (k.length > 0) out[k] = v;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Resolve the effective query tag. Honors an explicit `--query-tag`\n * flag value verbatim; otherwise auto-generates\n * `ddt:<projectName>@<projectVersion>:<unix-ms>` using the supplied\n * project context (pac manifest or .ddtproj). When projectName is\n * unknown, falls back to the source-path basename / sourceLabel; when\n * projectVersion is unknown the `@<projectVersion>` segment is omitted\n * so the tag remains valid. Returns undefined only when the user\n * explicitly passed `--query-tag ''`.\n */\nexport function resolveQueryTag(\n raw: unknown,\n sourcePath: string,\n sourceLabel: string,\n projectName: string | undefined,\n projectVersion: string | undefined,\n): string | undefined {\n if (raw !== undefined) {\n const s = String(raw).trim();\n return s.length === 0 ? undefined : s;\n }\n const fallback = sourcePath.endsWith('.ddtpac')\n ? path.basename(sourcePath, '.ddtpac')\n : sourceLabel;\n const base = projectName ?? fallback;\n const version = projectVersion ? `@${projectVersion}` : '';\n return `ddt:${base}${version}:${Date.now()}`;\n}\n\n/**\n * `ddt publish --restore-from-snapshot <batchId>` recovery path.\n *\n * Loads the batch from the registry, emits the restore SQL (DROP target +\n * CREATE … SHALLOW CLONE from snapshot, or a MANUAL hint row for\n * materialised / streaming objects), and either prints it (dry-run / no\n * --apply) or executes it against the live target (--apply --yes).\n *\n * Skips: pac freshness, compare, safety classifier, AI preflight, approval\n * gate, lint gate, manifest writing. Recovery is an emergency lever — the\n * gates that protect the forward path don't apply when we're restoring a\n * known-good snapshot the safety classifier itself authored.\n */\nasync function runRestoreFromSnapshot(opts: Record<string, unknown>): Promise<void> {\n const batchId = String(opts.restoreFromSnapshot);\n const registryDir = path.resolve(String(opts.snapshotDir ?? '.ddt/snapshots'));\n\n let batch: safety.SnapshotBatch;\n try {\n batch = await safety.readSnapshotBatch(registryDir, batchId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Snapshot batch \"${batchId}\" not found in ${registryDir}: ${msg}`);\n process.exitCode = 1;\n return;\n }\n\n const sqlLines = safety.emitRestoreFromSnapshotSql(batch);\n const sql = sqlLines.join('\\n');\n\n console.log(`Restoring from batch ${batch.batchId} (createdAt=${batch.createdAt})`);\n console.log(\n ` ${batch.entries.length} object(s) recorded; ${sqlLines.filter((l) => !l.startsWith('--')).length} statement(s) will run.`,\n );\n\n if (opts.out) {\n const out = path.resolve(String(opts.out));\n await fs.mkdir(path.dirname(out), { recursive: true });\n await fs.writeFile(out, sql + '\\n', 'utf8');\n console.log(`Wrote restore SQL → ${out}`);\n }\n\n // The DDT publish defaults --dry-run to true; recovery follows the same\n // convention: only --apply executes anything.\n if (!opts.apply) {\n const colorMode = (opts.color as 'auto' | 'always' | 'never' | undefined) ?? 'auto';\n process.stdout.write(colorizeMigrationScript(sql, { mode: colorMode }) + '\\n');\n return;\n }\n\n if (!opts.yes) {\n console.error(\n '--apply requires --yes (restore replaces target tables via DROP + SHALLOW CLONE).',\n );\n process.exitCode = 1;\n return;\n }\n if (!opts.connection) {\n console.error('--apply --restore-from-snapshot requires --connection <profile>.');\n process.exitCode = 1;\n return;\n }\n\n const profile = await getProfile(String(opts.connection));\n const conn = createConnection(profile);\n let executed = 0;\n let failed = 0;\n try {\n await conn.connect();\n for (const stmt of queryExecution.splitStatements(sql).map((s) => s.sql)) {\n try {\n await conn.executeRows(stmt);\n executed += 1;\n const head = stmt.split('\\n')[0]?.slice(0, 100) ?? '';\n console.log(' → ' + head + (stmt.length > 100 ? ' …' : ''));\n } catch (err) {\n failed += 1;\n const msg = err instanceof Error ? err.message : String(err);\n console.error(` [restore ${batch.batchId}] ${msg}\\n ${stmt}`);\n }\n }\n } finally {\n await conn.disconnect();\n }\n\n if (failed === 0) {\n console.log(`Restore complete. ${executed} statement(s) executed.`);\n } else {\n console.error(\n `Restore finished with failures: ${executed} ok, ${failed} failed. ` +\n `Inspect the snapshot tables under ${registryDir} and recover by hand.`,\n );\n process.exitCode = 1;\n }\n}\n\n/**\n * Read the AI-preflight narrative from --ai-preflight-text (preferred for CI)\n * or from stdin (interactive). Returns trimmed text, or undefined when no\n * narrative was supplied.\n */\nasync function readPreflightNarrative(inlineText: unknown): Promise<string | undefined> {\n if (typeof inlineText === 'string' && inlineText.trim().length > 0) {\n return inlineText.trim();\n }\n if (process.stdin.isTTY) {\n process.stdout.write(\n '\\n--ai-preflight is active. In one paragraph, describe what you expect this deploy to do.\\nEnd with an empty line (or Ctrl-D):\\n> ',\n );\n }\n process.stdin.setEncoding('utf8');\n let buffer = '';\n await new Promise<void>((resolve) => {\n const onData = (chunk: string): void => {\n buffer += chunk;\n if (/\\n\\s*\\n/.test(buffer)) {\n process.stdin.removeListener('data', onData);\n process.stdin.removeListener('end', onEnd);\n resolve();\n }\n };\n const onEnd = (): void => {\n process.stdin.removeListener('data', onData);\n resolve();\n };\n process.stdin.on('data', onData);\n process.stdin.on('end', onEnd);\n });\n const trimmed = buffer.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n"],"mappings":"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","names":[]}
|