Files
memind_adm/scripts/check-gray-ready.sh
T

160 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
HOST="${STUDIO_HOST:-10.10.0.2}"
GRAY_WEB_PORT="${GRAY_WEB_PORT:-15174}"
GRAY_API_PORT="${GRAY_API_PORT:-18085}"
REQUIRE_IMAGE_INTEGRATION=0
CANDIDATE_DIR=""
usage() {
cat <<'EOF'
Usage:
bash scripts/check-gray-ready.sh --candidate-dir DIR [--require-image-integration]
Read-only checks only. This script never uploads, restarts, or changes 103.
Checks:
- candidate checksum and traceability manifest
- current memind_adm production health
- reserved gray ports are free on 103
- expected image_make release is healthy on 103
- optional end-to-end Portal/image_make runtime wiring readiness
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--candidate-dir)
shift
[[ $# -gt 0 ]] || { echo "--candidate-dir requires a directory" >&2; exit 2; }
CANDIDATE_DIR="$1"
;;
--require-image-integration) REQUIRE_IMAGE_INTEGRATION=1 ;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
shift
done
[[ -n "${CANDIDATE_DIR}" ]] || { usage >&2; exit 2; }
if [[ "${CANDIDATE_DIR}" != /* ]]; then
CANDIDATE_DIR="${ROOT}/${CANDIDATE_DIR}"
fi
[[ -d "${CANDIDATE_DIR}" ]] || { echo "Candidate directory not found: ${CANDIDATE_DIR}" >&2; exit 1; }
manifest="$(find "${CANDIDATE_DIR}" -maxdepth 1 -type f -name '*.manifest.txt' -print -quit)"
sha_file="$(find "${CANDIDATE_DIR}" -maxdepth 1 -type f -name '*.sha256' -print -quit)"
bundle="$(find "${CANDIDATE_DIR}" -maxdepth 1 -type f -name '*.tar.gz' -print -quit)"
[[ -n "${manifest}" && -n "${sha_file}" && -n "${bundle}" ]] || {
echo "Candidate bundle, manifest, or checksum file missing in ${CANDIDATE_DIR}" >&2
exit 1
}
(
cd "${CANDIDATE_DIR}"
shasum -a 256 -c "$(basename "${sha_file}")"
)
if tar -tzf "${bundle}" | grep -Eq '(^|/)(\.env($|\.)|[^/]*\.pid$|src/|release-candidates/)'; then
echo "Candidate contains forbidden runtime or development assets" >&2
tar -tzf "${bundle}" | grep -E '(^|/)(\.env($|\.)|[^/]*\.pid$|src/|release-candidates/)' >&2 || true
exit 1
fi
manifest_value() {
local key="$1"
sed -n "s/^${key}=//p" "${manifest}" | head -n 1
}
release_id="$(manifest_value release_id)"
git_head="$(manifest_value git_head)"
memind_head="$(manifest_value memind_git_head)"
memind_status="$(manifest_value memind_git_status)"
image_make_release_id="$(manifest_value image_make_release_id)"
[[ -n "${release_id}" && -n "${git_head}" && -n "${memind_head}" ]] || {
echo "Candidate manifest is missing release traceability fields" >&2
exit 1
}
[[ "${memind_status}" == "clean" ]] || {
echo "Candidate manifest does not prove a clean Memind shared source" >&2
exit 1
}
[[ "${image_make_release_id}" != "not-declared" && -n "${image_make_release_id}" ]] || {
echo "Candidate manifest must declare IMAGE_MAKE_RELEASE_ID" >&2
exit 1
}
echo "candidate_release=${release_id}"
echo "candidate_git_head=${git_head}"
echo "candidate_memind_head=${memind_head}"
echo "candidate_image_make_release=${image_make_release_id}"
remote_result="$(ssh -o BatchMode=yes -o ConnectTimeout=15 "${HOST}" \
"GRAY_WEB_PORT='${GRAY_WEB_PORT}' GRAY_API_PORT='${GRAY_API_PORT}' IMAGE_MAKE_RELEASE_ID='${image_make_release_id}' REQUIRE_IMAGE_INTEGRATION='${REQUIRE_IMAGE_INTEGRATION}' /bin/bash" <<'REMOTE'
set -euo pipefail
api_code="$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8085/health || true)"
web_code="$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:5174/ops/ || true)"
image_code="$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:18083/health || true)"
[[ "${api_code}" == "200" && "${web_code}" == "200" && "${image_code}" == "200" ]] || {
echo "health_failed admin_api=${api_code} admin_web=${web_code} image_make=${image_code}" >&2
exit 1
}
for port in "${GRAY_WEB_PORT}" "${GRAY_API_PORT}"; do
if lsof -nP -iTCP:"${port}" -sTCP:LISTEN -t | grep -q .; then
echo "gray_port_in_use=${port}" >&2
exit 1
fi
done
export DOCKER_HOST=unix:///Users/john/.colima/default/docker.sock
docker_bin=/usr/local/bin/docker
container="$(${docker_bin} ps --format '{{.Names}} {{.Ports}}' | awk '/18083/ {print $1; exit}')"
[[ -n "${container}" ]] || { echo "image_make_container_missing" >&2; exit 1; }
image="$(${docker_bin} inspect --format '{{.Config.Image}}' "${container}")"
health="$(${docker_bin} inspect --format '{{.State.Health.Status}}' "${container}")"
[[ "${image}" == *"${IMAGE_MAKE_RELEASE_ID}"* && "${health}" == "healthy" ]] || {
echo "image_make_release_mismatch image=${image} health=${health}" >&2
exit 1
}
worker_enabled="$(${docker_bin} inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "${container}" | sed -n 's/^WORKER_ENABLED=//p' | head -n 1)"
config_url_present=0
if ${docker_bin} inspect --format '{{range .Config.Env}}{{println .}}{{end}}' "${container}" | grep -q '^IMAGE_MAKE_MEMIND_CONFIG_URL=.'; then
config_url_present=1
fi
portal_route_present=0
if grep -q '/internal/image-make/runtime-config' /Users/john/Project/Memind/server.mjs 2>/dev/null; then
portal_route_present=1
fi
echo "admin_api=200 admin_web=200 image_make=200"
echo "gray_ports_free=${GRAY_WEB_PORT},${GRAY_API_PORT}"
echo "image_make_image=${image}"
echo "image_make_worker_enabled=${worker_enabled:-unset}"
echo "image_make_config_url_present=${config_url_present}"
echo "portal_runtime_route_present=${portal_route_present}"
if [[ "${REQUIRE_IMAGE_INTEGRATION}" == "1" ]]; then
[[ "${worker_enabled}" == "true" && "${config_url_present}" == "1" && "${portal_route_present}" == "1" ]] || {
echo "image_make_integration_not_ready" >&2
exit 1
}
fi
REMOTE
)"
printf '%s\n' "${remote_result}"
echo "gray_preflight=passed"