Merge pull request 'fix: remount goosed after portal runtime swap' (#11) from codex/fix-goosed-remount into main
Memind CI / Test, build, and release guards (push) Failing after 1m23s
Memind CI / Test, build, and release guards (push) Failing after 1m23s
Ensure goosed containers remount the new Portal/MindSpace live directory after each atomic runtime swap.
This commit was merged in pull request #11.
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
|
||||||
|
const releaseScript = fs.readFileSync(
|
||||||
|
new URL('./scripts/release-portal-runtime-prod.sh', import.meta.url),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
test('103 release remounts goosed after swapping the live directory', () => {
|
||||||
|
assert.match(releaseScript, /remount_goosed_after_live_swap\(\)/);
|
||||||
|
assert.match(releaseScript, /docker-compose\.prod\.yml/);
|
||||||
|
assert.match(releaseScript, /up -d --force-recreate/);
|
||||||
|
assert.match(releaseScript, /MindSpace\/\.goosed-remount-/);
|
||||||
|
assert.match(releaseScript, /containers\[@\].*!= 9/);
|
||||||
|
assert.match(releaseScript, /seq 18006 18014/);
|
||||||
|
assert.match(releaseScript, /\/opt\/portal\/mindspace-sandbox-mcp\.mjs/);
|
||||||
|
|
||||||
|
const swapIndex = releaseScript.indexOf('mv "${RUNTIME_DIR}" "${APP_DIR}"');
|
||||||
|
const remountIndex = releaseScript.indexOf('remount_goosed_after_live_swap', swapIndex);
|
||||||
|
const portalStartIndex = releaseScript.indexOf('say "启动新的 Portal runtime"');
|
||||||
|
assert.ok(swapIndex >= 0, 'live directory swap must exist');
|
||||||
|
assert.ok(remountIndex > swapIndex, 'goosed remount must happen after the live swap');
|
||||||
|
assert.ok(portalStartIndex > remountIndex, 'Portal must start only after goosed remount succeeds');
|
||||||
|
});
|
||||||
@@ -378,6 +378,84 @@ OLD_LIVE_DIR="${ARCHIVE_DIR}/Memind-source-before-${RELEASE_ID}"
|
|||||||
BUNDLE="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.tar.gz"
|
BUNDLE="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.tar.gz"
|
||||||
MANIFEST="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.manifest.txt"
|
MANIFEST="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.manifest.txt"
|
||||||
SHA_FILE="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.sha256"
|
SHA_FILE="${INCOMING_DIR}/memind-portal-runtime-${RELEASE_ID}.sha256"
|
||||||
|
|
||||||
|
remount_goosed_after_live_swap() {
|
||||||
|
local docker_bin="/opt/homebrew/bin/docker"
|
||||||
|
local compose_dir="/Users/john/Project/goosed-prod"
|
||||||
|
local compose_file="${compose_dir}/docker-compose.prod.yml"
|
||||||
|
local marker_rel="MindSpace/.goosed-remount-${RELEASE_ID}"
|
||||||
|
local marker_host="${APP_DIR}/${marker_rel}"
|
||||||
|
local marker_value="${RELEASE_ID}-$(date +%s)"
|
||||||
|
local ready=0
|
||||||
|
|
||||||
|
if [[ ! -x "${docker_bin}" || ! -f "${compose_file}" ]]; then
|
||||||
|
echo "goosed remount failed: docker or ${compose_file} is unavailable" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${APP_DIR}/MindSpace"
|
||||||
|
printf '%s\n' "${marker_value}" > "${marker_host}"
|
||||||
|
|
||||||
|
say "重建 goosed 容器以刷新 Portal/MindSpace bind mount"
|
||||||
|
if ! (
|
||||||
|
cd "${compose_dir}"
|
||||||
|
"${docker_bin}" compose -f "${compose_file}" up -d --force-recreate
|
||||||
|
); then
|
||||||
|
rm -f "${marker_host}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for _ in $(seq 1 60); do
|
||||||
|
local healthy=1
|
||||||
|
local containers=()
|
||||||
|
while IFS= read -r container; do
|
||||||
|
[[ -n "${container}" ]] && containers+=("${container}")
|
||||||
|
done < <("${docker_bin}" ps \
|
||||||
|
--filter 'label=com.docker.compose.project=goosed-prod' \
|
||||||
|
--format '{{.Names}}')
|
||||||
|
|
||||||
|
if ((${#containers[@]} != 9)); then
|
||||||
|
healthy=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local container
|
||||||
|
for container in "${containers[@]}"; do
|
||||||
|
local state
|
||||||
|
state="$("${docker_bin}" inspect "${container}" --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' 2>/dev/null || true)"
|
||||||
|
if [[ "${state}" != "healthy" ]]; then
|
||||||
|
healthy=0
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
local observed
|
||||||
|
observed="$("${docker_bin}" exec "${container}" sh -lc "cat '${APP_DIR}/${marker_rel}'" 2>/dev/null || true)"
|
||||||
|
if [[ "${observed}" != "${marker_value}" ]]; then
|
||||||
|
healthy=0
|
||||||
|
fi
|
||||||
|
if ! "${docker_bin}" exec "${container}" sh -lc 'test -f /opt/portal/mindspace-sandbox-mcp.mjs' >/dev/null 2>&1; then
|
||||||
|
healthy=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
local port
|
||||||
|
for port in $(seq 18006 18014); do
|
||||||
|
if [[ "$(curl -skS -m 5 "https://127.0.0.1:${port}/status" 2>/dev/null || true)" != "ok" ]]; then
|
||||||
|
healthy=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${healthy}" -eq 1 ]]; then
|
||||||
|
ready=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -f "${marker_host}"
|
||||||
|
if [[ "${ready}" -ne 1 ]]; then
|
||||||
|
echo "goosed remount failed: containers did not become healthy on the new live directory" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
FULL_BACKUP_TAR="${BACKUP_DIR}/memind-full-${RELEASE_ID}-before.tar.gz"
|
FULL_BACKUP_TAR="${BACKUP_DIR}/memind-full-${RELEASE_ID}-before.tar.gz"
|
||||||
PERSIST_BACKUP_TAR="${BACKUP_DIR}/memind-persisted-${RELEASE_ID}-before.tar.gz"
|
PERSIST_BACKUP_TAR="${BACKUP_DIR}/memind-persisted-${RELEASE_ID}-before.tar.gz"
|
||||||
PERSISTED_ITEMS=(
|
PERSISTED_ITEMS=(
|
||||||
@@ -570,6 +648,8 @@ rm -rf "${OLD_LIVE_DIR}"
|
|||||||
mv "${APP_DIR}" "${OLD_LIVE_DIR}"
|
mv "${APP_DIR}" "${OLD_LIVE_DIR}"
|
||||||
mv "${RUNTIME_DIR}" "${APP_DIR}"
|
mv "${RUNTIME_DIR}" "${APP_DIR}"
|
||||||
|
|
||||||
|
remount_goosed_after_live_swap
|
||||||
|
|
||||||
say "更新 LaunchAgent 指向 runtime 启动脚本"
|
say "更新 LaunchAgent 指向 runtime 启动脚本"
|
||||||
cat > "${HOME}/Library/LaunchAgents/${PORTAL_LABEL}.plist" <<EOF
|
cat > "${HOME}/Library/LaunchAgents/${PORTAL_LABEL}.plist" <<EOF
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|||||||
Reference in New Issue
Block a user