frg-data-diff 2.0.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 +628 -0
- package/dist/apply/apply-diff.d.ts +24 -0
- package/dist/apply/apply-diff.js +205 -0
- package/dist/apply/conflict.d.ts +20 -0
- package/dist/apply/conflict.js +14 -0
- package/dist/apply/plan.d.ts +13 -0
- package/dist/apply/plan.js +37 -0
- package/dist/cli/apply.d.ts +8 -0
- package/dist/cli/apply.js +270 -0
- package/dist/cli/generator.d.ts +9 -0
- package/dist/cli/generator.js +804 -0
- package/dist/cli/pg-triggers.d.ts +2 -0
- package/dist/cli/pg-triggers.js +185 -0
- package/dist/cli/root.d.ts +8 -0
- package/dist/cli/root.js +231 -0
- package/dist/cli/sql.d.ts +9 -0
- package/dist/cli/sql.js +158 -0
- package/dist/config/config-schema.d.ts +380 -0
- package/dist/config/config-schema.js +95 -0
- package/dist/config/load-config.d.ts +12 -0
- package/dist/config/load-config.js +87 -0
- package/dist/config/resolve-options.d.ts +95 -0
- package/dist/config/resolve-options.js +183 -0
- package/dist/config/write-config.d.ts +18 -0
- package/dist/config/write-config.js +103 -0
- package/dist/db/connection.d.ts +28 -0
- package/dist/db/connection.js +34 -0
- package/dist/db/metadata.d.ts +70 -0
- package/dist/db/metadata.js +238 -0
- package/dist/db/pg-triggers.d.ts +27 -0
- package/dist/db/pg-triggers.js +59 -0
- package/dist/db/sql.d.ts +72 -0
- package/dist/db/sql.js +250 -0
- package/dist/diff/diff-schema.d.ts +380 -0
- package/dist/diff/diff-schema.js +67 -0
- package/dist/diff/generate-diff.d.ts +20 -0
- package/dist/diff/generate-diff.js +224 -0
- package/dist/diff/pg-triggers-diff.d.ts +11 -0
- package/dist/diff/pg-triggers-diff.js +161 -0
- package/dist/diff/serialize-value.d.ts +35 -0
- package/dist/diff/serialize-value.js +242 -0
- package/dist/diff/write-diff-yaml.d.ts +3 -0
- package/dist/diff/write-diff-yaml.js +48 -0
- package/dist/schema-diff/generate-schema-diff.d.ts +12 -0
- package/dist/schema-diff/generate-schema-diff.js +355 -0
- package/dist/schema-diff/generate-schema-sql.d.ts +20 -0
- package/dist/schema-diff/generate-schema-sql.js +187 -0
- package/dist/schema-diff/schema-diff-schema.d.ts +1088 -0
- package/dist/schema-diff/schema-diff-schema.js +70 -0
- package/dist/shared/env-values.d.ts +11 -0
- package/dist/shared/env-values.js +100 -0
- package/dist/shared/generator-wizard.d.ts +10 -0
- package/dist/shared/generator-wizard.js +337 -0
- package/dist/shared/identifiers.d.ts +24 -0
- package/dist/shared/identifiers.js +45 -0
- package/dist/shared/prompts.d.ts +26 -0
- package/dist/shared/prompts.js +104 -0
- package/dist/shared/summary.d.ts +33 -0
- package/dist/shared/summary.js +41 -0
- package/dist/sql/generate-sql.d.ts +19 -0
- package/dist/sql/generate-sql.js +223 -0
- package/package.json +39 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const commander_1 = require("commander");
|
|
39
|
+
const load_config_1 = require("../config/load-config");
|
|
40
|
+
const resolve_options_1 = require("../config/resolve-options");
|
|
41
|
+
const connection_1 = require("../db/connection");
|
|
42
|
+
const pg_triggers_diff_1 = require("../diff/pg-triggers-diff");
|
|
43
|
+
const program = new commander_1.Command();
|
|
44
|
+
program
|
|
45
|
+
.name("frg-data-diff pg-triggers")
|
|
46
|
+
.description("Compare PostgreSQL triggers and functions and write a SQL diff script.")
|
|
47
|
+
.option("--source-pg-host <host>", "Source database host")
|
|
48
|
+
.option("--source-pg-port <port>", "Source database port", (v) => parseInt(v, 10))
|
|
49
|
+
.option("--source-pg-database <db>", "Source database name")
|
|
50
|
+
.option("--source-pg-user <user>", "Source database user")
|
|
51
|
+
.option("--source-pg-password-env <value>", "Source DB password or $ENV_VAR reference")
|
|
52
|
+
.option("--source-pg-ssl", "Use SSL for source database connection")
|
|
53
|
+
.option("--no-source-pg-ssl", "Do not use SSL for source database connection")
|
|
54
|
+
.option("--dest-pg-host <host>", "Destination database host")
|
|
55
|
+
.option("--dest-pg-port <port>", "Destination database port", (v) => parseInt(v, 10))
|
|
56
|
+
.option("--dest-pg-database <db>", "Destination database name")
|
|
57
|
+
.option("--dest-pg-user <user>", "Destination database user")
|
|
58
|
+
.option("--dest-pg-password-env <value>", "Destination DB password or $ENV_VAR reference")
|
|
59
|
+
.option("--dest-pg-ssl", "Use SSL for destination database connection")
|
|
60
|
+
.option("--no-dest-pg-ssl", "Do not use SSL for destination database connection")
|
|
61
|
+
.option("--schema <schema>", "PostgreSQL schema to compare")
|
|
62
|
+
.option("--table <table...>", "Table(s) to include")
|
|
63
|
+
.option("--exclude-table <table...>", "Table(s) to exclude")
|
|
64
|
+
.option("--output <file>", "Output diff file path", "frg-triggers-diff.sql")
|
|
65
|
+
.option("--verbose", "Enable verbose logging")
|
|
66
|
+
.option("--config <file>", "Path to config file", load_config_1.DEFAULT_CONFIG_FILENAME);
|
|
67
|
+
program.parse(process.argv);
|
|
68
|
+
const opts = program.opts();
|
|
69
|
+
function normalizeOptionalBoolean(value) {
|
|
70
|
+
if (value === true)
|
|
71
|
+
return true;
|
|
72
|
+
if (value === false)
|
|
73
|
+
return false;
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
async function main() {
|
|
77
|
+
const configFilePath = path.resolve(opts["config"] || load_config_1.DEFAULT_CONFIG_FILENAME);
|
|
78
|
+
const configExists = fs.existsSync(configFilePath);
|
|
79
|
+
let generatorConfig = undefined;
|
|
80
|
+
if (configExists) {
|
|
81
|
+
const loadedConfig = (0, load_config_1.loadConfig)(configFilePath);
|
|
82
|
+
generatorConfig = loadedConfig.generator;
|
|
83
|
+
}
|
|
84
|
+
const cliArgs = {
|
|
85
|
+
sourcePgHost: opts["sourcePgHost"],
|
|
86
|
+
sourcePgPort: opts["sourcePgPort"],
|
|
87
|
+
sourcePgDatabase: opts["sourcePgDatabase"],
|
|
88
|
+
sourcePgUser: opts["sourcePgUser"],
|
|
89
|
+
sourcePgPassword: opts["sourcePgPasswordEnv"],
|
|
90
|
+
sourcePgSsl: normalizeOptionalBoolean(opts["sourcePgSsl"]),
|
|
91
|
+
destPgHost: opts["destPgHost"],
|
|
92
|
+
destPgPort: opts["destPgPort"],
|
|
93
|
+
destPgDatabase: opts["destPgDatabase"],
|
|
94
|
+
destPgUser: opts["destPgUser"],
|
|
95
|
+
destPgPassword: opts["destPgPasswordEnv"],
|
|
96
|
+
destPgSsl: normalizeOptionalBoolean(opts["destPgSsl"]),
|
|
97
|
+
schema: opts["schema"],
|
|
98
|
+
pgTriggersTables: opts["table"],
|
|
99
|
+
pgTriggersExcludeTables: opts["excludeTable"],
|
|
100
|
+
pgTriggersOutput: opts["output"],
|
|
101
|
+
verbose: opts["verbose"] ? true : undefined,
|
|
102
|
+
};
|
|
103
|
+
const cleanCliArgs = Object.fromEntries(Object.entries(cliArgs).filter(([, v]) => v !== undefined));
|
|
104
|
+
const resolved = (0, resolve_options_1.resolveGeneratorOptions)(generatorConfig, cleanCliArgs);
|
|
105
|
+
const runtimeResolved = (0, resolve_options_1.resolveRuntimeGeneratorOptions)({
|
|
106
|
+
...resolved,
|
|
107
|
+
tables: [...resolved.tables],
|
|
108
|
+
excludeTables: [...resolved.excludeTables],
|
|
109
|
+
schemaDiffTables: [...resolved.schemaDiffTables],
|
|
110
|
+
schemaDiffExcludeTables: [...resolved.schemaDiffExcludeTables],
|
|
111
|
+
pgTriggersTables: [...resolved.pgTriggersTables],
|
|
112
|
+
pgTriggersExcludeTables: [...resolved.pgTriggersExcludeTables],
|
|
113
|
+
ignoreColumns: [...resolved.ignoreColumns],
|
|
114
|
+
});
|
|
115
|
+
console.log("frg-data-diff: generate pg-triggers");
|
|
116
|
+
if (!resolved.sourcePgHost || !resolved.destPgHost) {
|
|
117
|
+
console.error("Missing generator configuration. Please run with full arguments or define config.");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const sourceConnection = (0, connection_1.resolveConnectionParams)({
|
|
121
|
+
host: resolved.sourcePgHost,
|
|
122
|
+
port: resolved.sourcePgPort,
|
|
123
|
+
database: resolved.sourcePgDatabase,
|
|
124
|
+
user: resolved.sourcePgUser,
|
|
125
|
+
password: resolved.sourcePgPassword,
|
|
126
|
+
ssl: runtimeResolved.sourcePgSsl,
|
|
127
|
+
}, {
|
|
128
|
+
host: "source host",
|
|
129
|
+
port: "source port",
|
|
130
|
+
database: "source database",
|
|
131
|
+
user: "source user",
|
|
132
|
+
password: "source password",
|
|
133
|
+
});
|
|
134
|
+
const destConnection = (0, connection_1.resolveConnectionParams)({
|
|
135
|
+
host: resolved.destPgHost,
|
|
136
|
+
port: resolved.destPgPort,
|
|
137
|
+
database: resolved.destPgDatabase,
|
|
138
|
+
user: resolved.destPgUser,
|
|
139
|
+
password: resolved.destPgPassword,
|
|
140
|
+
ssl: runtimeResolved.destPgSsl,
|
|
141
|
+
}, {
|
|
142
|
+
host: "dest host",
|
|
143
|
+
port: "dest port",
|
|
144
|
+
database: "dest database",
|
|
145
|
+
user: "dest user",
|
|
146
|
+
password: "dest password",
|
|
147
|
+
});
|
|
148
|
+
const sourcePool = (0, connection_1.createPool)(sourceConnection);
|
|
149
|
+
const destPool = (0, connection_1.createPool)(destConnection);
|
|
150
|
+
try {
|
|
151
|
+
console.log("Preparing pg-triggers diff...");
|
|
152
|
+
const diffOptions = {
|
|
153
|
+
schema: runtimeResolved.schema,
|
|
154
|
+
tables: runtimeResolved.pgTriggersTables,
|
|
155
|
+
excludeTables: runtimeResolved.pgTriggersExcludeTables,
|
|
156
|
+
verbose: runtimeResolved.verbose,
|
|
157
|
+
onProgress: (message) => {
|
|
158
|
+
console.log(message);
|
|
159
|
+
},
|
|
160
|
+
onVerboseProgress: runtimeResolved.verbose
|
|
161
|
+
? (message) => {
|
|
162
|
+
console.log(message);
|
|
163
|
+
}
|
|
164
|
+
: undefined,
|
|
165
|
+
};
|
|
166
|
+
const sql = await (0, pg_triggers_diff_1.generatePgTriggersDiffSql)(sourcePool, destPool, diffOptions);
|
|
167
|
+
const output = runtimeResolved.pgTriggersOutput;
|
|
168
|
+
fs.writeFileSync(output, sql, "utf-8");
|
|
169
|
+
console.log(`Wrote triggers and functions diff SQL to ${output}`);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error("Failed to generate pg-triggers diff.");
|
|
173
|
+
console.error(error.message);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
finally {
|
|
177
|
+
await sourcePool.end();
|
|
178
|
+
await destPool.end();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
main().catch((err) => {
|
|
182
|
+
console.error("Unhandled error:", err);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=pg-triggers.js.map
|
package/dist/cli/root.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* frg-data-diff
|
|
5
|
+
*
|
|
6
|
+
* Root CLI dispatcher. Routes subcommands to the focused tools.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
const child_process_1 = require("child_process");
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const subcommand = process.argv[2];
|
|
46
|
+
const subcommandArgs = process.argv.slice(3);
|
|
47
|
+
const commandFiles = {
|
|
48
|
+
generate: "generator.js",
|
|
49
|
+
apply: "apply.js",
|
|
50
|
+
sql: "sql.js",
|
|
51
|
+
"pg-triggers": "pg-triggers.js",
|
|
52
|
+
};
|
|
53
|
+
const helpText = `frg-data-diff
|
|
54
|
+
|
|
55
|
+
Usage:
|
|
56
|
+
|
|
57
|
+
npx frg-data-diff <command> [options]
|
|
58
|
+
|
|
59
|
+
Commands:
|
|
60
|
+
|
|
61
|
+
generate Compare source and destination PostgreSQL databases and write diff artifacts.
|
|
62
|
+
apply Read a JSON diff file and safely apply it to a destination PostgreSQL database.
|
|
63
|
+
sql Read a JSON diff file and write a plain SQL script for manual review and execution.
|
|
64
|
+
pg-triggers Compare PostgreSQL triggers and functions and write a SQL diff script.
|
|
65
|
+
version Print the package version.
|
|
66
|
+
|
|
67
|
+
Basic usage:
|
|
68
|
+
|
|
69
|
+
npx frg-data-diff generate
|
|
70
|
+
npx frg-data-diff apply --dry-run
|
|
71
|
+
npx frg-data-diff apply --execute
|
|
72
|
+
npx frg-data-diff sql --yes
|
|
73
|
+
npx frg-data-diff pg-triggers
|
|
74
|
+
npx frg-data-diff version
|
|
75
|
+
|
|
76
|
+
Config file:
|
|
77
|
+
.frg-data-diff.config.json
|
|
78
|
+
|
|
79
|
+
Commands look for .frg-data-diff.config.json in the current directory.
|
|
80
|
+
Create it by running a command with --config or with CLI args on first run.
|
|
81
|
+
|
|
82
|
+
How to generate a diff:
|
|
83
|
+
|
|
84
|
+
npx frg-data-diff generate \\
|
|
85
|
+
--source-pg-host dev-db.example.com \\
|
|
86
|
+
--source-pg-port 5432 \\
|
|
87
|
+
--source-pg-database app \\
|
|
88
|
+
--source-pg-user app_user \\
|
|
89
|
+
--source-pg-password-env '$PG_PASSWORD_DEV' \\
|
|
90
|
+
--dest-pg-host prod-db.example.com \\
|
|
91
|
+
--dest-pg-port 5432 \\
|
|
92
|
+
--dest-pg-database app \\
|
|
93
|
+
--dest-pg-user app_user \\
|
|
94
|
+
--dest-pg-password-env '$PG_PASSWORD_PROD' \\
|
|
95
|
+
--schema public \\
|
|
96
|
+
--output frg-data-diff.json \\
|
|
97
|
+
--include-deletes \\
|
|
98
|
+
--pretty
|
|
99
|
+
|
|
100
|
+
How to apply a diff (dry-run first):
|
|
101
|
+
|
|
102
|
+
npx frg-data-diff apply \\
|
|
103
|
+
--dest-pg-host prod-db.example.com \\
|
|
104
|
+
--dest-pg-port 5432 \\
|
|
105
|
+
--dest-pg-database app \\
|
|
106
|
+
--dest-pg-user app_user \\
|
|
107
|
+
--dest-pg-password-env '$PG_PASSWORD_PROD' \\
|
|
108
|
+
--input frg-data-diff.json \\
|
|
109
|
+
--dry-run
|
|
110
|
+
|
|
111
|
+
How to apply a diff (real execution):
|
|
112
|
+
|
|
113
|
+
npx frg-data-diff apply \\
|
|
114
|
+
--dest-pg-host prod-db.example.com \\
|
|
115
|
+
--dest-pg-port 5432 \\
|
|
116
|
+
--dest-pg-database app \\
|
|
117
|
+
--dest-pg-user app_user \\
|
|
118
|
+
--dest-pg-password-env '$PG_PASSWORD_PROD' \\
|
|
119
|
+
--input frg-data-diff.json \\
|
|
120
|
+
--execute
|
|
121
|
+
|
|
122
|
+
How to generate SQL from a diff:
|
|
123
|
+
|
|
124
|
+
npx frg-data-diff sql \\
|
|
125
|
+
--input frg-data-diff.json \\
|
|
126
|
+
--output frg-data-diff.sql \\
|
|
127
|
+
--apply-deletes \\
|
|
128
|
+
--yes
|
|
129
|
+
|
|
130
|
+
Config file example (.frg-data-diff.config.json):
|
|
131
|
+
|
|
132
|
+
{
|
|
133
|
+
"format": "frg-data-diff-config/v1",
|
|
134
|
+
"generator": {
|
|
135
|
+
"sourcePgHost": "dev-db.example.com",
|
|
136
|
+
"sourcePgPort": 5432,
|
|
137
|
+
"sourcePgDatabase": "app",
|
|
138
|
+
"sourcePgUser": "app_user",
|
|
139
|
+
"sourcePgPassword": "$PG_PASSWORD_DEV",
|
|
140
|
+
"destPgHost": "prod-db.example.com",
|
|
141
|
+
"destPgPort": 5432,
|
|
142
|
+
"destPgDatabase": "app",
|
|
143
|
+
"destPgUser": "app_user",
|
|
144
|
+
"destPgPassword": "$PG_PASSWORD_PROD",
|
|
145
|
+
"schema": "public",
|
|
146
|
+
"tables": ["my_table"],
|
|
147
|
+
"output": "frg-data-diff.json",
|
|
148
|
+
"schemaDiffTables": ["my_table"],
|
|
149
|
+
"schemaDiffOutput": "frg-schema-diff.json",
|
|
150
|
+
"pgTriggersTables": ["my_table"],
|
|
151
|
+
"pgTriggersOutput": "frg-triggers-diff.sql",
|
|
152
|
+
"includeDeletes": true,
|
|
153
|
+
"pretty": true
|
|
154
|
+
},
|
|
155
|
+
"apply": {
|
|
156
|
+
"destPgHost": "prod-db.example.com",
|
|
157
|
+
"destPgPort": 5432,
|
|
158
|
+
"destPgDatabase": "app",
|
|
159
|
+
"destPgUser": "app_user",
|
|
160
|
+
"destPgPassword": "$PG_PASSWORD_PROD",
|
|
161
|
+
"input": "frg-data-diff.json",
|
|
162
|
+
"dryRun": true,
|
|
163
|
+
"applyDeletes": false
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Safety notes:
|
|
168
|
+
|
|
169
|
+
- Do not commit secrets (passwords, connection strings) to version control.
|
|
170
|
+
- Connection values may be plain text or $ENV_VAR references.
|
|
171
|
+
- Passwords may be plain text, but $ENV_VAR is strongly recommended.
|
|
172
|
+
- Missing env vars will cause a clear error showing only the env var name, never the value.
|
|
173
|
+
- Apply defaults to dry-run mode. Use --execute to apply real changes.
|
|
174
|
+
- applyDeletes defaults to false. Deletes require explicit opt-in.
|
|
175
|
+
- Use --yes for CI/CD to skip interactive confirmation.
|
|
176
|
+
- If you choose plain-text passwords, they will be written exactly that way to the config file.
|
|
177
|
+
|
|
178
|
+
For CI/CD:
|
|
179
|
+
|
|
180
|
+
npx frg-data-diff generate --yes
|
|
181
|
+
npx frg-data-diff apply --yes --execute
|
|
182
|
+
`;
|
|
183
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
184
|
+
process.stdout.write(helpText);
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
if (subcommand === "version" ||
|
|
188
|
+
subcommand === "--version" ||
|
|
189
|
+
subcommand === "-v") {
|
|
190
|
+
console.log(readPackageVersion());
|
|
191
|
+
process.exit(0);
|
|
192
|
+
}
|
|
193
|
+
const commandFile = commandFiles[subcommand];
|
|
194
|
+
if (!commandFile) {
|
|
195
|
+
console.error(`Unknown command: ${subcommand}`);
|
|
196
|
+
console.error("");
|
|
197
|
+
process.stderr.write(helpText);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
const commandPath = path.join(__dirname, commandFile);
|
|
201
|
+
if (!fs.existsSync(commandPath)) {
|
|
202
|
+
console.error(`Command file not found: ${commandPath}`);
|
|
203
|
+
console.error("Run npm run build and try again.");
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
const result = (0, child_process_1.spawnSync)(process.execPath, [commandPath, ...subcommandArgs], {
|
|
207
|
+
stdio: "inherit",
|
|
208
|
+
});
|
|
209
|
+
if (result.error) {
|
|
210
|
+
console.error(result.error.message);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
if (result.signal) {
|
|
214
|
+
console.error(`Command terminated by signal: ${result.signal}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
process.exit(result.status ?? 0);
|
|
218
|
+
function readPackageVersion() {
|
|
219
|
+
const packagePath = path.resolve(__dirname, "../../package.json");
|
|
220
|
+
try {
|
|
221
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
222
|
+
if (typeof packageJson.version === "string" && packageJson.version) {
|
|
223
|
+
return packageJson.version;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// Fall through to a clear, stable value if package metadata is unavailable.
|
|
228
|
+
}
|
|
229
|
+
return "unknown";
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=root.js.map
|
package/dist/cli/sql.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* frg-data-diff sql
|
|
5
|
+
*
|
|
6
|
+
* Reads a JSON diff file and writes a plain SQL script that can be reviewed
|
|
7
|
+
* and executed manually.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
const commander_1 = require("commander");
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const load_config_1 = require("../config/load-config");
|
|
47
|
+
const resolve_options_1 = require("../config/resolve-options");
|
|
48
|
+
const diff_schema_1 = require("../diff/diff-schema");
|
|
49
|
+
const generate_sql_1 = require("../sql/generate-sql");
|
|
50
|
+
const prompts_1 = require("../shared/prompts");
|
|
51
|
+
const program = new commander_1.Command();
|
|
52
|
+
program
|
|
53
|
+
.name("frg-data-diff sql")
|
|
54
|
+
.description("Reads a JSON diff file and writes a SQL script for manual execution.")
|
|
55
|
+
.option("--input <file>", "Input diff file path")
|
|
56
|
+
.option("--output <file>", "Output SQL file path")
|
|
57
|
+
.option("--apply-inserts", "Include inserts in the SQL output")
|
|
58
|
+
.option("--no-apply-inserts", "Do not include inserts in the SQL output")
|
|
59
|
+
.option("--apply-updates", "Include updates in the SQL output")
|
|
60
|
+
.option("--no-apply-updates", "Do not include updates in the SQL output")
|
|
61
|
+
.option("--apply-deletes", "Include deletes in the SQL output")
|
|
62
|
+
.option("--no-apply-deletes", "Do not include deletes in the SQL output")
|
|
63
|
+
.option("--transaction", "Wrap the SQL in BEGIN/COMMIT")
|
|
64
|
+
.option("--no-transaction", "Do not wrap the SQL in BEGIN/COMMIT")
|
|
65
|
+
.option("--config <file>", "Path to config file", load_config_1.DEFAULT_CONFIG_FILENAME)
|
|
66
|
+
.option("--yes", "Skip interactive confirmation");
|
|
67
|
+
program.parse(process.argv);
|
|
68
|
+
const opts = program.opts();
|
|
69
|
+
async function main() {
|
|
70
|
+
const configFilePath = path.resolve(opts["config"] || load_config_1.DEFAULT_CONFIG_FILENAME);
|
|
71
|
+
const configExists = fs.existsSync(configFilePath);
|
|
72
|
+
const config = configExists ? (0, load_config_1.loadConfig)(configFilePath) : undefined;
|
|
73
|
+
const rawApplyOptions = (0, resolve_options_1.resolveApplyOptions)(config?.apply, {
|
|
74
|
+
input: opts["input"],
|
|
75
|
+
applyInserts: normalizeOptionalBoolean(opts["applyInserts"]),
|
|
76
|
+
applyUpdates: normalizeOptionalBoolean(opts["applyUpdates"]),
|
|
77
|
+
applyDeletes: normalizeOptionalBoolean(opts["applyDeletes"]),
|
|
78
|
+
transaction: normalizeOptionalBoolean(opts["transaction"]),
|
|
79
|
+
});
|
|
80
|
+
const runtimeApplyOptions = (0, resolve_options_1.resolveRuntimeApplyOptions)(rawApplyOptions);
|
|
81
|
+
const input = runtimeApplyOptions.input;
|
|
82
|
+
const output = opts["output"]
|
|
83
|
+
? path.resolve(opts["output"])
|
|
84
|
+
: path.resolve((0, generate_sql_1.buildSqlOutputPath)(input));
|
|
85
|
+
const applyInserts = runtimeApplyOptions.applyInserts;
|
|
86
|
+
const applyUpdates = runtimeApplyOptions.applyUpdates;
|
|
87
|
+
const applyDeletes = runtimeApplyOptions.applyDeletes;
|
|
88
|
+
const transaction = runtimeApplyOptions.transaction;
|
|
89
|
+
const inputPath = path.resolve(input);
|
|
90
|
+
const outputPath = output;
|
|
91
|
+
if (!fs.existsSync(inputPath)) {
|
|
92
|
+
console.error(`Error: Diff file not found: ${inputPath}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
printResolvedPlan({
|
|
96
|
+
inputPath,
|
|
97
|
+
outputPath,
|
|
98
|
+
applyInserts,
|
|
99
|
+
applyUpdates,
|
|
100
|
+
applyDeletes,
|
|
101
|
+
transaction,
|
|
102
|
+
});
|
|
103
|
+
if (!opts["yes"]) {
|
|
104
|
+
const proceed = await (0, prompts_1.confirmProceed)();
|
|
105
|
+
if (!proceed) {
|
|
106
|
+
console.log("Aborted.");
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let rawDiff;
|
|
111
|
+
try {
|
|
112
|
+
rawDiff = JSON.parse(fs.readFileSync(inputPath, "utf-8"));
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
console.error(`Error reading diff file: ${inputPath}`);
|
|
116
|
+
if (err instanceof Error)
|
|
117
|
+
console.error(err.message);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const diff = (0, diff_schema_1.validateDiffJson)(rawDiff);
|
|
121
|
+
const result = (0, generate_sql_1.generateSqlScript)(diff, {
|
|
122
|
+
applyInserts,
|
|
123
|
+
applyUpdates,
|
|
124
|
+
applyDeletes,
|
|
125
|
+
transaction,
|
|
126
|
+
});
|
|
127
|
+
fs.writeFileSync(outputPath, result.sql, "utf-8");
|
|
128
|
+
console.log(`\nSQL written to: ${outputPath}`);
|
|
129
|
+
console.log("\nSummary:");
|
|
130
|
+
console.log(` Inserts: ${result.summary.inserts}`);
|
|
131
|
+
console.log(` Updates: ${result.summary.updates}`);
|
|
132
|
+
console.log(` Deletes: ${result.summary.deletes}`);
|
|
133
|
+
console.log("\nReview the SQL carefully before executing it.");
|
|
134
|
+
}
|
|
135
|
+
function printResolvedPlan(args) {
|
|
136
|
+
console.log("\ntool:");
|
|
137
|
+
console.log(" frg-data-diff sql");
|
|
138
|
+
console.log(`\ninput: ${args.inputPath}`);
|
|
139
|
+
console.log(`output: ${args.outputPath}`);
|
|
140
|
+
console.log(`apply inserts: ${args.applyInserts}`);
|
|
141
|
+
console.log(`apply updates: ${args.applyUpdates}`);
|
|
142
|
+
console.log(`apply deletes: ${args.applyDeletes}`);
|
|
143
|
+
console.log(`transaction: ${args.transaction}`);
|
|
144
|
+
}
|
|
145
|
+
main().catch((err) => {
|
|
146
|
+
console.error("Fatal error:", err instanceof Error ? err.message : String(err));
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
149
|
+
function normalizeOptionalBoolean(value) {
|
|
150
|
+
if (value === true) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (value === false) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=sql.js.map
|