From 6ec82081b53f7e7bd19a7e26c3985a41d348b172 Mon Sep 17 00:00:00 2001 From: Zareian Date: Fri, 18 Sep 2026 00:41:11 +0330 Subject: [PATCH] stop deploy.sh from pulling a new copy of itself out from under bash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bash اسکریپت را یک‌جا نمی‌خواند؛ حین اجرا از روی دیسک ادامه می‌دهد. پس وقتی `git pull` همین فایل را عوض می‌کند، ادامه‌ی اجرا مخلوطی از نسخه‌ی قدیم و جدید است. یک بار همین اتفاق افتاد و گران تمام شد: نسخه‌ی قدیمی که در حال اجرا بود سرویسِ تیمِ دیگری را ری‌استارت کرد، در حالی که نسخه‌ی اصلاح‌شده همان لحظه روی دیسک نشسته بود. حالا امضای فایل قبل و بعدِ pull مقایسه می‌شود و اگر عوض شده باشد، نسخه‌ی تازه از اول اجرا می‌شود (با SKIP_PULL=1 و یک نگهبان در برابرِ حلقه‌ی بی‌پایان). و یک حفره‌ی کوچک‌ترِ هم‌خانواده: اگر زمانِ بالا آمدنِ سرویس اصلاً خوانده نشود، دیگر موفقیت فرض نمی‌شود. «نمی‌دانم» همان چیزی بود که دو روز پنهانش کرد. Co-Authored-By: Claude Opus 5 --- deploy.sh | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/deploy.sh b/deploy.sh index d0b95a1..6a4d2e9 100755 --- a/deploy.sh +++ b/deploy.sh @@ -42,7 +42,26 @@ fi # ─── ۱. کدِ تازه ──────────────────────────────────────────────────────────── if [ "${SKIP_PULL:-0}" != "1" ]; then step "گرفتنِ کدِ تازه" + # امضای خودِ این فایل قبل از pull. توضیحش پایین‌تر. + SELF_BEFORE="$(cksum "$0" 2>/dev/null || true)" git pull --ff-only || fail "git pull نشد. اگر تغییرِ محلی دارید اول تعیین تکلیفش کنید." + + # اگر همین اسکریپت در pull عوض شد، نسخه‌ی تازه از اول اجرا می‌شود. + # + # چرا لازم است: bash اسکریپت را یک‌جا نمی‌خواند، حین اجرا از روی دیسک ادامه + # می‌دهد. پس وقتی pull همین فایل را زیر پای خودش عوض می‌کند، ادامه‌ی اجرا + # مخلوطی از نسخه‌ی قدیم و جدید می‌شود. یک بار همین اتفاق افتاد: نسخه‌ی قدیمی + # که هنوز در حال اجرا بود سرویسِ تیمِ دیگری را ری‌استارت کرد، در حالی که + # نسخه‌ی اصلاح‌شده همان لحظه روی دیسک نشسته بود. + # + # DEPLOY_REEXEC جلوی حلقه‌ی بی‌پایان را می‌گیرد: نسخه‌ی تازه دیگر خودش را + # صدا نمی‌زند چون بعد از pullِ دوم دیگر چیزی عوض نمی‌شود. + if [ "${DEPLOY_REEXEC:-0}" != "1" ] \ + && [ -n "$SELF_BEFORE" ] \ + && [ "$SELF_BEFORE" != "$(cksum "$0" 2>/dev/null || true)" ]; then + printf ' این اسکریپت خودش به‌روز شد؛ نسخه‌ی تازه از اول اجرا می‌شود.\n' + DEPLOY_REEXEC=1 SKIP_PULL=1 exec "$0" "$@" + fi else step "گرفتنِ کد رد شد (SKIP_PULL=1)" fi @@ -175,7 +194,12 @@ if [ -n "$SERVICE" ]; then BEFORE="$(systemctl show "$SERVICE" -p ActiveEnterTimestamp --value 2>/dev/null || true)" if sudo systemctl restart "$SERVICE"; then AFTER="$(systemctl show "$SERVICE" -p ActiveEnterTimestamp --value 2>/dev/null || true)" - if [ -n "$BEFORE" ] && [ "$BEFORE" = "$AFTER" ]; then + if [ -z "$BEFORE" ] || [ -z "$AFTER" ]; then + # نتوانستیم ثابت کنیم ری‌استارت شده. «نمی‌دانم» را موفقیت حساب نمی‌کنیم — + # همین اشتباه بود که دو روز پنهان ماند. + warn "زمانِ بالا آمدنِ $SERVICE خوانده نشد، پس ری‌استارت تأیید نشد. + خودتان بررسی کنید: systemctl status $SERVICE" + elif [ "$BEFORE" = "$AFTER" ]; then warn "دستورِ ری‌استارت خطا نداد ولی سرویس بالا نیامد (هنوز از $BEFORE در حال اجراست). یعنی کدِ جدید بار نشده. لاگ: sudo journalctl -u $SERVICE -n 50 --no-pager" else