fix(format): Improve concurrency control in cache and rollback management with mutex locking and refine formatting details

This commit is contained in:
2025-08-08 06:25:40 +00:00
parent d32d47b706
commit 859cbc733d
38 changed files with 784 additions and 726 deletions

View File

@@ -16,27 +16,29 @@ import { PrettierFormatter } from './formatters/prettier.formatter.js';
import { ReadmeFormatter } from './formatters/readme.formatter.js';
import { CopyFormatter } from './formatters/copy.formatter.js';
export let run = async (options: {
dryRun?: boolean;
yes?: boolean;
planOnly?: boolean;
savePlan?: string;
fromPlan?: string;
detailed?: boolean;
interactive?: boolean;
parallel?: boolean;
verbose?: boolean;
} = {}): Promise<any> => {
export let run = async (
options: {
dryRun?: boolean;
yes?: boolean;
planOnly?: boolean;
savePlan?: string;
fromPlan?: string;
detailed?: boolean;
interactive?: boolean;
parallel?: boolean;
verbose?: boolean;
} = {},
): Promise<any> => {
// Set verbose mode if requested
if (options.verbose) {
setVerboseMode(true);
}
const project = await Project.fromCwd();
const context = new FormatContext();
await context.initializeCache(); // Initialize the cache system
// Cache system removed - no longer needed
const planner = new FormatPlanner();
// Get configuration from npmextra
const npmextraConfig = new plugins.npmextra.Npmextra();
const formatConfig = npmextraConfig.dataFor<any>('gitzone.format', {
@@ -49,30 +51,27 @@ export let run = async (options: {
autoRollbackOnError: true,
backupRetentionDays: 7,
maxBackupSize: '100MB',
excludePatterns: ['node_modules/**', '.git/**']
excludePatterns: ['node_modules/**', '.git/**'],
},
modules: {
skip: [],
only: [],
order: []
order: [],
},
parallel: true,
cache: {
enabled: true,
clean: true // Clean invalid entries from cache
}
clean: true, // Clean invalid entries from cache
},
});
// Clean cache if configured
if (formatConfig.cache.clean) {
await context.getChangeCache().clean();
}
// Cache cleaning removed - no longer using cache system
// Override config with command options
const interactive = options.interactive ?? formatConfig.interactive;
const autoApprove = options.yes ?? formatConfig.autoApprove;
const parallel = options.parallel ?? formatConfig.parallel;
try {
// Initialize formatters
const formatters = [
@@ -87,9 +86,9 @@ export let run = async (options: {
new ReadmeFormatter(context, project),
new CopyFormatter(context, project),
];
// Filter formatters based on configuration
const activeFormatters = formatters.filter(formatter => {
const activeFormatters = formatters.filter((formatter) => {
if (formatConfig.modules.only.length > 0) {
return formatConfig.modules.only.includes(formatter.name);
}
@@ -98,33 +97,36 @@ export let run = async (options: {
}
return true;
});
// Plan phase
logger.log('info', 'Analyzing project for format operations...');
let plan = options.fromPlan
? JSON.parse(await plugins.smartfile.fs.toStringSync(options.fromPlan))
: await planner.planFormat(activeFormatters);
// Display plan
await planner.displayPlan(plan, options.detailed);
// Save plan if requested
if (options.savePlan) {
await plugins.smartfile.memory.toFs(JSON.stringify(plan, null, 2), options.savePlan);
await plugins.smartfile.memory.toFs(
JSON.stringify(plan, null, 2),
options.savePlan,
);
logger.log('info', `Plan saved to ${options.savePlan}`);
}
// Exit if plan-only mode
if (options.planOnly) {
return;
}
// Dry-run mode
if (options.dryRun) {
logger.log('info', 'Dry-run mode - no changes will be made');
return;
}
// Interactive confirmation
if (interactive && !autoApprove) {
const interactInstance = new plugins.smartinteract.SmartInteract();
@@ -132,117 +134,56 @@ export let run = async (options: {
type: 'confirm',
name: 'proceed',
message: 'Proceed with formatting?',
default: true
default: true,
});
if (!(response as any).value) {
logger.log('info', 'Format operation cancelled by user');
return;
}
}
// Execute phase
logger.log('info', `Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`);
logger.log(
'info',
`Executing format operations${parallel ? ' in parallel' : ' sequentially'}...`,
);
await planner.executePlan(plan, activeFormatters, context, parallel);
// Finish statistics tracking
context.getFormatStats().finish();
// Display statistics
const showStats = npmextraConfig.dataFor('gitzone.format.showStats', true);
if (showStats) {
context.getFormatStats().displayStats();
}
// Save stats if requested
if (options.detailed) {
const statsPath = `.nogit/format-stats-${Date.now()}.json`;
await context.getFormatStats().saveReport(statsPath);
}
logger.log('success', 'Format operations completed successfully!');
} catch (error) {
logger.log('error', `Format operation failed: ${error.message}`);
// Automatic rollback if enabled
if (formatConfig.rollback.enabled && formatConfig.rollback.autoRollbackOnError) {
logger.log('info', 'Attempting automatic rollback...');
try {
await context.rollbackOperation();
logger.log('success', 'Rollback completed successfully');
} catch (rollbackError) {
logger.log('error', `Rollback failed: ${rollbackError.message}`);
}
}
// Rollback system has been removed for stability
throw error;
}
};
// Export CLI command handlers
export const handleRollback = async (operationId?: string): Promise<void> => {
const context = new FormatContext();
const rollbackManager = context.getRollbackManager();
if (!operationId) {
// Rollback to last operation
const backups = await rollbackManager.listBackups();
const lastOperation = backups
.filter(op => op.status !== 'rolled-back')
.sort((a, b) => b.timestamp - a.timestamp)[0];
if (!lastOperation) {
logger.log('warn', 'No operations available for rollback');
return;
}
operationId = lastOperation.id;
}
try {
await rollbackManager.rollback(operationId);
logger.log('success', `Successfully rolled back operation ${operationId}`);
} catch (error) {
logger.log('error', `Rollback failed: ${error.message}`);
throw error;
}
logger.log('info', 'Rollback system has been disabled for stability');
};
export const handleListBackups = async (): Promise<void> => {
const context = new FormatContext();
const rollbackManager = context.getRollbackManager();
const backups = await rollbackManager.listBackups();
if (backups.length === 0) {
logger.log('info', 'No backup operations found');
return;
}
console.log('\nAvailable backups:');
console.log('━'.repeat(50));
for (const backup of backups) {
const date = new Date(backup.timestamp).toLocaleString();
const status = backup.status;
const filesCount = backup.files.length;
console.log(`ID: ${backup.id}`);
console.log(`Date: ${date}`);
console.log(`Status: ${status}`);
console.log(`Files: ${filesCount}`);
console.log('─'.repeat(50));
}
logger.log('info', 'Backup system has been disabled for stability');
};
export const handleCleanBackups = async (): Promise<void> => {
const context = new FormatContext();
const rollbackManager = context.getRollbackManager();
// Get retention days from config
const npmextraConfig = new plugins.npmextra.Npmextra();
const retentionDays = npmextraConfig.dataFor<any>('gitzone.format.rollback.backupRetentionDays', 7);
await rollbackManager.cleanOldBackups(retentionDays);
logger.log('success', `Cleaned backups older than ${retentionDays} days`);
};
logger.log('info', 'Backup cleaning has been disabled - backup system removed');
};