Prepare secure config and reliable workout sync

This commit is contained in:
Hamza-Ayed
2026-10-04 00:33:44 +03:00
parent 05f1c9ec6b
commit e7f3c777d1
17 changed files with 553 additions and 119 deletions
+2 -2
View File
@@ -40,12 +40,12 @@ DEPLOY_DRY_RUN=1 bash deploy/sync-to-server.sh
bash deploy/sync-to-server.sh
```
السكريبت يرفض شجرة عمل غير نظيفة، ويتأكد أن `HEAD` يطابق آخر commit منشور للفرع نفسه في Git. يستخدم SSH بتهيئة غير تفاعلية، ثم يجلب ذلك الفرع على الخادم، يستخرج commit إلى مجلد إصدار منفصل، ويفحص بناء PHP نحويًا إن كان PHP CLI متوفرًا. أخيرًا يحول رابط `current` إلى الإصدار الجديد.
السكريبت يرفض شجرة عمل غير نظيفة، ويتأكد أن `HEAD` يطابق آخر commit منشور للفرع نفسه في Git. يستخدم SSH بتهيئة غير تفاعلية، ثم يجلب ذلك الفرع على الخادم، يستخرج commit إلى مجلد مؤقت، ويفحص بناء PHP نحويًا إن كان PHP CLI متوفرًا. عند نجاح التحقق ينقل النسخة إلى مجلد الإصدار ويحوّل رابط `current` إليها. الفشل قبل ذلك يزيل مجلد الاستخراج المؤقت ويترك النسخة الحالية كما هي.
أول تشغيل يحتاج مفتاح SSH صالحًا للوصول إلى Site User عبر المنفذ 2101 ومفتاح Git صالحًا على الخادم. مفاتيح SSH لا تمرر كمتغير ولا تحفظ في هذا المستودع.
## الاسترجاع
كل إصدار محفوظ تحت `<DEPLOY_ROOT>/releases/<commit-sha>`. عند الحاجة، أنشئ رابطًا مؤقتًا إلى مجلد الإصدار السابق ثم استبدل `current` ذريًا من جلسة SSH الخاصة بـSite User. لا تحذف مجلدات الإصدار السابقة أثناء فترة المراجعة. استرجاع ملفات التطبيق لا يسترجع قاعدة البيانات؛ migrations تحتاج سياسة رجوع منفصلة ونسخة احتياطية مختبرة.
كل إصدار محفوظ تحت `<DEPLOY_ROOT>/releases/<commit-sha>`. إعادة نشر commit محفوظ تعيد تفعيل نفس الإصدار، ما يسمح بالاسترجاع إلى نسخة سابقة. لا تحذف مجلدات الإصدارات أثناء فترة المراجعة. استرجاع ملفات التطبيق لا يسترجع قاعدة البيانات؛ migrations تحتاج سياسة رجوع منفصلة ونسخة احتياطية مختبرة.
لا ينفذ السكربت أوامر SQL أو يغيّر صلاحيات قاعدة البيانات أو ينشئ مستخدمين. بعد إضافة migrations، تُدار كخطوة إصدار مراجعة ومختبرة، ولا تُنفّذ تلقائيًا من آلية النشر قبل تحديد سياسة التراجع.
+30 -11
View File
@@ -71,33 +71,52 @@ release_dir="$releases_dir/$expected_sha"
mkdir -p "$deploy_dir" "$releases_dir" "$shared_dir"
chmod 700 "$deploy_dir" "$shared_dir"
[[ -f "$shared_dir/.env" ]] || die "missing $shared_dir/.env; create it privately before the first deploy"
[[ -f "$shared_dir/.env" && ! -L "$shared_dir/.env" ]] || die "missing private regular file $shared_dir/.env; create it before the first deploy"
chmod 600 "$shared_dir/.env"
if [[ ! -d "$repo_dir" ]]; then
git init --bare --quiet "$repo_dir"
git --git-dir="$repo_dir" remote add origin "$repo_url"
else
configured_remote="$(git --git-dir="$repo_dir" remote get-url origin 2>/dev/null || true)"
if [[ -z "$configured_remote" ]]; then
git --git-dir="$repo_dir" remote add origin "$repo_url"
elif [[ "$configured_remote" != "$repo_url" ]]; then
die 'server-side repository origin does not match the local origin'
fi
fi
git --git-dir="$repo_dir" fetch --quiet --no-tags origin \
"+refs/heads/$ref:refs/remotes/origin/$ref"
actual_sha="$(git --git-dir="$repo_dir" rev-parse "refs/remotes/origin/$ref")"
[[ "$actual_sha" == "$expected_sha" ]] || die 'origin moved during deploy; rerun using the new pushed commit'
[[ ! -e "$release_dir" ]] || die "release already exists: $release_dir"
mkdir "$release_dir"
git --git-dir="$repo_dir" archive "$actual_sha" | tar -x -C "$release_dir"
[[ -d "$release_dir/public" ]] || die 'release has no public/ web root; configure the app layout before deployment'
if [[ -e "$release_dir" ]]; then
[[ -d "$release_dir/public" ]] || die "existing release is incomplete: $release_dir"
[[ -L "$release_dir/.env" ]] || die "existing release has no private environment link: $release_dir"
[[ "$(readlink -f "$release_dir/.env")" == "$(readlink -f "$shared_dir/.env")" ]] || die 'existing release points to a different environment file'
else
staging_dir="$(mktemp -d "$releases_dir/.staging-$expected_sha.XXXXXX")"
trap 'rm -rf -- "$staging_dir"' EXIT
git --git-dir="$repo_dir" archive "$actual_sha" | tar -x -C "$staging_dir"
[[ -d "$staging_dir/public" ]] || die 'release has no public/ web root; configure the app layout before deployment'
[[ ! -e "$staging_dir/.env" && ! -L "$staging_dir/.env" ]] || die 'release must not contain a tracked .env file'
ln -s "$shared_dir/.env" "$staging_dir/.env"
if command -v php >/dev/null 2>&1 && [[ -d "$release_dir/backend" ]]; then
while IFS= read -r -d '' php_file; do
php -l "$php_file" >/dev/null || die "PHP syntax check failed: $php_file"
done < <(find "$release_dir/backend" -type f -name '*.php' -print0)
if command -v php >/dev/null 2>&1 && [[ -d "$staging_dir/backend" ]]; then
while IFS= read -r -d '' php_file; do
php -l "$php_file" >/dev/null || die "PHP syntax check failed: $php_file"
done < <(find "$staging_dir/backend" -type f -name '*.php' -print0)
fi
mv "$staging_dir" "$release_dir"
trap - EXIT
fi
printf '%s\n' "$domain" > "$shared_dir/.site-domain"
ln -s "$release_dir" "$root/current.next"
mv -Tf "$root/current.next" "$root/current"
next_link="$root/.current-$expected_sha-$$"
ln -s "$release_dir" "$next_link"
mv -Tf "$next_link" "$root/current"
printf 'Published %s to %s\n' "$actual_sha" "$root/current"
printf 'CloudPanel document root must point to: %s/current/public\n' "$root"