@memberjunction/cli 5.37.0 → 5.39.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 +74 -0
- package/dist/baseline/cli-helpers.d.ts +12 -0
- package/dist/baseline/cli-helpers.d.ts.map +1 -0
- package/dist/baseline/cli-helpers.js +40 -0
- package/dist/baseline/cli-helpers.js.map +1 -0
- package/dist/baseline/comparator.d.ts +26 -0
- package/dist/baseline/comparator.d.ts.map +1 -0
- package/dist/baseline/comparator.js +527 -0
- package/dist/baseline/comparator.js.map +1 -0
- package/dist/baseline/connection.d.ts +39 -0
- package/dist/baseline/connection.d.ts.map +1 -0
- package/dist/baseline/connection.js +104 -0
- package/dist/baseline/connection.js.map +1 -0
- package/dist/baseline/data-dumper.d.ts +23 -0
- package/dist/baseline/data-dumper.d.ts.map +1 -0
- package/dist/baseline/data-dumper.js +94 -0
- package/dist/baseline/data-dumper.js.map +1 -0
- package/dist/baseline/emitter.d.ts +36 -0
- package/dist/baseline/emitter.d.ts.map +1 -0
- package/dist/baseline/emitter.js +519 -0
- package/dist/baseline/emitter.js.map +1 -0
- package/dist/baseline/index.d.ts +18 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +14 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/baseline/introspector-mssql.d.ts +15 -0
- package/dist/baseline/introspector-mssql.d.ts.map +1 -0
- package/dist/baseline/introspector-mssql.js +673 -0
- package/dist/baseline/introspector-mssql.js.map +1 -0
- package/dist/baseline/introspector-postgres.d.ts +14 -0
- package/dist/baseline/introspector-postgres.d.ts.map +1 -0
- package/dist/baseline/introspector-postgres.js +277 -0
- package/dist/baseline/introspector-postgres.js.map +1 -0
- package/dist/baseline/report.d.ts +7 -0
- package/dist/baseline/report.d.ts.map +1 -0
- package/dist/baseline/report.js +105 -0
- package/dist/baseline/report.js.map +1 -0
- package/dist/baseline/types.d.ts +286 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +10 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/baseline/util.d.ts +112 -0
- package/dist/baseline/util.d.ts.map +1 -0
- package/dist/baseline/util.js +325 -0
- package/dist/baseline/util.js.map +1 -0
- package/dist/commands/baseline/build.d.ts +29 -0
- package/dist/commands/baseline/build.d.ts.map +1 -0
- package/dist/commands/baseline/build.js +188 -0
- package/dist/commands/baseline/build.js.map +1 -0
- package/dist/commands/baseline/compare.d.ts +19 -0
- package/dist/commands/baseline/compare.d.ts.map +1 -0
- package/dist/commands/baseline/compare.js +133 -0
- package/dist/commands/baseline/compare.js.map +1 -0
- package/dist/commands/baseline/roundtrip.d.ts +23 -0
- package/dist/commands/baseline/roundtrip.d.ts.map +1 -0
- package/dist/commands/baseline/roundtrip.js +245 -0
- package/dist/commands/baseline/roundtrip.js.map +1 -0
- package/dist/commands/bundle/index.d.ts +21 -0
- package/dist/commands/bundle/index.d.ts.map +1 -0
- package/dist/commands/bundle/index.js +62 -0
- package/dist/commands/bundle/index.js.map +1 -0
- package/dist/commands/migrate/index.d.ts +29 -0
- package/dist/commands/migrate/index.d.ts.map +1 -1
- package/dist/commands/migrate/index.js +111 -5
- package/dist/commands/migrate/index.js.map +1 -1
- package/dist/commands/test/compare.d.ts +3 -0
- package/dist/commands/test/compare.d.ts.map +1 -1
- package/dist/commands/test/compare.js +23 -3
- package/dist/commands/test/compare.js.map +1 -1
- package/dist/commands/test/regression/build.d.ts +14 -0
- package/dist/commands/test/regression/build.d.ts.map +1 -0
- package/dist/commands/test/regression/build.js +48 -0
- package/dist/commands/test/regression/build.js.map +1 -0
- package/dist/commands/test/regression/compare.d.ts +15 -0
- package/dist/commands/test/regression/compare.d.ts.map +1 -0
- package/dist/commands/test/regression/compare.js +66 -0
- package/dist/commands/test/regression/compare.js.map +1 -0
- package/dist/commands/test/regression/down.d.ts +10 -0
- package/dist/commands/test/regression/down.d.ts.map +1 -0
- package/dist/commands/test/regression/down.js +29 -0
- package/dist/commands/test/regression/down.js.map +1 -0
- package/dist/commands/test/regression/export.d.ts +11 -0
- package/dist/commands/test/regression/export.d.ts.map +1 -0
- package/dist/commands/test/regression/export.js +56 -0
- package/dist/commands/test/regression/export.js.map +1 -0
- package/dist/commands/test/regression/gen-forms.d.ts +7 -0
- package/dist/commands/test/regression/gen-forms.d.ts.map +1 -0
- package/dist/commands/test/regression/gen-forms.js +18 -0
- package/dist/commands/test/regression/gen-forms.js.map +1 -0
- package/dist/commands/test/regression/index.d.ts +7 -0
- package/dist/commands/test/regression/index.d.ts.map +1 -0
- package/dist/commands/test/regression/index.js +33 -0
- package/dist/commands/test/regression/index.js.map +1 -0
- package/dist/commands/test/regression/init.d.ts +21 -0
- package/dist/commands/test/regression/init.d.ts.map +1 -0
- package/dist/commands/test/regression/init.js +111 -0
- package/dist/commands/test/regression/init.js.map +1 -0
- package/dist/commands/test/regression/remote.d.ts +23 -0
- package/dist/commands/test/regression/remote.d.ts.map +1 -0
- package/dist/commands/test/regression/remote.js +140 -0
- package/dist/commands/test/regression/remote.js.map +1 -0
- package/dist/commands/test/regression/up.d.ts +21 -0
- package/dist/commands/test/regression/up.d.ts.map +1 -0
- package/dist/commands/test/regression/up.js +111 -0
- package/dist/commands/test/regression/up.js.map +1 -0
- package/dist/commands/test/run.d.ts +1 -0
- package/dist/commands/test/run.d.ts.map +1 -1
- package/dist/commands/test/run.js +6 -0
- package/dist/commands/test/run.js.map +1 -1
- package/dist/commands/test/suite.d.ts +5 -0
- package/dist/commands/test/suite.d.ts.map +1 -1
- package/dist/commands/test/suite.js +27 -0
- package/dist/commands/test/suite.js.map +1 -1
- package/dist/commands/translate-sql/index.d.ts.map +1 -1
- package/dist/commands/translate-sql/index.js +3 -2
- package/dist/commands/translate-sql/index.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/init-templates/bring-your-own-app/README.md +74 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/.mj-sync.json +10 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/test-suites/.byo-suite.json +51 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/test-suites/.mj-sync.json +12 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/tests/.T01-counter-increment.json +63 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/tests/.T02-contact-form-submit.json +63 -0
- package/dist/init-templates/bring-your-own-app/byo-metadata/tests/.mj-sync.json +12 -0
- package/dist/init-templates/bring-your-own-app/docker-compose.app.yml +76 -0
- package/dist/init-templates/bring-your-own-app/target.json +23 -0
- package/dist/init-templates/generic-web/README.md +115 -0
- package/dist/init-templates/generic-web/metadata/.mj-sync.json +10 -0
- package/dist/init-templates/generic-web/metadata/test-suites/.generic-web-suite.json +48 -0
- package/dist/init-templates/generic-web/metadata/test-suites/.mj-sync.json +12 -0
- package/dist/init-templates/generic-web/metadata/tests/.GW01-home-loads.json +74 -0
- package/dist/init-templates/generic-web/metadata/tests/.GW02-search-array-map.json +82 -0
- package/dist/init-templates/generic-web/metadata/tests/.mj-sync.json +12 -0
- package/dist/init-templates/generic-web/oracles/mdn-oracles.cjs +121 -0
- package/dist/init-templates/generic-web/target.json +25 -0
- package/dist/init-templates/github-actions/README.md +26 -0
- package/dist/init-templates/github-actions/regression.yml +52 -0
- package/dist/init-templates/remote-mj/README.md +95 -0
- package/dist/init-templates/remote-mj/target.json +22 -0
- package/dist/init-templates/static-file-server/Dockerfile +23 -0
- package/dist/init-templates/static-file-server/README.md +88 -0
- package/dist/init-templates/static-file-server/docker-compose.app.yml +44 -0
- package/dist/init-templates/static-file-server/metadata/.mj-sync.json +10 -0
- package/dist/init-templates/static-file-server/metadata/test-suites/.mj-sync.json +12 -0
- package/dist/init-templates/static-file-server/metadata/test-suites/.static-server-suite.json +27 -0
- package/dist/init-templates/static-file-server/metadata/tests/.SS01-home-loads.json +45 -0
- package/dist/init-templates/static-file-server/metadata/tests/.SS02-navigate-about.json +45 -0
- package/dist/init-templates/static-file-server/metadata/tests/.mj-sync.json +12 -0
- package/dist/init-templates/static-file-server/public/about.html +12 -0
- package/dist/init-templates/static-file-server/public/index.html +27 -0
- package/dist/init-templates/static-file-server/target.json +16 -0
- package/dist/lib/db-preflight.d.ts +42 -0
- package/dist/lib/db-preflight.d.ts.map +1 -0
- package/dist/lib/db-preflight.js +113 -0
- package/dist/lib/db-preflight.js.map +1 -0
- package/dist/lib/db-version.d.ts +36 -0
- package/dist/lib/db-version.d.ts.map +1 -0
- package/dist/lib/db-version.js +45 -0
- package/dist/lib/db-version.js.map +1 -0
- package/dist/lib/migration-fetch.d.ts +49 -0
- package/dist/lib/migration-fetch.d.ts.map +1 -0
- package/dist/lib/migration-fetch.js +152 -0
- package/dist/lib/migration-fetch.js.map +1 -0
- package/dist/lib/regression/docker-helpers.d.ts +98 -0
- package/dist/lib/regression/docker-helpers.d.ts.map +1 -0
- package/dist/lib/regression/docker-helpers.js +184 -0
- package/dist/lib/regression/docker-helpers.js.map +1 -0
- package/dist/light-commands.d.ts.map +1 -1
- package/dist/light-commands.js +2 -0
- package/dist/light-commands.js.map +1 -1
- package/oclif.manifest.json +1635 -702
- package/package.json +22 -17
- package/regression-compose/docker-compose.bacpac-standalone.yml +109 -0
- package/regression-compose/docker-compose.standalone.yml +50 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import ora from 'ora-classic';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { resolveConnection, isTty } from '../../baseline/cli-helpers.js';
|
|
7
|
+
import { openConnection } from '../../baseline/connection.js';
|
|
8
|
+
import { introspectMssql } from '../../baseline/introspector-mssql.js';
|
|
9
|
+
import { introspectPostgres } from '../../baseline/introspector-postgres.js';
|
|
10
|
+
import { dumpTables } from '../../baseline/data-dumper.js';
|
|
11
|
+
import { compareSnapshots } from '../../baseline/comparator.js';
|
|
12
|
+
import { renderJson, renderMarkdown } from '../../baseline/report.js';
|
|
13
|
+
export default class BaselineCompare extends Command {
|
|
14
|
+
static { this.description = 'Deterministically compare two databases object-by-object and row-by-row.'; }
|
|
15
|
+
static { this.examples = [
|
|
16
|
+
'<%= config.bin %> <%= command.id %> --left MJ_BL_Stack --right MJ_BL_New',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --left A --right B --row-compare counts',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --left A --right B --fail-on-diff --out ./diffs/',
|
|
19
|
+
]; }
|
|
20
|
+
static { this.flags = {
|
|
21
|
+
'left': Flags.string({ description: 'Left database name (override).', required: true }),
|
|
22
|
+
'right': Flags.string({ description: 'Right database name (override).', required: true }),
|
|
23
|
+
'dialect': Flags.string({
|
|
24
|
+
description: 'Dialect of both DBs (must match).',
|
|
25
|
+
options: ['mssql', 'postgres'],
|
|
26
|
+
default: 'mssql',
|
|
27
|
+
}),
|
|
28
|
+
'row-compare': Flags.string({
|
|
29
|
+
description: 'Row comparison mode.',
|
|
30
|
+
options: ['full', 'hash', 'counts', 'none'],
|
|
31
|
+
default: 'full',
|
|
32
|
+
}),
|
|
33
|
+
'row-hash-algo': Flags.string({
|
|
34
|
+
description: 'Hash algorithm when --row-compare hash.',
|
|
35
|
+
options: ['sha256', 'md5', 'checksum_agg'],
|
|
36
|
+
default: 'sha256',
|
|
37
|
+
}),
|
|
38
|
+
'ignore': Flags.string({
|
|
39
|
+
description: 'Regex of object names to skip.',
|
|
40
|
+
default: '^flyway_schema_history$',
|
|
41
|
+
}),
|
|
42
|
+
'sample-limit': Flags.integer({
|
|
43
|
+
description: 'Maximum row diffs to capture per table.',
|
|
44
|
+
default: 100,
|
|
45
|
+
}),
|
|
46
|
+
'out': Flags.string({ description: 'Directory to write JSON + Markdown reports.' }),
|
|
47
|
+
'fail-on-diff': Flags.boolean({ description: 'Exit non-zero if any diff is found.', default: false }),
|
|
48
|
+
'verbose': Flags.boolean({ description: 'Print per-phase progress.', default: false }),
|
|
49
|
+
}; }
|
|
50
|
+
async run() {
|
|
51
|
+
const { flags } = await this.parse(BaselineCompare);
|
|
52
|
+
const dialect = flags.dialect;
|
|
53
|
+
const useSpinner = isTty();
|
|
54
|
+
const spinner = useSpinner ? ora() : null;
|
|
55
|
+
const phase = (text) => {
|
|
56
|
+
if (spinner) {
|
|
57
|
+
spinner.text = text;
|
|
58
|
+
if (!spinner.isSpinning)
|
|
59
|
+
spinner.start();
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
this.log(`• ${text}`);
|
|
63
|
+
};
|
|
64
|
+
const succeed = (text) => spinner ? spinner.succeed(text) : this.log(`✓ ${text}`);
|
|
65
|
+
const fail = (text) => spinner ? spinner.fail(text) : this.logToStderr(`✗ ${text}`);
|
|
66
|
+
const leftParams = resolveConnection({ database: flags.left }, dialect);
|
|
67
|
+
const rightParams = resolveConnection({ database: flags.right }, dialect);
|
|
68
|
+
phase(`Connecting (${leftParams.database} & ${rightParams.database})`);
|
|
69
|
+
const left = await openConnection(leftParams);
|
|
70
|
+
const right = await openConnection(rightParams);
|
|
71
|
+
succeed(`Connected to both databases`);
|
|
72
|
+
try {
|
|
73
|
+
phase(`Introspecting ${leftParams.database}`);
|
|
74
|
+
const leftSnapshot = dialect === 'mssql'
|
|
75
|
+
? await introspectMssql(left)
|
|
76
|
+
: await introspectPostgres(left);
|
|
77
|
+
succeed(`${leftParams.database}: ${leftSnapshot.tables.length} tables, ${leftSnapshot.views.length} views`);
|
|
78
|
+
phase(`Introspecting ${rightParams.database}`);
|
|
79
|
+
const rightSnapshot = dialect === 'mssql'
|
|
80
|
+
? await introspectMssql(right)
|
|
81
|
+
: await introspectPostgres(right);
|
|
82
|
+
succeed(`${rightParams.database}: ${rightSnapshot.tables.length} tables, ${rightSnapshot.views.length} views`);
|
|
83
|
+
const rowMode = flags['row-compare'];
|
|
84
|
+
let leftDumps = [];
|
|
85
|
+
let rightDumps = [];
|
|
86
|
+
if (rowMode !== 'none' && rowMode !== 'counts') {
|
|
87
|
+
if (dialect !== 'mssql') {
|
|
88
|
+
this.warn('row data dump streaming optimised for MSSQL; PG path uses cursor fallback.');
|
|
89
|
+
}
|
|
90
|
+
phase(`Dumping rows (left)`);
|
|
91
|
+
leftDumps = await dumpTables(left, leftSnapshot.tables, { excludedTables: new Set() });
|
|
92
|
+
succeed(`Dumped ${leftDumps.reduce((s, d) => s + d.rowCount, 0).toLocaleString()} rows from ${leftParams.database}`);
|
|
93
|
+
phase(`Dumping rows (right)`);
|
|
94
|
+
rightDumps = await dumpTables(right, rightSnapshot.tables, { excludedTables: new Set() });
|
|
95
|
+
succeed(`Dumped ${rightDumps.reduce((s, d) => s + d.rowCount, 0).toLocaleString()} rows from ${rightParams.database}`);
|
|
96
|
+
}
|
|
97
|
+
phase('Comparing');
|
|
98
|
+
const report = compareSnapshots({
|
|
99
|
+
left: { snapshot: leftSnapshot, data: leftDumps, label: leftParams.database },
|
|
100
|
+
right: { snapshot: rightSnapshot, data: rightDumps, label: rightParams.database },
|
|
101
|
+
options: {
|
|
102
|
+
rowCompareMode: rowMode,
|
|
103
|
+
rowHashAlgo: flags['row-hash-algo'],
|
|
104
|
+
ignorePattern: flags.ignore ? new RegExp(flags.ignore, 'i') : undefined,
|
|
105
|
+
rowDiffSampleLimit: flags['sample-limit'],
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
report.isClean ? succeed('Comparison complete: CLEAN') : fail(`Comparison complete: ${report.summary.objectsWithDiffs} object diff(s), ${report.summary.totalRowDiffs} row diff(s)`);
|
|
109
|
+
if (flags.out) {
|
|
110
|
+
fs.mkdirSync(flags.out, { recursive: true });
|
|
111
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
112
|
+
const jsonPath = path.resolve(flags.out, `baseline-compare-${stamp}.json`);
|
|
113
|
+
const mdPath = path.resolve(flags.out, `baseline-compare-${stamp}.md`);
|
|
114
|
+
fs.writeFileSync(jsonPath, renderJson(report), 'utf8');
|
|
115
|
+
fs.writeFileSync(mdPath, renderMarkdown(report), 'utf8');
|
|
116
|
+
this.log('');
|
|
117
|
+
this.log(chalk.dim(` Reports: ${jsonPath}`));
|
|
118
|
+
this.log(chalk.dim(` ${mdPath}`));
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
process.stdout.write(renderMarkdown(report));
|
|
122
|
+
}
|
|
123
|
+
if (flags['fail-on-diff'] && !report.isClean) {
|
|
124
|
+
this.exit(2);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
await left.close();
|
|
129
|
+
await right.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../../src/commands/baseline/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class BaselineRoundtrip extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
'baseline-version': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
'source-dir': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
description: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
dialect: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
source: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
target: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
out: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
'apply-cmd': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
'row-compare': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
'fail-on-diff': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
'keep-target': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
/** Same logic as in `BaselineBuild`: explicit version → now; otherwise auto-detect. */
|
|
21
|
+
private resolveVersionAndStamp;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=roundtrip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roundtrip.d.ts","sourceRoot":"","sources":["../../../src/commands/baseline/roundtrip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAsB7C,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,OAAO;IACpD,MAAM,CAAC,WAAW,SACgF;IAElG,MAAM,CAAC,QAAQ,WAIb;IAEF,MAAM,CAAC,KAAK;;;;;;;;;;;;;MAuCV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA8H1B,uFAAuF;IACvF,OAAO,CAAC,sBAAsB;CAmC/B"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import ora from 'ora-classic';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { resolveConnection, isTty } from '../../baseline/cli-helpers.js';
|
|
8
|
+
import { openConnection } from '../../baseline/connection.js';
|
|
9
|
+
import { introspectMssql } from '../../baseline/introspector-mssql.js';
|
|
10
|
+
import { introspectPostgres } from '../../baseline/introspector-postgres.js';
|
|
11
|
+
import { dumpTables } from '../../baseline/data-dumper.js';
|
|
12
|
+
import { emitBaselineTsql } from '../../baseline/emitter.js';
|
|
13
|
+
import { compareSnapshots } from '../../baseline/comparator.js';
|
|
14
|
+
import { renderJson, renderMarkdown } from '../../baseline/report.js';
|
|
15
|
+
import { baselineFilename, computeAutoBaselineStamp, discoverMigrationsSourceDir, findLatestVersionedMigration, } from '../../baseline/util.js';
|
|
16
|
+
export default class BaselineRoundtrip extends Command {
|
|
17
|
+
static { this.description = 'Build a baseline from a V-stack database, apply it to a fresh DB, and prove byte-equivalence.'; }
|
|
18
|
+
static { this.examples = [
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --source MJ_BL_Stack --target MJ_BL_New # auto: within-major',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> --baseline-version 6.0 --source MJ_BL_Stack --target MJ_BL_New # explicit major-boundary',
|
|
21
|
+
'<%= config.bin %> <%= command.id %> --source-dir ./migrations/v5 --source MJ_BL_Stack --target MJ_BL_New',
|
|
22
|
+
]; }
|
|
23
|
+
static { this.flags = {
|
|
24
|
+
'baseline-version': Flags.string({
|
|
25
|
+
description: 'Major.Minor version stamp. Omit to auto-detect from --source-dir (within-major rebaseline).',
|
|
26
|
+
}),
|
|
27
|
+
'source-dir': Flags.string({
|
|
28
|
+
description: 'Migrations source directory used to auto-detect baseline version + timestamp when --baseline-version is omitted. ' +
|
|
29
|
+
'Defaults to the highest migrations/v*/ near cwd.',
|
|
30
|
+
}),
|
|
31
|
+
'description': Flags.string({ description: 'Header description.', default: 'MemberJunction Baseline' }),
|
|
32
|
+
'dialect': Flags.string({
|
|
33
|
+
description: 'Dialect to test. PG path runs the converter via /pg-migrate first.',
|
|
34
|
+
options: ['mssql', 'postgres'],
|
|
35
|
+
default: 'mssql',
|
|
36
|
+
}),
|
|
37
|
+
'source': Flags.string({
|
|
38
|
+
description: 'Database name with the V-stack already applied (used as comparison gold standard).',
|
|
39
|
+
required: true,
|
|
40
|
+
}),
|
|
41
|
+
'target': Flags.string({
|
|
42
|
+
description: 'Empty database name to apply the new baseline to.',
|
|
43
|
+
required: true,
|
|
44
|
+
}),
|
|
45
|
+
'out': Flags.string({ description: 'Output directory for baseline file + reports.', default: '.' }),
|
|
46
|
+
'apply-cmd': Flags.string({
|
|
47
|
+
description: 'Command template to apply the baseline. Tokens: {file} {database}. Defaults to sqlcmd.',
|
|
48
|
+
}),
|
|
49
|
+
'row-compare': Flags.string({
|
|
50
|
+
description: 'Row compare mode passed to the comparator.',
|
|
51
|
+
options: ['full', 'hash', 'counts', 'none'],
|
|
52
|
+
default: 'full',
|
|
53
|
+
}),
|
|
54
|
+
'fail-on-diff': Flags.boolean({ default: true }),
|
|
55
|
+
'keep-target': Flags.boolean({
|
|
56
|
+
description: 'Do not drop the target database after run (useful for debugging diffs).',
|
|
57
|
+
default: false,
|
|
58
|
+
}),
|
|
59
|
+
'verbose': Flags.boolean({ default: false }),
|
|
60
|
+
}; }
|
|
61
|
+
async run() {
|
|
62
|
+
const { flags } = await this.parse(BaselineRoundtrip);
|
|
63
|
+
const { baselineVersion, generatedAtUtc, autoSource } = this.resolveVersionAndStamp(flags);
|
|
64
|
+
if (autoSource) {
|
|
65
|
+
this.log(chalk.dim(` Auto-detected baseline: v${baselineVersion}.x (from ${autoSource.filename})`));
|
|
66
|
+
this.log(chalk.dim(` Auto timestamp : ${autoSource.timestamp} + 1m`));
|
|
67
|
+
}
|
|
68
|
+
const dialect = flags.dialect;
|
|
69
|
+
const useSpinner = isTty();
|
|
70
|
+
const spinner = useSpinner ? ora() : null;
|
|
71
|
+
const phase = (text) => { if (spinner) {
|
|
72
|
+
spinner.text = text;
|
|
73
|
+
if (!spinner.isSpinning)
|
|
74
|
+
spinner.start();
|
|
75
|
+
}
|
|
76
|
+
else
|
|
77
|
+
this.log(`• ${text}`); };
|
|
78
|
+
const succeed = (text) => spinner ? spinner.succeed(text) : this.log(`✓ ${text}`);
|
|
79
|
+
const fail = (text) => spinner ? spinner.fail(text) : this.logToStderr(`✗ ${text}`);
|
|
80
|
+
const sourceParams = resolveConnection({ database: flags.source }, 'mssql');
|
|
81
|
+
fs.mkdirSync(flags.out, { recursive: true });
|
|
82
|
+
// 1. Introspect source + dump rows
|
|
83
|
+
phase(`Connecting to ${sourceParams.database} (gold)`);
|
|
84
|
+
const sourceDb = await openConnection(sourceParams);
|
|
85
|
+
let sourceSnapshot;
|
|
86
|
+
let sourceDumps;
|
|
87
|
+
try {
|
|
88
|
+
succeed(`Connected to ${sourceParams.database}`);
|
|
89
|
+
phase('Introspecting gold database');
|
|
90
|
+
sourceSnapshot = await introspectMssql(sourceDb);
|
|
91
|
+
succeed(`Gold: ${sourceSnapshot.tables.length} tables`);
|
|
92
|
+
phase('Dumping all rows from gold');
|
|
93
|
+
sourceDumps = await dumpTables(sourceDb, sourceSnapshot.tables, { excludedTables: new Set() });
|
|
94
|
+
succeed(`Gold: dumped ${sourceDumps.reduce((s, d) => s + d.rowCount, 0).toLocaleString()} rows`);
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await sourceDb.close();
|
|
98
|
+
}
|
|
99
|
+
// 2. Emit baseline
|
|
100
|
+
phase('Emitting baseline SQL');
|
|
101
|
+
const sql = emitBaselineTsql({
|
|
102
|
+
snapshot: sourceSnapshot,
|
|
103
|
+
dataDumps: sourceDumps,
|
|
104
|
+
options: {
|
|
105
|
+
baselineVersion,
|
|
106
|
+
description: flags.description,
|
|
107
|
+
generatedAtUtc,
|
|
108
|
+
includeData: true,
|
|
109
|
+
excludedDataTables: new Set(['flyway_schema_history', 'dbo.flyway_schema_history']),
|
|
110
|
+
batchSize: 1000,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
const filename = baselineFilename({ generatedAtUtc, baselineVersion });
|
|
114
|
+
const baselinePath = path.resolve(flags.out, filename);
|
|
115
|
+
fs.writeFileSync(baselinePath, sql, 'utf8');
|
|
116
|
+
succeed(`Baseline emitted: ${baselinePath}`);
|
|
117
|
+
// 3. (PG only) convert via /pg-migrate toolchain — currently delegated to a
|
|
118
|
+
// stub `mj migrate convert` invocation; user must run /pg-migrate manually
|
|
119
|
+
// to refine. We try the CLI command first.
|
|
120
|
+
let applyPath = baselinePath;
|
|
121
|
+
if (dialect === 'postgres') {
|
|
122
|
+
phase('Converting baseline to Postgres via mj migrate convert');
|
|
123
|
+
const pgPath = baselinePath.replace(/\.sql$/, '.pg.sql');
|
|
124
|
+
const result = spawnSync('mj', ['migrate', 'convert', '--input', baselinePath, '--output', pgPath], {
|
|
125
|
+
stdio: 'inherit',
|
|
126
|
+
});
|
|
127
|
+
if (result.status !== 0) {
|
|
128
|
+
fail('PG conversion failed — run /pg-migrate manually for richer conversion.');
|
|
129
|
+
throw new Error('PG conversion failed');
|
|
130
|
+
}
|
|
131
|
+
succeed(`PG baseline written: ${pgPath}`);
|
|
132
|
+
applyPath = pgPath;
|
|
133
|
+
}
|
|
134
|
+
// 4. Apply baseline to target
|
|
135
|
+
const targetParams = resolveConnection({ database: flags.target }, dialect);
|
|
136
|
+
phase(`Applying baseline to ${targetParams.database}`);
|
|
137
|
+
const applyResult = applyBaseline(applyPath, targetParams, flags['apply-cmd']);
|
|
138
|
+
if (applyResult.status !== 0) {
|
|
139
|
+
fail(`Apply failed (exit ${applyResult.status})`);
|
|
140
|
+
throw new Error('Baseline apply failed');
|
|
141
|
+
}
|
|
142
|
+
succeed(`Applied baseline to ${targetParams.database}`);
|
|
143
|
+
// 5. Compare target vs source
|
|
144
|
+
phase('Re-introspecting target for comparison');
|
|
145
|
+
const targetDb = await openConnection(targetParams);
|
|
146
|
+
let report;
|
|
147
|
+
try {
|
|
148
|
+
const targetSnapshot = dialect === 'mssql'
|
|
149
|
+
? await introspectMssql(targetDb)
|
|
150
|
+
: await introspectPostgres(targetDb);
|
|
151
|
+
const targetDumps = await dumpTables(targetDb, targetSnapshot.tables, { excludedTables: new Set() });
|
|
152
|
+
succeed(`Target: ${targetSnapshot.tables.length} tables, ${targetDumps.reduce((s, d) => s + d.rowCount, 0).toLocaleString()} rows`);
|
|
153
|
+
phase('Comparing snapshots');
|
|
154
|
+
report = compareSnapshots({
|
|
155
|
+
left: { snapshot: sourceSnapshot, data: sourceDumps, label: sourceParams.database },
|
|
156
|
+
right: { snapshot: targetSnapshot, data: targetDumps, label: targetParams.database },
|
|
157
|
+
options: {
|
|
158
|
+
rowCompareMode: flags['row-compare'],
|
|
159
|
+
rowHashAlgo: 'sha256',
|
|
160
|
+
ignorePattern: /^flyway_schema_history$/i,
|
|
161
|
+
rowDiffSampleLimit: 100,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
report.isClean
|
|
165
|
+
? succeed('Comparison: CLEAN ✓ baseline matches V-stack end-state')
|
|
166
|
+
: fail(`Comparison: ${report.summary.objectsWithDiffs} object diffs, ${report.summary.totalRowDiffs} row diffs`);
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
await targetDb.close();
|
|
170
|
+
}
|
|
171
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
172
|
+
const jsonPath = path.resolve(flags.out, `baseline-compare-${stamp}.json`);
|
|
173
|
+
const mdPath = path.resolve(flags.out, `baseline-compare-${stamp}.md`);
|
|
174
|
+
fs.writeFileSync(jsonPath, renderJson(report), 'utf8');
|
|
175
|
+
fs.writeFileSync(mdPath, renderMarkdown(report), 'utf8');
|
|
176
|
+
this.log('');
|
|
177
|
+
this.log(chalk.bold(report.isClean ? chalk.green('ROUNDTRIP CLEAN ✓') : chalk.red('ROUNDTRIP HAS DIFFS ✗')));
|
|
178
|
+
this.log(chalk.dim(` Baseline : ${baselinePath}`));
|
|
179
|
+
this.log(chalk.dim(` Reports : ${jsonPath}`));
|
|
180
|
+
this.log(chalk.dim(` ${mdPath}`));
|
|
181
|
+
if (flags['fail-on-diff'] && !report.isClean) {
|
|
182
|
+
this.exit(2);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Same logic as in `BaselineBuild`: explicit version → now; otherwise auto-detect. */
|
|
186
|
+
resolveVersionAndStamp(flags) {
|
|
187
|
+
const explicit = flags['baseline-version'];
|
|
188
|
+
if (explicit) {
|
|
189
|
+
if (!/^\d+\.\d+$/.test(explicit)) {
|
|
190
|
+
this.error(`--baseline-version must be Major.Minor (got "${explicit}")`);
|
|
191
|
+
}
|
|
192
|
+
return { baselineVersion: explicit, generatedAtUtc: new Date(), autoSource: null };
|
|
193
|
+
}
|
|
194
|
+
const sourceDir = flags['source-dir'] ?? discoverMigrationsSourceDir(process.cwd());
|
|
195
|
+
if (!sourceDir) {
|
|
196
|
+
this.error('No --baseline-version provided and could not auto-discover a migrations directory. ' +
|
|
197
|
+
'Pass --source-dir or --baseline-version.');
|
|
198
|
+
}
|
|
199
|
+
const latest = findLatestVersionedMigration(sourceDir);
|
|
200
|
+
if (!latest) {
|
|
201
|
+
this.error(`No V-files found in ${sourceDir}. Pass --baseline-version explicitly or point --source-dir at a folder with V<ts>__v<Major>.<Minor>...sql migrations.`);
|
|
202
|
+
}
|
|
203
|
+
const { generatedAtUtc } = computeAutoBaselineStamp(latest.timestamp);
|
|
204
|
+
return {
|
|
205
|
+
baselineVersion: latest.majorMinor,
|
|
206
|
+
generatedAtUtc,
|
|
207
|
+
autoSource: { filename: latest.filename, timestamp: latest.timestamp },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function applyBaseline(file, params, applyCmd) {
|
|
212
|
+
if (applyCmd) {
|
|
213
|
+
const cmd = applyCmd.replace('{file}', file).replace('{database}', params.database);
|
|
214
|
+
const result = spawnSync('sh', ['-c', cmd], { stdio: 'inherit' });
|
|
215
|
+
return { status: result.status };
|
|
216
|
+
}
|
|
217
|
+
if (params.dialect === 'mssql') {
|
|
218
|
+
const args = [
|
|
219
|
+
'-S', `${params.host},${params.port ?? 1433}`,
|
|
220
|
+
'-U', params.user,
|
|
221
|
+
'-P', params.password,
|
|
222
|
+
'-d', params.database,
|
|
223
|
+
'-i', file,
|
|
224
|
+
'-b',
|
|
225
|
+
];
|
|
226
|
+
if (params.trustServerCertificate)
|
|
227
|
+
args.push('-C');
|
|
228
|
+
const result = spawnSync('sqlcmd', args, { stdio: 'inherit' });
|
|
229
|
+
return { status: result.status };
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const env = { ...process.env, PGPASSWORD: params.password };
|
|
233
|
+
const args = [
|
|
234
|
+
'-h', params.host,
|
|
235
|
+
'-p', String(params.port ?? 5432),
|
|
236
|
+
'-U', params.user,
|
|
237
|
+
'-d', params.database,
|
|
238
|
+
'-v', 'ON_ERROR_STOP=1',
|
|
239
|
+
'-f', file,
|
|
240
|
+
];
|
|
241
|
+
const result = spawnSync('psql', args, { stdio: 'inherit', env });
|
|
242
|
+
return { status: result.status };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=roundtrip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roundtrip.js","sourceRoot":"","sources":["../../../src/commands/baseline/roundtrip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* `mj bundle` — produce a self-contained MemberJunction distribution zip for
|
|
4
|
+
* offline / air-gapped installs. Thin wrapper over the installer's bundle logic;
|
|
5
|
+
* the heavy lifting (sparse fetch + distribution assembly) lives in
|
|
6
|
+
* `@memberjunction/installer`.
|
|
7
|
+
*/
|
|
8
|
+
export default class Bundle extends Command {
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static flags: {
|
|
12
|
+
out: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
tag: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
|
+
source: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
|
+
'with-migrations': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
'db-platform': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
+
verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
};
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/bundle/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAK7C;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,OAAO;IACzC,MAAM,CAAC,WAAW,SAA6F;IAE/G,MAAM,CAAC,QAAQ,WAMb;IAEF,MAAM,CAAC,KAAK;;;;;;;MAaV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC3B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
|
+
import { createDistributionBundle } from '@memberjunction/installer';
|
|
4
|
+
import { getOptionalConfig } from '../../config.js';
|
|
5
|
+
import { resolveGitRef } from '../../lib/migration-fetch.js';
|
|
6
|
+
/**
|
|
7
|
+
* `mj bundle` — produce a self-contained MemberJunction distribution zip for
|
|
8
|
+
* offline / air-gapped installs. Thin wrapper over the installer's bundle logic;
|
|
9
|
+
* the heavy lifting (sparse fetch + distribution assembly) lives in
|
|
10
|
+
* `@memberjunction/installer`.
|
|
11
|
+
*/
|
|
12
|
+
export default class Bundle extends Command {
|
|
13
|
+
static { this.description = 'Create a self-contained MemberJunction distribution zip for offline/air-gapped installs'; }
|
|
14
|
+
static { this.examples = [
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --out ./mj-install.zip',
|
|
16
|
+
'<%= config.bin %> <%= command.id %> --tag v5.38.0 --out ./mj-install.zip',
|
|
17
|
+
'<%= config.bin %> <%= command.id %> --source . --out ./mj-install.zip',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> --tag v5.38.0 --with-migrations --out ./mj-install.zip',
|
|
19
|
+
'<%= config.bin %> <%= command.id %> --with-migrations --db-platform postgresql --out ./mj-install.zip',
|
|
20
|
+
]; }
|
|
21
|
+
static { this.flags = {
|
|
22
|
+
out: Flags.string({ char: 'o', description: 'Output zip path', required: true }),
|
|
23
|
+
tag: Flags.string({ char: 't', description: 'Release tag to bundle (e.g. v5.38.0). Defaults to the latest branch tip.' }),
|
|
24
|
+
source: Flags.string({ description: 'Bundle from a local working-tree directory instead of fetching from the repo' }),
|
|
25
|
+
'with-migrations': Flags.boolean({
|
|
26
|
+
description: 'Include the migration tree(s) so an air-gapped "mj migrate" can run offline. Ships both SQL Server and PostgreSQL trees by default.',
|
|
27
|
+
}),
|
|
28
|
+
'db-platform': Flags.string({
|
|
29
|
+
description: "With --with-migrations, ship only this platform's migration tree instead of both",
|
|
30
|
+
options: ['sqlserver', 'postgresql'],
|
|
31
|
+
dependsOn: ['with-migrations'],
|
|
32
|
+
}),
|
|
33
|
+
verbose: Flags.boolean({ char: 'v', description: 'Show detailed output' }),
|
|
34
|
+
}; }
|
|
35
|
+
async run() {
|
|
36
|
+
const { flags } = await this.parse(Bundle);
|
|
37
|
+
const config = getOptionalConfig();
|
|
38
|
+
const out = path.resolve(flags.out);
|
|
39
|
+
const source = flags.source ? path.resolve(flags.source) : undefined;
|
|
40
|
+
// Local source skips fetching; otherwise resolve the ref (tag → vX.Y.Z, branch unchanged).
|
|
41
|
+
const ref = source ? undefined : resolveGitRef(flags.tag ?? config?.mjRepoBranch ?? 'main');
|
|
42
|
+
const repoUrl = config?.mjRepoUrl;
|
|
43
|
+
this.log(source ? `Bundling from local source: ${source}` : `Bundling ${ref} from ${repoUrl ?? 'the canonical MemberJunction repo'}...`);
|
|
44
|
+
// oclif validates the value against `options`, but types it as a plain string;
|
|
45
|
+
// map it to the literal union without an assertion.
|
|
46
|
+
const dbPlatform = flags['db-platform'] === 'postgresql' ? 'postgresql' : flags['db-platform'] === 'sqlserver' ? 'sqlserver' : undefined;
|
|
47
|
+
const result = await createDistributionBundle({
|
|
48
|
+
Out: out,
|
|
49
|
+
Ref: ref,
|
|
50
|
+
RepoUrl: repoUrl,
|
|
51
|
+
SourceDir: source,
|
|
52
|
+
IncludeMigrations: flags['with-migrations'],
|
|
53
|
+
MigrationPlatform: dbPlatform,
|
|
54
|
+
});
|
|
55
|
+
if (flags.verbose && result.UsedFallback) {
|
|
56
|
+
this.log('Blobless partial clone unavailable; used the full sparse-checkout fallback.');
|
|
57
|
+
}
|
|
58
|
+
const migrationsNote = flags['with-migrations'] ? `, including migrations${dbPlatform ? ` (${dbPlatform})` : ''}` : '';
|
|
59
|
+
this.log(`Created ${result.Out} (${result.EntryCount} entries${migrationsNote}).`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/bundle/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,8 +7,37 @@ export default class Migrate extends Command {
|
|
|
7
7
|
tag: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
8
|
schema: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
dir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
'check-connection': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
};
|
|
11
12
|
run(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Reads the database's current migration version, logs it, then fetches the minimal
|
|
15
|
+
* slice to reach `ref`: versioned migrations after the current version for an existing
|
|
16
|
+
* database, or baseline + tail for a fresh one.
|
|
17
|
+
*/
|
|
18
|
+
private fetchSliceForRef;
|
|
19
|
+
/**
|
|
20
|
+
* Connects to read the highest migration version already applied (from Skyway's
|
|
21
|
+
* history table). Returns null for a fresh database (no history table yet).
|
|
22
|
+
*/
|
|
23
|
+
private readInstalledVersion;
|
|
24
|
+
/** Surfaces the detected installed version so the user sees what's being upgraded from. */
|
|
25
|
+
private logInstalledVersion;
|
|
26
|
+
/**
|
|
27
|
+
* Verify the database is reachable with the configured TLS/auth settings before
|
|
28
|
+
* running migrations. On failure, prints a classified message (and an actionable
|
|
29
|
+
* suggestion such as DB_TRUST_SERVER_CERTIFICATE for a self-signed cert) and exits.
|
|
30
|
+
*/
|
|
31
|
+
private preflightConnection;
|
|
32
|
+
/**
|
|
33
|
+
* Resolves the migration source directory handed to Skyway. With a fetched slice,
|
|
34
|
+
* the temp clone root is used (Skyway scans it recursively); a `--dir` override is
|
|
35
|
+
* applied as a subpath within that clone.
|
|
36
|
+
*/
|
|
37
|
+
private resolveSourceDir;
|
|
38
|
+
private logFetchSummary;
|
|
39
|
+
/** Runs Skyway against the prepared config and reports the outcome. */
|
|
40
|
+
private executeMigration;
|
|
12
41
|
/**
|
|
13
42
|
* Prints error details captured by OnProgress callbacks.
|
|
14
43
|
* Used when Skyway's result.Details is empty (transaction-level errors).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/migrate/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAS,MAAM,aAAa,CAAC;AAY7C,MAAM,CAAC,OAAO,OAAO,OAAQ,SAAQ,OAAO;IAC1C,MAAM,CAAC,WAAW,SAAuD;IAEzE,MAAM,CAAC,QAAQ,WAOb;IAEF,MAAM,CAAC,KAAK;;;;;;MAQV;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC1B;;;;OAIG;YACW,gBAAgB;IAW9B;;;OAGG;YACW,oBAAoB;IAMlC,2FAA2F;IAC3F,OAAO,CAAC,mBAAmB;IAQ3B;;;;OAIG;YACW,mBAAmB;IAWjC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,eAAe;IASvB,uEAAuE;YACzD,gBAAgB;IAgG9B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CA+B5B"}
|