| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 |
- import { spawn, spawnSync } from 'node:child_process'
- import { setTimeout as delay } from 'node:timers/promises'
- import { mkdtempSync, rmSync } from 'node:fs'
- import { tmpdir } from 'node:os'
- import { join } from 'node:path'
- const port = Number(process.env.HERMES_YAOYAO_VERIFY_PORT || 18800)
- const entry = new URL('../dist-server/server/index.js', import.meta.url)
- const verificationHome = mkdtempSync(join(tmpdir(), 'yaoyao-lifecycle-'))
- function listenerPids(targetPort) {
- const result = spawnSync('lsof', ['-nP', `-iTCP:${targetPort}`, '-sTCP:LISTEN', '-t'], {
- encoding: 'utf8',
- })
- return new Set(result.stdout.split(/\s+/).map(value => value.trim()).filter(Boolean))
- }
- async function waitForHealth() {
- const deadline = Date.now() + 15_000
- while (Date.now() < deadline) {
- try {
- const response = await fetch(`http://127.0.0.1:${port}/healthz`, {
- headers: { Host: `127.0.0.1:${port}` },
- })
- if (response.ok) return
- } catch {
- // Server may still be materializing the production bundle.
- }
- await delay(150)
- }
- throw new Error('Timed out waiting for the verification server')
- }
- const gatewayBefore = listenerPids(9119)
- const child = spawn(process.execPath, [entry.pathname], {
- cwd: new URL('..', import.meta.url),
- env: {
- ...process.env,
- NODE_ENV: 'production',
- HERMES_YAOYAO_HOST: '127.0.0.1',
- HERMES_YAOYAO_HOME: verificationHome,
- HERMES_YAOYAO_PORT: String(port),
- HERMES_YAOYAO_UPSTREAM: process.env.HERMES_YAOYAO_UPSTREAM || 'http://127.0.0.1:9119',
- HERMES_YAOYAO_SUPERVISE_DASHBOARD: '0',
- },
- stdio: ['ignore', 'pipe', 'pipe'],
- })
- let stderr = ''
- child.stderr.on('data', chunk => { stderr += chunk.toString() })
- try {
- await waitForHealth()
- const webPids = listenerPids(port)
- if (webPids.size !== 1) throw new Error(`Expected one ${port} listener, received ${[...webPids].join(', ') || 'none'}`)
- } finally {
- child.kill('SIGTERM')
- await Promise.race([
- new Promise(resolve => child.once('exit', resolve)),
- delay(5_000).then(() => child.kill('SIGKILL')),
- ])
- rmSync(verificationHome, { recursive: true, force: true })
- }
- const gatewayAfter = listenerPids(9119)
- if ([...gatewayBefore].join(',') !== [...gatewayAfter].join(',')) {
- throw new Error(`9119 listener changed: before=${[...gatewayBefore]} after=${[...gatewayAfter]}`)
- }
- if (child.exitCode !== 0 && child.signalCode !== 'SIGTERM') {
- throw new Error(`Verification server failed: ${stderr.trim()}`)
- }
- process.stdout.write(`Lifecycle verified: one ${port} listener; 9119 unchanged (${[...gatewayAfter].join(',') || 'no listener'}).\n`)
|