@memberjunction/cli 2.133.0 ā 3.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/bin/dev.cmd +2 -2
- package/bin/run.cmd +3 -3
- package/dist/commands/dbdoc/review.d.ts +10 -0
- package/dist/commands/dbdoc/review.js +81 -0
- package/dist/commands/migrate/index.d.ts +1 -0
- package/dist/commands/migrate/index.js +160 -0
- package/dist/config.d.ts +11 -3
- package/dist/config.js +45 -3
- package/oclif.manifest.json +1136 -1096
- package/package.json +12 -11
package/bin/dev.cmd
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
|
|
1
|
+
@echo off
|
|
2
|
+
|
|
3
3
|
node "%~dp0\dev" %*
|
package/bin/run.cmd
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
|
|
3
|
-
node "%~dp0\run" %*
|
|
1
|
+
@echo off
|
|
2
|
+
|
|
3
|
+
node "%~dp0\run" %*
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Review extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
schema: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
'unapproved-only': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const db_auto_doc_1 = require("@memberjunction/db-auto-doc");
|
|
10
|
+
class Review extends core_1.Command {
|
|
11
|
+
static description = 'Review and approve AI-generated documentation';
|
|
12
|
+
static examples = [
|
|
13
|
+
'<%= config.bin %> <%= command.id %>',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> --schema dbo',
|
|
15
|
+
'<%= config.bin %> <%= command.id %> --unapproved-only',
|
|
16
|
+
];
|
|
17
|
+
static flags = {
|
|
18
|
+
schema: core_1.Flags.string({
|
|
19
|
+
description: 'Review specific schema',
|
|
20
|
+
}),
|
|
21
|
+
'unapproved-only': core_1.Flags.boolean({
|
|
22
|
+
description: 'Only show unapproved items',
|
|
23
|
+
default: false,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(Review);
|
|
28
|
+
this.log(chalk_1.default.blue.bold('\nš Review Documentation\n'));
|
|
29
|
+
try {
|
|
30
|
+
const stateManager = new db_auto_doc_1.StateManager();
|
|
31
|
+
const state = await stateManager.load();
|
|
32
|
+
const unapproved = stateManager.getUnapprovedTables(flags.schema);
|
|
33
|
+
if (unapproved.length === 0) {
|
|
34
|
+
this.log(chalk_1.default.green('ā
All tables approved!'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
this.log(`Found ${unapproved.length} unapproved tables\n`);
|
|
38
|
+
for (const { schema, table } of unapproved) {
|
|
39
|
+
const schemaState = state.schemas[schema];
|
|
40
|
+
const tableState = schemaState.tables[table];
|
|
41
|
+
this.log(chalk_1.default.cyan.bold(`\n${schema}.${table}`));
|
|
42
|
+
this.log('ā'.repeat(50));
|
|
43
|
+
if (tableState.aiGenerated) {
|
|
44
|
+
this.log(chalk_1.default.white('Description:'));
|
|
45
|
+
this.log(tableState.aiGenerated.description);
|
|
46
|
+
this.log('');
|
|
47
|
+
this.log(chalk_1.default.gray(`Confidence: ${(tableState.aiGenerated.confidence * 100).toFixed(0)}%`));
|
|
48
|
+
}
|
|
49
|
+
const action = await (0, prompts_1.select)({
|
|
50
|
+
message: 'Action:',
|
|
51
|
+
choices: [
|
|
52
|
+
{ name: 'Approve', value: 'approve' },
|
|
53
|
+
{ name: 'Add notes', value: 'notes' },
|
|
54
|
+
{ name: 'Skip', value: 'skip' },
|
|
55
|
+
{ name: 'Exit review', value: 'exit' },
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
if (action === 'exit') {
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
if (action === 'approve') {
|
|
62
|
+
stateManager.approveTable(schema, table);
|
|
63
|
+
this.log(chalk_1.default.green('ā Approved'));
|
|
64
|
+
}
|
|
65
|
+
if (action === 'notes') {
|
|
66
|
+
const notes = await (0, prompts_1.input)({
|
|
67
|
+
message: 'Enter notes:',
|
|
68
|
+
});
|
|
69
|
+
stateManager.addTableNotes(schema, table, notes);
|
|
70
|
+
this.log(chalk_1.default.green('ā Notes added'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
await stateManager.save();
|
|
74
|
+
this.log(chalk_1.default.green('\nā
Review complete!'));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.error(error.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.default = Review;
|
|
@@ -24,7 +24,9 @@ class Migrate extends core_1.Command {
|
|
|
24
24
|
const flyway = new node_flyway_1.Flyway(flywayConfig);
|
|
25
25
|
if (flags.verbose) {
|
|
26
26
|
this.log(`Connecting to ${flywayConfig.url}`);
|
|
27
|
+
this.log(`Database Connection: ${config.dbHost}, ${config.dbDatabase}, User: ${flywayConfig.user}`);
|
|
27
28
|
this.log(`Migrating ${config.coreSchema} schema using migrations from:\n\t- ${flywayConfig.migrationLocations.join('\n\t- ')}\n`);
|
|
29
|
+
this.log(`Flyway config settings: baselineVersion: ${config.baselineVersion}, baselineMigrate: ${config.baselineOnMigrate}\n`);
|
|
28
30
|
}
|
|
29
31
|
if (flags.tag) {
|
|
30
32
|
this.log(`Migrating to ${flags.tag}`);
|
|
@@ -32,6 +34,7 @@ class Migrate extends core_1.Command {
|
|
|
32
34
|
const spinner = (0, ora_classic_1.default)('Running migrations...');
|
|
33
35
|
spinner.start();
|
|
34
36
|
const result = await flyway.migrate();
|
|
37
|
+
const isParseError = result.error?.errorCode === 'UNABLE_TO_PARSE_RESPONSE';
|
|
35
38
|
if (result.success) {
|
|
36
39
|
spinner.succeed();
|
|
37
40
|
this.log(`Migrations complete in ${result.additionalDetails.executionTime / 1000}s`);
|
|
@@ -39,6 +42,73 @@ class Migrate extends core_1.Command {
|
|
|
39
42
|
this.log(`\tUpdated to ${result.flywayResponse?.targetSchemaVersion}`);
|
|
40
43
|
}
|
|
41
44
|
}
|
|
45
|
+
else if (isParseError) {
|
|
46
|
+
// Parse error - could be SQL error or connection issue
|
|
47
|
+
// Run Flyway CLI directly to get the actual error
|
|
48
|
+
spinner.fail();
|
|
49
|
+
this.logToStderr('\nā Migration failed - unable to parse Flyway response.');
|
|
50
|
+
this.logToStderr(`Execution time: ${result.additionalDetails.executionTime / 1000}s\n`);
|
|
51
|
+
this.logToStderr('š Running diagnostic to identify the actual error...');
|
|
52
|
+
const dbInfo = `${config.dbHost}:${config.dbPort}/${config.dbDatabase}`;
|
|
53
|
+
this.logToStderr(` Database: ${dbInfo}`);
|
|
54
|
+
this.logToStderr(` User: ${config.codeGenLogin}\n`);
|
|
55
|
+
try {
|
|
56
|
+
const { spawnSync } = require('child_process');
|
|
57
|
+
const path = require('path');
|
|
58
|
+
const os = require('os');
|
|
59
|
+
// Construct Flyway executable path
|
|
60
|
+
const flywayDir = result.additionalDetails.flywayCli.location;
|
|
61
|
+
const flywayExeName = os.platform() === 'win32' ? 'flyway.cmd' : 'flyway';
|
|
62
|
+
const flywayExePath = path.join(flywayDir, flywayExeName);
|
|
63
|
+
// Use spawnSync to avoid shell interpretation of special characters in password
|
|
64
|
+
const jdbcUrl = `jdbc:sqlserver://${config.dbHost}:${config.dbPort};databaseName=${config.dbDatabase};trustServerCertificate=${config.dbTrustServerCertificate}`;
|
|
65
|
+
// Build common args
|
|
66
|
+
const baseArgs = [
|
|
67
|
+
`-url=${jdbcUrl}`,
|
|
68
|
+
`-user=${config.codeGenLogin}`,
|
|
69
|
+
`-password=${config.codeGenPassword}`,
|
|
70
|
+
`-schemas=${config.coreSchema}`
|
|
71
|
+
];
|
|
72
|
+
// Convert relative migration paths to absolute paths
|
|
73
|
+
const absoluteMigrationPaths = flywayConfig.migrationLocations.map((loc) => {
|
|
74
|
+
// Remove 'filesystem:' prefix if present
|
|
75
|
+
const cleanLoc = loc.replace(/^filesystem:/, '');
|
|
76
|
+
// Convert to absolute path if relative
|
|
77
|
+
return path.isAbsolute(cleanLoc) ? loc : `filesystem:${path.resolve(cleanLoc)}`;
|
|
78
|
+
});
|
|
79
|
+
// First try migrate to see the actual SQL error
|
|
80
|
+
const migrateArgs = [
|
|
81
|
+
...baseArgs,
|
|
82
|
+
`-baselineVersion=${config.baselineVersion}`,
|
|
83
|
+
`-baselineOnMigrate=${config.baselineOnMigrate}`,
|
|
84
|
+
`-locations=${absoluteMigrationPaths.join(',')}`,
|
|
85
|
+
'migrate'
|
|
86
|
+
];
|
|
87
|
+
const migrateResult = spawnSync(flywayExePath, migrateArgs, { encoding: 'utf8' });
|
|
88
|
+
const migrateOutput = migrateResult.stderr || migrateResult.stdout || '';
|
|
89
|
+
// Check if output contains error messages even if exit code is 0
|
|
90
|
+
const hasErrorsInOutput = migrateOutput.toLowerCase().includes('error') ||
|
|
91
|
+
migrateOutput.toLowerCase().includes('incorrect syntax') ||
|
|
92
|
+
migrateOutput.toLowerCase().includes('must be the only statement');
|
|
93
|
+
if (migrateResult.status === 0 && !hasErrorsInOutput) {
|
|
94
|
+
this.logToStderr('ā Migration executed successfully (Flyway CLI reports success)');
|
|
95
|
+
this.logToStderr(' The issue was with node-flyway response parsing only\n');
|
|
96
|
+
}
|
|
97
|
+
else if (migrateResult.status === 0 && hasErrorsInOutput) {
|
|
98
|
+
// Exit code was 0 but output contains errors - SQL script likely has error handling
|
|
99
|
+
this.logToStderr('ā ļø Migration completed but errors were detected in output:\n');
|
|
100
|
+
this.analyzeFlywayError(migrateOutput, config);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Migration failed with non-zero exit code
|
|
104
|
+
this.analyzeFlywayError(migrateOutput, config);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
this.logToStderr(`ā Error running diagnostic: ${err.message || err}\n`);
|
|
109
|
+
}
|
|
110
|
+
this.error('Migration failed - see diagnostic information above');
|
|
111
|
+
}
|
|
42
112
|
else {
|
|
43
113
|
spinner.fail();
|
|
44
114
|
if (result.error) {
|
|
@@ -51,5 +121,95 @@ class Migrate extends core_1.Command {
|
|
|
51
121
|
this.error('Migrations failed');
|
|
52
122
|
}
|
|
53
123
|
}
|
|
124
|
+
analyzeFlywayError(errorOutput, config) {
|
|
125
|
+
const errorLines = errorOutput.split('\n');
|
|
126
|
+
const fullError = errorOutput.toLowerCase();
|
|
127
|
+
// Find error details
|
|
128
|
+
const errorCodeLine = errorLines.find(line => line.includes('Error Code')) || '';
|
|
129
|
+
const messageLine = errorLines.find(line => line.includes('Message')) || '';
|
|
130
|
+
const errorLine = errorLines.find(line => line.includes('ERROR:') && !line.includes('Skipping filesystem location')) || '';
|
|
131
|
+
// Determine error type
|
|
132
|
+
const isSqlError = fullError.includes('incorrect syntax') ||
|
|
133
|
+
fullError.includes('must be the only statement in the batch') ||
|
|
134
|
+
fullError.includes('invalid object name') ||
|
|
135
|
+
fullError.includes('permission denied') ||
|
|
136
|
+
(fullError.includes('error') && fullError.includes('line'));
|
|
137
|
+
const isConnectionError = fullError.includes('login failed') ||
|
|
138
|
+
fullError.includes('unable to obtain connection') ||
|
|
139
|
+
fullError.includes('connection') && !fullError.includes('clientconnectionid');
|
|
140
|
+
// Display error header
|
|
141
|
+
if (isSqlError) {
|
|
142
|
+
this.logToStderr('ā SQL Migration Error Detected\n');
|
|
143
|
+
}
|
|
144
|
+
else if (isConnectionError) {
|
|
145
|
+
this.logToStderr('ā Database Connection Failed\n');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.logToStderr('ā Migration Failed\n');
|
|
149
|
+
}
|
|
150
|
+
// Display error details
|
|
151
|
+
if (errorCodeLine && messageLine) {
|
|
152
|
+
this.logToStderr(` ${errorCodeLine.trim()}`);
|
|
153
|
+
this.logToStderr(` ${messageLine.trim()}\n`);
|
|
154
|
+
}
|
|
155
|
+
else if (errorLine) {
|
|
156
|
+
const cleanError = errorLine.replace(/^ERROR:\s*/, '').trim();
|
|
157
|
+
this.logToStderr(` Error: ${cleanError}\n`);
|
|
158
|
+
}
|
|
159
|
+
else if (errorOutput) {
|
|
160
|
+
// Show lines containing error-related keywords
|
|
161
|
+
const errorKeywords = ['error', 'incorrect', 'syntax', 'invalid', 'failed', 'must be', 'cannot', 'line'];
|
|
162
|
+
const relevantLines = errorOutput.split('\n')
|
|
163
|
+
.filter(line => {
|
|
164
|
+
const lower = line.toLowerCase();
|
|
165
|
+
return line.trim() &&
|
|
166
|
+
!lower.includes('flyway community') &&
|
|
167
|
+
!lower.includes('skipping filesystem location') &&
|
|
168
|
+
errorKeywords.some(keyword => lower.includes(keyword));
|
|
169
|
+
})
|
|
170
|
+
.slice(0, 15);
|
|
171
|
+
if (relevantLines.length > 0) {
|
|
172
|
+
this.logToStderr(' Error details from Flyway output:');
|
|
173
|
+
relevantLines.forEach(line => this.logToStderr(` ${line.trim()}`));
|
|
174
|
+
this.logToStderr('');
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// If no error keywords found, show last 20 lines of output
|
|
178
|
+
const allLines = errorOutput.split('\n').filter(l => l.trim());
|
|
179
|
+
const lastLines = allLines.slice(-20);
|
|
180
|
+
this.logToStderr(' Last 20 lines of Flyway output:');
|
|
181
|
+
lastLines.forEach(line => this.logToStderr(` ${line.trim()}`));
|
|
182
|
+
this.logToStderr('');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Provide guidance
|
|
186
|
+
if (isSqlError) {
|
|
187
|
+
this.logToStderr('š” This is a SQL script error:');
|
|
188
|
+
this.logToStderr(' - Check the migration SQL file for syntax errors');
|
|
189
|
+
this.logToStderr(' - Look for missing GO statements before CREATE TRIGGER/PROCEDURE/FUNCTION');
|
|
190
|
+
this.logToStderr(' - Verify all object names and references are correct');
|
|
191
|
+
this.logToStderr(' - Check database permissions for the migration user');
|
|
192
|
+
}
|
|
193
|
+
else if (fullError.includes('login failed') || fullError.includes('password')) {
|
|
194
|
+
this.logToStderr('š” This is a credential issue:');
|
|
195
|
+
this.logToStderr(' - Check CODEGEN_DB_USERNAME and CODEGEN_DB_PASSWORD in .env file');
|
|
196
|
+
this.logToStderr(' - Verify the user has permission to access the database');
|
|
197
|
+
this.logToStderr(' - Check for shell environment variables overriding .env settings');
|
|
198
|
+
}
|
|
199
|
+
else if (fullError.includes('database') && fullError.includes('does not exist')) {
|
|
200
|
+
this.logToStderr('š” The database does not exist:');
|
|
201
|
+
this.logToStderr(` - Create the database: CREATE DATABASE ${config.dbDatabase}`);
|
|
202
|
+
this.logToStderr(' - Or verify DB_DATABASE is set correctly in .env file');
|
|
203
|
+
}
|
|
204
|
+
else if (fullError.includes('connection') || fullError.includes('timeout') || fullError.includes('refused')) {
|
|
205
|
+
this.logToStderr('š” Cannot connect to SQL Server:');
|
|
206
|
+
this.logToStderr(' - Verify SQL Server is running');
|
|
207
|
+
this.logToStderr(` - Check host and port: ${config.dbHost}:${config.dbPort}`);
|
|
208
|
+
this.logToStderr(' - Check firewall settings');
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
this.logToStderr('š” Check the error details above to diagnose the issue');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
54
214
|
}
|
|
55
215
|
exports.default = Migrate;
|
package/dist/config.d.ts
CHANGED
|
@@ -12,8 +12,9 @@ declare const mjConfigSchema: z.ZodObject<{
|
|
|
12
12
|
coreSchema: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
13
13
|
cleanDisabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
14
14
|
mjRepoUrl: z.ZodCatch<z.ZodString>;
|
|
15
|
+
baselineVersion: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
16
|
+
baselineOnMigrate: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
15
17
|
}, "strip", z.ZodTypeAny, {
|
|
16
|
-
cleanDisabled: boolean;
|
|
17
18
|
dbHost: string;
|
|
18
19
|
dbDatabase: string;
|
|
19
20
|
dbPort: number;
|
|
@@ -22,21 +23,25 @@ declare const mjConfigSchema: z.ZodObject<{
|
|
|
22
23
|
migrationsLocation: string;
|
|
23
24
|
dbTrustServerCertificate: boolean;
|
|
24
25
|
coreSchema: string;
|
|
26
|
+
cleanDisabled: boolean;
|
|
25
27
|
mjRepoUrl: string;
|
|
28
|
+
baselineVersion: string;
|
|
29
|
+
baselineOnMigrate: boolean;
|
|
26
30
|
}, {
|
|
27
31
|
dbDatabase: string;
|
|
28
32
|
codeGenLogin: string;
|
|
29
33
|
codeGenPassword: string;
|
|
30
|
-
cleanDisabled?: boolean | undefined;
|
|
31
34
|
dbHost?: string | undefined;
|
|
32
35
|
dbPort?: number | undefined;
|
|
33
36
|
migrationsLocation?: string | undefined;
|
|
34
37
|
dbTrustServerCertificate?: boolean | undefined;
|
|
35
38
|
coreSchema?: string | undefined;
|
|
39
|
+
cleanDisabled?: boolean | undefined;
|
|
36
40
|
mjRepoUrl?: unknown;
|
|
41
|
+
baselineVersion?: string | undefined;
|
|
42
|
+
baselineOnMigrate?: boolean | undefined;
|
|
37
43
|
}>;
|
|
38
44
|
export declare const config: {
|
|
39
|
-
cleanDisabled: boolean;
|
|
40
45
|
dbHost: string;
|
|
41
46
|
dbDatabase: string;
|
|
42
47
|
dbPort: number;
|
|
@@ -45,7 +50,10 @@ export declare const config: {
|
|
|
45
50
|
migrationsLocation: string;
|
|
46
51
|
dbTrustServerCertificate: boolean;
|
|
47
52
|
coreSchema: string;
|
|
53
|
+
cleanDisabled: boolean;
|
|
48
54
|
mjRepoUrl: string;
|
|
55
|
+
baselineVersion: string;
|
|
56
|
+
baselineOnMigrate: boolean;
|
|
49
57
|
} | undefined;
|
|
50
58
|
/**
|
|
51
59
|
* Get validated config for commands that require database connection.
|
package/dist/config.js
CHANGED
|
@@ -6,9 +6,37 @@ const node_fs_1 = require("node:fs");
|
|
|
6
6
|
const node_os_1 = require("node:os");
|
|
7
7
|
const simple_git_1 = require("simple-git");
|
|
8
8
|
const zod_1 = require("zod");
|
|
9
|
+
const config_1 = require("@memberjunction/config");
|
|
9
10
|
const MJ_REPO_URL = 'https://github.com/MemberJunction/MJ.git';
|
|
11
|
+
/**
|
|
12
|
+
* Default database configuration for MJCLI.
|
|
13
|
+
* Database settings come from environment variables with sensible defaults.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_CLI_CONFIG = {
|
|
16
|
+
dbHost: process.env.DB_HOST ?? 'localhost',
|
|
17
|
+
dbPort: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433,
|
|
18
|
+
dbDatabase: process.env.DB_DATABASE ?? '',
|
|
19
|
+
dbTrustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'true',
|
|
20
|
+
codeGenLogin: process.env.CODEGEN_DB_USERNAME ?? '',
|
|
21
|
+
codeGenPassword: process.env.CODEGEN_DB_PASSWORD ?? '',
|
|
22
|
+
coreSchema: '__mj',
|
|
23
|
+
cleanDisabled: true,
|
|
24
|
+
baselineVersion: '202601122300',
|
|
25
|
+
baselineOnMigrate: true,
|
|
26
|
+
mjRepoUrl: MJ_REPO_URL,
|
|
27
|
+
migrationsLocation: 'filesystem:./migrations',
|
|
28
|
+
};
|
|
10
29
|
const explorer = (0, cosmiconfig_1.cosmiconfigSync)('mj', { searchStrategy: 'global' });
|
|
11
|
-
const
|
|
30
|
+
const searchResult = explorer.search(process.cwd());
|
|
31
|
+
// Merge user config with DEFAULT_CLI_CONFIG to support minimal config
|
|
32
|
+
// This allows database fields to come from environment variables
|
|
33
|
+
const mergedConfig = searchResult?.config
|
|
34
|
+
? (0, config_1.mergeConfigs)(DEFAULT_CLI_CONFIG, searchResult.config)
|
|
35
|
+
: DEFAULT_CLI_CONFIG;
|
|
36
|
+
// Create a result object with merged config for backward compatibility
|
|
37
|
+
const result = searchResult
|
|
38
|
+
? { ...searchResult, config: mergedConfig }
|
|
39
|
+
: { config: mergedConfig, filepath: '', isEmpty: false };
|
|
12
40
|
// Schema for database-dependent config (required fields)
|
|
13
41
|
const mjConfigSchema = zod_1.z.object({
|
|
14
42
|
dbHost: zod_1.z.string().default('localhost'),
|
|
@@ -21,6 +49,8 @@ const mjConfigSchema = zod_1.z.object({
|
|
|
21
49
|
coreSchema: zod_1.z.string().optional().default('__mj'),
|
|
22
50
|
cleanDisabled: zod_1.z.boolean().optional().default(true),
|
|
23
51
|
mjRepoUrl: zod_1.z.string().url().catch(MJ_REPO_URL),
|
|
52
|
+
baselineVersion: zod_1.z.string().optional().default('202601122300'),
|
|
53
|
+
baselineOnMigrate: zod_1.z.boolean().optional().default(true),
|
|
24
54
|
});
|
|
25
55
|
// Schema for non-database commands (all fields optional)
|
|
26
56
|
const mjConfigSchemaOptional = zod_1.z.object({
|
|
@@ -34,6 +64,8 @@ const mjConfigSchemaOptional = zod_1.z.object({
|
|
|
34
64
|
coreSchema: zod_1.z.string().optional().default('__mj'),
|
|
35
65
|
cleanDisabled: zod_1.z.boolean().optional().default(true),
|
|
36
66
|
mjRepoUrl: zod_1.z.string().url().catch(MJ_REPO_URL),
|
|
67
|
+
baselineVersion: zod_1.z.string().optional().default('202601122300'),
|
|
68
|
+
baselineOnMigrate: zod_1.z.boolean().optional().default(true),
|
|
37
69
|
});
|
|
38
70
|
// Don't validate at module load - let commands decide when they need validated config
|
|
39
71
|
exports.config = result?.config;
|
|
@@ -65,7 +97,12 @@ exports.getOptionalConfig = getOptionalConfig;
|
|
|
65
97
|
* Returns undefined silently if config is invalid (command will handle the error).
|
|
66
98
|
*/
|
|
67
99
|
const updatedConfig = () => {
|
|
68
|
-
const
|
|
100
|
+
const freshSearchResult = explorer.search(process.cwd());
|
|
101
|
+
// Merge fresh config with DEFAULT_CLI_CONFIG
|
|
102
|
+
const freshMergedConfig = freshSearchResult?.config
|
|
103
|
+
? (0, config_1.mergeConfigs)(DEFAULT_CLI_CONFIG, freshSearchResult.config)
|
|
104
|
+
: DEFAULT_CLI_CONFIG;
|
|
105
|
+
const maybeConfig = mjConfigSchema.safeParse(freshMergedConfig);
|
|
69
106
|
// Don't log errors here - let the calling command handle validation
|
|
70
107
|
return maybeConfig.success ? maybeConfig.data : undefined;
|
|
71
108
|
};
|
|
@@ -91,7 +128,12 @@ const getFlywayConfig = async (mjConfig, tag) => {
|
|
|
91
128
|
user: mjConfig.codeGenLogin,
|
|
92
129
|
password: mjConfig.codeGenPassword,
|
|
93
130
|
migrationLocations: [location],
|
|
94
|
-
advanced: {
|
|
131
|
+
advanced: {
|
|
132
|
+
schemas: [mjConfig.coreSchema],
|
|
133
|
+
cleanDisabled: mjConfig.cleanDisabled === false ? false : undefined,
|
|
134
|
+
baselineVersion: mjConfig.baselineVersion,
|
|
135
|
+
baselineOnMigrate: mjConfig.baselineOnMigrate,
|
|
136
|
+
},
|
|
95
137
|
};
|
|
96
138
|
};
|
|
97
139
|
exports.getFlywayConfig = getFlywayConfig;
|