cfn-forge
Version:
CloudFormation deployment automation tool with git-based workflows
190 lines (161 loc) โข 5.88 kB
JavaScript
/**
* Global Error Handler for CFN-Forge CLI
* Catches unhandled errors and provides helpful guidance
*/
const { logger } = require('./logger');
const { handleAWSError } = require('./aws-error-parser');
/**
* Setup global error handlers
*/
function setupGlobalErrorHandlers() {
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
logger.section('Uncaught Exception');
logger.errorWithContext(error, 'Uncaught Exception');
logger.info('\n๐ฅ CFN-Forge encountered an unexpected error');
logger.info('๐ Please report this issue at: https://github.com/dmleblanc/cfn-forge/issues');
if (error.code && error.code.includes('AWS')) {
handleAWSError(error, 'Global Handler');
}
logger.reportErrorSummary();
// Give time for logs to flush
setTimeout(() => {
process.exit(1);
}, 100);
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
logger.section('Unhandled Promise Rejection');
const error = reason instanceof Error ? reason : new Error(String(reason));
logger.errorWithContext(error, 'Unhandled Promise Rejection');
logger.info('\n๐ฅ CFN-Forge encountered an unhandled promise rejection');
logger.info('๐ This usually indicates a missing .catch() or try/catch block');
if (error.code && error.code.includes('AWS')) {
handleAWSError(error, 'Promise Rejection');
}
logger.reportErrorSummary();
// Give time for logs to flush
setTimeout(() => {
process.exit(1);
}, 100);
});
// Handle SIGINT (Ctrl+C) gracefully
process.on('SIGINT', () => {
logger.info('\n\n๐ CFN-Forge interrupted by user');
logger.info('๐ Goodbye!');
process.exit(0);
});
// Handle SIGTERM gracefully
process.on('SIGTERM', () => {
logger.info('\n\n๐ CFN-Forge terminated');
logger.info('๐งน Cleaning up...');
process.exit(0);
});
// Handle exit event
process.on('exit', (code) => {
if (code !== 0 && logger.errorHistory && logger.errorHistory.length > 0) {
// Final error summary on non-zero exit
const errorCount = logger.errorHistory.length;
console.error(`\nโ CFN-Forge exited with ${errorCount} error${errorCount > 1 ? 's' : ''}`);
if (process.env.DEBUG) {
logger.reportErrorSummary();
} else {
console.error('๐ก Run with DEBUG=1 for detailed error information');
}
}
});
}
/**
* Create error context for commands
*/
function createCommandContext(commandName, options = {}) {
return {
command: commandName,
timestamp: new Date().toISOString(),
cwd: process.cwd(),
nodeVersion: process.version,
platform: process.platform,
arch: process.arch,
env: {
DEBUG: process.env.DEBUG,
CFN_FORGE_DEBUG: process.env.CFN_FORGE_DEBUG,
AWS_REGION: process.env.AWS_REGION,
AWS_DEFAULT_REGION: process.env.AWS_DEFAULT_REGION,
AWS_PROFILE: process.env.AWS_PROFILE,
},
options,
};
}
/**
* Wrap command functions with enhanced error handling
*/
function withCommandErrorHandling(commandName, commandFn) {
return async (...args) => {
const context = createCommandContext(commandName, args[args.length - 1]);
logger.setContext('commandContext', context);
try {
const startTime = Date.now();
logger.debug(`Starting command: ${commandName}`);
const result = await commandFn(...args);
const duration = Date.now() - startTime;
logger.debug(`Command completed: ${commandName} (${logger.formatDuration(duration)})`);
return result;
} catch (error) {
// Enhanced error handling for common error types
if (error.code && error.code.includes('AWS')) {
handleAWSError(error, `${commandName} Command`);
} else if (error.code === 'ENOENT') {
logger.errorWithContext(error, `${commandName} Command - File Not Found`);
logger.info('\n๐ก Common file issues:');
logger.info(' โข Check file paths in your configuration');
logger.info(' โข Ensure you\'re in the correct directory');
logger.info(' โข Run `cfn-forge init` if this is a new project');
} else if (error.code === 'EACCES') {
logger.errorWithContext(error, `${commandName} Command - Permission Denied`);
logger.info('\n๐ก Permission issues:');
logger.info(' โข Check file/directory permissions');
logger.info(' โข Run with appropriate user privileges');
logger.info(' โข Ensure files are not locked by other processes');
} else {
logger.errorWithContext(error, `${commandName} Command`);
}
throw error;
} finally {
logger.clearContext();
}
};
}
/**
* Display system information for debugging
*/
function displaySystemInfo() {
if (!logger.debugEnabled) return;
logger.debug('System Information:');
logger.debug(` Node.js: ${process.version}`);
logger.debug(` Platform: ${process.platform} ${process.arch}`);
logger.debug(` CWD: ${process.cwd()}`);
logger.debug(` User: ${process.env.USER || process.env.USERNAME || 'unknown'}`);
logger.debug(` Shell: ${process.env.SHELL || 'unknown'}`);
// AWS Environment
const awsVars = [
'AWS_REGION', 'AWS_DEFAULT_REGION', 'AWS_PROFILE',
'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY',
];
logger.debug('AWS Environment:');
awsVars.forEach((varName) => {
const value = process.env[varName];
if (value) {
// Mask sensitive values
const maskedValue = varName.includes('KEY') || varName.includes('SECRET')
? `${value.substring(0, 4)}****`
: value;
logger.debug(` ${varName}: ${maskedValue}`);
}
});
}
module.exports = {
setupGlobalErrorHandlers,
createCommandContext,
withCommandErrorHandling,
displaySystemInfo,
};