deploy.sh: health check through the gunicorn socket, optional DEPLOY_BUNDLE when Gitea is unreachable
From the server the public URL goes through ArvanCloud and returned 504/000 (2026-10-07), so the final health step failed even for a healthy service. It now asks the gunicorn socket (path read from the unit) with our Host header first. DEPLOY_BUNDLE=<file> fetches from a git bundle instead of Gitea, only when set explicitly; without it an unreachable Gitea still stops the deploy. Tested: probe_health against a throwaway unix-socket server and a dead URL (bash -n clean). Not tested: the whole script end to end, until the next deploy. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
47
deploy.sh
47
deploy.sh
@@ -39,6 +39,21 @@ else
|
||||
)
|
||||
fi
|
||||
|
||||
# یک آدرسِ سلامت را میزند و CODE و BODY را میگذارد. آدرسِ `unix:/مسیر` یعنی مستقیم از سوکتِ
|
||||
# gunicorn (با هدرِ Host دامنهی خودمان): از داخلِ سرور آدرسِ عمومی از مسیرِ ArvanCloud برمیگردد و
|
||||
# ۲۰۲۶-۱۰-۰۷ ۵۰۴/۰۰۰ میداد، پس «سلامتِ» اسکریپت همیشه شکست میخورد حتی وقتی سرویس سالم بود.
|
||||
probe_health() {
|
||||
local target="$1" RESPONSE
|
||||
if [[ "$target" == unix:* ]]; then
|
||||
RESPONSE="$(curl -s -m 10 -w '\n%{http_code}' --unix-socket "${target#unix:}" \
|
||||
-H "Host: ${PUBLIC_HOST:-localhost}" http://localhost/api/health 2>/dev/null || printf '\n000')"
|
||||
else
|
||||
RESPONSE="$(curl -s -m 10 -w '\n%{http_code}' "$target" 2>/dev/null || printf '\n000')"
|
||||
fi
|
||||
CODE="${RESPONSE##*$'\n'}"
|
||||
BODY="${RESPONSE%$'\n'*}"
|
||||
}
|
||||
|
||||
# ─── ۱. کدِ تازه ────────────────────────────────────────────────────────────
|
||||
if [ "${SKIP_PULL:-0}" != "1" ]; then
|
||||
step "گرفتنِ کدِ تازه"
|
||||
@@ -49,8 +64,21 @@ if [ "${SKIP_PULL:-0}" != "1" ]; then
|
||||
BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)"
|
||||
[ "$BRANCH" = "HEAD" ] && BRANCH=main
|
||||
|
||||
git fetch origin "$BRANCH" \
|
||||
|| fail "به gitea وصل نشد. شبکه یا دسترسیِ مخزن را بررسی کنید."
|
||||
# DEPLOY_BUNDLE=/مسیر/فایل.bundle: کد را از یک git bundle میگیرد، نه از gitea. فقط وقتی صریحاً
|
||||
# داده شود؛ برای روزی که سرور به gitea نمیرسد (۲۰۲۶-۱۰-۰۷: از سرور ۵۰۴ از مسیرِ ArvanCloud، از بیرون
|
||||
# ۲۰۰). bundle را از جایی بسازید که به origin/<branch> دسترسی دارد و هشِ کامیتِ چاپشده را با آنجا
|
||||
# مقایسه کنید. بدونِ این متغیر هیچ جایگزینی نیست و نرسیدن به gitea همچنان دیپلو را میایستاند.
|
||||
if [ -n "${DEPLOY_BUNDLE:-}" ]; then
|
||||
[ -f "$DEPLOY_BUNDLE" ] || fail "DEPLOY_BUNDLE=$DEPLOY_BUNDLE وجود ندارد."
|
||||
warn "کد از bundle گرفته میشود، نه از gitea: $DEPLOY_BUNDLE"
|
||||
git bundle verify "$DEPLOY_BUNDLE" >/dev/null 2>&1 || fail "bundle معتبر نیست یا کامیتِ پایهاش روی این سرور نیست."
|
||||
git fetch "$DEPLOY_BUNDLE" "$BRANCH:refs/remotes/origin/$BRANCH" \
|
||||
|| fail "گرفتن از bundle نشد."
|
||||
else
|
||||
git fetch origin "$BRANCH" \
|
||||
|| fail "به gitea وصل نشد. شبکه یا دسترسیِ مخزن را بررسی کنید (اگر فقط از این سرور نمیرسد: DEPLOY_BUNDLE، بالای همین فایل)."
|
||||
fi
|
||||
printf ' origin/%s = %s\n' "$BRANCH" "$(git rev-parse --short "origin/$BRANCH")"
|
||||
|
||||
# اگر کسی روی سرور فایلی را دستی عوض کرده، اول نسخهی پشتیبان، بعد کنار
|
||||
# گذاشتنش.
|
||||
@@ -350,26 +378,31 @@ fi
|
||||
|
||||
# ─── ۸. آیا واقعاً بالا آمد؟ ───────────────────────────────────────────────
|
||||
step "بررسیِ سلامت"
|
||||
PUBLIC_URL=""
|
||||
if [ -z "${TADRISINO_HEALTH_URL:-}" ]; then
|
||||
PUBLIC_URL="$(python -c 'import django, os; os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings"); django.setup(); from django.conf import settings; print(settings.PUBLIC_BASE_URL)' 2>/dev/null | tail -n 1)"
|
||||
if [ -n "$PUBLIC_URL" ]; then
|
||||
PUBLIC_HOST="${PUBLIC_URL#*://}"; PUBLIC_HOST="${PUBLIC_HOST%%/*}"
|
||||
# سوکتِ gunicorn را از خودِ یونیت میخوانیم تا مسیرش در این فایل تکرار نشود.
|
||||
SOCK=""
|
||||
if [ -n "${SERVICE:-}" ] && command -v systemctl >/dev/null 2>&1; then
|
||||
SOCK="$(systemctl cat "${SERVICE}.service" 2>/dev/null | grep -oE 'unix:[^ "]+' | head -n 1 || true)"
|
||||
fi
|
||||
[ -n "$SOCK" ] && [ -S "${SOCK#unix:}" ] && HEALTH_URLS=("$SOCK" "${HEALTH_URLS[@]}")
|
||||
HEALTH_URLS+=("$PUBLIC_URL/api/health" "$PUBLIC_URL/api/health/")
|
||||
fi
|
||||
fi
|
||||
CODE=000; BODY=""
|
||||
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
||||
for url in "${HEALTH_URLS[@]}"; do
|
||||
# یک درخواست، نه دو تا: دو فراخوانیِ جدا میتوانست دو وضعیتِ متفاوت ببیند.
|
||||
RESPONSE="$(curl -s -m 10 -w '\n%{http_code}' "$url" 2>/dev/null || printf '\n000')"
|
||||
CODE="${RESPONSE##*$'\n'}"
|
||||
BODY="${RESPONSE%$'\n'*}"
|
||||
probe_health "$url"
|
||||
if [ "$CODE" = "200" ]; then
|
||||
printf '%s✓ سرویس سالم بالا آمد%s (%s)\n' "$C_OK" "$C_OFF" "$url"
|
||||
printf '%s\n' "$BODY" | python -m json.tool 2>/dev/null | sed 's/^/ /' || printf ' %s\n' "$BODY"
|
||||
# ۲۰۰ گرفتن بهتنهایی یعنی «چیزی جواب میدهد»، نه «کدِ جدید بالاست».
|
||||
if [ "$RESTART_OK" != "1" ]; then
|
||||
warn "ولی سرویس ریاستارت نشد — این جوابْ از پروسهی قدیمی است و کدِ
|
||||
جدید هنوز سرو نمیشود. تا ریاستارت نکنید دیپلوی ناتمام است."
|
||||
جدید هنوز سرو نمیشود. تا ریاستارت نکنید دیپلو ناتمام است."
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user