claude-git-hooks 2.15.5 → 2.16.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/CHANGELOG.md +22 -0
- package/lib/commands/bump-version.js +346 -114
- package/lib/commands/generate-changelog.js +5 -6
- package/lib/utils/interactive-ui.js +93 -0
- package/lib/utils/version-manager.js +676 -296
- package/package.json +1 -1
|
@@ -4,24 +4,28 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Workflow:
|
|
6
6
|
* 1. Validate prerequisites (clean working directory, valid branch, remote)
|
|
7
|
-
* 2.
|
|
8
|
-
* 3.
|
|
9
|
-
* 4.
|
|
10
|
-
* 5. [
|
|
11
|
-
* 6.
|
|
12
|
-
* 7.
|
|
13
|
-
* 8.
|
|
7
|
+
* 2. Discover all version files recursively
|
|
8
|
+
* 3. [If mismatch OR --interactive] File selection menu
|
|
9
|
+
* 4. Calculate new version OR apply suffix operation
|
|
10
|
+
* 5. [Dry-run check]
|
|
11
|
+
* 6. Confirm with user
|
|
12
|
+
* 7. Snapshot files for rollback
|
|
13
|
+
* 8. Update version files
|
|
14
|
+
* 9. [Optional] Generate and update CHANGELOG
|
|
15
|
+
* 10. Stage and commit changes (skipped with --no-commit)
|
|
16
|
+
* 11. Create annotated Git tag (skipped with --no-tag or --no-commit)
|
|
17
|
+
* 12. Push tag to remote (only if --push flag provided, otherwise pushed by create-pr)
|
|
14
18
|
*/
|
|
15
19
|
|
|
16
20
|
import { execSync } from 'child_process';
|
|
17
21
|
import fs from 'fs';
|
|
18
22
|
import path from 'path';
|
|
19
23
|
import {
|
|
20
|
-
|
|
21
|
-
getCurrentVersion,
|
|
24
|
+
discoverVersionFiles,
|
|
22
25
|
incrementVersion,
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
modifySuffix,
|
|
27
|
+
updateVersionFiles,
|
|
28
|
+
validateVersionFormat
|
|
25
29
|
} from '../utils/version-manager.js';
|
|
26
30
|
import {
|
|
27
31
|
createTag,
|
|
@@ -42,12 +46,20 @@ import {
|
|
|
42
46
|
createCommit
|
|
43
47
|
} from '../utils/git-operations.js';
|
|
44
48
|
import { getConfig } from '../config.js';
|
|
45
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
showInfo,
|
|
51
|
+
showSuccess,
|
|
52
|
+
showError,
|
|
53
|
+
showWarning,
|
|
54
|
+
promptConfirmation,
|
|
55
|
+
promptMenu,
|
|
56
|
+
promptToggleList,
|
|
57
|
+
promptEditField
|
|
58
|
+
} from '../utils/interactive-ui.js';
|
|
46
59
|
import logger from '../utils/logger.js';
|
|
47
60
|
import {
|
|
48
61
|
colors,
|
|
49
62
|
error,
|
|
50
|
-
info,
|
|
51
63
|
checkGitRepo
|
|
52
64
|
} from './helpers.js';
|
|
53
65
|
|
|
@@ -103,7 +115,7 @@ function validatePrerequisites() {
|
|
|
103
115
|
logger.error('bump-version - validatePrerequisites', 'Validation failed', err);
|
|
104
116
|
return {
|
|
105
117
|
valid: false,
|
|
106
|
-
errors: [`Validation error: ${
|
|
118
|
+
errors: [`Validation error: ${err.message}`]
|
|
107
119
|
};
|
|
108
120
|
}
|
|
109
121
|
}
|
|
@@ -113,7 +125,7 @@ function validatePrerequisites() {
|
|
|
113
125
|
* Why: Extracts bump type and options from CLI args
|
|
114
126
|
*
|
|
115
127
|
* @param {Array<string>} args - CLI arguments
|
|
116
|
-
* @returns {Object} Parsed args
|
|
128
|
+
* @returns {Object} Parsed args
|
|
117
129
|
*/
|
|
118
130
|
function parseArguments(args) {
|
|
119
131
|
logger.debug('bump-version - parseArguments', 'Parsing arguments', { args });
|
|
@@ -121,31 +133,45 @@ function parseArguments(args) {
|
|
|
121
133
|
const parsed = {
|
|
122
134
|
bumpType: null,
|
|
123
135
|
suffix: null,
|
|
136
|
+
removeSuffix: false,
|
|
137
|
+
setSuffix: null,
|
|
138
|
+
interactive: false,
|
|
124
139
|
updateChangelog: false,
|
|
125
140
|
baseBranch: 'main',
|
|
126
141
|
dryRun: false,
|
|
127
142
|
noTag: false,
|
|
128
|
-
push: false,
|
|
143
|
+
push: false,
|
|
129
144
|
noCommit: false
|
|
130
145
|
};
|
|
131
146
|
|
|
132
|
-
// First argument should be bump type
|
|
147
|
+
// First argument should be bump type (or suffix-only flags)
|
|
133
148
|
if (args.length === 0) {
|
|
134
149
|
return parsed;
|
|
135
150
|
}
|
|
136
151
|
|
|
152
|
+
let i = 0;
|
|
153
|
+
|
|
154
|
+
// Check if first arg is bump type
|
|
137
155
|
const firstArg = args[0].toLowerCase();
|
|
138
156
|
if (['major', 'minor', 'patch'].includes(firstArg)) {
|
|
139
157
|
parsed.bumpType = firstArg;
|
|
158
|
+
i = 1;
|
|
140
159
|
}
|
|
141
160
|
|
|
142
161
|
// Parse options
|
|
143
|
-
for (
|
|
162
|
+
for (; i < args.length; i++) {
|
|
144
163
|
const arg = args[i];
|
|
145
164
|
|
|
146
165
|
if (arg === '--suffix' && i + 1 < args.length) {
|
|
147
166
|
parsed.suffix = args[i + 1];
|
|
148
167
|
i++; // Skip next arg
|
|
168
|
+
} else if (arg === '--remove-suffix') {
|
|
169
|
+
parsed.removeSuffix = true;
|
|
170
|
+
} else if (arg === '--set-suffix' && i + 1 < args.length) {
|
|
171
|
+
parsed.setSuffix = args[i + 1];
|
|
172
|
+
i++; // Skip next arg
|
|
173
|
+
} else if (arg === '--interactive') {
|
|
174
|
+
parsed.interactive = true;
|
|
149
175
|
} else if (arg === '--update-changelog') {
|
|
150
176
|
parsed.updateChangelog = true;
|
|
151
177
|
// Check if next arg is a branch name (not starting with --)
|
|
@@ -169,6 +195,50 @@ function parseArguments(args) {
|
|
|
169
195
|
return parsed;
|
|
170
196
|
}
|
|
171
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Displays version files discovery table
|
|
200
|
+
* Why: Show user all discovered version files
|
|
201
|
+
*
|
|
202
|
+
* @param {Object} discovery - DiscoveryResult from discoverVersionFiles()
|
|
203
|
+
*/
|
|
204
|
+
function displayDiscoveryTable(discovery) {
|
|
205
|
+
console.log('');
|
|
206
|
+
console.log(`${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`);
|
|
207
|
+
console.log(`${colors.blue} Version Files Discovery ${colors.reset}`);
|
|
208
|
+
console.log(`${colors.blue}═══════════════════════════════════════════════════════════════${colors.reset}`);
|
|
209
|
+
console.log('');
|
|
210
|
+
|
|
211
|
+
if (discovery.files.length === 0) {
|
|
212
|
+
console.log(' No version files found.');
|
|
213
|
+
console.log('');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Table header
|
|
218
|
+
console.log(` ${'#'.padEnd(3)} ${'File'.padEnd(35)} ${'Type'.padEnd(15)} ${'Version'.padEnd(15)}`);
|
|
219
|
+
console.log(` ${'-'.repeat(3)} ${'-'.repeat(35)} ${'-'.repeat(15)} ${'-'.repeat(15)}`);
|
|
220
|
+
|
|
221
|
+
// Table rows
|
|
222
|
+
discovery.files.forEach((file, index) => {
|
|
223
|
+
const num = `${index + 1}`.padEnd(3);
|
|
224
|
+
const filePath = file.relativePath.padEnd(35);
|
|
225
|
+
const fileType = file.projectLabel.padEnd(15);
|
|
226
|
+
const version = file.version ? file.version.padEnd(15) : 'N/A'.padEnd(15);
|
|
227
|
+
const mismatchMarker = discovery.mismatch && file.version !== discovery.resolvedVersion ? ' ⚠️ ' : '';
|
|
228
|
+
|
|
229
|
+
console.log(` ${num} ${filePath} ${fileType} ${version}${mismatchMarker}`);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
console.log('');
|
|
233
|
+
console.log(` Resolved version: ${discovery.resolvedVersion || 'N/A'}`);
|
|
234
|
+
|
|
235
|
+
if (discovery.mismatch) {
|
|
236
|
+
console.log(` ${colors.yellow}⚠️ Version mismatch detected${colors.reset}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log('');
|
|
240
|
+
}
|
|
241
|
+
|
|
172
242
|
/**
|
|
173
243
|
* Displays dry run preview
|
|
174
244
|
* Why: Shows what would change without applying
|
|
@@ -181,15 +251,21 @@ function showDryRunPreview(info) {
|
|
|
181
251
|
console.log(`${colors.yellow} DRY RUN - No changes will be made ${colors.reset}`);
|
|
182
252
|
console.log(`${colors.yellow}═════════════════════════════════════════════════${colors.reset}`);
|
|
183
253
|
console.log('');
|
|
184
|
-
|
|
254
|
+
|
|
255
|
+
displayDiscoveryTable(info.discovery);
|
|
256
|
+
|
|
257
|
+
console.log(`${colors.blue}Operation:${colors.reset} ${info.operation}`);
|
|
185
258
|
console.log(`${colors.blue}Current Version:${colors.reset} ${info.currentVersion}`);
|
|
186
259
|
console.log(`${colors.green}New Version:${colors.reset} ${info.newVersion}`);
|
|
187
260
|
console.log(`${colors.blue}Tag Name:${colors.reset} ${info.tagName}`);
|
|
188
261
|
console.log('');
|
|
189
262
|
|
|
190
|
-
if (info.
|
|
263
|
+
if (info.selectedFiles.length > 0) {
|
|
191
264
|
console.log(`${colors.blue}Files to update:${colors.reset}`);
|
|
192
|
-
info.
|
|
265
|
+
info.selectedFiles.forEach(file => {
|
|
266
|
+
const targetVer = file.targetVersion || info.newVersion;
|
|
267
|
+
console.log(` - ${file.relativePath} (${file.version} → ${targetVer})`);
|
|
268
|
+
});
|
|
193
269
|
console.log('');
|
|
194
270
|
}
|
|
195
271
|
|
|
@@ -202,6 +278,100 @@ function showDryRunPreview(info) {
|
|
|
202
278
|
console.log('');
|
|
203
279
|
}
|
|
204
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Prompts user for file selection when mismatch or --interactive
|
|
283
|
+
* Why: Let user choose which files to update
|
|
284
|
+
*
|
|
285
|
+
* @param {Object} discovery - DiscoveryResult
|
|
286
|
+
* @returns {Promise<Array>} Selected files
|
|
287
|
+
*/
|
|
288
|
+
async function promptFileSelection(discovery) {
|
|
289
|
+
console.log('');
|
|
290
|
+
showWarning('Version mismatch detected or interactive mode enabled');
|
|
291
|
+
console.log('');
|
|
292
|
+
|
|
293
|
+
const options = [
|
|
294
|
+
{ key: 'a', label: 'Update all files' },
|
|
295
|
+
{ key: 's', label: 'Select which files to update' },
|
|
296
|
+
{ key: 'e', label: 'Edit version per file (advanced)' },
|
|
297
|
+
{ key: 'c', label: 'Cancel' }
|
|
298
|
+
];
|
|
299
|
+
|
|
300
|
+
const choice = await promptMenu('Choose action:', options, 'a');
|
|
301
|
+
|
|
302
|
+
if (choice === 'c') {
|
|
303
|
+
showInfo('Version bump cancelled');
|
|
304
|
+
process.exit(0);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (choice === 'a') {
|
|
308
|
+
// All files selected
|
|
309
|
+
return discovery.files;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (choice === 's') {
|
|
313
|
+
// Toggle list selection
|
|
314
|
+
const items = discovery.files.map((file, index) => ({
|
|
315
|
+
key: `${index}`,
|
|
316
|
+
label: `${file.relativePath} (${file.projectLabel}) - ${file.version || 'N/A'}`,
|
|
317
|
+
selected: file.selected
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
const selectedKeys = await promptToggleList(
|
|
321
|
+
'Select files to update (type number to toggle, Enter to confirm):',
|
|
322
|
+
items
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
// Filter files by selected keys
|
|
326
|
+
return discovery.files.filter((_, index) => selectedKeys.includes(`${index}`));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (choice === 'e') {
|
|
330
|
+
// Per-file version editing
|
|
331
|
+
showInfo('Enter target version for each file (press Enter to keep calculated version)');
|
|
332
|
+
console.log('');
|
|
333
|
+
|
|
334
|
+
for (const file of discovery.files) {
|
|
335
|
+
const input = await promptEditField(
|
|
336
|
+
`${file.relativePath} (${file.projectLabel})`,
|
|
337
|
+
file.version || 'N/A'
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// If user entered something different from current version, store it
|
|
341
|
+
if (input !== file.version && input !== 'N/A') {
|
|
342
|
+
if (!validateVersionFormat(input)) {
|
|
343
|
+
showWarning(`Invalid version format: ${input} — skipping ${file.relativePath}`);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
file.targetVersion = input;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return discovery.files;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return discovery.files;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Restores files from a snapshot map
|
|
358
|
+
* Why: Centralized rollback logic used by multiple error paths
|
|
359
|
+
*
|
|
360
|
+
* @param {Map<string, string>} snapshot - Map of filePath → original content
|
|
361
|
+
*/
|
|
362
|
+
function restoreSnapshot(snapshot) {
|
|
363
|
+
snapshot.forEach((content, filePath) => {
|
|
364
|
+
try {
|
|
365
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
366
|
+
} catch (rollbackErr) {
|
|
367
|
+
logger.error('bump-version', 'Rollback failed', {
|
|
368
|
+
path: filePath,
|
|
369
|
+
error: rollbackErr.message
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
205
375
|
/**
|
|
206
376
|
* Bump version command
|
|
207
377
|
* @param {Array<string>} args - Command arguments
|
|
@@ -212,25 +382,40 @@ export async function runBumpVersion(args) {
|
|
|
212
382
|
// Parse arguments
|
|
213
383
|
const options = parseArguments(args);
|
|
214
384
|
|
|
215
|
-
if
|
|
385
|
+
// Determine if this is a suffix-only operation
|
|
386
|
+
const isSuffixOnly = (options.removeSuffix || options.setSuffix) && !options.bumpType;
|
|
387
|
+
|
|
388
|
+
// Validate arguments
|
|
389
|
+
if (!options.bumpType && !isSuffixOnly) {
|
|
216
390
|
error('Usage: claude-hooks bump-version <major|minor|patch> [options]');
|
|
391
|
+
error(' or: claude-hooks bump-version --remove-suffix [options]');
|
|
392
|
+
error(' or: claude-hooks bump-version --set-suffix <value> [options]');
|
|
217
393
|
console.log('');
|
|
218
394
|
console.log('Options:');
|
|
219
|
-
console.log(' --suffix <value>
|
|
395
|
+
console.log(' --suffix <value> Add version suffix (e.g., SNAPSHOT, RC)');
|
|
396
|
+
console.log(' --remove-suffix Remove version suffix (2.15.5-SNAPSHOT → 2.15.5)');
|
|
397
|
+
console.log(' --set-suffix <value> Set/replace suffix (2.15.5 → 2.15.5-SNAPSHOT)');
|
|
398
|
+
console.log(' --interactive Force interactive file selection menu');
|
|
220
399
|
console.log(' --update-changelog [branch] Generate CHANGELOG entry (default branch: main)');
|
|
221
|
-
console.log(' --dry-run
|
|
222
|
-
console.log(' --no-tag
|
|
223
|
-
console.log(' --push
|
|
224
|
-
console.log(' --no-commit
|
|
400
|
+
console.log(' --dry-run Preview changes without applying');
|
|
401
|
+
console.log(' --no-tag Skip Git tag creation');
|
|
402
|
+
console.log(' --push Push tag to remote (default: tags stay local)');
|
|
403
|
+
console.log(' --no-commit Skip automatic commit (manual workflow)');
|
|
225
404
|
console.log('');
|
|
226
405
|
console.log('Examples:');
|
|
227
406
|
console.log(' claude-hooks bump-version minor --suffix SNAPSHOT');
|
|
228
407
|
console.log(' claude-hooks bump-version patch --update-changelog');
|
|
408
|
+
console.log(' claude-hooks bump-version --remove-suffix');
|
|
409
|
+
console.log(' claude-hooks bump-version --set-suffix RC1');
|
|
229
410
|
console.log(' claude-hooks bump-version major --dry-run');
|
|
230
411
|
return;
|
|
231
412
|
}
|
|
232
413
|
|
|
233
|
-
|
|
414
|
+
if (isSuffixOnly) {
|
|
415
|
+
showInfo('🔧 Performing suffix-only operation...');
|
|
416
|
+
} else {
|
|
417
|
+
showInfo(`🔢 Bumping version (${options.bumpType})...`);
|
|
418
|
+
}
|
|
234
419
|
console.log('');
|
|
235
420
|
|
|
236
421
|
// Step 1: Validate prerequisites
|
|
@@ -252,86 +437,92 @@ export async function runBumpVersion(args) {
|
|
|
252
437
|
|
|
253
438
|
showSuccess('Prerequisites validated');
|
|
254
439
|
|
|
255
|
-
// Step 2:
|
|
256
|
-
logger.debug('bump-version', 'Step 2:
|
|
257
|
-
const
|
|
440
|
+
// Step 2: Discover all version files
|
|
441
|
+
logger.debug('bump-version', 'Step 2: Discovering version files');
|
|
442
|
+
const discovery = discoverVersionFiles();
|
|
258
443
|
|
|
259
|
-
if (
|
|
444
|
+
if (discovery.files.length === 0) {
|
|
260
445
|
showError('No version files found');
|
|
261
446
|
console.log('');
|
|
262
|
-
console.log('This command requires
|
|
263
|
-
console.log(' - package.json (Node.js
|
|
264
|
-
console.log(' - pom.xml (Maven
|
|
447
|
+
console.log('This command requires at least one of:');
|
|
448
|
+
console.log(' - package.json (Node.js)');
|
|
449
|
+
console.log(' - pom.xml (Maven)');
|
|
450
|
+
console.log(' - build.gradle (Gradle)');
|
|
451
|
+
console.log(' - build.gradle.kts (Gradle Kotlin)');
|
|
452
|
+
console.log(' - pyproject.toml (Python)');
|
|
453
|
+
console.log(' - Cargo.toml (Rust)');
|
|
454
|
+
console.log(' - version.sbt (Scala/sbt)');
|
|
265
455
|
console.log('');
|
|
266
|
-
console.log('Searched
|
|
456
|
+
console.log('Searched recursively up to 3 levels deep.');
|
|
267
457
|
console.log('');
|
|
268
458
|
process.exit(1);
|
|
269
459
|
}
|
|
270
460
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
const repoRoot = getRepoRoot();
|
|
274
|
-
if (paths.packageJson) {
|
|
275
|
-
const relativePath = path.relative(repoRoot, paths.packageJson);
|
|
276
|
-
info(`Found package.json: ${relativePath}`);
|
|
277
|
-
}
|
|
278
|
-
if (paths.pomXml) {
|
|
279
|
-
const relativePath = path.relative(repoRoot, paths.pomXml);
|
|
280
|
-
info(`Found pom.xml: ${relativePath}`);
|
|
281
|
-
}
|
|
461
|
+
// Display discovery table
|
|
462
|
+
displayDiscoveryTable(discovery);
|
|
282
463
|
|
|
283
|
-
const
|
|
284
|
-
const currentVersion = versions.resolved;
|
|
464
|
+
const currentVersion = discovery.resolvedVersion;
|
|
285
465
|
|
|
286
466
|
if (!currentVersion) {
|
|
287
|
-
showError('Could not determine current version');
|
|
467
|
+
showError('Could not determine current version from discovered files');
|
|
288
468
|
process.exit(1);
|
|
289
469
|
}
|
|
290
470
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
471
|
+
showInfo(`Current version: ${currentVersion}`);
|
|
472
|
+
|
|
473
|
+
// Step 3: File selection (if mismatch or interactive mode)
|
|
474
|
+
let selectedFiles = discovery.files.filter(f => f.selected);
|
|
475
|
+
|
|
476
|
+
if (discovery.mismatch || options.interactive) {
|
|
477
|
+
selectedFiles = await promptFileSelection(discovery);
|
|
478
|
+
|
|
479
|
+
if (selectedFiles.length === 0) {
|
|
480
|
+
showError('No files selected');
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
showSuccess(`${selectedFiles.length} file(s) selected for update`);
|
|
298
485
|
console.log('');
|
|
299
486
|
}
|
|
300
487
|
|
|
301
|
-
|
|
302
|
-
|
|
488
|
+
// Step 4: Calculate new version or apply suffix operation
|
|
489
|
+
logger.debug('bump-version', 'Step 4: Calculating new version');
|
|
490
|
+
let newVersion;
|
|
491
|
+
let operation;
|
|
492
|
+
|
|
493
|
+
if (isSuffixOnly) {
|
|
494
|
+
if (options.removeSuffix) {
|
|
495
|
+
newVersion = modifySuffix(currentVersion, { remove: true });
|
|
496
|
+
operation = 'Remove suffix';
|
|
497
|
+
} else if (options.setSuffix) {
|
|
498
|
+
newVersion = modifySuffix(currentVersion, { set: options.setSuffix });
|
|
499
|
+
operation = `Set suffix to ${options.setSuffix}`;
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
newVersion = incrementVersion(currentVersion, options.bumpType, options.suffix || options.setSuffix);
|
|
503
|
+
operation = `Bump ${options.bumpType}`;
|
|
504
|
+
}
|
|
303
505
|
|
|
304
|
-
// Step 3: Calculate new version
|
|
305
|
-
logger.debug('bump-version', 'Step 3: Calculating new version');
|
|
306
|
-
const newVersion = incrementVersion(currentVersion, options.bumpType, options.suffix);
|
|
307
506
|
const tagName = formatTagName(newVersion);
|
|
308
507
|
|
|
309
508
|
showSuccess(`New version: ${newVersion}`);
|
|
310
509
|
console.log('');
|
|
311
510
|
|
|
312
|
-
//
|
|
313
|
-
const filesToUpdate = [];
|
|
314
|
-
if (projectType === 'node' || projectType === 'both') {
|
|
315
|
-
filesToUpdate.push('package.json');
|
|
316
|
-
}
|
|
317
|
-
if (projectType === 'maven' || projectType === 'both') {
|
|
318
|
-
filesToUpdate.push('pom.xml');
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Dry run preview
|
|
511
|
+
// Step 5: Dry run preview
|
|
322
512
|
if (options.dryRun) {
|
|
323
513
|
showDryRunPreview({
|
|
324
|
-
|
|
514
|
+
discovery,
|
|
515
|
+
operation,
|
|
325
516
|
currentVersion,
|
|
326
517
|
newVersion,
|
|
327
518
|
tagName,
|
|
328
|
-
|
|
519
|
+
selectedFiles,
|
|
329
520
|
updateChangelog: options.updateChangelog
|
|
330
521
|
});
|
|
331
522
|
return;
|
|
332
523
|
}
|
|
333
524
|
|
|
334
|
-
// Confirm with user
|
|
525
|
+
// Step 6: Confirm with user
|
|
335
526
|
const shouldContinue = await promptConfirmation(
|
|
336
527
|
`Update version to ${newVersion}?`,
|
|
337
528
|
true
|
|
@@ -350,23 +541,53 @@ export async function runBumpVersion(args) {
|
|
|
350
541
|
const defaultBranch = config.github?.pr?.defaultBase || '{target-branch}';
|
|
351
542
|
logger.debug('bump-version', 'Default branch for PR', { defaultBranch });
|
|
352
543
|
|
|
353
|
-
// Step
|
|
354
|
-
logger.debug('bump-version', 'Step
|
|
355
|
-
|
|
356
|
-
|
|
544
|
+
// Step 7: Snapshot files for rollback
|
|
545
|
+
logger.debug('bump-version', 'Step 7: Creating snapshot for rollback');
|
|
546
|
+
const snapshot = new Map();
|
|
547
|
+
selectedFiles.forEach(file => {
|
|
548
|
+
try {
|
|
549
|
+
const content = fs.readFileSync(file.path, 'utf8');
|
|
550
|
+
snapshot.set(file.path, content);
|
|
551
|
+
} catch (err) {
|
|
552
|
+
logger.error('bump-version', 'Failed to snapshot file', {
|
|
553
|
+
path: file.path,
|
|
554
|
+
error: err.message
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
});
|
|
357
558
|
|
|
358
|
-
|
|
359
|
-
|
|
559
|
+
logger.debug('bump-version', 'Snapshot created', {
|
|
560
|
+
fileCount: snapshot.size
|
|
360
561
|
});
|
|
361
562
|
|
|
362
|
-
|
|
563
|
+
// Step 8: Update version files
|
|
564
|
+
logger.debug('bump-version', 'Step 8: Updating version files');
|
|
565
|
+
showInfo('Updating version files...');
|
|
363
566
|
|
|
364
|
-
|
|
567
|
+
try {
|
|
568
|
+
updateVersionFiles(selectedFiles, newVersion);
|
|
569
|
+
|
|
570
|
+
selectedFiles.forEach(file => {
|
|
571
|
+
const targetVer = file.targetVersion || newVersion;
|
|
572
|
+
showSuccess(`✓ Updated ${file.relativePath} → ${targetVer}`);
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
console.log('');
|
|
576
|
+
} catch (err) {
|
|
577
|
+
// Rollback on failure
|
|
578
|
+
showError(`Failed to update version files: ${err.message}`);
|
|
579
|
+
showWarning('Rolling back changes...');
|
|
580
|
+
restoreSnapshot(snapshot);
|
|
581
|
+
showSuccess('Changes rolled back');
|
|
582
|
+
process.exit(1);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Step 9: Generate CHANGELOG (if requested)
|
|
365
586
|
if (options.updateChangelog) {
|
|
366
|
-
logger.debug('bump-version', 'Step
|
|
587
|
+
logger.debug('bump-version', 'Step 9: Generating CHANGELOG');
|
|
367
588
|
showInfo('Generating CHANGELOG entry...');
|
|
368
589
|
|
|
369
|
-
const isReleaseVersion = !options.suffix
|
|
590
|
+
const isReleaseVersion = !options.suffix && !options.setSuffix;
|
|
370
591
|
|
|
371
592
|
const changelogResult = await generateChangelogEntry({
|
|
372
593
|
version: newVersion,
|
|
@@ -389,24 +610,15 @@ export async function runBumpVersion(args) {
|
|
|
389
610
|
console.log('');
|
|
390
611
|
}
|
|
391
612
|
|
|
392
|
-
// Step
|
|
613
|
+
// Step 10: Stage and commit changes
|
|
393
614
|
let commitCreated = false;
|
|
394
615
|
|
|
395
616
|
if (!options.noCommit) {
|
|
396
|
-
logger.debug('bump-version', 'Step
|
|
617
|
+
logger.debug('bump-version', 'Step 10: Staging and committing changes');
|
|
397
618
|
showInfo('Staging and committing changes...');
|
|
398
619
|
|
|
399
620
|
// Collect files to stage
|
|
400
|
-
const filesToStage =
|
|
401
|
-
|
|
402
|
-
// Add discovered version files
|
|
403
|
-
const paths = getDiscoveredPaths();
|
|
404
|
-
if (paths.packageJson) {
|
|
405
|
-
filesToStage.push(paths.packageJson);
|
|
406
|
-
}
|
|
407
|
-
if (paths.pomXml) {
|
|
408
|
-
filesToStage.push(paths.pomXml);
|
|
409
|
-
}
|
|
621
|
+
const filesToStage = selectedFiles.map(f => f.path);
|
|
410
622
|
|
|
411
623
|
// Add CHANGELOG if it was updated
|
|
412
624
|
if (options.updateChangelog) {
|
|
@@ -425,26 +637,39 @@ export async function runBumpVersion(args) {
|
|
|
425
637
|
console.log('Files that should be staged:');
|
|
426
638
|
filesToStage.forEach(f => console.log(` - ${path.relative(getRepoRoot(), f)}`));
|
|
427
639
|
console.log('');
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.log(` git commit -m "chore(version): bump to ${newVersion}"`);
|
|
640
|
+
|
|
641
|
+
// Rollback version changes
|
|
642
|
+
showWarning('Rolling back version changes...');
|
|
643
|
+
restoreSnapshot(snapshot);
|
|
644
|
+
showSuccess('Changes rolled back');
|
|
434
645
|
process.exit(1);
|
|
435
646
|
}
|
|
436
647
|
|
|
437
648
|
showSuccess(`✓ Staged ${stageResult.stagedFiles.length} file(s)`);
|
|
438
649
|
|
|
439
650
|
// Create commit
|
|
440
|
-
const commitMessage =
|
|
651
|
+
const commitMessage = isSuffixOnly
|
|
652
|
+
? `chore(version): update suffix to ${newVersion}`
|
|
653
|
+
: `chore(version): bump to ${newVersion}`;
|
|
654
|
+
|
|
441
655
|
const commitResult = createCommit(commitMessage, { noVerify: true });
|
|
442
656
|
|
|
443
657
|
if (!commitResult.success) {
|
|
444
658
|
showError(`Failed to create commit: ${commitResult.error}`);
|
|
445
659
|
console.log('');
|
|
446
|
-
|
|
447
|
-
|
|
660
|
+
|
|
661
|
+
// Rollback version changes and unstage
|
|
662
|
+
showWarning('Rolling back version changes...');
|
|
663
|
+
restoreSnapshot(snapshot);
|
|
664
|
+
|
|
665
|
+
// Unstage files
|
|
666
|
+
try {
|
|
667
|
+
execSync('git reset HEAD', { encoding: 'utf8' });
|
|
668
|
+
} catch (unstageErr) {
|
|
669
|
+
logger.error('bump-version', 'Failed to unstage', unstageErr);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
showSuccess('Changes rolled back');
|
|
448
673
|
process.exit(1);
|
|
449
674
|
}
|
|
450
675
|
|
|
@@ -456,9 +681,9 @@ export async function runBumpVersion(args) {
|
|
|
456
681
|
console.log('');
|
|
457
682
|
}
|
|
458
683
|
|
|
459
|
-
// Step
|
|
684
|
+
// Step 11: Create Git tag (if not disabled)
|
|
460
685
|
if (!options.noTag) {
|
|
461
|
-
logger.debug('bump-version', 'Step
|
|
686
|
+
logger.debug('bump-version', 'Step 11: Creating Git tag');
|
|
462
687
|
|
|
463
688
|
// Prevent tag creation if changes not committed
|
|
464
689
|
if (options.noCommit) {
|
|
@@ -495,9 +720,9 @@ export async function runBumpVersion(args) {
|
|
|
495
720
|
showSuccess(`✓ Tag created: ${tagName}`);
|
|
496
721
|
console.log('');
|
|
497
722
|
|
|
498
|
-
// Step
|
|
723
|
+
// Step 12: Push tag (if --push flag provided)
|
|
499
724
|
if (options.push) {
|
|
500
|
-
logger.debug('bump-version', 'Step
|
|
725
|
+
logger.debug('bump-version', 'Step 12: Pushing tag to remote');
|
|
501
726
|
showInfo('Pushing tag to remote...');
|
|
502
727
|
|
|
503
728
|
const pushResult = pushTags(null, tagName);
|
|
@@ -530,6 +755,15 @@ export async function runBumpVersion(args) {
|
|
|
530
755
|
console.log(`${colors.green} Version Bump Complete ✅ ${colors.reset}`);
|
|
531
756
|
console.log(`${colors.green}════════════════════════════════════════════════${colors.reset}`);
|
|
532
757
|
console.log('');
|
|
758
|
+
|
|
759
|
+
// Show updated files table
|
|
760
|
+
console.log(`${colors.blue}Updated files:${colors.reset}`);
|
|
761
|
+
selectedFiles.forEach(file => {
|
|
762
|
+
const targetVer = file.targetVersion || newVersion;
|
|
763
|
+
console.log(` ✓ ${file.relativePath} (${file.projectLabel}) - ${file.version} → ${targetVer}`);
|
|
764
|
+
});
|
|
765
|
+
console.log('');
|
|
766
|
+
|
|
533
767
|
console.log(`${colors.blue}Old version:${colors.reset} ${currentVersion}`);
|
|
534
768
|
console.log(`${colors.blue}New version:${colors.reset} ${newVersion}`);
|
|
535
769
|
console.log(`${colors.blue}Tag:${colors.reset} ${tagName}`);
|
|
@@ -539,7 +773,7 @@ export async function runBumpVersion(args) {
|
|
|
539
773
|
if (options.noCommit) {
|
|
540
774
|
console.log('Next steps:');
|
|
541
775
|
console.log(' 1. Review changes: git diff');
|
|
542
|
-
console.log(
|
|
776
|
+
console.log(' 2. Stage files: git add <files>');
|
|
543
777
|
if (options.updateChangelog) {
|
|
544
778
|
console.log(' git add CHANGELOG.md');
|
|
545
779
|
}
|
|
@@ -566,9 +800,7 @@ export async function runBumpVersion(args) {
|
|
|
566
800
|
logger.error('bump-version', 'Version bump failed', err);
|
|
567
801
|
showError(`Version bump failed: ${err.message}`);
|
|
568
802
|
console.log('');
|
|
569
|
-
console.log('The operation was interrupted.
|
|
570
|
-
console.log(' - Revert changes: git checkout .');
|
|
571
|
-
console.log(` - Delete tag if created: git tag -d ${ tagName}`);
|
|
803
|
+
console.log('The operation was interrupted.');
|
|
572
804
|
console.log('');
|
|
573
805
|
process.exit(1);
|
|
574
806
|
}
|