perf: share Turbo cache across release worktrees

This commit is contained in:
2026-08-11 11:59:18 +00:00
parent c737ee2cd1
commit 3e938e17d6
10 changed files with 159 additions and 59 deletions
+4 -7
View File
@@ -29,6 +29,9 @@ Gateway process 환경에 전달하지 않습니다. 이 값이 frontend 정의
frontend build 계약입니다.
- `RELEASE_CONTROLLER_POLL_MS`, `RELEASE_CONTROLLER_READINESS_TIMEOUT_MS`: queue
poll과 준비 제한 시간입니다.
- `TURBO_CACHE_DIR`: 선택 사항인 공유 local cache 경로입니다. 없으면 원래
`RELEASE_CONTROLLER_WORKSPACE_ROOT/.turbo/release-cache`를 사용합니다. 상대 경로는
원래 workspace 기준으로 해석합니다.
비밀값은 Git에서 제외된 환경 파일 또는 process 환경으로 전달해 주세요.
`VITE_*`에는 공개 URL만 넣어 주세요.
@@ -46,13 +49,7 @@ DEPLOY의 rollback이 frontend build가 없는 controller worktree를 이전 Gat
```sh
pnpm install --frozen-lockfile
pnpm --filter @sammo-ts/infra prisma:generate
pnpm --filter @sammo-ts/common build
pnpm --filter @sammo-ts/infra build
pnpm --filter @sammo-ts/logic build
pnpm --filter @sammo-ts/game-engine build
pnpm --filter @sammo-ts/gateway-api build
pnpm --filter @sammo-ts/release-controller build
pnpm exec turbo run build --filter=@sammo-ts/release-controller --concurrency=2 --ui=stream
pnpm --filter @sammo-ts/infra prisma:migrate:deploy:gateway
pnpm --filter @sammo-ts/release-controller start
```
@@ -4,6 +4,7 @@ import { stripVTControlCharacters } from 'node:util';
import {
assertReleaseComponents,
buildTurboReleaseCommand,
type BuildCommand,
type BuildProgressEvent,
type BuildRunner,
@@ -38,13 +39,12 @@ export const buildGatewayReleaseCommands = (
};
return [
...(needsInstall ? [{ command: 'pnpm', args: ['install', '--frozen-lockfile'], cwd: workspaceRoot, env }] : []),
{ command: 'pnpm', args: ['--filter', '@sammo-ts/common', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/infra', 'prisma:generate'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/infra', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/logic', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/game-engine', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/gateway-api', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/gateway-frontend', 'build'], cwd: workspaceRoot, env },
buildTurboReleaseCommand(
workspaceRoot,
config.workspaceRoot,
['@sammo-ts/gateway-api', '@sammo-ts/gateway-frontend'],
env
),
];
};
@@ -244,7 +244,12 @@ export class GatewayReleaseController {
await this.startDefinitions(buildGatewayProcessDefinitions(workspace.root, this.config), operation.id);
await this.waitForReadiness(operation.id);
} catch (error) {
await this.appendLog(operation.id, 'rollback', '새 Gateway 시작에 실패하여 이전 process를 복구합니다.', 'ERROR');
await this.appendLog(
operation.id,
'rollback',
'새 Gateway 시작에 실패하여 이전 process를 복구합니다.',
'ERROR'
);
await this.stopManagedProcesses(operation.id);
if (previousDefinitions.length) {
await this.startDefinitions(previousDefinitions, operation.id);
+2 -7
View File
@@ -2,6 +2,7 @@ import path from 'node:path';
import {
assertReleaseComponents,
buildTurboReleaseCommand,
type BuildCommand,
type BuildRunner,
type GitWorkspaceManager,
@@ -24,13 +25,7 @@ export const buildReleaseControllerCommands = (
const env = sanitizeManagedProcessEnv(config.baseEnv);
return [
...(needsInstall ? [{ command: 'pnpm', args: ['install', '--frozen-lockfile'], cwd: workspaceRoot, env }] : []),
{ command: 'pnpm', args: ['--filter', '@sammo-ts/common', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/infra', 'prisma:generate'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/infra', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/logic', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/game-engine', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/gateway-api', 'build'], cwd: workspaceRoot, env },
{ command: 'pnpm', args: ['--filter', '@sammo-ts/release-controller', 'build'], cwd: workspaceRoot, env },
buildTurboReleaseCommand(workspaceRoot, config.workspaceRoot, ['@sammo-ts/release-controller'], env),
];
};
@@ -209,6 +209,8 @@ describe('GatewayReleaseController', () => {
expect(commandGroups).toHaveLength(2);
expect(commandGroups[0]?.[0]).toBe('install --frozen-lockfile');
expect(commandGroups[0]?.[1]).toContain('turbo run build');
expect(commandGroups[0]?.[1]).toContain('--cache-dir=/srv/sammo/controller/.turbo/release-cache');
expect(commandGroups[1]).toEqual(['--filter @sammo-ts/infra prisma:migrate:deploy:gateway']);
expect([...running.keys()].sort()).toEqual([...gatewayNames].sort());
expect(harness.published).toEqual([
@@ -216,7 +218,16 @@ describe('GatewayReleaseController', () => {
]);
expect(harness.completions).toEqual(['SUCCEEDED']);
expect(harness.logs.map((entry) => entry.phase)).toEqual(
expect.arrayContaining(['claim', 'resolve', 'workspace', 'build', 'migration', 'switch', 'readiness', 'publish'])
expect.arrayContaining([
'claim',
'resolve',
'workspace',
'build',
'migration',
'switch',
'readiness',
'publish',
])
);
});
@@ -275,8 +286,7 @@ describe('GatewayReleaseController', () => {
await onProgress?.({
type: 'OUTPUT',
stream: 'stdout',
message:
'bootstrap-secret-value postgresql://operator:visible-password@db.invalid/sammo',
message: 'bootstrap-secret-value postgresql://operator:visible-password@db.invalid/sammo',
});
return { ok: true, exitCode: 0, output: '' };
},
@@ -405,7 +415,7 @@ describe('upgradeReleaseController', () => {
).resolves.toEqual({ commitSha: SHA, workspace });
expect(commandGroups).toHaveLength(2);
expect(commandGroups[0]?.at(-1)).toBe('--filter @sammo-ts/release-controller build');
expect(commandGroups[0]?.at(-1)).toContain('turbo run build --filter=@sammo-ts/release-controller');
expect(starts.at(-1)).toMatchObject({
name: 'sammo:release-controller',
cwd: path.join(workspace, 'app', 'release-controller'),