From 43ae16303801e6e5bb25522e91d3f44e22f4a16a Mon Sep 17 00:00:00 2001 From: hided62 Date: Sat, 25 Jul 2026 14:13:24 +0000 Subject: [PATCH] Tolerate concurrent PM2 profile cleanup --- .../src/orchestrator/gatewayOrchestrator.ts | 7 ++++++- .../test/orchestratorOperations.test.ts | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts index cde47af..d3c8262 100644 --- a/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts +++ b/app/gateway-api/src/orchestrator/gatewayOrchestrator.ts @@ -273,6 +273,9 @@ const parseInstallOptions = ( const buildProcessName = (profileName: string, role: 'api' | 'daemon'): string => `sammo:${profileName}:${role === 'api' ? 'game-api' : 'turn-daemon'}`; +const isMissingProcessError = (error: unknown): boolean => + error instanceof Error && /process or namespace not found/i.test(error.message); + export const buildProcessDefinitions = ( profile: GatewayProfileRecord, config: GatewayProcessConfig @@ -980,7 +983,9 @@ export class GatewayOrchestrator implements GatewayOrchestratorHandle { try { await this.processManager.delete(name); } catch (error) { - failures.push(`${name}: ${error instanceof Error ? error.message : String(error)}`); + if (!isMissingProcessError(error)) { + failures.push(`${name}: ${error instanceof Error ? error.message : String(error)}`); + } } } if (failures.length > 0) { diff --git a/app/gateway-api/test/orchestratorOperations.test.ts b/app/gateway-api/test/orchestratorOperations.test.ts index ece1b25..48184a2 100644 --- a/app/gateway-api/test/orchestratorOperations.test.ts +++ b/app/gateway-api/test/orchestratorOperations.test.ts @@ -40,7 +40,8 @@ const createHarness = ( operation: GatewayOperationRecord, failStart = false, failStop = false, - processesPresent = true + processesPresent = true, + missingOnDelete = false ) => { let nextOperation: GatewayOperationRecord | null = operation; const statuses: string[] = []; @@ -103,6 +104,9 @@ const createHarness = ( }, delete: async (name) => { deleted.push(name); + if (missingOnDelete) { + throw new Error('process or namespace not found'); + } if (failStop) { throw new Error('pm2 delete failed'); } @@ -168,6 +172,15 @@ describe('GatewayOrchestrator first-class operations', () => { expect(harness.completions).toEqual(['SUCCEEDED']); }); + it('treats a process removed concurrently as a successful stop', async () => { + const harness = createHarness(buildOperation('STOP'), false, false, true, true); + + await harness.orchestrator.runOperationsNow(); + + expect(harness.deleted).toEqual(['sammo:che:2:game-api', 'sammo:che:2:turn-daemon']); + expect(harness.completions).toEqual(['SUCCEEDED']); + }); + it('records a failed start instead of reporting a false success', async () => { const harness = createHarness(buildOperation('START'), true);