scripts-orchestrator
Version:
A powerful script orchestrator for running parallel commands with dependency management, background processes, and health checks
123 lines (104 loc) • 3.27 kB
JavaScript
import chalk from 'chalk';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import fs from 'fs';
import path from 'path';
const argv = yargs(hideBin(process.argv))
.option('verbose', {
alias: 'v',
type: 'boolean',
description: 'Run with verbose logging',
})
.option('phase', {
type: 'string',
description: 'Start execution from a specific phase',
})
.option('phases', {
type: 'string',
description: 'Comma-separated list of phases to run (for optional phases)',
})
.option('logFolder', {
type: 'string',
description: 'Specify the directory for log files',
})
.parse();
class Logger {
constructor() {
this.isVerbose = argv.verbose;
this.logFolder = argv.logFolder || 'scripts-orchestrator-logs';
this.logFile = null;
this.logStream = null;
this.initializeLogFile();
}
initializeLogFile() {
try {
// Create log directory if it doesn't exist
if (!fs.existsSync(this.logFolder)) {
fs.mkdirSync(this.logFolder, { recursive: true });
}
// Create main log file with timestamp
const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\..+/, '');
this.logFile = path.join(this.logFolder, `orchestrator-main-${timestamp}.log`);
// Create write stream
this.logStream = fs.createWriteStream(this.logFile, { flags: 'a' });
// Handle stream errors
this.logStream.on('error', (err) => {
console.error(`Error writing to log file: ${err.message}`);
});
// Ensure the stream is closed on process exit
process.on('exit', () => {
if (this.logStream) {
this.logStream.end();
}
});
// Write initial log entry
this.writeToFile(`[START] Orchestrator started at ${new Date().toISOString()}\n`);
} catch (error) {
console.error(`Failed to initialize log file: ${error.message}`);
}
}
setLogFolder(newLogFolder) {
// Close existing stream if it exists
if (this.logStream) {
this.logStream.end();
}
// Update log folder
this.logFolder = newLogFolder;
// Reinitialize with new folder
this.initializeLogFile();
}
writeToFile(message) {
if (this.logStream) {
// Strip ANSI color codes for file output
// eslint-disable-next-line no-control-regex
const cleanMessage = message.replace(/\x1b\[[0-9;]*m/g, '');
this.logStream.write(`${cleanMessage}\n`);
}
}
info(message) {
console.log(chalk.blue(`[INFO] ${message}`));
this.writeToFile(`[INFO] ${message}`);
}
success(message) {
console.log(chalk.green(`[SUCCESS] ${message}`));
this.writeToFile(`[SUCCESS] ${message}`);
}
error(message) {
console.error(chalk.red(`[ERROR] ${message}`));
this.writeToFile(`[ERROR] ${message}`);
}
warn(message) {
console.warn(chalk.yellow(`[WARN] ${message}`));
this.writeToFile(`[WARN] ${message}`);
}
verbose(message) {
if (this.isVerbose) {
console.log(chalk.gray(`[VERBOSE] ${message}`));
this.writeToFile(`[VERBOSE] ${message}`);
}
}
}
// Create a single instance
const logger = new Logger();
// Export both the class and the instance
export { Logger, logger as log };