Files
core2026/app/release-controller/src/index.ts
T
Hide_D 1279a68f1c fix(gateway): 배포 worktree 자동 정리 추가
Profile과 Gateway release의 현재 실행 및 rollback 경계를 보호하면서 오래된 commit worktree를 주기적으로 정리한다.
2026-08-20 16:25:40 +00:00

100 lines
3.7 KiB
TypeScript

import { createGatewayPostgresConnector, type GatewayPrismaClient } from '@sammo-ts/infra';
import {
createGatewayReleaseRepository,
GitWorkspaceManager,
Pm2ProcessManager,
PnpmBuildRunner,
} from '@sammo-ts/gateway-api';
import { resolveReleaseControllerConfig } from './config.js';
import { GatewayReleaseController, RELEASE_WORKSPACE_CLEANUP_INTERVAL_MS } from './releaseController.js';
import { upgradeReleaseController } from './selfUpgrade.js';
export * from './config.js';
export * from './releaseController.js';
export * from './selfUpgrade.js';
const main = async (): Promise<void> => {
const config = resolveReleaseControllerConfig();
const postgres = createGatewayPostgresConnector({
url: config.gatewayDatabaseUrl,
maxConnections: config.postgresPoolMax,
});
await postgres.connect();
const repository = createGatewayReleaseRepository(postgres.prisma as GatewayPrismaClient);
const workspaceManager = new GitWorkspaceManager({
repoRoot: config.workspaceRoot,
worktreeRoot: config.worktreeRoot,
baseEnv: config.baseEnv,
});
const buildRunner = new PnpmBuildRunner();
const processManager = new Pm2ProcessManager();
const controller = new GatewayReleaseController(repository, workspaceManager, buildRunner, processManager, config);
const command = process.argv[2] ?? 'daemon';
if (command === 'status') {
console.log(
JSON.stringify(
{ state: await repository.getState(), operations: await repository.listOperations(20) },
null,
2
)
);
await postgres.disconnect();
return;
}
if (command === 'run-once') {
console.log(JSON.stringify(await controller.runOnce(), null, 2));
await postgres.disconnect();
return;
}
if (command === 'self-upgrade') {
const sourceMode = process.argv[3];
const sourceRef = process.argv[4];
if ((sourceMode !== 'BRANCH' && sourceMode !== 'COMMIT') || !sourceRef) {
throw new Error('usage: release-controller self-upgrade <BRANCH|COMMIT> <ref>');
}
const result = await upgradeReleaseController({
sourceMode,
sourceRef,
workspaceManager,
buildRunner,
processManager,
config,
});
console.log(JSON.stringify(result, null, 2));
await postgres.disconnect();
return;
}
if (command !== 'daemon') throw new Error(`Unknown release-controller command: ${command}`);
let stopping = false;
let nextWorkspaceCleanupAt = 0;
const stop = async (): Promise<void> => {
if (stopping) return;
stopping = true;
await postgres.disconnect();
};
process.once('SIGINT', () => void stop());
process.once('SIGTERM', () => void stop());
while (!stopping) {
const now = Date.now();
if (now >= nextWorkspaceCleanupAt) {
nextWorkspaceCleanupAt = now + RELEASE_WORKSPACE_CLEANUP_INTERVAL_MS;
try {
const result = await controller.cleanupStaleWorkspaces();
if (result.removed.length > 0) {
console.info(`[release-controller] removed ${result.removed.length} stale Gateway worktrees`);
}
} catch (error) {
console.error('[release-controller] workspace cleanup failed', error);
}
}
await controller.runOnce();
await new Promise<void>((resolve) => setTimeout(resolve, config.pollIntervalMs));
}
};
main().catch((error) => {
console.error('[release-controller] failed', error);
process.exitCode = 1;
});