fix: harden release gate and page delivery
Memind CI / Test, build, and release guards (push) Failing after 12m3s
Memind CI / Test, build, and release guards (push) Failing after 12m3s
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
|
||||
import {
|
||||
createLocalGateStack,
|
||||
selectBackendLlmProvider,
|
||||
seedSelectedProviderKeys,
|
||||
} from '../release-gate/local-stack.mjs';
|
||||
|
||||
const root = path.resolve(new URL('..', import.meta.url).pathname);
|
||||
const runtimeRoot = path.join(root, '.runtime', 'portal');
|
||||
const scenarioIds = [
|
||||
'ai-usage-survey',
|
||||
'customer-order-system',
|
||||
'supplier-data-report',
|
||||
'tkmind-feature-survey',
|
||||
];
|
||||
const concurrency = Math.max(
|
||||
1,
|
||||
Math.min(scenarioIds.length, Number(process.env.RELEASE_GATE_SCENARIO_CONCURRENCY ?? 4) || 4),
|
||||
);
|
||||
const childTimeoutMs = Math.max(
|
||||
30_000,
|
||||
Number(process.env.RELEASE_GATE_SCENARIO_PROCESS_TIMEOUT_MS ?? 360_000) || 360_000,
|
||||
);
|
||||
const stepTimeoutMs = Math.max(
|
||||
30_000,
|
||||
Number(process.env.RELEASE_GATE_SCENARIO_TIMEOUT_MS ?? 300_000) || 300_000,
|
||||
);
|
||||
|
||||
async function runScenario(scenarioId, port) {
|
||||
const startedAt = Date.now();
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
['scripts/run-scenario-test.mjs', '--scenario', scenarioId, '--port', String(port)],
|
||||
{
|
||||
cwd: root,
|
||||
env: {
|
||||
...process.env,
|
||||
JOHN_PASSWORD: '888888',
|
||||
RELEASE_GATE_SCENARIO_TIMEOUT_MS: String(stepTimeoutMs),
|
||||
},
|
||||
stdio: 'inherit',
|
||||
},
|
||||
);
|
||||
const timeout = setTimeout(() => {
|
||||
child.kill('SIGTERM');
|
||||
setTimeout(() => child.kill('SIGKILL'), 3_000).unref();
|
||||
}, childTimeoutMs);
|
||||
timeout.unref();
|
||||
child.once('error', reject);
|
||||
child.once('close', (status, signal) => {
|
||||
clearTimeout(timeout);
|
||||
resolve({
|
||||
code: status ?? 1,
|
||||
signal,
|
||||
timedOut: signal === 'SIGTERM' || signal === 'SIGKILL',
|
||||
});
|
||||
});
|
||||
});
|
||||
return {
|
||||
scenarioId,
|
||||
...result,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
};
|
||||
}
|
||||
|
||||
const runId = `page-data-${new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14)}`;
|
||||
const stack = await createLocalGateStack({
|
||||
root,
|
||||
runId,
|
||||
port: Number(process.env.MEMIND_RELEASE_GATE_PAGE_DATA_PORT ?? 19086),
|
||||
portalRoot: runtimeRoot,
|
||||
nodeEnv: 'test',
|
||||
runtimeProfile: 'local',
|
||||
});
|
||||
await seedSelectedProviderKeys({ sourceUrl: stack.sourceDatabaseUrl, targetUrl: stack.mysqlUrl });
|
||||
await selectBackendLlmProvider({ targetUrl: stack.mysqlUrl });
|
||||
|
||||
async function register(username) {
|
||||
const response = await fetch(`${stack.baseUrl}/auth/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
password: '888888',
|
||||
displayName: `Release Gate ${username}`,
|
||||
email: `${username}@example.invalid`,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`register ${username} failed: ${response.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (const username of ['john', 'john4']) await register(username);
|
||||
let nextIndex = 0;
|
||||
const results = [];
|
||||
async function worker() {
|
||||
while (nextIndex < scenarioIds.length) {
|
||||
const scenarioId = scenarioIds[nextIndex];
|
||||
nextIndex += 1;
|
||||
results.push(await runScenario(scenarioId, new URL(stack.baseUrl).port));
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
||||
results.sort(
|
||||
(left, right) => scenarioIds.indexOf(left.scenarioId) - scenarioIds.indexOf(right.scenarioId),
|
||||
);
|
||||
console.log('\nPage Data scenario timing:');
|
||||
for (const result of results) {
|
||||
console.log(
|
||||
`${result.code === 0 ? 'PASS' : 'FAIL'} ${result.scenarioId} `
|
||||
+ `${Math.ceil(result.elapsedMs / 1000)}s${result.timedOut ? ' timeout' : ''}`,
|
||||
);
|
||||
}
|
||||
if (results.some((result) => result.code !== 0)) process.exitCode = 1;
|
||||
} finally {
|
||||
await stack.cleanup();
|
||||
}
|
||||
Reference in New Issue
Block a user