fix(canary): share the native 103 goosed pool instead of Docker
Memind CI / Test, build, and release guards (push) Failing after 2m40s

103 retired Docker goosed on 2026-07-30. Portal canary now health-checks
18006-18014 and inherits those targets, instead of requiring goosed-prod-1.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-08-14 22:35:58 +08:00
parent 19c8cdb970
commit e59809eff4
3 changed files with 25 additions and 42 deletions
+7 -5
View File
@@ -154,7 +154,7 @@ test('production canary keeps stable 8081 live and switches only after verified
const fullBackup = source.indexOf('Create and verify the full stable backup'); const fullBackup = source.indexOf('Create and verify the full stable backup');
const persistBackup = source.indexOf('Create and verify the persisted-data backup'); const persistBackup = source.indexOf('Create and verify the persisted-data backup');
const edgeBackup = source.indexOf('Create and verify the active 105 nginx routing backup'); const edgeBackup = source.indexOf('Create and verify the active 105 nginx routing backup');
const goosedStart = source.indexOf('Start an isolated goosed candidate on 18015'); const goosedStart = source.indexOf('Use the native goosed pool 18006-18014');
const candidateStart = source.indexOf('Start the passive candidate Portal on 18081'); const candidateStart = source.indexOf('Start the passive candidate Portal on 18081');
const deepseekCompatStart = source.indexOf( const deepseekCompatStart = source.indexOf(
'Start the DeepSeek tool-round compatibility proxy on 18036', 'Start the DeepSeek tool-round compatibility proxy on 18036',
@@ -183,6 +183,8 @@ test('production canary keeps stable 8081 live and switches only after verified
assert.match(source, /CANARY_PROXY_PORT=18082/); assert.match(source, /CANARY_PROXY_PORT=18082/);
assert.match(source, /CANARY_TUNNEL_REMOTE_PORT=19082/); assert.match(source, /CANARY_TUNNEL_REMOTE_PORT=19082/);
assert.match(source, /DEEPSEEK_COMPAT_PORT=18036/); assert.match(source, /DEEPSEEK_COMPAT_PORT=18036/);
assert.match(source, /Use the native goosed pool 18006-18014/);
assert.doesNotMatch(source, /docker inspect goosed-prod-1/);
assert.match(source, /deepseek-no-think-proxy\.mjs/); assert.match(source, /deepseek-no-think-proxy\.mjs/);
assert.match(source, /run-deepseek-compat-proxy-candidate\.sh/); assert.match(source, /run-deepseek-compat-proxy-candidate\.sh/);
assert.match(source, /MEMIND_CANARY_CANDIDATE_HEALTH_URLS/); assert.match(source, /MEMIND_CANARY_CANDIDATE_HEALTH_URLS/);
@@ -221,7 +223,7 @@ test('production canary keeps stable 8081 live and switches only after verified
assert.doesNotMatch(source, /bootout.*cn\.tkmind\.memind-portal/); assert.doesNotMatch(source, /bootout.*cn\.tkmind\.memind-portal/);
}); });
test('candidate runner overrides stable host MCP paths with container-visible paths', async (t) => { test('candidate runner inherits native goosed MCP paths from the stable root', async (t) => {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-canary-runner-')); const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'memind-canary-runner-'));
t.after(() => fs.rm(tempRoot, { recursive: true, force: true })); t.after(() => fs.rm(tempRoot, { recursive: true, force: true }));
@@ -289,14 +291,14 @@ test('candidate runner overrides stable host MCP paths with container-visible pa
assert.equal(result.status, 0, result.stderr); assert.equal(result.status, 0, result.stderr);
assert.match(result.stdout, new RegExp(`^cwd=${ROOT}$`, 'm')); 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_node=\/opt\/homebrew\/opt\/node@24\/bin\/node$/m);
assert.match( assert.match(
result.stdout, result.stdout,
/^mcp_server=\/opt\/portal\/mindspace-sandbox-mcp\.mjs$/m, /^mcp_server=\/Users\/john\/Project\/Memind\/mindspace-sandbox-mcp\.mjs$/m,
); );
assert.match(result.stdout, /^deepseek_disable=1$/m); assert.match(result.stdout, /^deepseek_disable=1$/m);
assert.match(result.stdout, /^deepseek_port=18036$/m); assert.match(result.stdout, /^deepseek_port=18036$/m);
assert.match(result.stdout, /^deepseek_gateway=host\.docker\.internal$/m); assert.match(result.stdout, /^deepseek_gateway=wrong\.invalid$/m);
assert.match(result.stdout, /^deepseek_base=unset$/m); assert.match(result.stdout, /^deepseek_base=unset$/m);
assert.match(result.stdout, /^deepseek_host=unset$/m); assert.match(result.stdout, /^deepseek_host=unset$/m);
assert.match(result.stdout, /^page_data_review=1$/m); assert.match(result.stdout, /^page_data_review=1$/m);
+10 -30
View File
@@ -50,8 +50,8 @@ Deploys a candidate Portal beside the stable 103 runtime:
stable Portal 127.0.0.1:8081 stable Portal 127.0.0.1:8081
canary router 127.0.0.1:18082 canary router 127.0.0.1:18082
candidate Portal 127.0.0.1:18081 candidate Portal 127.0.0.1:18081
candidate goosed 127.0.0.1:18015 native goosed 127.0.0.1:18006-18014
DeepSeek compat 0.0.0.0:18036 (host/container only) DeepSeek compat 0.0.0.0:18036 (host only; reused if already healthy)
The stable runtime is not replaced. A dedicated reverse tunnel exposes the The stable runtime is not replaced. A dedicated reverse tunnel exposes the
router only to 105 at 127.0.0.1:19082. The committed release workflow updates router only to 105 at 127.0.0.1:19082. The committed release workflow updates
@@ -183,7 +183,7 @@ printf '%s %s\n' "${bundle_sha}" "$(basename "${BUNDLE_PATH}")" > "${SHA_PATH}"
echo "git_branch=${branch}" echo "git_branch=${branch}"
echo "artifact_tree=.runtime/portal" echo "artifact_tree=.runtime/portal"
echo "artifact_bundle_sha256=${bundle_sha}" echo "artifact_bundle_sha256=${bundle_sha}"
echo "routing=stable:8081,proxy:18082,edge-tunnel:19082,candidate:18081,goosed-canary:18015,deepseek-compat:18036" echo "routing=stable:8081,proxy:18082,edge-tunnel:19082,candidate:18081,goosed-native:18006-18014,deepseek-compat:18036"
echo "canary_usernames=${CANARY_USERNAMES}" echo "canary_usernames=${CANARY_USERNAMES}"
echo "canary_wechat_user_ids=${CANARY_WECHAT_USER_IDS}" echo "canary_wechat_user_ids=${CANARY_WECHAT_USER_IDS}"
echo "canary_wechat_page_data_aider_review_enabled=${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED}" echo "canary_wechat_page_data_aider_review_enabled=${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED}"
@@ -211,7 +211,9 @@ ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" \
| grep -q '"deepseekThinking":"disabled"'; } \ | grep -q '"deepseekThinking":"disabled"'; } \
&& ! lsof -nP -iTCP:18081 -sTCP:LISTEN >/dev/null 2>&1 \ && ! lsof -nP -iTCP:18081 -sTCP:LISTEN >/dev/null 2>&1 \
&& ! lsof -nP -iTCP:18015 -sTCP:LISTEN >/dev/null 2>&1 \ && ! lsof -nP -iTCP:18015 -sTCP:LISTEN >/dev/null 2>&1 \
&& /opt/homebrew/bin/docker inspect goosed-prod-1 >/dev/null \ && for _p in 18006 18007 18008 18009 18010 18011 18012 18013 18014; do \
curl -kfsS --max-time 2 https://127.0.0.1:\${_p}/status | grep -qx ok; \
done \
&& test \"\$(df -Pk '${REMOTE_ROOT}' | awk 'NR==2 {print \$4}')\" -gt 10485760 \ && test \"\$(df -Pk '${REMOTE_ROOT}' | awk 'NR==2 {print \$4}')\" -gt 10485760 \
&& ssh -o BatchMode=yes -o ConnectTimeout=10 '${EDGE_HOST}' \ && ssh -o BatchMode=yes -o ConnectTimeout=10 '${EDGE_HOST}' \
\"test -f '${EDGE_MOBILE_CONFIG}' \ \"test -f '${EDGE_MOBILE_CONFIG}' \
@@ -514,28 +516,10 @@ say "Stop an older canary without touching stable Portal 8081"
restore_edge_to_stable restore_edge_to_stable
stop_candidate_services stop_candidate_services
say "Start an isolated goosed candidate on 18015" say "Use the native goosed pool 18006-18014"
( for _p in 18006 18007 18008 18009 18010 18011 18012 18013 18014; do
cd "${GOOSED_DIR}" [[ "$(curl -skS -m 5 "https://127.0.0.1:${_p}/status" 2>/dev/null || true)" == "ok" ]]
set -a
# shellcheck disable=SC1091
source .env
set +a
export CANDIDATE_RUNTIME_DIR="${CANDIDATE_DIR}"
"${DOCKER_BIN}" compose -p goosed-prod \
-f "${GOOSED_COMPOSE}" \
-f "${CANDIDATE_DIR}/scripts/goosed-canary.compose.yml" \
up -d --no-deps goosed-canary
)
for _ in $(seq 1 60); do
if [[ "$(curl -skS -m 5 https://127.0.0.1:18015/status 2>/dev/null || true)" == "ok" ]]; then
break
fi
sleep 2
done done
[[ "$(curl -skS -m 5 https://127.0.0.1:18015/status 2>/dev/null || true)" == "ok" ]]
"${DOCKER_BIN}" exec goosed-prod-canary \
sh -lc 'test -x /usr/local/bin/node && test -f /opt/portal/mindspace-sandbox-mcp.mjs'
say "Start the DeepSeek tool-round compatibility proxy on 18036" say "Start the DeepSeek tool-round compatibility proxy on 18036"
if deepseek_compat_healthy; then if deepseek_compat_healthy; then
@@ -570,9 +554,6 @@ for _ in $(seq 1 30); do
done done
deepseek_compat_healthy deepseek_compat_healthy
fi fi
"${DOCKER_BIN}" exec goosed-prod-canary \
sh -lc "curl -fsS --max-time 5 http://host.docker.internal:${DEEPSEEK_COMPAT_PORT}/health \
| grep -q '\"deepseekThinking\":\"disabled\"'"
say "Start the passive candidate Portal on 18081" say "Start the passive candidate Portal on 18081"
cat > "${CANDIDATE_PLIST}" <<EOF cat > "${CANDIDATE_PLIST}" <<EOF
@@ -588,7 +569,6 @@ cat > "${CANDIDATE_PLIST}" <<EOF
<key>MEMIND_CANARY_STABLE_ROOT</key><string>${STABLE_DIR}</string> <key>MEMIND_CANARY_STABLE_ROOT</key><string>${STABLE_DIR}</string>
<key>MEMIND_CANARY_RELEASE_ID</key><string>${RELEASE_ID}</string> <key>MEMIND_CANARY_RELEASE_ID</key><string>${RELEASE_ID}</string>
<key>MEMIND_CANARY_CANDIDATE_PORT</key><string>18081</string> <key>MEMIND_CANARY_CANDIDATE_PORT</key><string>18081</string>
<key>MEMIND_CANARY_GOOSED_URL</key><string>https://127.0.0.1:18015</string>
<key>MEMIND_CANARY_DEEPSEEK_PROXY_PORT</key><string>${DEEPSEEK_COMPAT_PORT}</string> <key>MEMIND_CANARY_DEEPSEEK_PROXY_PORT</key><string>${DEEPSEEK_COMPAT_PORT}</string>
<key>MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED</key><string>${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED}</string> <key>MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED</key><string>${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED}</string>
<key>MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS</key><string>${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS}</string> <key>MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS</key><string>${CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS}</string>
@@ -819,7 +799,7 @@ printf 'stable_health=http://127.0.0.1:8081/api/status\n'
printf 'proxy_health=http://127.0.0.1:%s/__memind_canary/health\n' "${CANARY_PROXY_PORT}" printf 'proxy_health=http://127.0.0.1:%s/__memind_canary/health\n' "${CANARY_PROXY_PORT}"
printf 'edge_tunnel=http://127.0.0.1:%s/api/status\n' "${CANARY_TUNNEL_REMOTE_PORT}" printf 'edge_tunnel=http://127.0.0.1:%s/api/status\n' "${CANARY_TUNNEL_REMOTE_PORT}"
printf 'candidate_health=http://127.0.0.1:18081/api/status\n' printf 'candidate_health=http://127.0.0.1:18081/api/status\n'
printf 'candidate_goosed=https://127.0.0.1:18015/status\n' printf 'candidate_goosed=https://127.0.0.1:18006-18014/status\n'
printf 'deepseek_compat_health=http://127.0.0.1:%s/health\n' "${DEEPSEEK_COMPAT_PORT}" printf 'deepseek_compat_health=http://127.0.0.1:%s/health\n' "${DEEPSEEK_COMPAT_PORT}"
REMOTE_SCRIPT REMOTE_SCRIPT
+8 -7
View File
@@ -27,17 +27,18 @@ export H5_REMINDER_WORKER_ENABLED=0
export H5_PORT="${MEMIND_CANARY_CANDIDATE_PORT:-18081}" export H5_PORT="${MEMIND_CANARY_CANDIDATE_PORT:-18081}"
export H5_HOST=127.0.0.1 export H5_HOST=127.0.0.1
export H5_PUBLIC_BASE_URL="${H5_PUBLIC_BASE_URL:-https://m.tkmind.cn}" 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}" if [[ -n "${MEMIND_CANARY_GOOSED_URL:-}" ]]; then
export TKMIND_API_TARGET="${MEMIND_CANARY_GOOSED_URL:-https://127.0.0.1:18015}" export TKMIND_API_TARGETS="${MEMIND_CANARY_GOOSED_URL}"
# Extensions are spawned inside goosed-canary, where the candidate artifact is export TKMIND_API_TARGET="${MEMIND_CANARY_GOOSED_URL}"
# mounted at /opt/portal. Never inherit host-only MCP paths from the stable .env. # Isolated Docker goosed mounts the candidate artifact at /opt/portal.
export GOOSED_MCP_NODE_PATH=/usr/local/bin/node export GOOSED_MCP_NODE_PATH=/usr/local/bin/node
export GOOSED_MCP_SERVER_PATH=/opt/portal/mindspace-sandbox-mcp.mjs export GOOSED_MCP_SERVER_PATH=/opt/portal/mindspace-sandbox-mcp.mjs
export MEMIND_GOOSED_HOST_GATEWAY=host.docker.internal
fi
# DeepSeek V4 tool rounds must use the same compatibility contract exercised # DeepSeek V4 tool rounds must use the same compatibility contract exercised
# by the release Gate. Stable .env values cannot disable or redirect it. # by the release Gate. Stable .env values cannot disable or redirect it.
export MEMIND_DEEPSEEK_DISABLE_THINKING=1 export MEMIND_DEEPSEEK_DISABLE_THINKING=1
export MEMIND_DEEPSEEK_NO_THINK_PORT="${MEMIND_CANARY_DEEPSEEK_PROXY_PORT:-18036}" export MEMIND_DEEPSEEK_NO_THINK_PORT="${MEMIND_CANARY_DEEPSEEK_PROXY_PORT:-18036}"
export MEMIND_GOOSED_HOST_GATEWAY=host.docker.internal
export H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_ENABLED="${MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED:-0}" export H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_ENABLED="${MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_ENABLED:-0}"
export H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_USERS="${MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS:-}" export H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_USERS="${MEMIND_CANARY_WECHAT_PAGE_DATA_AIDER_REVIEW_USER_IDS:-}"
export AIDER_BIN="${MEMIND_CANARY_AIDER_BIN:-/opt/homebrew/bin/aider}" export AIDER_BIN="${MEMIND_CANARY_AIDER_BIN:-/opt/homebrew/bin/aider}"