fix: make cutover restart argv upgrade-safe and tests platform-honest

- cutover-orchestrator: restart via 'node --import tsx' (matching install.sh)
  instead of a hardcoded .pnpm/tsx@4.22.4 loader path that breaks on tsx bumps
- production-gateway: use process.execPath for the legacy-start fixture;
  POSIX-only 0600 mode assertion now win32-guarded
- cli-runtime + installer suites: skip where they require POSIX-only
  process-group SIGTERM, lsof, or /bin/sh
- fixture-safety: fall back to config.example.json when config.json is absent
  on a fresh clone
This commit is contained in:
chick
2026-09-07 01:09:49 +08:00
parent eed134a25c
commit 4f42a6f835
5 changed files with 20 additions and 10 deletions
+3 -1
View File
@@ -131,7 +131,9 @@ afterEach(async () => {
await Promise.all(cleanup.splice(0).map((fn) => fn()));
});
describe.sequential('executable package runtimes', () => {
// The runtime lifecycle under test is macOS/POSIX-only: process-group SIGTERM,
// lsof port inspection, and corepack as a direct spawn target.
describe.sequential('executable package runtimes', { skip: process.platform === 'win32' }, () => {
it('makes the exact production gateway executable attempt 8788 only with acknowledgement', async () => {
await expectPortFree(8788);
const fixture = await listenOn(8788);
+1 -1
View File
@@ -23,7 +23,7 @@ const STATE_FILE = 'cutover-state.json';
const GATEWAY_ARGV = [
process.execPath,
'--import',
resolve(process.cwd(), 'node_modules/.pnpm/tsx@4.22.4/node_modules/tsx/dist/loader.mjs'),
'tsx',
resolve(process.cwd(), 'apps/api/src/production-gateway-cli.ts'),
] as const;
const DIGEST = /^[a-f0-9]{64}$/u;
+7 -6
View File
@@ -45,9 +45,9 @@ async function fixture(name = 'cutover'): Promise<CutoverPlan> {
drillEvidence: drill,
drillEvidenceSha256: createHash('sha256').update(drillContent).digest('hex'),
legacyPid: 41,
legacyCommand: '/usr/local/bin/node /absolute/legacy/server.js',
legacyCommand: `${process.execPath} /absolute/legacy/server.js`,
legacyIdentity: 'legacy-start-1',
legacyStart: ['/usr/local/bin/node', '/absolute/legacy/server.js'],
legacyStart: [process.execPath, '/absolute/legacy/server.js'],
stateDir: join(root, 'state'),
};
}
@@ -130,11 +130,12 @@ describe('cutover validation and reversible orchestration', () => {
expect(signals).toEqual([41]);
expect(starts[0]?.environment?.MULTI_SIMADMIN_GATEWAY_TOKEN).toBe(token);
const statePath = join(plan.stateDir, 'cutover-state.json');
expect((await lstat(statePath)).mode & 0o777).toBe(0o600);
if (process.platform !== 'win32')
expect((await lstat(statePath)).mode & 0o777).toBe(0o600);
expect(await readFile(statePath, 'utf8')).not.toContain(token);
expect(await rollback(plan, system)).toBe(43);
expect(signals).toEqual([41, 42]);
expect(starts[1]?.argv).toEqual(['/usr/local/bin/node', '/absolute/legacy/server.js']);
expect(starts[1]?.argv).toEqual([process.execPath, '/absolute/legacy/server.js']);
});
it('automatically restarts legacy if the gateway fails to own authenticated 8788', async () => {
@@ -146,10 +147,10 @@ describe('cutover validation and reversible orchestration', () => {
[
process.execPath,
'--import',
resolve(process.cwd(), 'node_modules/.pnpm/tsx@4.22.4/node_modules/tsx/dist/loader.mjs'),
'tsx',
resolve(process.cwd(), 'apps/api/src/production-gateway-cli.ts'),
],
['/usr/local/bin/node', '/absolute/legacy/server.js'],
[process.execPath, '/absolute/legacy/server.js'],
]);
const state = JSON.parse(await readFile(join(plan.stateDir, 'cutover-state.json'), 'utf8')) as {
phase: string;