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,116 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
|
||||
import { assertPortalRuntimePath, inspectPortalRuntime } from '../release-gate/artifact.mjs';
|
||||
|
||||
const root = path.resolve(new URL('..', import.meta.url).pathname);
|
||||
const runtime = assertPortalRuntimePath(path.join(root, '.runtime', 'portal'), { repoRoot: root });
|
||||
const image = process.env.RELEASE_GATE_NODE_IMAGE || 'node:24-bookworm';
|
||||
|
||||
async function runDocker(commandArgs, { input = '' } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('docker', commandArgs, {
|
||||
cwd: root,
|
||||
env: process.env,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
child.stdout.setEncoding('utf8');
|
||||
child.stderr.setEncoding('utf8');
|
||||
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
||||
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
||||
child.once('error', reject);
|
||||
child.once('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
|
||||
child.stdin.end(input);
|
||||
});
|
||||
}
|
||||
|
||||
function baseDockerArgs() {
|
||||
return [
|
||||
'run',
|
||||
'--rm',
|
||||
'-i',
|
||||
'--network',
|
||||
'none',
|
||||
'--mount',
|
||||
`type=bind,src=${runtime},dst=/opt/portal,readonly`,
|
||||
'--tmpfs',
|
||||
'/workspace:rw,noexec,nosuid,size=64m',
|
||||
image,
|
||||
];
|
||||
}
|
||||
|
||||
async function requireSuccess(label, args, options) {
|
||||
const result = await runDocker([...baseDockerArgs(), ...args], options);
|
||||
if (result.code !== 0) {
|
||||
throw new Error(`${label} failed (${result.code}): ${result.stderr.slice(-2_000)}`);
|
||||
}
|
||||
console.log(`PASS REL-11 ${label}`);
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
const inspection = await inspectPortalRuntime(runtime);
|
||||
if (inspection.missing.length || inspection.forbidden.length) {
|
||||
throw new Error(
|
||||
`runtime inspection failed: missing=${inspection.missing.join(',')} forbidden=${inspection.forbidden.join(',')}`,
|
||||
);
|
||||
}
|
||||
|
||||
await requireSuccess('server bundle parses in Linux ARM64 Node', [
|
||||
'node',
|
||||
'--check',
|
||||
'/opt/portal/server.mjs',
|
||||
]);
|
||||
await requireSuccess('WeChat bundle parses in Linux ARM64 Node', [
|
||||
'node',
|
||||
'--check',
|
||||
'/opt/portal/wechat-mp.bundle.mjs',
|
||||
]);
|
||||
await requireSuccess('Agent Run worker loads its runtime dependencies', [
|
||||
'node',
|
||||
'/opt/portal/scripts/agent-run-worker.mjs',
|
||||
'--help',
|
||||
]);
|
||||
|
||||
const initialize = `${JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'initialize',
|
||||
params: {},
|
||||
})}\n`;
|
||||
const sandboxOutput = await requireSuccess(
|
||||
'sandbox MCP starts and answers initialize',
|
||||
['node', '/opt/portal/mindspace-sandbox-mcp.mjs', '/workspace'],
|
||||
{ input: initialize },
|
||||
);
|
||||
if (!sandboxOutput.includes('"name":"mindspace-sandbox"')) {
|
||||
throw new Error('sandbox MCP initialize response is missing server identity');
|
||||
}
|
||||
|
||||
const searchOutput = await requireSuccess(
|
||||
'MindSearch MCP starts and answers initialize',
|
||||
['node', '/opt/portal/tkmind-search-mcp.mjs'],
|
||||
{ input: initialize },
|
||||
);
|
||||
if (!searchOutput.includes('"name":"tkmind-search"')) {
|
||||
throw new Error('MindSearch MCP initialize response is missing server identity');
|
||||
}
|
||||
|
||||
const excelResult = await runDocker(
|
||||
[
|
||||
...baseDockerArgs(),
|
||||
'env',
|
||||
'EXCEL_ANALYST_ENABLED=1',
|
||||
'MINDSPACE_WORKSPACE_ROOT=/workspace',
|
||||
'node',
|
||||
'/opt/portal/tkmind-excel-mcp.mjs',
|
||||
'/workspace',
|
||||
],
|
||||
{ input: initialize },
|
||||
);
|
||||
if (excelResult.code !== 0 || !excelResult.stdout.includes('"name":"tkmind-excel"')) {
|
||||
throw new Error(`Excel MCP failed to start: ${excelResult.stderr.slice(-2_000)}`);
|
||||
}
|
||||
console.log('PASS REL-11 Excel MCP starts and answers initialize');
|
||||
Reference in New Issue
Block a user