Files
memind/scripts/release-deep-search-runtime-prod.sh
T
john 22a45f34f5
Memind CI / Test, build, and release guards (pull_request) Successful in 4m7s
fix: start deep search launchagent reliably
2026-07-24 10:26:16 +08:00

199 lines
6.1 KiB
Bash

#!/usr/bin/env bash
# Publishes only the independently versioned Deep Search runtime to 103.
# Portal and memindadm are not modified by this workflow.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HOST="${STUDIO_HOST:-58.38.22.103}"
REMOTE_ROOT="${STUDIO_REMOTE_ROOT:-/Users/john/Project}"
RUNTIME_BASE="${DEEP_SEARCH_RUNTIME_BASE:-${REMOTE_ROOT}/deep-search-runtime}"
INCOMING="${REMOTE_ROOT}/incoming/deep-search-runtime"
RELEASE_ID="$(date +%Y%m%d-%H%M%S)-$(git -C "${ROOT}" rev-parse --short HEAD)"
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/deep-search-runtime-release.XXXXXX")"
DRY_RUN=0
AUTO_YES=0
cleanup() {
rm -rf "${TMP_DIR}"
}
trap cleanup EXIT
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=1 ;;
--yes) AUTO_YES=1 ;;
-h|--help)
cat <<'EOF'
Usage: bash scripts/release-deep-search-runtime-prod.sh [--dry-run] [--yes]
Required production gates:
- current branch is main
- HEAD exactly matches origin/main
- worktree is clean
- CONFIRMED_CI_SHA equals the full current HEAD
This installs only Deep Search on 103. It does not release Portal or memindadm.
EOF
exit 0
;;
*)
echo "unknown argument: $1" >&2
exit 1
;;
esac
shift
done
git -C "${ROOT}" fetch origin main --quiet
branch="$(git -C "${ROOT}" branch --show-current)"
head_sha="$(git -C "${ROOT}" rev-parse HEAD)"
origin_sha="$(git -C "${ROOT}" rev-parse origin/main)"
[[ "${branch}" == "main" ]] || {
echo "Deep Search production release must run from main" >&2
exit 1
}
[[ "${head_sha}" == "${origin_sha}" ]] || {
echo "main must exactly match origin/main" >&2
exit 1
}
[[ -z "$(git -C "${ROOT}" status --porcelain=v1 --untracked-files=all)" ]] || {
echo "worktree must be clean" >&2
exit 1
}
[[ "${CONFIRMED_CI_SHA:-}" == "${head_sha}" ]] || {
echo "CONFIRMED_CI_SHA must equal the verified main HEAD: ${head_sha}" >&2
exit 1
}
ALLOW_MAIN_RELEASE=1 bash "${ROOT}/scripts/check-release-ready.sh"
(
cd "${ROOT}"
npm run test:deep-search
node --test capabilities.test.mjs scripts/deep-search-runtime-package.test.mjs
npm run build:deep-search-runtime
)
RUNTIME_DIR="${ROOT}/.runtime/deep-search"
for file in \
deep-search-server.mjs \
scripts/run-deep-search-prod.sh \
scripts/install-deep-search-runtime-prod.sh \
RUNBOOK.txt; do
[[ -f "${RUNTIME_DIR}/${file}" ]] || {
echo "missing runtime artifact: ${file}" >&2
exit 1
}
done
MANIFEST="${RUNTIME_DIR}/release-manifest.txt"
{
echo "release_id=${RELEASE_ID}"
echo "git_head=${head_sha}"
echo "git_branch=${branch}"
echo "created_at=$(date '+%Y-%m-%d %H:%M:%S %z')"
echo "artifact=.runtime/deep-search"
echo "persistent_env=${RUNTIME_BASE}/shared/.env"
echo "persistent_db=${RUNTIME_BASE}/data/research.sqlite"
} > "${MANIFEST}"
ARCHIVE="${TMP_DIR}/deep-search-runtime-${RELEASE_ID}.tar.gz"
SHA_FILE="${ARCHIVE}.sha256"
tar -C "${RUNTIME_DIR}" -czf "${ARCHIVE}" .
(cd "${TMP_DIR}" && shasum -a 256 "$(basename "${ARCHIVE}")" > "$(basename "${SHA_FILE}")")
if [[ "${DRY_RUN}" -eq 1 ]]; then
shasum -a 256 -c "${SHA_FILE}"
echo "Deep Search dry-run artifact verified: ${ARCHIVE}"
exit 0
fi
if [[ "${AUTO_YES}" -ne 1 ]]; then
echo "Target: ${HOST}:${RUNTIME_BASE}"
echo "Release: ${RELEASE_ID}"
read -r -p "Install standalone Deep Search on 103? [y/N] " confirm </dev/tty
[[ "${confirm}" =~ ^[Yy]$ ]] || exit 0
fi
ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" "mkdir -p '${INCOMING}' '${RUNTIME_BASE}/releases' '${RUNTIME_BASE}/backups'"
scp -q "${ARCHIVE}" "${SHA_FILE}" "${HOST}:${INCOMING}/"
ssh -o BatchMode=yes "${HOST}" \
"RELEASE_ID='${RELEASE_ID}' RUNTIME_BASE='${RUNTIME_BASE}' INCOMING='${INCOMING}' /bin/bash" <<'REMOTE'
set -euo pipefail
archive="${INCOMING}/deep-search-runtime-${RELEASE_ID}.tar.gz"
sha_file="${archive}.sha256"
release_dir="${RUNTIME_BASE}/releases/${RELEASE_ID}"
backup_dir="${RUNTIME_BASE}/backups/${RELEASE_ID}"
current_link="${RUNTIME_BASE}/current"
plist="${HOME}/Library/LaunchAgents/cn.tkmind.memind-deep-search.plist"
label="cn.tkmind.memind-deep-search"
gui="gui/$(id -u)"
previous_target="$(readlink "${current_link}" 2>/dev/null || true)"
database_path="${RUNTIME_BASE}/data/research.sqlite"
mkdir -p "${release_dir}" "${backup_dir}"
cp "${plist}" "${backup_dir}/launchagent.plist" 2>/dev/null || true
if [[ -f "${database_path}" ]]; then
sqlite3 "${database_path}" ".timeout 10000" \
".backup '${backup_dir}/research.sqlite'" >/dev/null
fi
cd "${INCOMING}"
shasum -a 256 -c "$(basename "${sha_file}")"
tar -xzf "${archive}" -C "${release_dir}"
rollback() {
launchctl bootout "${gui}/${label}" >/dev/null 2>&1 || true
if [[ -n "${previous_target}" && -d "${previous_target}" ]]; then
ln -sfn "${previous_target}" "${current_link}"
else
rm -f "${current_link}"
fi
if [[ -f "${backup_dir}/launchagent.plist" ]]; then
cp "${backup_dir}/launchagent.plist" "${plist}"
launchctl bootstrap "${gui}" "${plist}" >/dev/null 2>&1 || true
launchctl kickstart -k "${gui}/${label}" >/dev/null 2>&1 || true
else
rm -f "${plist}"
fi
}
trap rollback ERR
DEEP_SEARCH_RUNTIME_BASE="${RUNTIME_BASE}" \
DEEP_SEARCH_RELEASE_DIR="${release_dir}" \
bash "${release_dir}/scripts/install-deep-search-runtime-prod.sh"
set -a
# shellcheck disable=SC1090
source "${RUNTIME_BASE}/shared/.env"
set +a
curl -fsS --max-time 5 "http://127.0.0.1:20100/health" >/dev/null
curl -fsS --max-time 20 \
-H "X-Secret-Key: ${TKMIND_DEEP_SEARCH_SECRET}" \
-H "Content-Type: application/json" \
-d '{"query":"OpenAI","limit":3}' \
"http://127.0.0.1:20100/v1/search" \
| grep -q '"results"'
docker_bin=""
for candidate in /usr/local/bin/docker /opt/homebrew/bin/docker; do
if [[ -x "${candidate}" ]]; then
docker_bin="${candidate}"
break
fi
done
if [[ -n "${docker_bin}" ]]; then
goosed_container="$("${docker_bin}" ps --format '{{.Names}}' | grep -E '^goosed-prod-' | head -1)"
if [[ -n "${goosed_container}" ]]; then
"${docker_bin}" exec "${goosed_container}" sh -lc \
'curl -fsS --max-time 5 http://host.docker.internal:20100/health >/dev/null'
fi
fi
trap - ERR
printf '%s\n' "${RELEASE_ID}" > "${RUNTIME_BASE}/current-release.txt"
REMOTE
echo "Deep Search installed and verified on 103: ${RELEASE_ID}"