From 5f66a797d839cf933e9cd7824756d0d2df75c802 Mon Sep 17 00:00:00 2001 From: Suriyakumarvijayanayagam Date: Thu, 13 Aug 2026 14:03:13 +0530 Subject: [PATCH] Add a post-deploy smoke test Separates three failures that are indistinguishable from a browser, and which this deployment hit in sequence: 502 on every route the container is not running - it exited at startup, so nothing reached the app and no route is special 200 + database:false the API is healthy, Postgres is not 200 + database:true working It also catches AUTH_ALLOW_ANY_LOGIN being left on, by asserting that a deliberately wrong password is rejected. That setting is a convenience locally and a total auth bypass on a published host, and nothing else about a running deployment looks different when it is true. ./scripts/check_deploy.sh ./scripts/check_deploy.sh http://localhost:3000 Co-Authored-By: Claude Opus 5 (1M context) --- scripts/check_deploy.sh | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 scripts/check_deploy.sh diff --git a/scripts/check_deploy.sh b/scripts/check_deploy.sh new file mode 100755 index 0000000..bb1631b --- /dev/null +++ b/scripts/check_deploy.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Post-deploy smoke test. +# +# ./scripts/check_deploy.sh # https://mcp.nearle.ai.in +# ./scripts/check_deploy.sh http://localhost:3000 # a local container +# +# Separates the three failures that all look alike from a browser: +# 502 everywhere - the container is not running (it exited at startup) +# 200 + database:false - the API is fine, Postgres is not +# 200 + database:true - working +set -uo pipefail + +BASE="${1:-https://mcp.nearle.ai.in}" +pass=0; fail=0 + +hit() { curl -sS -m 20 -o /tmp/_body -w '%{http_code}' "$BASE$1" 2>/dev/null || echo 000; } + +check() { # path expected label + local code; code=$(hit "$1") + if [ "$code" = "$2" ]; then printf ' \033[32mPASS\033[0m %-16s %s\n' "$1" "$3"; pass=$((pass+1)) + else printf ' \033[31mFAIL\033[0m %-16s expected %s, got %s\n' "$1" "$2" "$code"; fail=$((fail+1)); fi +} + +echo "Checking $BASE" +echo +echo "Is the container running at all?" +code=$(hit /) +if [ "$code" = "502" ] || [ "$code" = "000" ]; then + echo " FAIL / -> $code" + echo + echo " The container is not serving. It almost certainly exited at startup." + echo " Read the Dokploy logs; settings.py names the missing value explicitly." + echo " Confirm the image actually contains /app/.env - the build log must show" + echo " a 'COPY .env .' step, and .dockerignore must not list .env." + exit 1 +fi +printf ' \033[32mPASS\033[0m %-16s container is up\n' "/" +pass=$((pass+1)) + +echo +echo "Routes that must not depend on the database:" +check /docs 200 "interactive API docs" +check /openapi.json 200 "OpenAPI schema" +check /api/health 200 "health endpoint answers" + +echo +echo "Dependencies (reported in the body; 'false' does not mean the API is broken):" +health=$(curl -sS -m 20 "$BASE/api/health" 2>/dev/null) +db=$(printf '%s' "$health" | sed -n 's/.*"database":\([a-z]*\).*/\1/p') +ol=$(printf '%s' "$health" | sed -n 's/.*"ollama":\([a-z]*\).*/\1/p') +if [ "$db" = "true" ]; then printf ' \033[32mPASS\033[0m database connected\n'; pass=$((pass+1)) +else + printf ' \033[33mWARN\033[0m database NOT connected\n' + echo " The API works; catalog pages will be empty. Check DB_HOST/DB_USER/" + echo " DB_PASSWORD/DB_NAME. A wrong password logs 'password authentication" + echo " failed' rather than a timeout." +fi +[ "$ol" = "true" ] \ + && printf ' \033[32mPASS\033[0m ollama connected\n' \ + || printf ' \033[33mWARN\033[0m ollama not connected (/api/chat unavailable; expected if USE_OLLAMA=false)\n' + +echo +echo "Auth:" +code=$(curl -sS -m 20 -o /dev/null -w '%{http_code}' -X POST "$BASE/api/auth/login" \ + -H 'Content-Type: application/json' -d '{"username":"admin","password":"definitely-not-the-password"}' 2>/dev/null) +if [ "$code" = "401" ]; then printf ' \033[32mPASS\033[0m wrong password rejected (401)\n'; pass=$((pass+1)) +elif [ "$code" = "200" ]; then + printf ' \033[31mFAIL\033[0m a WRONG PASSWORD WAS ACCEPTED - AUTH_ALLOW_ANY_LOGIN is true.\n' + echo " Anyone who finds this host can sign in as admin. Set it to false." + fail=$((fail+1)) +else printf ' \033[31mFAIL\033[0m login endpoint returned %s\n' "$code"; fail=$((fail+1)); fi + +echo +echo "MCP:" +code=$(curl -sS -m 20 -o /dev/null -w '%{http_code}' "$BASE/api/mcp/info" 2>/dev/null) +[ "$code" = "401" ] \ + && { printf ' \033[32mPASS\033[0m /api/mcp/info guarded (401 without a token)\n'; pass=$((pass+1)); } \ + || printf ' \033[33mWARN\033[0m /api/mcp/info returned %s (expected 401)\n' "$code" + +echo +echo "-------- $pass passed, $fail failed --------" +[ "$fail" -eq 0 ]