docker-pilot
Version:
A powerful, scalable Docker CLI library for managing containerized applications of any size
404 lines (400 loc) ⢠17 kB
JavaScript
;
/**
* Compose Command - Enhanced Docker Compose file management
* Provides commands for discovering, analyzing, and managing docker-compose files
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ComposeCommand = void 0;
const BaseCommand_1 = require("./BaseCommand");
const FileUtils_1 = require("../utils/FileUtils");
const path = __importStar(require("path"));
const fs = __importStar(require("fs"));
class ComposeCommand extends BaseCommand_1.BaseCommand {
constructor(context) {
super('compose', 'Enhanced Docker Compose file management', 'Provides commands for discovering, analyzing, and managing docker-compose files', context);
this.fileUtils = new FileUtils_1.FileUtils();
this.initializeI18n();
}
initializeI18n() {
const config = this.context.config;
if (config?.language) {
this.i18n.setLanguage(config.language);
}
}
async execute(args) {
const subcommand = args[0] || 'list';
try {
switch (subcommand) {
case 'list':
case 'ls':
return await this.listComposeFiles(args.slice(1));
case 'find':
case 'search':
return await this.findComposeFiles(args.slice(1));
case 'analyze':
case 'info':
return await this.analyzeComposeFile(args.slice(1));
case 'validate':
return await this.validateComposeFile(args.slice(1));
case 'services':
return await this.listServices(args.slice(1));
case 'help':
this.showHelp();
return {
success: true,
output: this.i18n.t('compose.help.displayed'),
error: '',
executionTime: 0
};
default:
return {
success: false,
output: '',
error: this.i18n.t('error.unknown_subcommand', { command: subcommand }),
executionTime: 0
};
}
}
catch (error) {
return {
success: false,
output: '',
error: error instanceof Error ? error.message : String(error),
executionTime: 0
};
}
}
async listComposeFiles(args) {
const startTime = Date.now();
const searchDir = args[0] || process.cwd();
const includeVariants = args.includes('--variants') || args.includes('-v');
const maxDepth = this.extractDepthOption(args) || 6;
try {
const foundFiles = await this.fileUtils.findDockerComposeFilesWithInfo(searchDir, {
maxDepth,
includeVariants,
includeEmptyFiles: false
});
if (foundFiles.length === 0) {
return {
success: true,
output: this.i18n.t('compose.no_files_found'),
error: '',
executionTime: Date.now() - startTime
};
}
let output = this.i18n.t('compose.found_files_summary', { count: foundFiles.length }) + '\n\n';
foundFiles.forEach((file, index) => {
const envText = file.environment ? ` (${file.environment})` : '';
const mainFileIndicator = file.isMainFile ? ' šÆ' : '';
const depthIndicator = file.depth === 0 ? ' š' : ` š(${file.depth})`;
output += `${index + 1}. ${file.relativePath}${envText}${mainFileIndicator}${depthIndicator}\n`;
output += ` š ${this.fileUtils.formatFileSize(file.size)} | š
${file.modified.toLocaleDateString()}\n`;
output += ` š ļø ${file.serviceCount} ${this.i18n.t('compose.services')}: ${file.services.join(', ') || this.i18n.t('menu.no_services')}\n\n`;
});
return {
success: true,
output: output.trim(),
error: '',
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
output: '',
error: this.i18n.t('error.generic', { message: error instanceof Error ? error.message : String(error) }),
executionTime: Date.now() - startTime
};
}
}
async findComposeFiles(args) {
const startTime = Date.now();
const searchDir = args[0] || process.cwd();
try {
const composeFiles = await this.fileUtils.findDockerComposeFiles(searchDir, {
maxDepth: 8,
includeVariants: true
});
if (composeFiles.length === 0) {
return {
success: true,
output: this.i18n.t('compose.no_files_found'),
error: '',
executionTime: Date.now() - startTime
};
}
const output = composeFiles.map((file, index) => `${index + 1}. ${path.relative(searchDir, file)}`).join('\n');
return {
success: true,
output: `${this.i18n.t('compose.found_files_summary', { count: composeFiles.length })}\n\n${output}`,
error: '',
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
output: '',
error: this.i18n.t('error.generic', { message: error instanceof Error ? error.message : String(error) }),
executionTime: Date.now() - startTime
};
}
}
async analyzeComposeFile(args) {
const startTime = Date.now();
const filePath = args[0];
if (!filePath) {
return {
success: false,
output: '',
error: this.i18n.t('error.missing_argument', { argument: 'file_path' }),
executionTime: Date.now() - startTime
};
}
try {
if (!(await this.fileUtils.exists(filePath))) {
return {
success: false,
output: '',
error: this.i18n.t('compose.file_not_found', { file: filePath }),
executionTime: Date.now() - startTime
};
}
const composeData = await this.fileUtils.readYaml(filePath);
const stats = fs.statSync(filePath);
let output = `š ${this.i18n.t('compose.file_analysis')}: ${path.basename(filePath)}\n\n`;
output += `š ${this.i18n.t('compose.file_size')}: ${this.fileUtils.formatFileSize(stats.size)}\n`;
output += `š
${this.i18n.t('compose.file_modified')}: ${stats.mtime.toLocaleString()}\n\n`;
if (composeData.version) {
output += `š§ Version: ${composeData.version}\n`;
}
if (composeData.services) {
const services = Object.keys(composeData.services);
output += `š ļø ${this.i18n.t('compose.services')} (${services.length}): ${services.join(', ')}\n`;
// Analyze each service
for (const [serviceName, serviceConfig] of Object.entries(composeData.services)) {
output += `\nš¦ ${serviceName}:\n`;
const service = serviceConfig;
if (service.image) {
output += ` š¼ļø Image: ${service.image}\n`;
}
if (service.build) {
output += ` šØ Build: ${typeof service.build === 'string' ? service.build : service.build.context || '.'}\n`;
}
if (service.ports) {
output += ` š Ports: ${service.ports.join(', ')}\n`;
}
if (service.volumes) {
output += ` š¾ Volumes: ${service.volumes.length} volume(s)\n`;
}
if (service.environment) {
const envCount = Array.isArray(service.environment) ? service.environment.length : Object.keys(service.environment).length;
output += ` š Environment: ${envCount} variable(s)\n`;
}
if (service.depends_on) {
const dependencies = Array.isArray(service.depends_on) ? service.depends_on : Object.keys(service.depends_on);
output += ` š Dependencies: ${dependencies.join(', ')}\n`;
}
}
}
if (composeData.networks) {
const networks = Object.keys(composeData.networks);
output += `\nš Networks (${networks.length}): ${networks.join(', ')}\n`;
}
if (composeData.volumes) {
const volumes = Object.keys(composeData.volumes);
output += `š¾ Volumes (${volumes.length}): ${volumes.join(', ')}\n`;
}
return {
success: true,
output: output.trim(),
error: '',
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
output: '',
error: this.i18n.t('error.failed_to_analyze', { file: filePath, error: error instanceof Error ? error.message : String(error) }),
executionTime: Date.now() - startTime
};
}
}
async validateComposeFile(args) {
const startTime = Date.now();
const filePath = args[0];
if (!filePath) {
return {
success: false,
output: '',
error: this.i18n.t('error.missing_argument', { argument: 'file_path' }),
executionTime: Date.now() - startTime
};
}
try {
// Basic file existence check
if (!(await this.fileUtils.exists(filePath))) {
return {
success: false,
output: '',
error: this.i18n.t('compose.file_not_found', { file: filePath }),
executionTime: Date.now() - startTime
};
}
// Try to parse YAML
const composeData = await this.fileUtils.readYaml(filePath);
let output = `ā
${this.i18n.t('compose.file_valid')}: ${path.basename(filePath)}\n\n`;
// Basic structure validation
if (!composeData.services) {
output += `ā ļø ${this.i18n.t('compose.no_services_warning')}\n`;
}
else {
const serviceCount = Object.keys(composeData.services).length;
output += `š ļø ${serviceCount} ${this.i18n.t('compose.services')} ${this.i18n.t('compose.found')}\n`;
}
if (composeData.version) {
output += `š§ Version: ${composeData.version}\n`;
}
else {
output += `ā ļø ${this.i18n.t('compose.no_version_warning')}\n`;
}
return {
success: true,
output: output.trim(),
error: '',
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
output: '',
error: this.i18n.t('compose.file_invalid', { file: filePath, error: error instanceof Error ? error.message : String(error) }),
executionTime: Date.now() - startTime
};
}
}
async listServices(args) {
const startTime = Date.now();
const filePath = args[0] || await this.findDefaultComposeFile();
try {
if (!(await this.fileUtils.exists(filePath))) {
return {
success: false,
output: '',
error: this.i18n.t('compose.file_not_found', { file: filePath }),
executionTime: Date.now() - startTime
};
}
const composeData = await this.fileUtils.readYaml(filePath);
if (!composeData.services) {
return {
success: true,
output: this.i18n.t('compose.no_services_in_file'),
error: '',
executionTime: Date.now() - startTime
};
}
const services = Object.keys(composeData.services);
const output = `š ļø ${this.i18n.t('compose.services')} in ${path.basename(filePath)} (${services.length}):\n\n${services.map((service, index) => `${index + 1}. ${service}`).join('\n')}`;
return {
success: true,
output,
error: '',
executionTime: Date.now() - startTime
};
}
catch (error) {
return {
success: false,
output: '',
error: this.i18n.t('error.generic', { message: error instanceof Error ? error.message : String(error) }),
executionTime: Date.now() - startTime
};
}
}
showHelp() {
const output = `
${this.i18n.t('cmd.compose.title')}
${this.i18n.t('cmd.compose.usage')}:
docker-pilot compose <subcommand> [options]
${this.i18n.t('cmd.compose.subcommands')}:
list, ls ${this.i18n.t('cmd.compose.list_desc')}
find, search ${this.i18n.t('cmd.compose.find_desc')}
analyze, info ${this.i18n.t('cmd.compose.analyze_desc')}
validate ${this.i18n.t('cmd.compose.validate_desc')}
services ${this.i18n.t('cmd.compose.services_desc')}
help ${this.i18n.t('cmd.compose.help_desc')}
${this.i18n.t('cmd.compose.options')}:
--variants, -v ${this.i18n.t('cmd.compose.variants_desc')}
--depth <n> ${this.i18n.t('cmd.compose.depth_desc')}
${this.i18n.t('cmd.compose.examples')}: docker-pilot compose list
docker-pilot compose find /path/to/project
docker-pilot compose analyze docker-compose.yml
docker-pilot compose list --variants --depth 4
`.trim();
console.log(output);
}
extractDepthOption(args) {
const depthIndex = args.findIndex(arg => arg === '--depth');
if (depthIndex !== -1 && depthIndex + 1 < args.length) {
const depthValue = args[depthIndex + 1];
if (depthValue) {
const depth = parseInt(depthValue);
return isNaN(depth) ? null : depth;
}
}
return null;
}
async findDefaultComposeFile() {
const possibleFiles = [
'docker-compose.yml',
'docker-compose.yaml',
'compose.yml',
'compose.yaml'
];
for (const file of possibleFiles) {
if (await this.fileUtils.exists(file)) {
return file;
}
}
return 'docker-compose.yml'; // Default fallback
}
}
exports.ComposeCommand = ComposeCommand;
//# sourceMappingURL=ComposeCommand.js.map