s9n-devops-agent 1.6.2 → 1.7.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/docs/AUTOMATED_TESTING_STRATEGY.md +316 -0
- package/docs/branch-management.md +101 -0
- package/package.json +1 -1
- package/src/branch-config-manager.js +544 -0
- package/src/enhanced-close-session.js +492 -0
- package/src/orphan-cleaner.js +538 -0
- package/src/weekly-consolidator.js +403 -0
- package/start-devops-session.sh +1 -1
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Branch Configuration Manager
|
|
5
|
+
* Manages branch management settings for the DevOps Agent
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const readline = require('readline');
|
|
12
|
+
|
|
13
|
+
// Configuration
|
|
14
|
+
const CONFIG = {
|
|
15
|
+
colors: {
|
|
16
|
+
reset: '\x1b[0m',
|
|
17
|
+
bright: '\x1b[1m',
|
|
18
|
+
red: '\x1b[31m',
|
|
19
|
+
green: '\x1b[32m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
cyan: '\x1b[36m',
|
|
23
|
+
dim: '\x1b[2m'
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
class BranchConfigManager {
|
|
28
|
+
constructor() {
|
|
29
|
+
this.repoRoot = this.getRepoRoot();
|
|
30
|
+
this.localDeployDir = path.join(this.repoRoot, 'local_deploy');
|
|
31
|
+
this.projectSettingsPath = path.join(this.localDeployDir, 'project-settings.json');
|
|
32
|
+
this.defaultSettings = this.getDefaultSettings();
|
|
33
|
+
this.ensureDirectories();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getRepoRoot() {
|
|
37
|
+
try {
|
|
38
|
+
return execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`${CONFIG.colors.red}Error: Not in a git repository${CONFIG.colors.reset}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ensureDirectories() {
|
|
46
|
+
if (!fs.existsSync(this.localDeployDir)) {
|
|
47
|
+
fs.mkdirSync(this.localDeployDir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getDefaultSettings() {
|
|
52
|
+
return {
|
|
53
|
+
version: "1.4.0",
|
|
54
|
+
branchManagement: {
|
|
55
|
+
defaultMergeTarget: "main",
|
|
56
|
+
enableDualMerge: false,
|
|
57
|
+
enableWeeklyConsolidation: true,
|
|
58
|
+
orphanSessionThresholdDays: 7,
|
|
59
|
+
mergeStrategy: "hierarchical-first",
|
|
60
|
+
conflictResolution: "prompt"
|
|
61
|
+
},
|
|
62
|
+
rolloverSettings: {
|
|
63
|
+
enableAutoRollover: true,
|
|
64
|
+
rolloverTime: "00:00",
|
|
65
|
+
timezone: "UTC",
|
|
66
|
+
preserveRunningAgent: true
|
|
67
|
+
},
|
|
68
|
+
cleanup: {
|
|
69
|
+
autoCleanupOrphans: false,
|
|
70
|
+
weeklyCleanupDay: "sunday",
|
|
71
|
+
retainWeeklyBranches: 12
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Load current project settings
|
|
78
|
+
*/
|
|
79
|
+
loadSettings() {
|
|
80
|
+
try {
|
|
81
|
+
if (fs.existsSync(this.projectSettingsPath)) {
|
|
82
|
+
const settings = JSON.parse(fs.readFileSync(this.projectSettingsPath, 'utf8'));
|
|
83
|
+
// Merge with defaults to ensure all properties exist
|
|
84
|
+
return this.mergeWithDefaults(settings);
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.warn(`${CONFIG.colors.yellow}Warning: Could not load project settings: ${error.message}${CONFIG.colors.reset}`);
|
|
88
|
+
}
|
|
89
|
+
return this.defaultSettings;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Merge loaded settings with defaults
|
|
94
|
+
*/
|
|
95
|
+
mergeWithDefaults(settings) {
|
|
96
|
+
const merged = JSON.parse(JSON.stringify(this.defaultSettings));
|
|
97
|
+
|
|
98
|
+
// Deep merge function
|
|
99
|
+
const deepMerge = (target, source) => {
|
|
100
|
+
for (const key in source) {
|
|
101
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
102
|
+
if (!target[key]) target[key] = {};
|
|
103
|
+
deepMerge(target[key], source[key]);
|
|
104
|
+
} else {
|
|
105
|
+
target[key] = source[key];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
deepMerge(merged, settings);
|
|
111
|
+
return merged;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Save settings to file
|
|
116
|
+
*/
|
|
117
|
+
saveSettings(settings) {
|
|
118
|
+
try {
|
|
119
|
+
// Ensure version is updated
|
|
120
|
+
settings.version = this.defaultSettings.version;
|
|
121
|
+
|
|
122
|
+
fs.writeFileSync(this.projectSettingsPath, JSON.stringify(settings, null, 2));
|
|
123
|
+
console.log(`${CONFIG.colors.green}✓ Settings saved to ${this.projectSettingsPath}${CONFIG.colors.reset}`);
|
|
124
|
+
return true;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(`${CONFIG.colors.red}✗ Failed to save settings: ${error.message}${CONFIG.colors.reset}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get a nested property value using dot notation
|
|
133
|
+
*/
|
|
134
|
+
getNestedValue(obj, path) {
|
|
135
|
+
return path.split('.').reduce((current, key) => current && current[key], obj);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Set a nested property value using dot notation
|
|
140
|
+
*/
|
|
141
|
+
setNestedValue(obj, path, value) {
|
|
142
|
+
const keys = path.split('.');
|
|
143
|
+
const lastKey = keys.pop();
|
|
144
|
+
const target = keys.reduce((current, key) => {
|
|
145
|
+
if (!current[key]) current[key] = {};
|
|
146
|
+
return current[key];
|
|
147
|
+
}, obj);
|
|
148
|
+
|
|
149
|
+
// Convert string values to appropriate types
|
|
150
|
+
if (value === 'true') value = true;
|
|
151
|
+
else if (value === 'false') value = false;
|
|
152
|
+
else if (!isNaN(value) && !isNaN(parseFloat(value))) value = parseFloat(value);
|
|
153
|
+
|
|
154
|
+
target[lastKey] = value;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Display current settings
|
|
159
|
+
*/
|
|
160
|
+
displaySettings(settings = null) {
|
|
161
|
+
if (!settings) {
|
|
162
|
+
settings = this.loadSettings();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(`\n${CONFIG.colors.bright}${CONFIG.colors.blue}Current Branch Management Settings${CONFIG.colors.reset}`);
|
|
166
|
+
console.log(`${CONFIG.colors.dim}Repository: ${this.repoRoot}${CONFIG.colors.reset}\n`);
|
|
167
|
+
|
|
168
|
+
// Branch Management
|
|
169
|
+
console.log(`${CONFIG.colors.bright}Branch Management:${CONFIG.colors.reset}`);
|
|
170
|
+
console.log(` Default merge target: ${CONFIG.colors.cyan}${settings.branchManagement.defaultMergeTarget}${CONFIG.colors.reset}`);
|
|
171
|
+
console.log(` Dual merge enabled: ${settings.branchManagement.enableDualMerge ? CONFIG.colors.green + 'Yes' : CONFIG.colors.yellow + 'No'}${CONFIG.colors.reset}`);
|
|
172
|
+
console.log(` Weekly consolidation: ${settings.branchManagement.enableWeeklyConsolidation ? CONFIG.colors.green + 'Yes' : CONFIG.colors.yellow + 'No'}${CONFIG.colors.reset}`);
|
|
173
|
+
console.log(` Orphan threshold: ${CONFIG.colors.cyan}${settings.branchManagement.orphanSessionThresholdDays} days${CONFIG.colors.reset}`);
|
|
174
|
+
console.log(` Merge strategy: ${CONFIG.colors.cyan}${settings.branchManagement.mergeStrategy}${CONFIG.colors.reset}`);
|
|
175
|
+
console.log(` Conflict resolution: ${CONFIG.colors.cyan}${settings.branchManagement.conflictResolution}${CONFIG.colors.reset}`);
|
|
176
|
+
|
|
177
|
+
// Rollover Settings
|
|
178
|
+
console.log(`\n${CONFIG.colors.bright}Rollover Settings:${CONFIG.colors.reset}`);
|
|
179
|
+
console.log(` Auto rollover: ${settings.rolloverSettings.enableAutoRollover ? CONFIG.colors.green + 'Yes' : CONFIG.colors.yellow + 'No'}${CONFIG.colors.reset}`);
|
|
180
|
+
console.log(` Rollover time: ${CONFIG.colors.cyan}${settings.rolloverSettings.rolloverTime}${CONFIG.colors.reset}`);
|
|
181
|
+
console.log(` Timezone: ${CONFIG.colors.cyan}${settings.rolloverSettings.timezone}${CONFIG.colors.reset}`);
|
|
182
|
+
console.log(` Preserve running agent: ${settings.rolloverSettings.preserveRunningAgent ? CONFIG.colors.green + 'Yes' : CONFIG.colors.yellow + 'No'}${CONFIG.colors.reset}`);
|
|
183
|
+
|
|
184
|
+
// Cleanup Settings
|
|
185
|
+
console.log(`\n${CONFIG.colors.bright}Cleanup Settings:${CONFIG.colors.reset}`);
|
|
186
|
+
console.log(` Auto cleanup orphans: ${settings.cleanup.autoCleanupOrphans ? CONFIG.colors.green + 'Yes' : CONFIG.colors.yellow + 'No'}${CONFIG.colors.reset}`);
|
|
187
|
+
console.log(` Weekly cleanup day: ${CONFIG.colors.cyan}${settings.cleanup.weeklyCleanupDay}${CONFIG.colors.reset}`);
|
|
188
|
+
console.log(` Retain weekly branches: ${CONFIG.colors.cyan}${settings.cleanup.retainWeeklyBranches}${CONFIG.colors.reset}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Interactive configuration wizard
|
|
193
|
+
*/
|
|
194
|
+
async runConfigWizard() {
|
|
195
|
+
console.log(`\n${CONFIG.colors.bright}${CONFIG.colors.blue}Branch Management Configuration Wizard${CONFIG.colors.reset}`);
|
|
196
|
+
console.log(`${CONFIG.colors.dim}This wizard will help you configure branch management settings${CONFIG.colors.reset}\n`);
|
|
197
|
+
|
|
198
|
+
const settings = this.loadSettings();
|
|
199
|
+
const rl = readline.createInterface({
|
|
200
|
+
input: process.stdin,
|
|
201
|
+
output: process.stdout
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const prompt = (question, defaultValue) => {
|
|
205
|
+
return new Promise((resolve) => {
|
|
206
|
+
const displayDefault = defaultValue !== undefined ? ` (${defaultValue})` : '';
|
|
207
|
+
rl.question(`${question}${displayDefault}: `, (answer) => {
|
|
208
|
+
resolve(answer.trim() || defaultValue);
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const promptYesNo = (question, defaultValue = false) => {
|
|
214
|
+
return new Promise((resolve) => {
|
|
215
|
+
const defaultDisplay = defaultValue ? 'Y/n' : 'y/N';
|
|
216
|
+
rl.question(`${question} (${defaultDisplay}): `, (answer) => {
|
|
217
|
+
const normalized = answer.toLowerCase().trim();
|
|
218
|
+
if (normalized === '') {
|
|
219
|
+
resolve(defaultValue);
|
|
220
|
+
} else {
|
|
221
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
// Branch Management Settings
|
|
229
|
+
console.log(`${CONFIG.colors.bright}Branch Management Settings:${CONFIG.colors.reset}`);
|
|
230
|
+
|
|
231
|
+
settings.branchManagement.defaultMergeTarget = await prompt(
|
|
232
|
+
'Default merge target branch',
|
|
233
|
+
settings.branchManagement.defaultMergeTarget
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
settings.branchManagement.enableDualMerge = await promptYesNo(
|
|
237
|
+
'Enable dual merge (merge to both daily and target branches)',
|
|
238
|
+
settings.branchManagement.enableDualMerge
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
if (settings.branchManagement.enableDualMerge) {
|
|
242
|
+
console.log('\nMerge strategy options:');
|
|
243
|
+
console.log(' 1. hierarchical-first - Merge to daily branch first, then target');
|
|
244
|
+
console.log(' 2. target-first - Merge to target branch first, then daily');
|
|
245
|
+
console.log(' 3. parallel - Merge to both branches simultaneously');
|
|
246
|
+
|
|
247
|
+
const strategyChoice = await prompt('Choose merge strategy (1-3)', '1');
|
|
248
|
+
const strategies = ['hierarchical-first', 'target-first', 'parallel'];
|
|
249
|
+
settings.branchManagement.mergeStrategy = strategies[parseInt(strategyChoice) - 1] || 'hierarchical-first';
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
settings.branchManagement.enableWeeklyConsolidation = await promptYesNo(
|
|
253
|
+
'Enable weekly branch consolidation',
|
|
254
|
+
settings.branchManagement.enableWeeklyConsolidation
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
const orphanDays = await prompt(
|
|
258
|
+
'Days before session considered orphaned',
|
|
259
|
+
settings.branchManagement.orphanSessionThresholdDays
|
|
260
|
+
);
|
|
261
|
+
settings.branchManagement.orphanSessionThresholdDays = parseInt(orphanDays) || 7;
|
|
262
|
+
|
|
263
|
+
// Rollover Settings
|
|
264
|
+
console.log(`\n${CONFIG.colors.bright}Rollover Settings:${CONFIG.colors.reset}`);
|
|
265
|
+
|
|
266
|
+
settings.rolloverSettings.enableAutoRollover = await promptYesNo(
|
|
267
|
+
'Enable automatic daily rollover',
|
|
268
|
+
settings.rolloverSettings.enableAutoRollover
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
if (settings.rolloverSettings.enableAutoRollover) {
|
|
272
|
+
settings.rolloverSettings.rolloverTime = await prompt(
|
|
273
|
+
'Rollover time (HH:MM format)',
|
|
274
|
+
settings.rolloverSettings.rolloverTime
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
settings.rolloverSettings.timezone = await prompt(
|
|
278
|
+
'Timezone for rollover',
|
|
279
|
+
settings.rolloverSettings.timezone
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
settings.rolloverSettings.preserveRunningAgent = await promptYesNo(
|
|
284
|
+
'Preserve running agent during rollover',
|
|
285
|
+
settings.rolloverSettings.preserveRunningAgent
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Cleanup Settings
|
|
289
|
+
console.log(`\n${CONFIG.colors.bright}Cleanup Settings:${CONFIG.colors.reset}`);
|
|
290
|
+
|
|
291
|
+
settings.cleanup.autoCleanupOrphans = await promptYesNo(
|
|
292
|
+
'Automatically cleanup orphaned sessions',
|
|
293
|
+
settings.cleanup.autoCleanupOrphans
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (settings.branchManagement.enableWeeklyConsolidation) {
|
|
297
|
+
console.log('\nWeekly cleanup day options: sunday, monday, tuesday, wednesday, thursday, friday, saturday');
|
|
298
|
+
settings.cleanup.weeklyCleanupDay = await prompt(
|
|
299
|
+
'Day of week for weekly cleanup',
|
|
300
|
+
settings.cleanup.weeklyCleanupDay
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const retainWeeks = await prompt(
|
|
304
|
+
'Number of weekly branches to retain',
|
|
305
|
+
settings.cleanup.retainWeeklyBranches
|
|
306
|
+
);
|
|
307
|
+
settings.cleanup.retainWeeklyBranches = parseInt(retainWeeks) || 12;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
rl.close();
|
|
311
|
+
|
|
312
|
+
// Display final configuration
|
|
313
|
+
console.log(`\n${CONFIG.colors.bright}Configuration Summary:${CONFIG.colors.reset}`);
|
|
314
|
+
this.displaySettings(settings);
|
|
315
|
+
|
|
316
|
+
// Confirm save
|
|
317
|
+
const rl2 = readline.createInterface({
|
|
318
|
+
input: process.stdin,
|
|
319
|
+
output: process.stdout
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const shouldSave = await new Promise((resolve) => {
|
|
323
|
+
rl2.question('\nSave this configuration? (Y/n): ', (answer) => {
|
|
324
|
+
rl2.close();
|
|
325
|
+
resolve(answer.toLowerCase() !== 'n');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (shouldSave) {
|
|
330
|
+
this.saveSettings(settings);
|
|
331
|
+
console.log(`\n${CONFIG.colors.green}✅ Configuration saved successfully${CONFIG.colors.reset}`);
|
|
332
|
+
} else {
|
|
333
|
+
console.log(`\n${CONFIG.colors.yellow}Configuration not saved${CONFIG.colors.reset}`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
} catch (error) {
|
|
337
|
+
rl.close();
|
|
338
|
+
console.error(`\n${CONFIG.colors.red}❌ Configuration wizard failed: ${error.message}${CONFIG.colors.reset}`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get a specific setting value
|
|
344
|
+
*/
|
|
345
|
+
getSetting(settingPath) {
|
|
346
|
+
const settings = this.loadSettings();
|
|
347
|
+
const value = this.getNestedValue(settings, settingPath);
|
|
348
|
+
|
|
349
|
+
if (value !== undefined) {
|
|
350
|
+
console.log(`${settingPath}: ${CONFIG.colors.cyan}${value}${CONFIG.colors.reset}`);
|
|
351
|
+
} else {
|
|
352
|
+
console.log(`${CONFIG.colors.red}Setting not found: ${settingPath}${CONFIG.colors.reset}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return value;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Set a specific setting value
|
|
360
|
+
*/
|
|
361
|
+
setSetting(settingPath, value) {
|
|
362
|
+
const settings = this.loadSettings();
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
this.setNestedValue(settings, settingPath, value);
|
|
366
|
+
|
|
367
|
+
if (this.saveSettings(settings)) {
|
|
368
|
+
console.log(`${CONFIG.colors.green}✓ Updated ${settingPath} = ${value}${CONFIG.colors.reset}`);
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error(`${CONFIG.colors.red}✗ Failed to set ${settingPath}: ${error.message}${CONFIG.colors.reset}`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Reset settings to defaults
|
|
380
|
+
*/
|
|
381
|
+
resetSettings() {
|
|
382
|
+
const rl = readline.createInterface({
|
|
383
|
+
input: process.stdin,
|
|
384
|
+
output: process.stdout
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return new Promise((resolve) => {
|
|
388
|
+
console.log(`${CONFIG.colors.yellow}⚠️ This will reset all branch management settings to defaults${CONFIG.colors.reset}`);
|
|
389
|
+
rl.question('Are you sure? (y/N): ', (answer) => {
|
|
390
|
+
rl.close();
|
|
391
|
+
|
|
392
|
+
if (answer.toLowerCase() === 'y') {
|
|
393
|
+
if (this.saveSettings(this.defaultSettings)) {
|
|
394
|
+
console.log(`${CONFIG.colors.green}✅ Settings reset to defaults${CONFIG.colors.reset}`);
|
|
395
|
+
resolve(true);
|
|
396
|
+
} else {
|
|
397
|
+
resolve(false);
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
console.log('Reset cancelled');
|
|
401
|
+
resolve(false);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Validate current settings
|
|
409
|
+
*/
|
|
410
|
+
validateSettings() {
|
|
411
|
+
const settings = this.loadSettings();
|
|
412
|
+
const issues = [];
|
|
413
|
+
|
|
414
|
+
console.log(`\n${CONFIG.colors.bright}${CONFIG.colors.blue}Validating Settings${CONFIG.colors.reset}\n`);
|
|
415
|
+
|
|
416
|
+
// Validate merge target branch exists
|
|
417
|
+
if (settings.branchManagement.defaultMergeTarget) {
|
|
418
|
+
try {
|
|
419
|
+
execSync(`git show-ref --verify --quiet refs/remotes/origin/${settings.branchManagement.defaultMergeTarget}`, { stdio: 'ignore' });
|
|
420
|
+
console.log(`${CONFIG.colors.green}✓ Target branch '${settings.branchManagement.defaultMergeTarget}' exists${CONFIG.colors.reset}`);
|
|
421
|
+
} catch {
|
|
422
|
+
issues.push(`Target branch '${settings.branchManagement.defaultMergeTarget}' does not exist`);
|
|
423
|
+
console.log(`${CONFIG.colors.red}✗ Target branch '${settings.branchManagement.defaultMergeTarget}' does not exist${CONFIG.colors.reset}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Validate merge strategy
|
|
428
|
+
const validStrategies = ['hierarchical-first', 'target-first', 'parallel'];
|
|
429
|
+
if (!validStrategies.includes(settings.branchManagement.mergeStrategy)) {
|
|
430
|
+
issues.push(`Invalid merge strategy: ${settings.branchManagement.mergeStrategy}`);
|
|
431
|
+
console.log(`${CONFIG.colors.red}✗ Invalid merge strategy: ${settings.branchManagement.mergeStrategy}${CONFIG.colors.reset}`);
|
|
432
|
+
} else {
|
|
433
|
+
console.log(`${CONFIG.colors.green}✓ Merge strategy '${settings.branchManagement.mergeStrategy}' is valid${CONFIG.colors.reset}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Validate orphan threshold
|
|
437
|
+
if (settings.branchManagement.orphanSessionThresholdDays < 1) {
|
|
438
|
+
issues.push('Orphan threshold must be at least 1 day');
|
|
439
|
+
console.log(`${CONFIG.colors.red}✗ Orphan threshold must be at least 1 day${CONFIG.colors.reset}`);
|
|
440
|
+
} else {
|
|
441
|
+
console.log(`${CONFIG.colors.green}✓ Orphan threshold (${settings.branchManagement.orphanSessionThresholdDays} days) is valid${CONFIG.colors.reset}`);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Validate rollover time format
|
|
445
|
+
const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/;
|
|
446
|
+
if (!timeRegex.test(settings.rolloverSettings.rolloverTime)) {
|
|
447
|
+
issues.push(`Invalid rollover time format: ${settings.rolloverSettings.rolloverTime}`);
|
|
448
|
+
console.log(`${CONFIG.colors.red}✗ Invalid rollover time format: ${settings.rolloverSettings.rolloverTime}${CONFIG.colors.reset}`);
|
|
449
|
+
} else {
|
|
450
|
+
console.log(`${CONFIG.colors.green}✓ Rollover time format is valid${CONFIG.colors.reset}`);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Validate weekly cleanup day
|
|
454
|
+
const validDays = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
|
455
|
+
if (!validDays.includes(settings.cleanup.weeklyCleanupDay.toLowerCase())) {
|
|
456
|
+
issues.push(`Invalid weekly cleanup day: ${settings.cleanup.weeklyCleanupDay}`);
|
|
457
|
+
console.log(`${CONFIG.colors.red}✗ Invalid weekly cleanup day: ${settings.cleanup.weeklyCleanupDay}${CONFIG.colors.reset}`);
|
|
458
|
+
} else {
|
|
459
|
+
console.log(`${CONFIG.colors.green}✓ Weekly cleanup day is valid${CONFIG.colors.reset}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Summary
|
|
463
|
+
if (issues.length === 0) {
|
|
464
|
+
console.log(`\n${CONFIG.colors.green}✅ All settings are valid${CONFIG.colors.reset}`);
|
|
465
|
+
} else {
|
|
466
|
+
console.log(`\n${CONFIG.colors.red}❌ Found ${issues.length} issue(s):${CONFIG.colors.reset}`);
|
|
467
|
+
issues.forEach(issue => {
|
|
468
|
+
console.log(` • ${issue}`);
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return issues.length === 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Main execution function
|
|
477
|
+
*/
|
|
478
|
+
async run(command, ...args) {
|
|
479
|
+
try {
|
|
480
|
+
switch (command) {
|
|
481
|
+
case 'show':
|
|
482
|
+
case 'display':
|
|
483
|
+
this.displaySettings();
|
|
484
|
+
break;
|
|
485
|
+
|
|
486
|
+
case 'wizard':
|
|
487
|
+
case 'setup':
|
|
488
|
+
await this.runConfigWizard();
|
|
489
|
+
break;
|
|
490
|
+
|
|
491
|
+
case 'get':
|
|
492
|
+
if (args.length === 0) {
|
|
493
|
+
console.log(`${CONFIG.colors.red}Usage: get <setting.path>${CONFIG.colors.reset}`);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
this.getSetting(args[0]);
|
|
497
|
+
break;
|
|
498
|
+
|
|
499
|
+
case 'set':
|
|
500
|
+
if (args.length < 2) {
|
|
501
|
+
console.log(`${CONFIG.colors.red}Usage: set <setting.path> <value>${CONFIG.colors.reset}`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
this.setSetting(args[0], args[1]);
|
|
505
|
+
break;
|
|
506
|
+
|
|
507
|
+
case 'reset':
|
|
508
|
+
await this.resetSettings();
|
|
509
|
+
break;
|
|
510
|
+
|
|
511
|
+
case 'validate':
|
|
512
|
+
this.validateSettings();
|
|
513
|
+
break;
|
|
514
|
+
|
|
515
|
+
default:
|
|
516
|
+
console.log(`${CONFIG.colors.red}Unknown command: ${command}${CONFIG.colors.reset}`);
|
|
517
|
+
console.log('\nAvailable commands:');
|
|
518
|
+
console.log(' show/display - Display current settings');
|
|
519
|
+
console.log(' wizard/setup - Run interactive configuration wizard');
|
|
520
|
+
console.log(' get <path> - Get a specific setting value');
|
|
521
|
+
console.log(' set <path> <value> - Set a specific setting value');
|
|
522
|
+
console.log(' reset - Reset all settings to defaults');
|
|
523
|
+
console.log(' validate - Validate current settings');
|
|
524
|
+
console.log('\nExample setting paths:');
|
|
525
|
+
console.log(' branchManagement.enableDualMerge');
|
|
526
|
+
console.log(' branchManagement.defaultMergeTarget');
|
|
527
|
+
console.log(' cleanup.retainWeeklyBranches');
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
console.error(`${CONFIG.colors.red}❌ Operation failed: ${error.message}${CONFIG.colors.reset}`);
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// CLI execution
|
|
537
|
+
if (require.main === module) {
|
|
538
|
+
const command = process.argv[2] || 'show';
|
|
539
|
+
const args = process.argv.slice(3);
|
|
540
|
+
const manager = new BranchConfigManager();
|
|
541
|
+
manager.run(command, ...args);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
module.exports = BranchConfigManager;
|