fix: isolate child PM2 arguments

This commit is contained in:
2026-08-08 15:05:55 +00:00
parent 19d76d23de
commit 5e82fb52c6
6 changed files with 43 additions and 3 deletions
@@ -24,6 +24,7 @@ export interface ProcessManager {
const PM2_INTERNAL_ENV_KEYS = new Set([
'NODE_APP_INSTANCE',
'args',
'autorestart',
'autostart',
'created_at',
@@ -179,6 +179,7 @@ describe('buildProcessDefinitions', () => {
DATABASE_URL: 'postgresql://integration.invalid/sammo',
VITE_APP_BASE_PATH: '/gateway',
GATEWAY_ROLE: 'orchestrator',
args: 'daemon',
NODE_APP_INSTANCE: '2',
name: 'sammo:gateway-orchestrator',
pm_id: '2',
@@ -189,6 +190,7 @@ describe('buildProcessDefinitions', () => {
for (const definition of Object.values(definitions)) {
expect(definition.env).toMatchObject({ DATABASE_URL: 'postgresql://integration.invalid/sammo' });
expect(definition.env).not.toHaveProperty('pm_id');
expect(definition.env).not.toHaveProperty('args');
expect(definition.env).not.toHaveProperty('pm_exec_path');
expect(definition.env).not.toHaveProperty('name');
expect(definition.env).not.toHaveProperty('NODE_APP_INSTANCE');
@@ -206,6 +208,7 @@ describe('sanitizeManagedProcessEnv', () => {
PATH: '/usr/local/bin:/usr/bin',
GATEWAY_ROLE: 'orchestrator',
GAME_API_ROLE: 'server',
args: 'daemon',
NODE_APP_INSTANCE: '2',
name: 'sammo:gateway-orchestrator',
pm_id: '2',
@@ -11,6 +11,7 @@ describe('buildPm2StartOptions', () => {
env: {
DATABASE_URL: 'postgresql://integration.invalid/sammo',
GAME_API_ROLE: 'server',
args: 'daemon',
pm_id: '2',
pm_exec_path: '/srv/sammo/app/gateway-api/dist/index.js',
name: 'sammo:gateway-orchestrator',
@@ -31,9 +32,27 @@ describe('buildPm2StartOptions', () => {
},
});
expect(options.env).not.toHaveProperty('pm_id');
expect(options.env).not.toHaveProperty('args');
expect(options.env).not.toHaveProperty('pm_exec_path');
expect(options.env).not.toHaveProperty('name');
expect(options.env).not.toHaveProperty('NODE_APP_INSTANCE');
expect(options.env).toHaveProperty('GAME_API_ROLE', 'server');
});
it('keeps explicit child arguments when a PM2 parent exposes its own args in the environment', () => {
const options = buildPm2StartOptions({
name: 'sammo:gateway-frontend',
script: '/srv/sammo/app/gateway-frontend/node_modules/vite/bin/vite.js',
cwd: '/srv/sammo/app/gateway-frontend',
args: ['preview', '--host', '0.0.0.0', '--port', '15000'],
env: {
args: 'daemon',
name: 'sammo:release-controller',
pm_id: '3',
},
});
expect(options.args).toEqual(['preview', '--host', '0.0.0.0', '--port', '15000']);
expect(options.env).not.toHaveProperty('args');
});
});
+5
View File
@@ -13,6 +13,11 @@
5. 두 HTTP endpoint와 세 PM2 process가 모두 준비된 경우에만 현재·이전
릴리스 상태를 게시합니다. 실패하면 이전 세 프로세스를 복구합니다.
Controller는 PM2 자식으로 실행되지만 자신의 `args=daemon`과 PM2 identity를
Gateway process 환경에 전달하지 않습니다. 이 값이 frontend 정의를 덮으면 Vite가
의도한 preview port 대신 기본 개발 port로 실행될 수 있으므로, process `online`
여부뿐 아니라 Gateway API와 frontend HTTP readiness를 모두 확인합니다.
## 환경 변수
- `GATEWAY_DATABASE_URL`: Gateway PostgreSQL URL입니다. 필수입니다.
@@ -119,9 +119,7 @@ const gatewayNames = ['sammo:gateway-api', 'sammo:gateway-frontend', 'sammo:gate
it('runs Gateway preview from the frontend workspace dependency', () => {
const definitions = buildGatewayProcessDefinitions('/srv/sammo/release', config);
const frontend = definitions.find((definition) => definition.name === 'sammo:gateway-frontend');
expect(frontend?.script).toBe(
'/srv/sammo/release/app/gateway-frontend/node_modules/vite/bin/vite.js'
);
expect(frontend?.script).toBe('/srv/sammo/release/app/gateway-frontend/node_modules/vite/bin/vite.js');
});
it('does not forward release-controller PM2 identity to Gateway processes', () => {
@@ -130,6 +128,7 @@ it('does not forward release-controller PM2 identity to Gateway processes', () =
baseEnv: {
DATABASE_URL: 'postgresql://integration.invalid/sammo',
GATEWAY_ROLE: 'orchestrator',
args: 'daemon',
name: 'sammo:release-controller',
pm_id: '3',
pm_exec_path: '/srv/release-controller.js',
@@ -139,6 +138,7 @@ it('does not forward release-controller PM2 identity to Gateway processes', () =
for (const definition of definitions) {
expect(definition.env).toMatchObject({ DATABASE_URL: 'postgresql://integration.invalid/sammo' });
expect(definition.env).not.toHaveProperty('pm_id');
expect(definition.env).not.toHaveProperty('args');
expect(definition.env).not.toHaveProperty('pm_exec_path');
expect(definition.env).not.toHaveProperty('name');
}
@@ -250,6 +250,7 @@ describe('resolveReleaseControllerConfig', () => {
const resolved = resolveReleaseControllerConfig({
GATEWAY_DATABASE_URL: 'postgresql://user:pass@127.0.0.1:5432/sammo',
RELEASE_CONTROLLER_WORKSPACE_ROOT: '/srv/sammo/controller',
args: 'daemon',
pm_id: '3',
name: 'sammo:release-controller',
axm_monitor: '{}',
@@ -260,6 +261,7 @@ describe('resolveReleaseControllerConfig', () => {
RELEASE_CONTROLLER_WORKSPACE_ROOT: '/srv/sammo/controller',
});
expect(resolved.baseEnv).not.toHaveProperty('pm_id');
expect(resolved.baseEnv).not.toHaveProperty('args');
expect(resolved.baseEnv).not.toHaveProperty('name');
expect(resolved.baseEnv).not.toHaveProperty('axm_monitor');
});