CI/CD — خط التسليم الآلي بـ GitHub Actions
CI/CD مثل خط تجميع آلي في مصنع سيارات — تدخل القطع (الكود)، وتمر عبر اللحام (lint)، وفحص الجودة (الاختبار)، والطلاء (الفحص الأمني)، وتُسلم المنتجات النهائية (صور Docker) التي تجتاز الفحص تلقائيًا إلى الوكالة (بيئة الإنتاج).
1. ما ستتعلمه
- أساسيات GitHub Actions: مفاهيم Workflow و Job و Step و Action
- خط CI: lint ← pytest ← فحص أمني ← بناء Docker
- خط CD: دفع الصورة ← Docker Hub/GHCR ← نشر الخادم
- إدارة البيئات: استراتيجية الفروع لـ dev / staging / production
- سيناريو Alice: يُرسل Bob طلب سحب ← اختبار آلي ← يدمج Charlie ← نشر آلي
2. القصة الحقيقية لـ Alice
(1) نقطة الألم: النشر اليدوي يؤدي غالبًا إلى أخطاء
بعد أن أرسل Bob الكود، جلبت Alice الكود يدويًا من الخادم، وشغلت الاختبارات، وبنت الصورة، وأعادت تشغيل الحاوية. هذه العملية تستغرق 30 دقيقة في كل مرة، وكثيرًا ما كانت تتخطى خطوات الاختبار — في إحدى المرات، نسيت Alice تشغيل الترحيلات، ونقطة النهاية الجديدة أبلغت عن خطأ 500 عبر الإنترنت. قال Charlie إنه يستطيع أداء هذه المهام يدويًا في كل من البيئات الثلاث (dev و staging و prod)، لكن الخطر كان كبيرًا جدًا.
(2) حل باستخدام GitHub Actions
يشغل GitHub Actions الخط الكامل تلقائيًا — lint ← اختبار ← بناء ← نشر — مع كل دفع أو طلب سحب. جميع الخطوات مُعرّفة بالكود، لذا لا يمكن إغفال أي شيء، والفشل يحظر الدمج تلقائيًا.
(3) العائد
انخفض وقت النشر من 30 دقيقة (يدوي) إلى 5 دقائق (آلي)، وتم القضاء تمامًا على مشكلة الاختبارات المتخطاة (طلبات السحب التي تفشل اختباراتها لا يمكن دمجها)، مما يضمن أن النشر عبر جميع البيئات الثلاث متطابق تمامًا.
3. أساسيات GitHub Actions
(1) المفاهيم الأساسية
flowchart LR
Trigger[مشغل: push/PR] --> Workflow[Workflow]
Workflow --> Job1[Job 1: Lint+اختبار]
Workflow --> Job2[Job 2: بناء]
Job1 --> Step1[Step: ruff check]
Job1 --> Step2[Step: pytest]
Job2 --> Step3[Step: docker build]
Job2 --> Step4[Step: docker push]
| المفهوم | الوصف | مثال |
|---|---|---|
| Workflow | تعريف العملية الآلية | .github/workflows/ci.yml |
| Trigger | شرط التشغيل | push، pull_request |
| Job | وحدة تنفيذ تتكون من مجموعة خطوات | test، build، deploy |
| Step | عملية واحدة | run: pytest |
| Action | إجراءات قابلة لإعادة الاستخدام | actions/checkout@v4 |
| Runner | بيئة التشغيل | ubuntu-latest |
(2) تعيين الفروع والبيئات
graph TD
Feature[feature/*] -->|PR| Develop[develop]
Develop -->|نشر| DevEnv[بيئة Dev]
Develop -->|PR| Main[main]
Main -->|نشر| Staging[بيئة Staging]
Main -->|Tag v*| Prod[بيئة الإنتاج]
| الفرع | البيئة | المشغل | طريقة النشر |
|---|---|---|---|
feature/* |
- | اختبار آلي لـ PR | غير منشور |
develop |
Dev | دفع تلقائي | Docker Compose |
main |
Staging | دفع تلقائي | Docker Compose |
v* tag |
الإنتاج | tag يدوي | Docker Compose / K8s |
4. خط CI
(1) ▶مثال: خط CI كامل
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: تثبيت UV
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: تثبيت التبعيات
run: uv sync --frozen
- name: تشغيل ruff lint
run: uv run ruff check app/ tests/
- name: تشغيل فحص تنسيق ruff
run: uv run ruff format --check app/ tests/
test:
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: pricetracker
POSTGRES_PASSWORD: test_password
POSTGRES_DB: pricetracker_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: تثبيت UV
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: تثبيت التبعيات
run: uv sync --frozen
- name: تشغيل pytest
env:
DATABASE_URL: postgresql+asyncpg://pricetracker:test_password@localhost:5432/pricetracker_test
REDIS_URL: redis://localhost:6379/0
SECRET_KEY: test-secret-key-for-ci
run: uv run pytest tests/ -v --cov=app --cov-report=xml
- name: رفع التغطية
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
security:
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- name: تشغيل فحص safety
run: |
pip install safety
safety check --json || true
- name: تشغيل bandit
run: |
pip install bandit
bandit -r app/ -f json || true
build:
runs-on: ubuntu-latest
needs: [test, security]
steps:
- uses: actions/checkout@v4
- name: إعداد Docker Buildx
uses: docker/setup-buildx-action@v3
- name: بناء صورة Docker
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
push: false
tags: pricetracker:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
الناتج:
ext CI/CD pipeline loaded Pipeline status: passed Tests: 12 passed, 0 failed
5. خط CD
(1) ▶مثال: خط نشر الإنتاج
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: إعداد Docker Buildx
uses: docker/setup-buildx-action@v3
- name: تسجيل الدخول إلى GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: بناء ودفع الصورة
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: النشر إلى الخادم
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/pricetracker
docker compose pull
docker compose up -d --remove-orphans
docker compose exec api alembic upgrade head
echo "Deployed version ${{ github.ref_name }}"
الناتج:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
(2) خط تدفق CI/CD الكامل
flowchart LR
Push[يدفع Bob الكود] --> Lint[Lint: ruff]
Lint --> Test[اختبار: pytest]
Lint --> Sec[أمان: safety + bandit]
Test --> Build[بناء Docker]
Sec --> Build
Build -->|عند tag| Push[دفع إلى GHCR]
Push --> Deploy[نشر إلى الإنتاج]
Deploy --> Health[فحص الصحة]
Health --> Done[✓ مباشر]
(2) ▶مثال: حماية الفروع وقواعد حماية البيئات
# .github/workflows/branch-protection.yml
name: Branch Protection Check
on:
pull_request:
types: [opened, synchronize]
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: التحقق من أن هدف PR هو main
run: |
if [ "${{ github.base_ref }}" != "main" ]; then
echo "PR must target main branch"
exit 1
fi
- name: فحص موافقة البيئة
if: github.event.pull_request.merged == true
uses: octokit/request-action@v2
with:
route: POST /repos/{owner}/{repo}/deployments
environment: staging
required_reviewers: 1
الناتج:
CI/CD pipeline loaded
Pipeline status: passed
Tests: 12 passed, 0 failed
❓أسئلة شائعة
Base.metadata.create_all() لإنشاء الجداول مباشرة (بدون Alembic). شغّل alembic upgrade head في سكريبت نشر CD.SERVER_SSH_KEY و DB_PASSWORD وغيرها هنا، ويمكنك الإشارة إليها في YAML باستخدام ${{ secrets.XXX }}.on: push: tags: ['v*'] لتشغيل النشر فقط عند إنشاء tag. على فرع التطوير، شغّل الاختبارات فقط — لا تنشر.cache-from: type=gha للاستفادة من تخزين GitHub Actions المؤقت. البناء الأول يستغرق 5 دقائق؛ التغييرات اللاحقة تعيد بناء الطبقات المعدلة فقط، وتستغرق حوالي 1-2 دقيقة.docker compose down + docker compose pull <previous-tag> + docker compose up -d. احتفظ بعلامة الصورة من الإصدار السابق لتسهيل التراجع.📖ملخص
- يتكون GitHub Actions من هيكل رباعي المستويات: Workflow ← Job ← Step ← Action
- خط CI: lint (ruff) ← اختبار (pytest + حاويات خدمة PostgreSQL/Redis) ← أمان ← بناء
- خط CD: مشغل tag ← بناء صورة Docker ← دفع إلى GHCR ← نشر إلى الخادم عبر SSH
- استراتيجية الفروع: PR للميزات للاختبار، develop لنشر Dev، main لنشر Staging، علامات v* لنشر الإنتاج
- تخزين طبقات Docker المؤقت + تخزين GitHub Actions المؤقت لتسريع البناء؛ Secrets لإدارة المفاتيح الآمنة
📝تمارين
- تمرين أساسي (الصعوبة ⭐): أنشئ
.github/workflows/ci.yml. إعداده بحيث يعملruff checkوpytestتلقائيًا عند الدفع أو إنشاء PR، وتحقق من أن صفحة GitHub Actions تظهر علامة "Passed" خضراء. تلميح:on: push: branches: [main] - تمرين متقدم (الصعوبة ⭐⭐): أضف حاويات خدمة PostgreSQL و Redis إلى مهمة الاختبار، إعداد متغيرات البيئة بحيث يمكن لـ pytest الاتصال بقاعدة بيانات الاختبار، وأضف خطوة بناء Docker للتحقق من نجاح بناء الصورة. تلميح:
services:+options: --health-cmd - تحدٍ (الصعوبة: ⭐⭐⭐): أكمل CI/CD — خط CI يتضمن lint واختبار وفحص أمني وبناء؛ خط CD يدفع الصورة إلى GHCR عند إنشاء tag
v*وينشرها إلى الخادم عبر SSH، باستخدام GitHub Secrets لتخزين المفاتيح. تلميح:docker/login-action+appleboy/ssh-action+${{ secrets.XXX }}
---|



