@deploystack/docker-to-iac
Version:
Translate docker run and docker compose file to Infrastructure as Code
188 lines (187 loc) • 7.89 kB
JavaScript
;
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;