UNPKG

@deploystack/docker-to-iac

Version:

Translate docker run and docker compose file to Infrastructure as Code

188 lines (187 loc) 7.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RunCommandParser = void 0; const base_1 = require("../base"); const parseDockerImage_1 = require("../../utils/parseDockerImage"); const base_parser_1 = require("../../parsers/base-parser"); const normalizePort_1 = require("../../utils/normalizePort"); const normalizeVolume_1 = require("../../utils/normalizeVolume"); const normalizeEnvironment_1 = require("../../utils/normalizeEnvironment"); const processEnvironmentVariablesGeneration_1 = require("../../utils/processEnvironmentVariablesGeneration"); class RunCommandParser { parse(content, environmentOptions) { this.validate(content); const parts = this.splitCommand(content.trim()); if (parts[0] !== 'docker' || parts[1] !== 'run') { throw new base_1.SourceValidationError('Command must start with "docker run"'); } const config = { image: { registry_type: base_parser_1.RegistryType.DOCKER_HUB, repository: '' }, ports: [], volumes: [], environment: {}, command: '', restart: '' }; // Parse the arguments let i = 2; // Skip 'docker' and 'run' let imageFound = false; const commandParts = []; while (i < parts.length) { const arg = parts[i]; // If we haven't found the image yet and the argument starts with a dash, // it's an option if (!imageFound && arg.startsWith('-')) { switch (arg.split('=')[0]) { case '-p': case '--publish': if (i + 1 < parts.length) { const portMapping = this.parsePortMapping(parts[++i]); if (portMapping) { config.ports.push(portMapping); } } break; case '-e': case '--env': if (i + 1 < parts.length) { const envVar = this.parseEnvironmentVariable(parts[++i]); config.environment = { ...config.environment, ...envVar }; } break; case '-v': case '--volume': if (i + 1 < parts.length) { const volume = this.parseVolumeMapping(parts[++i]); config.volumes.push(volume); } break; case '--restart': if (arg.includes('=')) { // Handle --restart=value format config.restart = arg.split('=')[1]; } else if (i + 1 < parts.length) { // Handle --restart value format config.restart = parts[++i]; } break; default: // Some flags take arguments, some don't if (arg === '--rm' || arg === '-d' || arg === '--detach') { // These are standalone flags - don't skip anything } else if (arg.includes('=')) { // Handle flag=value format - don't skip anything } else if (i + 1 < parts.length && !parts[i + 1].startsWith('-')) { // This option likely has a value - skip it i++; } } } else if (!imageFound) { // First non-option argument is the image config.image = (0, parseDockerImage_1.parseDockerImage)(arg); imageFound = true; } else { // Any arguments after the image are part of the command commandParts.push(arg); } i++; } // Join the command parts if any were found if (commandParts.length > 0) { config.command = commandParts.join(' '); } if (environmentOptions) { const serviceName = 'default'; // Docker run always uses 'default' as service name // Get persisted environment variables if available const persistedEnv = environmentOptions.getPersistedEnvVars?.(serviceName, config.image) || {}; // Start with original environment variables let processedEnv = { ...config.environment, ...persistedEnv }; // If we have auto-generation config, use it if (environmentOptions.environmentGeneration) { processedEnv = (0, processEnvironmentVariablesGeneration_1.processEnvironmentVariablesGeneration)(processedEnv, config.image, environmentOptions.environmentGeneration); } // Then apply any .env file substitutions if they exist if (environmentOptions.environmentVariables) { for (const [key, value] of Object.entries(processedEnv)) { if (typeof value === 'string' && value.startsWith('${') && value.endsWith('}')) { const envVarName = value.slice(2, -1); if (environmentOptions.environmentVariables[envVarName]) { processedEnv[key] = environmentOptions.environmentVariables[envVarName]; } } } } // Store the processed environment variables if persistence is enabled if (environmentOptions.setPersistedEnvVars) { environmentOptions.setPersistedEnvVars(serviceName, processedEnv); } config.environment = processedEnv; } return { services: { 'default': config } }; } validate(content) { if (!content.trim().startsWith('docker run')) { throw new base_1.SourceValidationError('Command must start with "docker run"'); } // Basic validation - we'll improve this if (!content.includes(' ')) { throw new base_1.SourceValidationError('Invalid docker run command format'); } return true; } splitCommand(command) { const parts = []; let current = ''; let inQuotes = false; let quoteChar = ''; for (let i = 0; i < command.length; i++) { const char = command[i]; if ((char === '\'' || char === '"') && (inQuotes === false || quoteChar === char)) { inQuotes = !inQuotes; if (inQuotes) quoteChar = char; else quoteChar = ''; continue; } if (char === ' ' && !inQuotes) { if (current) { parts.push(current); current = ''; } } else { current += char; } } if (current) { parts.push(current); } return parts; } parsePortMapping(portString) { return (0, normalizePort_1.normalizePort)(portString); } parseEnvironmentVariable(envString) { return (0, normalizeEnvironment_1.normalizeEnvironment)(envString); } parseVolumeMapping(volumeString) { return (0, normalizeVolume_1.normalizeVolume)(volumeString); } } exports.RunCommandParser = RunCommandParser;