fix two checks that only broke in the case they exist for
بازبینیِ کارِ امروز، سه مورد پیدا کرد. /api/health وقتی مهاجرتِ اعمالنشده وجود داشت میشکست: کدِ فهرستکردنِ مهاجرتها به migration.key تکیه میکرد که اصلاً وجود ندارد (فیلدها app_label و name هستند). یعنی دقیقاً در همان حالتی که این بررسی برایش ساخته شده، بهجای نامِ مهاجرتها فقط AttributeError برمیگرداند. حالا تستی دارد که مهاجرتِ معلق را شبیهسازی میکند — چیزی که نبودش باعث شد اولین بار غلط بماند. وارد کردنِ سوال با replace عددِ غلط گزارش میکرد: delete() در جنگو مجموعِ همهی ردیفهای آبشاری را میدهد، پس «۵ سوال پاک شد» در واقع گزینهها و پاسخها را هم میشمرد. deploy.sh بررسیِ متغیرهای محیطیاش با تنظیماتِ جدید ناسازگار شده بود: متغیرهایی را اجباری میدانست که حالا پیشفرض دارند، و فقط شل را میخواند نه .env — یعنی روی سرورِ درست هم دیپلوی را رد میکرد. حالا بهجای تکرارِ منطقِ تنظیمات، از خودِ جنگو میپرسد چه چیزی مؤثر است و گزارش میدهد. کوچکتر: بررسیِ سلامت یک درخواست میزند نه دو تا، و شکستِ ریاستارت دیگر اسکریپت را بیصدا تمام نمیکند. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
79
deploy.sh
79
deploy.sh
@@ -60,39 +60,40 @@ python -m pip install "${PIP_ARGS[@]}" \
|
||||
|| fail "نصبِ وابستگیها شکست خورد. اگر pypi.org تایماوت میدهد، این را امتحان کنید:
|
||||
PIP_INDEX_URL=https://mirror-pypi.runflare.com/simple ./deploy.sh"
|
||||
|
||||
# ─── ۳.۵ بررسیِ متغیرهای محیطی، قبل از دست زدن به دیتابیس ──────────────────
|
||||
# چرا اینجا: اگر SECRET_KEY یا CACHE_URL نباشد، جنگو موقعِ بالا آمدن میمیرد و
|
||||
# از بیرون فقط 502 دیده میشود. بهتر است همینجا و با پیامِ روشن متوقف شویم.
|
||||
step "بررسیِ متغیرهای محیطی"
|
||||
python - <<'PYCHECK' || fail "متغیرهای محیطیِ لازم ست نیستند؛ هیچ تغییری اعمال نشد."
|
||||
import os, sys
|
||||
# ─── ۳.۵ تنظیماتِ مؤثر ─────────────────────────────────────────────────────
|
||||
# اینجا عمداً چیزی را اجباری نمیکنیم: تنظیماتِ پروژه خودش پیشفرضِ سالم دارد و
|
||||
# با فایلِ خالی هم بالا میآید. تکرارِ آن منطق در این اسکریپت یعنی دو جا که
|
||||
# باید همزمان بهروز بمانند — و همان باعث میشد دیپلویِ درست هم رد شود.
|
||||
# پس فقط از خودِ جنگو میپرسیم چه چیزی واقعاً فعال است (شاملِ مقادیرِ .env).
|
||||
step "تنظیماتِ مؤثر"
|
||||
python - <<'PYCHECK' || fail "تنظیمات اصلاً بار نشد؛ هیچ تغییری اعمال نشد."
|
||||
import os
|
||||
|
||||
debug = os.environ.get('DJANGO_DEBUG', '').strip().lower() in {'1', 'true', 'yes', 'on'}
|
||||
missing = []
|
||||
import django
|
||||
|
||||
if not os.environ.get('DJANGO_ALLOWED_HOSTS', '').strip():
|
||||
missing.append(('DJANGO_ALLOWED_HOSTS', 'دامنههایی که سرو میشوند، مثلاً api.math.tadrisino2.ir'))
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
django.setup()
|
||||
from django.conf import settings
|
||||
|
||||
if not debug:
|
||||
if not os.environ.get('DJANGO_SECRET_KEY', '').strip():
|
||||
missing.append(('DJANGO_SECRET_KEY', 'کلیدِ امضا؛ با DEBUG خاموش اجباری است'))
|
||||
if not os.environ.get('CACHE_URL', '').strip():
|
||||
missing.append(('CACHE_URL', 'آدرسِ Redis؛ محدودیتِ نرخ بدونش بینِ workerها مشترک نیست'))
|
||||
|
||||
if missing:
|
||||
print(' این متغیرها ست نیستند:')
|
||||
for name, why in missing:
|
||||
print(f' {name:24} {why}')
|
||||
print()
|
||||
print(' در فایلِ .env سرور اضافهشان کنید (نمونه: .env.example).')
|
||||
sys.exit(1)
|
||||
|
||||
if debug:
|
||||
print(' ⚠ DJANGO_DEBUG روشن است.')
|
||||
print(' روی سرورِ عمومی یعنی: صفحههای خطا کلِ تنظیمات را نشان میدهند،')
|
||||
print(' و کدِ ورودِ 0000 برای هر شمارهای پذیرفته میشود.')
|
||||
print(f' DEBUG : {settings.DEBUG}')
|
||||
print(f' ALLOWED_HOSTS : {", ".join(settings.ALLOWED_HOSTS) or "—"}')
|
||||
print(f' CORS مجاز : {", ".join(settings.CORS_ALLOWED_ORIGINS) or "—"}')
|
||||
print(f' کش : {settings.CACHES["default"]["BACKEND"].rsplit(".", 1)[-1]}')
|
||||
if os.environ.get('DJANGO_SECRET_KEY'):
|
||||
key_source = 'از متغیرِ محیطی'
|
||||
elif settings.DEBUG:
|
||||
key_source = 'کلیدِ ثابتِ حالتِ توسعه (در پروداکشن استفاده نمیشود)'
|
||||
else:
|
||||
print(' ✓ حالتِ پروداکشن')
|
||||
key_source = 'تولیدشده و ذخیرهشده در .django_secret_key'
|
||||
print(f' کلیدِ امضا : {key_source}')
|
||||
|
||||
if settings.DEBUG:
|
||||
print()
|
||||
print(' ⚠ DEBUG روشن است. روی سرورِ عمومی یعنی:')
|
||||
print(' • صفحههای خطا کلِ تنظیمات و traceback را نشان میدهند')
|
||||
print(' • کدِ ورودِ 0000 برای هر شمارهای پذیرفته میشود')
|
||||
if 'LocMem' in settings.CACHES['default']['BACKEND'] and not settings.DEBUG:
|
||||
print(' ⚠ بدونِ Redis محدودیتِ نرخ بینِ workerها مشترک نیست (CACHE_URL).')
|
||||
PYCHECK
|
||||
|
||||
# ─── ۴. دیتابیس ────────────────────────────────────────────────────────────
|
||||
@@ -124,16 +125,24 @@ python manage.py check --deploy 2>&1 | sed 's/^/ /' || true
|
||||
step "ریاستارتِ سرویس"
|
||||
SERVICE="${TADRISINO_SERVICE:-}"
|
||||
if [ -z "$SERVICE" ] && command -v systemctl >/dev/null 2>&1; then
|
||||
# `systemctl cat` تنها بررسیِ قابلِ اتکاست: list-unit-files حتی وقتی چیزی
|
||||
# پیدا نکند هم کدِ ۰ برمیگرداند.
|
||||
for candidate in tadrisino tadrisino-api mathapp gunicorn; do
|
||||
if systemctl list-unit-files "${candidate}.service" >/dev/null 2>&1 \
|
||||
&& systemctl cat "${candidate}.service" >/dev/null 2>&1; then
|
||||
if systemctl cat "${candidate}.service" >/dev/null 2>&1; then
|
||||
SERVICE="$candidate"; break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -n "$SERVICE" ]; then
|
||||
sudo systemctl restart "$SERVICE" && printf ' سرویسِ %s ریاستارت شد\n' "$SERVICE"
|
||||
# شکستِ ریاستارت نباید اسکریپت را بیصدا تمام کند؛ بررسیِ سلامتِ پایین
|
||||
# همچنان باید اجرا شود تا معلوم شود سرویسِ قبلی هنوز بالاست یا نه.
|
||||
if sudo systemctl restart "$SERVICE"; then
|
||||
printf ' سرویسِ %s ریاستارت شد\n' "$SERVICE"
|
||||
else
|
||||
warn "ریاستارتِ $SERVICE شکست خورد (شاید sudo رمز میخواهد). خودتان اجرا کنید:
|
||||
sudo systemctl restart $SERVICE"
|
||||
fi
|
||||
else
|
||||
warn "سرویسِ systemd پیدا نشد؛ خودتان ریاستارت کنید، مثلاً:
|
||||
sudo systemctl restart <نامِ سرویس>
|
||||
@@ -145,8 +154,10 @@ fi
|
||||
step "بررسیِ سلامت"
|
||||
printf ' %s\n' "$HEALTH_URL"
|
||||
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
||||
BODY="$(curl -s -m 10 "$HEALTH_URL" 2>/dev/null || true)"
|
||||
CODE="$(curl -s -m 10 -o /dev/null -w '%{http_code}' "$HEALTH_URL" 2>/dev/null || echo 000)"
|
||||
# یک درخواست، نه دو تا: دو فراخوانیِ جدا میتوانست دو وضعیتِ متفاوت ببیند.
|
||||
RESPONSE="$(curl -s -m 10 -w '\n%{http_code}' "$HEALTH_URL" 2>/dev/null || printf '\n000')"
|
||||
CODE="${RESPONSE##*$'\n'}"
|
||||
BODY="${RESPONSE%$'\n'*}"
|
||||
if [ "$CODE" = "200" ]; then
|
||||
printf '%s✓ سرویس سالم بالا آمد.%s\n' "$C_OK" "$C_OFF"
|
||||
printf '%s\n' "$BODY" | python -m json.tool 2>/dev/null | sed 's/^/ /' || printf ' %s\n' "$BODY"
|
||||
|
||||
Reference in New Issue
Block a user