#!/usr/bin/env bash
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# S40 cutover smoke test. Exercises the public-facing routes that
# matter at launch: landing page, signup/login forms render with
# a fresh CSRF token, health endpoints respond, the docs subdomain
# is reachable, the API authenticates a known PAT.
#
# Usage:
# deploy/cutover/smoke.sh https://shithub.sh
#
# Optional env (when set, the script also exercises the API):
# SHITHUB_SMOKE_PAT — a valid shp_ token for `user:read`
# SHITHUB_SMOKE_DOCS — docs subdomain URL (default: docs.)
#
# Exit status:
# 0 — all green
# 1 — at least one check failed
# 2 — usage error
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "usage: $0 " >&2
exit 2
fi
BASE="$1"
DOCS="${SHITHUB_SMOKE_DOCS:-${BASE/shithub./docs.shithub.}}"
fail=0
say() { printf '\n=== %s ===\n' "$*"; }
ok() { printf ' PASS: %s\n' "$*"; }
bad() { printf ' FAIL: %s\n' "$*"; fail=$((fail + 1)); }
# 1. Landing.
say "GET $BASE/"
body=$(curl -fsS -o - -w "\n%{http_code}\n" "$BASE/" 2>&1) || { bad "landing fetch"; body=""; }
if [[ "$body" == *"shithub"* ]]; then ok "body contains shithub"; else bad "body missing shithub"; fi
# 2. Health endpoints. /readyz proves DB + storage are reachable.
say "GET $BASE/-/health"
curl -fsS "$BASE/-/health" >/dev/null && ok "/-/health 200" || bad "/-/health"
say "GET $BASE/healthz"
curl -fsS "$BASE/healthz" >/dev/null && ok "/healthz 200" || bad "/healthz"
say "GET $BASE/readyz"
curl -fsS "$BASE/readyz" >/dev/null && ok "/readyz 200" || bad "/readyz"
# 3. Signup form renders.
say "GET $BASE/signup"
body=$(curl -fsS "$BASE/signup") || { bad "signup fetch"; body=""; }
if [[ "$body" == *"csrf_token"* ]]; then ok "CSRF token present"; else bad "no csrf_token in signup form"; fi
# 4. Login form renders.
say "GET $BASE/login"
body=$(curl -fsS "$BASE/login") || { bad "login fetch"; body=""; }
if [[ "$body" == *"username"* ]] && [[ "$body" == *"password"* ]]; then
ok "login form fields present"
else
bad "login form missing username/password"
fi
# 5. TLS posture. Strict-Transport-Security must be set.
say "TLS / HSTS"
hdrs=$(curl -fsS -I "$BASE/" 2>&1) || { bad "headers fetch"; hdrs=""; }
if grep -qi "strict-transport-security" <<<"$hdrs"; then
ok "HSTS header set"
else
bad "HSTS header missing"
fi
if grep -qi "x-content-type-options" <<<"$hdrs"; then
ok "X-Content-Type-Options set"
else
bad "X-Content-Type-Options missing"
fi
# 6. Docs subdomain.
say "GET $DOCS/"
curl -fsS -o /dev/null "$DOCS/" && ok "docs site 200" || bad "docs site"
# 7. API (only if a PAT is provided).
if [[ -n "${SHITHUB_SMOKE_PAT:-}" ]]; then
say "GET $BASE/api/v1/user (with PAT)"
body=$(curl -fsS -H "Authorization: Bearer $SHITHUB_SMOKE_PAT" "$BASE/api/v1/user") || { bad "api fetch"; body=""; }
if [[ "$body" == *'"username"'* ]]; then
ok "API returned a user object"
else
bad "API response unexpected: $body"
fi
else
printf ' SKIP: API check (set SHITHUB_SMOKE_PAT to run)\n'
fi
printf '\n'
if [[ "$fail" -eq 0 ]]; then
echo "smoke: all checks passed"
exit 0
fi
echo "smoke: $fail check(s) FAILED"
exit 1