@sdt-tools/cli 0.2.5 → 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 +44 -9
- package/dist/cli.js.map +1 -1
- package/dist/{connection-SYTH4V53.js → connection-GNTZDHXF.js} +24 -1
- package/dist/connection-GNTZDHXF.js.map +1 -0
- package/dist/{errorReporting-ZRNJ3VW7.js → errorReporting-AQXKKGZH.js} +2 -2
- package/dist/{errorReporting-ZRNJ3VW7.js.map → errorReporting-AQXKKGZH.js.map} +1 -1
- package/dist/import-script-2OF5BI6A.js +83 -0
- package/dist/import-script-2OF5BI6A.js.map +1 -0
- package/dist/index.cjs +35 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +35 -6
- package/dist/index.js.map +1 -1
- package/dist/{mcp-3QI4TH4N.js → mcp-SARDMCDV.js} +2 -2
- package/dist/{mcp-3QI4TH4N.js.map → mcp-SARDMCDV.js.map} +1 -1
- package/dist/{publish-Y2J56K4Y.js → publish-UMVIWH6H.js} +13 -7
- package/dist/publish-UMVIWH6H.js.map +1 -0
- package/package.json +13 -13
- package/dist/connection-SYTH4V53.js.map +0 -1
- package/dist/publish-Y2J56K4Y.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/import-script.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport {\n importScript,\n loadProject,\n parseScript,\n type ParsedStatement,\n} from '@sdt-tools/core/project';\n\n/**\n * `sdt import-script` — parse a SQL script and apply each DDL statement to a\n * `.sdtproj` tree, writing each into the canonical folder for its object\n * type (`databases/<db>/schemas/<sch>/<type-folder>/<name>.sql`).\n *\n * This is the script→tree splitter. For converting another tool's artifacts\n * (schemachange / SnowDDL / dacpac / dbt / Terraform / …) into a fresh\n * project, use `sdt import` instead. Byte-aligned with `ddt import-script`.\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 importScriptCommand(): Command {\n const cmd = new Command('import-script');\n cmd\n .description(\n 'Parse a SQL script and write each DDL statement into the .sdtproj 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 .sdtproj 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":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -716,11 +716,14 @@ function splitStatements(sql) {
|
|
|
716
716
|
function publishCommand() {
|
|
717
717
|
const cmd = new import_commander6.Command("publish");
|
|
718
718
|
cmd.description(
|
|
719
|
-
"Compare a .sdtpac to a live Snowflake target and apply (or dry-run) the migration."
|
|
719
|
+
"Compare a .sdtpac (the desired state) to a live Snowflake target and apply (or dry-run) the migration. Shared form with `ddt publish`: `--source <desired> --connection <live-target>`."
|
|
720
|
+
).option(
|
|
721
|
+
"--source <path>",
|
|
722
|
+
"Path to the desired-state .sdtpac (canonical name; the same flag `ddt publish` uses). Required for a normal publish; omit only with --restore-from-snapshot."
|
|
720
723
|
).option(
|
|
721
724
|
"--pac <path>",
|
|
722
|
-
"
|
|
723
|
-
).requiredOption("-c, --connection <profile>", "Connection profile name").option(
|
|
725
|
+
"Back-compat alias for --source (the desired-state .sdtpac). Prefer --source for cross-platform parity with `ddt publish`."
|
|
726
|
+
).requiredOption("-c, --connection <profile>", "Connection profile name (the live target).").option(
|
|
724
727
|
"--restore-from-snapshot <batchId>",
|
|
725
728
|
"Recovery mode: skip the compare step and emit ALTER TABLE \u2026 SWAP WITH against the snapshot batch <batchId> from the registry. Dry-run by default; pass --apply --yes to execute. This is the command printed by TRUST.4's post-deploy-smoke + TRUST.8's restore-hint when a deploy fails \u2014 they tell the operator to run `sdt publish --restore-from-snapshot <id>`."
|
|
726
729
|
).option(
|
|
@@ -807,12 +810,15 @@ function publishCommand() {
|
|
|
807
810
|
await runRestoreFromSnapshot(opts);
|
|
808
811
|
return;
|
|
809
812
|
}
|
|
810
|
-
|
|
811
|
-
|
|
813
|
+
const pacRef = opts.source ?? opts.pac;
|
|
814
|
+
if (!pacRef) {
|
|
815
|
+
logger.error(
|
|
816
|
+
"--source <path> (or its alias --pac) is required (unless --restore-from-snapshot is given)."
|
|
817
|
+
);
|
|
812
818
|
process.exitCode = 1;
|
|
813
819
|
return;
|
|
814
820
|
}
|
|
815
|
-
const pacPath = import_node_path4.default.resolve(String(
|
|
821
|
+
const pacPath = import_node_path4.default.resolve(String(pacRef));
|
|
816
822
|
const pac10 = await (0, import_pac2.readPac)(pacPath);
|
|
817
823
|
const freshnessMode = opts.freshness ?? "warn";
|
|
818
824
|
if (freshnessMode !== "skip") {
|
|
@@ -1735,6 +1741,11 @@ function connectionCommand() {
|
|
|
1735
1741
|
await (0, import_connection5.upsertProfile)(profile);
|
|
1736
1742
|
logger.success(`Saved profile "${profile.name}".`);
|
|
1737
1743
|
});
|
|
1744
|
+
cmd.command("get <name>").description("Print a profile (secrets are redacted).").action(async (name) => {
|
|
1745
|
+
const profile = await (0, import_connection5.getProfile)(String(name));
|
|
1746
|
+
const redacted = { ...profile, auth: redactAuth(profile.auth) };
|
|
1747
|
+
logger.info(JSON.stringify(redacted, null, 2));
|
|
1748
|
+
});
|
|
1738
1749
|
cmd.command("remove").description("Remove a connection profile.").argument("<name>").action(async (name) => {
|
|
1739
1750
|
const ok = await (0, import_connection5.removeProfile)(String(name));
|
|
1740
1751
|
if (ok) logger.success(`Removed profile "${name}".`);
|
|
@@ -1752,6 +1763,24 @@ function connectionCommand() {
|
|
|
1752
1763
|
});
|
|
1753
1764
|
return cmd;
|
|
1754
1765
|
}
|
|
1766
|
+
function redactAuth(auth) {
|
|
1767
|
+
const isPlaceholder = (v) => v.startsWith("env:") || v.startsWith("keyring:");
|
|
1768
|
+
switch (auth.method) {
|
|
1769
|
+
case "PASSWORD":
|
|
1770
|
+
case "MFA":
|
|
1771
|
+
return auth.password && !isPlaceholder(auth.password) ? { ...auth, password: "<redacted>" } : auth;
|
|
1772
|
+
case "OAUTH":
|
|
1773
|
+
return auth.token && !isPlaceholder(auth.token) ? { ...auth, token: "<redacted>" } : auth;
|
|
1774
|
+
case "KEY_PAIR":
|
|
1775
|
+
return auth.privateKeyPassphrase && !isPlaceholder(auth.privateKeyPassphrase) ? { ...auth, privateKeyPassphrase: "<redacted>" } : auth;
|
|
1776
|
+
case "EXTERNAL_BROWSER":
|
|
1777
|
+
return auth;
|
|
1778
|
+
default: {
|
|
1779
|
+
const _exhaustive = auth;
|
|
1780
|
+
return _exhaustive;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1755
1784
|
|
|
1756
1785
|
// src/commands/scaffold.ts
|
|
1757
1786
|
var import_commander10 = require("commander");
|