Preview Environments بالعربي: اختبر كل Pull Request قبل الدمج
لو كل Pull Request عندك بيتراجع بالعين فقط، المقال ده هيديك طريقة تشغل نسخة مؤقتة من التطبيق وتكشف مشاكل التكامل قبل الدمج.
المشكلة باختصار
اللي بيحصل فعلاً إن المراجعة بتشوف naming، structure، وربما test unit بسيط. لكن أول مرة الـ frontend يكلم الـ API والـ API يكلم PostgreSQL بتكون بعد الدمج أو على staging. هنا الباج بيبقى أغلى.
الافتراض إن عندك تطبيق صغير أو متوسط: API، قاعدة بيانات، وربما frontend. أقل من 20 خدمة. لو عندك منصة Kubernetes كبيرة، نفس الفكرة تنفع، لكن التنفيذ هيبقى بأدوات مختلفة.
الفكرة ببساطة
Preview Environment يعني نسخة مؤقتة من التطبيق مرتبطة بـ Pull Request معين. افتح PR رقم 42، يتبني image، يتشغل compose project باسم مختلف، وبعدها يتعمل smoke test. لو الاختبار فشل، الـ PR يتوقف قبل ما يلمس main.
ركز في النقطة دي: الهدف مش تعمل staging ثاني دائم. الهدف إنك تعمل بيئة قصيرة العمر. مثل مكتب صغير بيجهز عينة من المنتج للعميل، وبعد الموافقة يرجع يفكها. التكلفة وقت runner ومساحة Docker، والمكسب إنك تكشف كسر التكامل قبل الدمج.
في سيناريو واقعي، فريق عنده 8 مطورين و12 Pull Request يوميًا. لو كل PR بياخد 5 دقائق Preview وSmoke Test، هتدفع تقريبًا 60 دقيقة runner يوميًا. مقابل ده، ممكن تمنع incident واحد أسبوعيًا كان بياخد ساعتين debug على staging.
إعداد Docker Compose لبيئة مؤقتة
أفضل طريقة للمشاريع الصغيرة: استخدم Docker Compose project name مختلف لكل PR. كده الشبكات والـ volumes تتعزل بدل ما الخدمات تدوس على بعض.
# compose.preview.yml
services:
api:
build: .
environment:
DATABASE_URL: postgres://app:app@db:5432/app
depends_on:
db:
condition: service_healthy
ports:
- "8080"
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: app
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d app"]
interval: 5s
timeout: 3s
retries: 10
smoke:
image: curlimages/curl:8.7.1
depends_on:
api:
condition: service_started
command: ["sh", "-c", "curl -fsS http://api:3000/health"]
profiles: ["test"]
الـ healthcheck هنا مش رفاهية. Docker Compose يدعم انتظار الخدمة التي عليها service_healthy قبل تشغيل الخدمة التابعة لها. ده يمنع حالة شائعة: الـ API يبدأ قبل PostgreSQL بثانيتين، فيفشل الاختبار مع إن الكود سليم.
GitHub Actions Workflow قابل للنسخ
استخدم workflow بسيط على حدث pull_request. الرقم المهم هنا هو COMPOSE_PROJECT_NAME. بدل ما كل PR يستخدم نفس الشبكة، كل PR ياخد namespace منفصل.
# .github/workflows/preview.yml
name: preview-smoke-test
on:
pull_request:
branches: [main]
concurrency:
group: preview-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
preview:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
COMPOSE_PROJECT_NAME: pr-${{ github.event.pull_request.number }}
steps:
- uses: actions/checkout@v4
- name: Build preview stack
run: docker compose -f compose.preview.yml up -d --build api db
- name: Run smoke test
run: docker compose -f compose.preview.yml --profile test run --rm smoke
- name: Show logs on failure
if: failure()
run: docker compose -f compose.preview.yml logs --no-color
- name: Cleanup
if: always()
run: docker compose -f compose.preview.yml down -v --remove-orphans
لو الـ build بياخد 3 دقائق والـ smoke test بياخد 30 ثانية، خلي timeout-minutes عند 10 دقائق. الرقم ده عملي: يكشف التعليق بدل ما runner يفضل شغال ساعة كاملة.
الـ trade-off هنا
هتكسب signal أقوى من unit tests وحدها. الاختبار بيشغل database حقيقية، network حقيقي، وcontainer startup حقيقي. هتخسر وقت CI ومساحة مؤقتة على runner. لو عندك 30 PR في اليوم، دقيقة زيادة لكل PR معناها 30 دقيقة runner يوميًا.
الطريقة دي بتفشل لو استخدمتها كبديل لاختبارات الوحدة. Preview Environment ممتازة لاكتشاف مشاكل الربط، migration ناقصة، env var غلط، أو endpoint ميت. لكنها مش مكان مناسب لاختبار كل branch منطقي في الكود.
تقدر تقلل التكلفة بتشغيلها على PRs التي تغير ملفات مؤثرة فقط. مثال: لو التغيير داخل docs، لا تشغل preview. لو التغيير داخل src/ أو compose.preview.yml، شغلها.
متى لا تستخدم هذه الطريقة
لا تستخدم Docker Compose Preview لو التطبيق محتاج خدمات سحابية حقيقية لا يمكن محاكاتها، أو لو عندك 50 microservice بتحتاج شبكة داخلية معقدة. في الحالة دي استخدم namespace مؤقت في Kubernetes أو منصة preview مخصصة.
لا تستخدمها أيضًا لو زمن الـ build أكبر من 15 دقيقة ولا يوجد cache. ركز الأول على تحسين build cache، وبعدها أضف preview. غير كده هتزود friction على كل PR.
مصادر اعتمد عليها المقال
- GitHub Docs: Deploying with GitHub Actions
- GitHub Docs: Managing environments for deployment
- Docker Docs: Control startup order
- Docker Docs: Use service profiles
الخطوة التالية
افتح repo واحد عندك، أضف compose.preview.yml والـ workflow فوق، وخلي أول smoke test يضرب /health فقط. لو نجح 10 مرات متتالية، زوّد اختبار endpoint واحد يعتمد على قاعدة البيانات.