fix: use container MCP path in portal canary
Memind CI / Test, build, and release guards (push) Successful in 1m41s

This commit is contained in:
john
2026-07-26 22:34:58 +08:00
parent 02f2dc6b17
commit 046704816f
3 changed files with 68 additions and 0 deletions
+7
View File
@@ -16,6 +16,13 @@ Portal 的首个生产动作必须是用户级灰度,不能直接整包替换
`MEMIND_RUNTIME_ROLE=candidate` 会禁止重复启动 MindSpace、Plaza、提醒、订阅续期和
Agent 恢复扫描等单例后台任务。
候选 Portal 必须从候选 runtime 目录启动,使相对模块只解析到本次候选 artifact。
候选 goosed 在 Docker 内执行扩展,MCP Node 和入口路径必须固定为
`/usr/local/bin/node``/opt/portal/mindspace-sandbox-mcp.mjs`;禁止继承稳定
`.env` 中仅宿主机可见的 `/Users/...` 路径。若日志出现
`Failed to add extension` 和宿主机绝对路径的 `MODULE_NOT_FOUND`,应先核对候选
进程环境与 `/opt/portal` 容器挂载,禁止把宿主机文件复制进容器临时修补。
## 身份和回落规则
- `john`:按数据库中的唯一用户名或用户 ID 匹配。
+55
View File
@@ -1,6 +1,7 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
@@ -163,6 +164,60 @@ test('production canary keeps stable 8081 live and switches only after verified
assert.doesNotMatch(source, /bootout.*cn\.tkmind\.memind-portal/);
});
test('candidate runner overrides stable host MCP paths with container-visible paths', async (t) => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-canary-runner-'));
t.after(() => fs.rm(tempRoot, { recursive: true, force: true }));
const stableRoot = path.join(tempRoot, 'stable');
const fakeNode = path.join(tempRoot, 'node');
await fs.mkdir(stableRoot);
await fs.writeFile(
path.join(stableRoot, '.env'),
[
'GOOSED_MCP_NODE_PATH=/opt/homebrew/opt/node@24/bin/node',
'GOOSED_MCP_SERVER_PATH=/Users/john/Project/Memind/mindspace-sandbox-mcp.mjs',
'',
].join('\n'),
);
await fs.writeFile(
fakeNode,
[
'#!/usr/bin/env bash',
'printf "cwd=%s\\n" "$PWD"',
'printf "mcp_node=%s\\n" "$GOOSED_MCP_NODE_PATH"',
'printf "mcp_server=%s\\n" "$GOOSED_MCP_SERVER_PATH"',
'printf "entrypoint=%s\\n" "$1"',
'',
].join('\n'),
{ mode: 0o755 },
);
const result = spawnSync(
'bash',
[path.join(ROOT, 'scripts', 'run-memind-portal-candidate.sh')],
{
cwd: '/',
encoding: 'utf8',
env: {
...process.env,
MEMIND_CANARY_STABLE_ROOT: stableRoot,
MEMIND_CANARY_RELEASE_ID: 'test-release',
MEMIND_CANARY_CANDIDATE_PORT: '65534',
NODE_BIN: fakeNode,
},
},
);
assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, new RegExp(`^cwd=${ROOT}$`, 'm'));
assert.match(result.stdout, /^mcp_node=\/usr\/local\/bin\/node$/m);
assert.match(
result.stdout,
/^mcp_server=\/opt\/portal\/mindspace-sandbox-mcp\.mjs$/m,
);
assert.match(result.stdout, new RegExp(`^entrypoint=${ROOT}/server\\.mjs$`, 'm'));
});
test('canary release and rollback remote shells remain syntactically valid', async () => {
const releaseSource = await fs.readFile(CANARY_RELEASE, 'utf8');
const rollbackSource = await fs.readFile(CANARY_ROLLBACK, 'utf8');
+6
View File
@@ -9,6 +9,8 @@ if [[ ! -d "${STABLE_ROOT}" || ! -f "${STABLE_ROOT}/.env" ]]; then
exit 1
fi
cd "${ROOT}"
set -a
# shellcheck disable=SC1091
source "${STABLE_ROOT}/.env"
@@ -27,6 +29,10 @@ export H5_HOST=127.0.0.1
export H5_PUBLIC_BASE_URL="${H5_PUBLIC_BASE_URL:-https://m.tkmind.cn}"
export TKMIND_API_TARGETS="${MEMIND_CANARY_GOOSED_URL:-https://127.0.0.1:18015}"
export TKMIND_API_TARGET="${MEMIND_CANARY_GOOSED_URL:-https://127.0.0.1:18015}"
# Extensions are spawned inside goosed-canary, where the candidate artifact is
# mounted at /opt/portal. Never inherit host-only MCP paths from the stable .env.
export GOOSED_MCP_NODE_PATH=/usr/local/bin/node
export GOOSED_MCP_SERVER_PATH=/opt/portal/mindspace-sandbox-mcp.mjs
unset TKMIND_API_TARGET_1
NODE_BIN="${NODE_BIN:-/opt/homebrew/opt/node@24/bin/node}"