Preview Environments: جرّب كل Pull Request قبل الدمج
هتكسب من المقال ده طريقة عملية تشغّل نسخة مؤقتة لكل Pull Request بدل ما تدمج وبعدين تكتشف إن الصفحة مكسورة. مستوى القارئ: متوسط، ومناسب لأي فريق عنده CI بسيط وبيستخدم Docker.
المشكلة باختصار
الطريقة الشائعة إنك تراجع الكود، تشوف سكرينشوت من المطور، وبعدين تعتمد على staging واحد مشترك. الطريقة دي بتفشل لما 3 فروع يتراجعوا في نفس الوقت. آخر واحد يعمل deploy على staging يكسر تجربة الباقي.
الـ Preview Environment هو بيئة مؤقتة مربوطة بـ Pull Request واحد. كأنك بتفتح شقة صغيرة لكل ضيف بدل ما كل الضيوف يدخلوا نفس الأوضة. المثال بسيط: PR رقم 42 يأخذ URL خاص، قاعدة بيانات مؤقتة، ونسخة app بنفس إعدادات الإنتاج تقريبًا. بعد إغلاق الـ PR، البيئة تتمسح.
اللي بيحصل فعلاً في فرق صغيرة
افترض إن عندك SaaS صغير بـ 8 مطورين و20 Pull Request في الأسبوع. لو كل مراجعة UI بتاخد 15 دقيقة setup محلي، فأنت بتصرف حوالي 5 ساعات أسبوعيًا على تشغيل الفروع يدويًا. Preview Environments تقلل الرقم ده غالبًا إلى دقيقة أو دقيقتين لفتح الرابط وتجربة السيناريو.
الافتراض هنا إن التطبيق يشتغل بـ Docker Compose، وعدد الخدمات أقل من 6، والـ traffic أثناء المراجعة ضعيف. لو عندك Kubernetes بالفعل، نفس الفكرة تنفع، لكن الأدوات هتختلف.
أفضل طريقة تبدأ بها بدون منصة كبيرة
ابدأ بـ GitHub Actions وDocker Compose على سيرفر مراجعة واحد. مش لازم تبدأ بـ ArgoCD أو Terraform لو الفريق لسه بيتعلم. المكسب: إعداد أسرع وتكلفة أقل. الخسارة: isolation أضعف من Kubernetes، ومحتاج تنظف البيئات القديمة بصرامة.
ركز في الفكرة: اسم المشروع يتبني من رقم الـ PR. كل PR يعمل stack منفصل، مثل app-pr-42. Docker Compose يحط containers وnetwork وvolumes تحت نفس الاسم، فالمراجعة تبقى معزولة بالقدر الكافي لفريق صغير.
مثال تنفيذي بـ GitHub Actions
ضع الملف ده في .github/workflows/preview.yml. المثال مبني على إن عندك سيرفر Linux عليه Docker، وGitHub Actions يقدر يدخل عليه بـ SSH key محفوظ في secrets.
name: preview-environment
on:
pull_request:
types: [opened, synchronize, reopened, closed]
jobs:
deploy-preview:
runs-on: ubuntu-latest
if: github.event.action != 'closed'
steps:
- uses: actions/checkout@v4
- name: Copy files to preview server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.PREVIEW_HOST }}
username: ${{ secrets.PREVIEW_USER }}
key: ${{ secrets.PREVIEW_SSH_KEY }}
source: "docker-compose.yml,src,package.json,package-lock.json"
target: "/srv/previews/pr-${{ github.event.pull_request.number }}"
- name: Start preview stack
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PREVIEW_HOST }}
username: ${{ secrets.PREVIEW_USER }}
key: ${{ secrets.PREVIEW_SSH_KEY }}
script: |
cd /srv/previews/pr-${{ github.event.pull_request.number }}
export COMPOSE_PROJECT_NAME=app_pr_${{ github.event.pull_request.number }}
export APP_PORT=$((5000 + ${{ github.event.pull_request.number }}))
docker compose up -d --build
echo "Preview URL: http://${{ secrets.PREVIEW_HOST }}:${APP_PORT}"
cleanup-preview:
runs-on: ubuntu-latest
if: github.event.action == 'closed'
steps:
- name: Remove preview stack
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.PREVIEW_HOST }}
username: ${{ secrets.PREVIEW_USER }}
key: ${{ secrets.PREVIEW_SSH_KEY }}
script: |
cd /srv/previews/pr-${{ github.event.pull_request.number }} || exit 0
export COMPOSE_PROJECT_NAME=app_pr_${{ github.event.pull_request.number }}
docker compose down -v --remove-orphans
rm -rf /srv/previews/pr-${{ github.event.pull_request.number }}