let a deploy repair the server instead of stopping at it
تا حالا اگر کسی روی سرور فایلی را دستی عوض کرده بود، `git pull --ff-only` شکست میخورد و کلِ اسکریپت همانجا میایستاد. چون هیچکدام از مراحلِ بعدی اجرا نمیشد، سرور با کدِ قدیمی میماند — بیصدا. یک بار همین دو روز طول کشید. حالا مرحلهی اول fetch میکند، اگر تغییرِ دستی (یا کامیتِ محلی) باشد آن را در .deploy-backups/*.patch نگه میدارد، و با `git reset --hard origin/<branch>` کنارش میگذارد و ادامه میدهد. سرور در هر وضعیتی باشد، یک اجرا دقیقاً همان چیزی میشود که روی gitea است. `git clean` عمداً زده نمیشود و نباید اضافه شود: db.sqlite3 و .env و .django_secret_key ردیابینشده و داخلِ همین پوشهاند. با مخزنِ ساختگی هر سه حالت آزموده شد: تغییرِ دستی، درختِ تمیز، و کامیتِ محلیِ واگرا. در هر سه، .env و db.sqlite3 دستنخورده ماندند. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,3 +14,6 @@ data/textbooks/*
|
|||||||
|
|
||||||
# خروجیِ collectstatic — تولیدی است، در هر دیپلوی از نو ساخته میشود.
|
# خروجیِ collectstatic — تولیدی است، در هر دیپلوی از نو ساخته میشود.
|
||||||
staticfiles/
|
staticfiles/
|
||||||
|
|
||||||
|
# نسخهی پشتیبانِ تغییرهای دستیِ روی سرور که deploy.sh کنار میگذارد.
|
||||||
|
.deploy-backups/
|
||||||
|
|||||||
34
CLAUDE.md
34
CLAUDE.md
@@ -127,8 +127,38 @@ colourless.
|
|||||||
|
|
||||||
## Operations
|
## Operations
|
||||||
|
|
||||||
`./deploy.sh` is the whole release: pull, install, migrate, collectstatic, restart, then
|
**The whole release, as one line:**
|
||||||
it polls `/api/health` and prints the likely cause if the service does not come back.
|
|
||||||
|
```bash
|
||||||
|
ssh -t administrator@78.38.35.249 'cd ~/mathapp-v2 && ./deploy.sh; sudo systemctl restart zareian-mathapp'
|
||||||
|
```
|
||||||
|
|
||||||
|
It asks for the password twice — once for ssh, once for sudo. **Wait for each prompt**;
|
||||||
|
pasting several lines at once feeds the queued text to the password prompt and locks you
|
||||||
|
out for three tries.
|
||||||
|
|
||||||
|
The trailing restart is separate because `sudo` inside `deploy.sh` cannot prompt for a
|
||||||
|
password. The script no longer pretends that worked: it compares the service's start time
|
||||||
|
before and after and exits non-zero when the restart did not happen, so a half-finished
|
||||||
|
deploy can never again look like a successful one. Giving `administrator` a `NOPASSWD`
|
||||||
|
sudoers line for just `systemctl restart zareian-mathapp` would collapse this to a single
|
||||||
|
command; that is the server admin's call, not ours.
|
||||||
|
|
||||||
|
`./deploy.sh` is the whole release: fetch, reset to `origin/<branch>`, install, migrate,
|
||||||
|
seed content, collectstatic, restart, then it polls `/api/health` and prints the likely
|
||||||
|
cause if the service does not come back.
|
||||||
|
|
||||||
|
**It repairs the server rather than stopping at it.** A hand-edited file used to make
|
||||||
|
`git pull --ff-only` fail, which aborted the whole script — and because every later step
|
||||||
|
was skipped, the server silently stayed on old code. Now local modifications (and local
|
||||||
|
commits) are saved to a timestamped patch under `.deploy-backups/` and then discarded with
|
||||||
|
`git reset --hard origin/<branch>`, and the deploy continues. Whatever state the server is
|
||||||
|
left in, one run puts it back on exactly what Gitea has.
|
||||||
|
|
||||||
|
**`git clean` is never run there and must never be added.** `db.sqlite3`, `.env` and
|
||||||
|
`.django_secret_key` are untracked and live in that directory; `reset --hard` leaves them
|
||||||
|
alone, `clean -fd` would destroy the database, the config and every signed-in session.
|
||||||
|
|
||||||
Settings boot with working defaults (hosts, CSRF, CORS, secret key, cache), so a stale or
|
Settings boot with working defaults (hosts, CSRF, CORS, secret key, cache), so a stale or
|
||||||
partial `.env` on the server no longer breaks startup — env values are *merged with*
|
partial `.env` on the server no longer breaks startup — env values are *merged with*
|
||||||
those defaults, not replaced by them.
|
those defaults, not replaced by them.
|
||||||
|
|||||||
34
deploy.sh
34
deploy.sh
@@ -42,9 +42,39 @@ fi
|
|||||||
# ─── ۱. کدِ تازه ────────────────────────────────────────────────────────────
|
# ─── ۱. کدِ تازه ────────────────────────────────────────────────────────────
|
||||||
if [ "${SKIP_PULL:-0}" != "1" ]; then
|
if [ "${SKIP_PULL:-0}" != "1" ]; then
|
||||||
step "گرفتنِ کدِ تازه"
|
step "گرفتنِ کدِ تازه"
|
||||||
# امضای خودِ این فایل قبل از pull. توضیحش پایینتر.
|
# امضای خودِ این فایل قبل از بهروزرسانی. توضیحش پایینتر.
|
||||||
SELF_BEFORE="$(cksum "$0" 2>/dev/null || true)"
|
SELF_BEFORE="$(cksum "$0" 2>/dev/null || true)"
|
||||||
git pull --ff-only || fail "git pull نشد. اگر تغییرِ محلی دارید اول تعیین تکلیفش کنید."
|
|
||||||
|
# شاخهی فعلی؛ اگر HEAD جدا افتاده باشد main مبنا میشود.
|
||||||
|
BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)"
|
||||||
|
[ "$BRANCH" = "HEAD" ] && BRANCH=main
|
||||||
|
|
||||||
|
git fetch origin "$BRANCH" \
|
||||||
|
|| fail "به gitea وصل نشد. شبکه یا دسترسیِ مخزن را بررسی کنید."
|
||||||
|
|
||||||
|
# اگر کسی روی سرور فایلی را دستی عوض کرده، اول نسخهی پشتیبان، بعد کنار
|
||||||
|
# گذاشتنش.
|
||||||
|
#
|
||||||
|
# چرا خودکار و نه توقف: قبلاً اینجا `git pull --ff-only` بود و با هر تغییرِ
|
||||||
|
# دستی کلِ دیپلوی همانجا میایستاد — و چون بقیهی مراحل اجرا نمیشد، سرور
|
||||||
|
# با کدِ قدیمی میماند بدونِ اینکه کسی بفهمد. دقیقاً همین یک بار دو روز طول
|
||||||
|
# کشید. حالا تغییرِ دستی مانعِ استقرار نیست؛ ذخیره میشود و رد میشویم.
|
||||||
|
if ! git diff --quiet HEAD 2>/dev/null; then
|
||||||
|
mkdir -p .deploy-backups
|
||||||
|
PATCH=".deploy-backups/local-changes-$(date +%Y%m%d-%H%M%S).patch"
|
||||||
|
git diff HEAD > "$PATCH" 2>/dev/null || true
|
||||||
|
warn "روی سرور تغییرِ دستی پیدا شد. در $PATCH ذخیره شد و کنار گذاشته میشود.
|
||||||
|
برای دیدنش: cat $PATCH
|
||||||
|
اگر لازم بود: git apply $PATCH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ⚠️ اینجا عمداً `git clean` زده نمیشود و هرگز هم نباید زده شود:
|
||||||
|
# db.sqlite3 و .env و .django_secret_key هیچکدام در گیت نیستند و داخلِ همین
|
||||||
|
# پوشهاند. یک `git clean -fd` یعنی نابودیِ دیتابیس، تنظیمات، و کلیدی که
|
||||||
|
# همهی نشستهای ورود با آن امضا شدهاند. reset --hard فقط فایلهای ردیابیشده
|
||||||
|
# را برمیگرداند و به اینها کار ندارد.
|
||||||
|
git reset --hard "origin/$BRANCH" \
|
||||||
|
|| fail "برگرداندن به origin/$BRANCH نشد؛ هیچ تغییری اعمال نشد."
|
||||||
|
|
||||||
# اگر همین اسکریپت در pull عوض شد، نسخهی تازه از اول اجرا میشود.
|
# اگر همین اسکریپت در pull عوض شد، نسخهی تازه از اول اجرا میشود.
|
||||||
#
|
#
|
||||||
|
|||||||
Reference in New Issue
Block a user