@memberjunction/cli 3.1.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,32 +76,46 @@ class Migrate extends core_1.Command {
76
76
  // Convert to absolute path if relative
77
77
  return path.isAbsolute(cleanLoc) ? loc : `filesystem:${path.resolve(cleanLoc)}`;
78
78
  });
79
- // First try migrate to see the actual SQL error
80
- const migrateArgs = [
79
+ // First try validate to catch checksum mismatches
80
+ const validateArgs = [
81
81
  ...baseArgs,
82
- `-baselineVersion=${config.baselineVersion}`,
83
- `-baselineOnMigrate=${config.baselineOnMigrate}`,
84
82
  `-locations=${absoluteMigrationPaths.join(',')}`,
85
- 'migrate'
83
+ 'validate'
86
84
  ];
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);
85
+ const validateResult = spawnSync(flywayExePath, validateArgs, { encoding: 'utf8' });
86
+ const validateOutput = validateResult.stderr || validateResult.stdout || '';
87
+ // Check if validation failed
88
+ if (validateResult.status !== 0 || validateOutput.toLowerCase().includes('validate failed')) {
89
+ this.analyzeFlywayError(validateOutput, config);
101
90
  }
102
91
  else {
103
- // Migration failed with non-zero exit code
104
- this.analyzeFlywayError(migrateOutput, config);
92
+ // Validation passed, try migrate to see the actual SQL error
93
+ const migrateArgs = [
94
+ ...baseArgs,
95
+ `-baselineVersion=${config.baselineVersion}`,
96
+ `-baselineOnMigrate=${config.baselineOnMigrate}`,
97
+ `-locations=${absoluteMigrationPaths.join(',')}`,
98
+ 'migrate'
99
+ ];
100
+ const migrateResult = spawnSync(flywayExePath, migrateArgs, { encoding: 'utf8' });
101
+ const migrateOutput = migrateResult.stderr || migrateResult.stdout || '';
102
+ // Check if output contains error messages even if exit code is 0
103
+ const hasErrorsInOutput = migrateOutput.toLowerCase().includes('error') ||
104
+ migrateOutput.toLowerCase().includes('incorrect syntax') ||
105
+ migrateOutput.toLowerCase().includes('must be the only statement');
106
+ if (migrateResult.status === 0 && !hasErrorsInOutput) {
107
+ this.logToStderr('✓ Migration executed successfully (Flyway CLI reports success)');
108
+ this.logToStderr(' The issue was with node-flyway response parsing only\n');
109
+ }
110
+ else if (migrateResult.status === 0 && hasErrorsInOutput) {
111
+ // Exit code was 0 but output contains errors - SQL script likely has error handling
112
+ this.logToStderr('⚠️ Migration completed but errors were detected in output:\n');
113
+ this.analyzeFlywayError(migrateOutput, config);
114
+ }
115
+ else {
116
+ // Migration failed with non-zero exit code
117
+ this.analyzeFlywayError(migrateOutput, config);
118
+ }
105
119
  }
106
120
  }
107
121
  catch (err) {
@@ -129,6 +143,9 @@ class Migrate extends core_1.Command {
129
143
  const messageLine = errorLines.find(line => line.includes('Message')) || '';
130
144
  const errorLine = errorLines.find(line => line.includes('ERROR:') && !line.includes('Skipping filesystem location')) || '';
131
145
  // Determine error type
146
+ const isValidationError = fullError.includes('validate failed') ||
147
+ fullError.includes('checksum mismatch') ||
148
+ fullError.includes('migrations have failed validation');
132
149
  const isSqlError = fullError.includes('incorrect syntax') ||
133
150
  fullError.includes('must be the only statement in the batch') ||
134
151
  fullError.includes('invalid object name') ||
@@ -138,7 +155,10 @@ class Migrate extends core_1.Command {
138
155
  fullError.includes('unable to obtain connection') ||
139
156
  fullError.includes('connection') && !fullError.includes('clientconnectionid');
140
157
  // Display error header
141
- if (isSqlError) {
158
+ if (isValidationError) {
159
+ this.logToStderr('❌ Validation Failed - Checksum Mismatch Detected\n');
160
+ }
161
+ else if (isSqlError) {
142
162
  this.logToStderr('❌ SQL Migration Error Detected\n');
143
163
  }
144
164
  else if (isConnectionError) {
@@ -148,7 +168,16 @@ class Migrate extends core_1.Command {
148
168
  this.logToStderr('❌ Migration Failed\n');
149
169
  }
150
170
  // Display error details
151
- if (errorCodeLine && messageLine) {
171
+ if (isValidationError) {
172
+ // For validation errors, show the COMPLETE raw Flyway output
173
+ // This includes all checksum details which are critical for debugging
174
+ this.logToStderr('\n📋 Full Flyway Validation Output:');
175
+ this.logToStderr('='.repeat(100));
176
+ this.logToStderr(errorOutput);
177
+ this.logToStderr('='.repeat(100));
178
+ this.logToStderr('');
179
+ }
180
+ else if (errorCodeLine && messageLine) {
152
181
  this.logToStderr(` ${errorCodeLine.trim()}`);
153
182
  this.logToStderr(` ${messageLine.trim()}\n`);
154
183
  }
@@ -157,33 +186,42 @@ class Migrate extends core_1.Command {
157
186
  this.logToStderr(` Error: ${cleanError}\n`);
158
187
  }
159
188
  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('');
189
+ if (false) { // Disabled - keeping structure for non-validation errors
190
+ // Show lines containing error-related keywords
191
+ const errorKeywords = ['error', 'incorrect', 'syntax', 'invalid', 'failed', 'must be', 'cannot', 'line'];
192
+ const relevantLines = errorOutput.split('\n')
193
+ .filter(line => {
194
+ const lower = line.toLowerCase();
195
+ return line.trim() &&
196
+ !lower.includes('flyway community') &&
197
+ !lower.includes('skipping filesystem location') &&
198
+ errorKeywords.some(keyword => lower.includes(keyword));
199
+ })
200
+ .slice(0, 15);
201
+ if (relevantLines.length > 0) {
202
+ this.logToStderr(' Error details from Flyway output:');
203
+ relevantLines.forEach(line => this.logToStderr(` ${line.trim()}`));
204
+ this.logToStderr('');
205
+ }
206
+ else {
207
+ // If no error keywords found, show last 20 lines of output
208
+ const allLines = errorOutput.split('\n').filter(l => l.trim());
209
+ const lastLines = allLines.slice(-20);
210
+ this.logToStderr(' Last 20 lines of Flyway output:');
211
+ lastLines.forEach(line => this.logToStderr(` ${line.trim()}`));
212
+ this.logToStderr('');
213
+ }
183
214
  }
184
215
  }
185
216
  // Provide guidance
186
- if (isSqlError) {
217
+ if (isValidationError) {
218
+ this.logToStderr('💡 Migration checksum validation failed:');
219
+ this.logToStderr(' - A migration file has been modified after it was applied to the database');
220
+ this.logToStderr(' - Check the migration file(s) listed above for unexpected changes');
221
+ this.logToStderr(' - To repair: Use `flyway repair` if you intentionally modified the file');
222
+ this.logToStderr(' - Or revert the file to match the checksum in the database');
223
+ }
224
+ else if (isSqlError) {
187
225
  this.logToStderr('💡 This is a SQL script error:');
188
226
  this.logToStderr(' - Check the migration SQL file for syntax errors');
189
227
  this.logToStderr(' - Look for missing GO statements before CREATE TRIGGER/PROCEDURE/FUNCTION');
@@ -94,24 +94,25 @@
94
94
  "index.js"
95
95
  ]
96
96
  },
97
- "codegen": {
97
+ "clean": {
98
98
  "aliases": [],
99
99
  "args": {},
100
- "description": "Run CodeGen to generate code and update metadata for MemberJunction",
100
+ "description": "Resets the MemberJunction database to a pre-installation state",
101
101
  "examples": [
102
102
  "<%= config.bin %> <%= command.id %>\n"
103
103
  ],
104
104
  "flags": {
105
- "skipdb": {
106
- "description": "Skip database migration",
107
- "name": "skipdb",
105
+ "verbose": {
106
+ "char": "v",
107
+ "description": "Enable additional logging",
108
+ "name": "verbose",
108
109
  "allowNo": false,
109
110
  "type": "boolean"
110
111
  }
111
112
  },
112
113
  "hasDynamicHelp": false,
113
114
  "hiddenAliases": [],
114
- "id": "codegen",
115
+ "id": "clean",
115
116
  "pluginAlias": "@memberjunction/cli",
116
117
  "pluginName": "@memberjunction/cli",
117
118
  "pluginType": "core",
@@ -121,29 +122,28 @@
121
122
  "relativePath": [
122
123
  "dist",
123
124
  "commands",
124
- "codegen",
125
+ "clean",
125
126
  "index.js"
126
127
  ]
127
128
  },
128
- "clean": {
129
+ "codegen": {
129
130
  "aliases": [],
130
131
  "args": {},
131
- "description": "Resets the MemberJunction database to a pre-installation state",
132
+ "description": "Run CodeGen to generate code and update metadata for MemberJunction",
132
133
  "examples": [
133
134
  "<%= config.bin %> <%= command.id %>\n"
134
135
  ],
135
136
  "flags": {
136
- "verbose": {
137
- "char": "v",
138
- "description": "Enable additional logging",
139
- "name": "verbose",
137
+ "skipdb": {
138
+ "description": "Skip database migration",
139
+ "name": "skipdb",
140
140
  "allowNo": false,
141
141
  "type": "boolean"
142
142
  }
143
143
  },
144
144
  "hasDynamicHelp": false,
145
145
  "hiddenAliases": [],
146
- "id": "clean",
146
+ "id": "codegen",
147
147
  "pluginAlias": "@memberjunction/cli",
148
148
  "pluginName": "@memberjunction/cli",
149
149
  "pluginType": "core",
@@ -153,7 +153,7 @@
153
153
  "relativePath": [
154
154
  "dist",
155
155
  "commands",
156
- "clean",
156
+ "codegen",
157
157
  "index.js"
158
158
  ]
159
159
  },
@@ -626,46 +626,6 @@
626
626
  "index.js"
627
627
  ]
628
628
  },
629
- "migrate": {
630
- "aliases": [],
631
- "args": {},
632
- "description": "Migrate MemberJunction database to latest version",
633
- "examples": [
634
- "<%= config.bin %> <%= command.id %>\n"
635
- ],
636
- "flags": {
637
- "verbose": {
638
- "char": "v",
639
- "description": "Enable additional logging",
640
- "name": "verbose",
641
- "allowNo": false,
642
- "type": "boolean"
643
- },
644
- "tag": {
645
- "char": "t",
646
- "description": "Version tag to use for running remote migrations",
647
- "name": "tag",
648
- "hasDynamicHelp": false,
649
- "multiple": false,
650
- "type": "option"
651
- }
652
- },
653
- "hasDynamicHelp": false,
654
- "hiddenAliases": [],
655
- "id": "migrate",
656
- "pluginAlias": "@memberjunction/cli",
657
- "pluginName": "@memberjunction/cli",
658
- "pluginType": "core",
659
- "strict": true,
660
- "enableJsonFlag": false,
661
- "isESM": false,
662
- "relativePath": [
663
- "dist",
664
- "commands",
665
- "migrate",
666
- "index.js"
667
- ]
668
- },
669
629
  "querygen:export": {
670
630
  "aliases": [],
671
631
  "args": {},
@@ -1892,6 +1852,46 @@
1892
1852
  "validate.js"
1893
1853
  ]
1894
1854
  },
1855
+ "migrate": {
1856
+ "aliases": [],
1857
+ "args": {},
1858
+ "description": "Migrate MemberJunction database to latest version",
1859
+ "examples": [
1860
+ "<%= config.bin %> <%= command.id %>\n"
1861
+ ],
1862
+ "flags": {
1863
+ "verbose": {
1864
+ "char": "v",
1865
+ "description": "Enable additional logging",
1866
+ "name": "verbose",
1867
+ "allowNo": false,
1868
+ "type": "boolean"
1869
+ },
1870
+ "tag": {
1871
+ "char": "t",
1872
+ "description": "Version tag to use for running remote migrations",
1873
+ "name": "tag",
1874
+ "hasDynamicHelp": false,
1875
+ "multiple": false,
1876
+ "type": "option"
1877
+ }
1878
+ },
1879
+ "hasDynamicHelp": false,
1880
+ "hiddenAliases": [],
1881
+ "id": "migrate",
1882
+ "pluginAlias": "@memberjunction/cli",
1883
+ "pluginName": "@memberjunction/cli",
1884
+ "pluginType": "core",
1885
+ "strict": true,
1886
+ "enableJsonFlag": false,
1887
+ "isESM": false,
1888
+ "relativePath": [
1889
+ "dist",
1890
+ "commands",
1891
+ "migrate",
1892
+ "index.js"
1893
+ ]
1894
+ },
1895
1895
  "ai:actions:list": {
1896
1896
  "aliases": [],
1897
1897
  "args": {},
@@ -2500,5 +2500,5 @@
2500
2500
  ]
2501
2501
  }
2502
2502
  },
2503
- "version": "3.1.1"
2503
+ "version": "3.3.0"
2504
2504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/cli",
3
- "version": "3.1.1",
3
+ "version": "3.3.0",
4
4
  "description": "MemberJunction command line tools",
5
5
  "keywords": [
6
6
  "oclif"
@@ -51,15 +51,15 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@inquirer/prompts": "^5.0.1",
54
- "@memberjunction/ai-cli": "3.1.1",
55
- "@memberjunction/codegen-lib": "3.1.1",
56
- "@memberjunction/config": "3.1.1",
57
- "@memberjunction/core": "3.1.1",
58
- "@memberjunction/db-auto-doc": "3.1.1",
59
- "@memberjunction/metadata-sync": "3.1.1",
60
- "@memberjunction/query-gen": "3.1.1",
61
- "@memberjunction/sqlserver-dataprovider": "3.1.1",
62
- "@memberjunction/testing-cli": "3.1.1",
54
+ "@memberjunction/ai-cli": "3.3.0",
55
+ "@memberjunction/codegen-lib": "3.3.0",
56
+ "@memberjunction/config": "3.3.0",
57
+ "@memberjunction/core": "3.3.0",
58
+ "@memberjunction/db-auto-doc": "3.3.0",
59
+ "@memberjunction/metadata-sync": "3.3.0",
60
+ "@memberjunction/query-gen": "3.3.0",
61
+ "@memberjunction/sqlserver-dataprovider": "3.3.0",
62
+ "@memberjunction/testing-cli": "3.3.0",
63
63
  "@oclif/core": "^3",
64
64
  "@oclif/plugin-help": "^6",
65
65
  "@oclif/plugin-version": "^2.0.17",