Files
memind/release-gate/preflight.mjs
T
john 2f240e7500 fix(release-gate): skip duplicate CI work and stop live LLM blowups
Shared paths like db.mjs were pulling PAGE/DATA live agent suites into every
hotfix. Keep those cases for actual page-data changes, resume passed suites on
the same artifact, and fail fast on Docker/port issues instead of rerunning 100+
scenarios.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 22:15:16 +08:00

80 lines
2.3 KiB
JavaScript

import fs from 'node:fs';
import net from 'node:net';
import { spawnSync } from 'node:child_process';
export const GATE_SUITE_PORTS = Object.freeze({
'runtime-sanitized-data-upgrade': 19085,
'page-data-product-scenarios': 19086,
'page-content-delivery-scenarios': 19087,
'runtime-production-homolog-cold-start': 19081,
});
const DOCKER_SOCKET_CANDIDATES = [
process.env.DOCKER_HOST?.replace(/^unix:\/\//, ''),
`${process.env.HOME ?? ''}/.docker/run/docker.sock`,
'/var/run/docker.sock',
].filter(Boolean);
export function dockerSocketExists() {
return DOCKER_SOCKET_CANDIDATES.some((socketPath) => {
try {
return fs.existsSync(socketPath);
} catch {
return false;
}
});
}
export function assertDockerDaemonAvailable() {
if (!dockerSocketExists()) {
throw new Error(
'Release gate REL-11 needs Docker; start Docker Desktop and retry. Missing docker.sock.',
);
}
const result = spawnSync('docker', ['info'], {
encoding: 'utf8',
timeout: 15_000,
});
if (result.status !== 0) {
throw new Error(
`Release gate REL-11 needs a running Docker daemon: ${(result.stderr || result.error?.message || 'docker info failed').trim().slice(0, 500)}`,
);
}
}
export function assertLoopbackPortAvailable(port, host = '127.0.0.1') {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.once('error', (error) => {
if (error?.code === 'EADDRINUSE') {
reject(new Error(
`Release gate port ${host}:${port} is already in use; stop the stale local gate process before retrying`,
));
return;
}
reject(error);
});
server.once('listening', () => {
server.close((closeError) => {
if (closeError) reject(closeError);
else resolve();
});
});
server.listen(port, host);
});
}
export async function preflightImpactSuites(suites) {
const ports = [...new Set(
suites
.map((suite) => GATE_SUITE_PORTS[suite.id])
.filter((port) => Number.isInteger(port)),
)].sort((left, right) => left - right);
for (const port of ports) {
await assertLoopbackPortAvailable(port);
}
if (suites.some((suite) => suite.id === 'runtime-linux-dependency-closure')) {
assertDockerDaemonAvailable();
}
}