@jin7942/ray
Version:
Lightweight CI/CD deployment tool powered by Docker and Git
99 lines (98 loc) • 4.09 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dockerDeployContainer = dockerDeployContainer;
const child_process_1 = require("child_process");
const util_1 = require("util");
const logger_1 = require("../utils/logger");
const path_1 = __importDefault(require("path"));
const execAsync = (0, util_1.promisify)(child_process_1.exec);
/**
* Deploys a Docker container (zero-downtime) or uses docker-compose.
*
* - If docker.type === 'docker':
* 1. Starts new container with temporary name
* 2. Connects to networks if needed
* 3. Stops and removes old container if exists
* 4. Renames new container to original name
*
* - If docker.type === 'compose':
* 1. Uses docker-compose up -d to recreate services
*
* @param ctx - Pipeline execution context
*/
async function dockerDeployContainer(ctx) {
const { docker, envFilePath, logDir, workspace } = ctx;
if (docker?.type === 'compose') {
// Compose 배포 처리
const composeFile = docker.path.compose;
if (!composeFile) {
throw new Error('docker compose.yml path is required for deploy type "compose".');
}
const composePath = path_1.default.resolve(workspace, composeFile);
logger_1.logger.info(`Deploying with docker-compose: ${composePath}`);
try {
logger_1.logger.info(`Executing: docker-compose -f ${composePath} up -d`);
await execAsync(`docker compose -f ${composePath} up -d`);
logger_1.logger.info('docker compose deployed successfully.');
}
catch (e) {
throw new Error(`docker-compose deploy failed: ${e instanceof Error ? e.message : String(e)}`);
}
return;
}
const original = docker.containername;
const temp = `${original}-temp`;
const image = docker.image;
const envFileOption = envFilePath ? `--env-file ${envFilePath}` : '';
const logDirOption = path_1.default.resolve(logDir || './logs');
const networkOption = () => {
return Array.isArray(docker.network)
? docker.network
: docker.network
? [docker.network]
: [];
};
const volumesOption = (() => {
if (docker.volumes !== undefined && docker.volumes.length > 0) {
return docker.volumes.map((volume) => `-v ${volume}`).join(' ');
}
else
return '';
})();
// Run new container
logger_1.logger.info(`Starting temporary container: ${temp}`);
try {
await execAsync(`docker run -d --name ${temp} -v ${logDirOption}:/app/logs ${envFileOption} ${volumesOption} ${image}`);
for (const net of networkOption()) {
const checkCmd = `docker network inspect ${net}`;
try {
await execAsync(checkCmd);
}
catch {
await execAsync(`docker network create ${net}`);
logger_1.logger.info(`Created docker network: ${net}`);
}
await execAsync(`docker network connect ${net} ${temp}`);
logger_1.logger.info(`Connected docker network: ${net} ${temp}`);
}
}
catch (e) {
throw new Error(`Failed to start new container: ${e instanceof Error ? e.message : String(e)}`);
}
// Stop & remove old container
logger_1.logger.info(`Stopping and removing previous container: ${original}`);
await execAsync(`docker stop ${original}`).catch(() => { }); // ignore if not exists
await execAsync(`docker rm ${original}`).catch(() => { });
// Rename temp to original
logger_1.logger.info(`Renaming ${temp} → ${original}`);
try {
await execAsync(`docker rename ${temp} ${original}`);
logger_1.logger.info('Container deployed successfully.');
}
catch (e) {
throw new Error(`Failed to rename container: ${e instanceof Error ? e.message : String(e)}`);
}
}