2026-07-23 00:40:13 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Image boot-smoke: prove the backend IMAGE actually boots and serves its contract
|
|
|
|
|
# (a `docker build` succeeding does not prove the container starts). Builds the image
|
|
|
|
|
# locally (or uses a provided BACKEND_IMAGE_TAG), runs it + a throwaway TimescaleDB via
|
|
|
|
|
# docker-compose.test.yml, waits for /healthz, then asserts /healthz and the version
|
|
|
|
|
# endpoint. Always tears the stack down.
|
|
|
|
|
#
|
|
|
|
|
# ./scripts/smoke.sh # build :local and smoke it
|
|
|
|
|
# BACKEND_IMAGE_TAG=sha-abc123 ./scripts/smoke.sh # smoke a published image (CI)
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
|
|
|
|
|
|
export POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-smoke}"
|
|
|
|
|
# The image to smoke, pulled from the registry (no local build). CI passes the just-
|
|
|
|
|
# pushed sha (BACKEND_IMAGE_TAG=sha-<12hex>); locally it defaults to a published tag.
|
|
|
|
|
export BACKEND_IMAGE_TAG="${BACKEND_IMAGE_TAG:-v0.0.2-split-ci}"
|
|
|
|
|
IMG="${REGISTRY_HOST:-git.thermograph.org}/${BACKEND_IMAGE_PATH:-emi/thermograph-backend/app}:${BACKEND_IMAGE_TAG}"
|
|
|
|
|
export SMOKE_HOST_PORT="${SMOKE_HOST_PORT:-18137}"
|
|
|
|
|
COMPOSE=(docker compose -f docker-compose.test.yml)
|
|
|
|
|
PORT_URL="http://127.0.0.1:${SMOKE_HOST_PORT}"
|
|
|
|
|
|
|
|
|
|
cleanup() { echo "==> tearing down"; "${COMPOSE[@]}" down -v --remove-orphans >/dev/null 2>&1 || true; }
|
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
|
|
|
|
echo "==> pulling $IMG"
|
|
|
|
|
"${COMPOSE[@]}" pull backend
|
|
|
|
|
|
|
|
|
|
echo "==> starting backend + db ($IMG)"
|
|
|
|
|
"${COMPOSE[@]}" up -d
|
|
|
|
|
|
|
|
|
|
echo "==> waiting for /healthz (up to 90s)"
|
shell: add shellcheck CI guard and drive the tree to zero findings
No static analysis has ever run over the ~2k lines of shell that deploy,
provision secrets, and bootstrap hosts as root over SSH. Add shell-lint.yml
(pinned shellcheck v0.11.0 + sha256, -x, default severity, fail on any
finding) and fix everything it reports, plus two defects it structurally
cannot see.
Not path-filtered, matching secrets-guard's call: a backstop that only runs
when you expect it to isn't a backstop. Scripts are discovered with find, so
new ones are covered on landing. The version is pinned to a static release
rather than apt's, so a drifted shellcheck can't fail CI on an unrelated push.
render-secrets.sh: the mktemp holding DECRYPTED vault contents was only
removed on the success path and one failure branch, so a sops decrypt failure
left plaintext POSTGRES_PASSWORD in /tmp indefinitely on a live host. A RETURN
trap makes removal unconditional, and the two sops calls now `|| return 1`
explicitly instead of relying on the caller's set -e (a bare set -e abort
skips the trap). The function stays free of `set -e` itself -- it is sourced,
and shell options would leak into the caller.
autoscale.sh: ran `set -eu` without pipefail while piping docker stats into
awk, so a failed left side was swallowed and the loop scaled on empty input.
Promoted to pipefail with avg_cpu's failure treated as a missed sample, so a
daemon hiccup can't kill the autoscaler. Verified busybox ash in docker:27-cli
supports pipefail and the script still parses there.
capture-fixtures.sh: `jq . || cat` ran cat after jq had already consumed
stdin, silently writing a truncated fixture; now a real if/else that fails
loudly. deploy.sh/deploy-stack.sh: `# shellcheck source=` paths corrected for
the monorepo layout, and /etc/thermograph.env marked unfollowable (it is
rendered at deploy time and cannot exist at lint time).
2026-07-23 21:59:47 +00:00
|
|
|
for _ in $(seq 1 45); do
|
2026-07-23 00:40:13 +00:00
|
|
|
if curl -fsS -o /dev/null "$PORT_URL/healthz"; then ok=1; break; fi
|
|
|
|
|
sleep 2
|
|
|
|
|
done
|
|
|
|
|
[ "${ok:-}" = 1 ] || { echo "!! backend never became healthy"; "${COMPOSE[@]}" logs --tail=40 backend; exit 1; }
|
|
|
|
|
|
|
|
|
|
echo "==> asserting contract"
|
|
|
|
|
curl -fsS "$PORT_URL/healthz" >/dev/null && echo " /healthz 200 ok"
|
|
|
|
|
ver=$(curl -fsS "$PORT_URL/api/version")
|
|
|
|
|
echo " /api/version -> $ver"
|
|
|
|
|
echo "$ver" | grep -q '"backend_version"[[:space:]]*:[[:space:]]*"2"' \
|
|
|
|
|
|| { echo "!! /api/version did not report backend_version=2"; exit 1; }
|
|
|
|
|
|
|
|
|
|
echo "==> SMOKE PASSED"
|