#!/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/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}"