404 Not Found

404 Not Found


nginx

CI/CD — خط التسليم الآلي بـ GitHub Actions

CI/CD مثل خط تجميع آلي في مصنع سيارات — تدخل القطع (الكود)، وتمر عبر اللحام (lint)، وفحص الجودة (الاختبار)، والطلاء (الفحص الأمني)، وتُسلم المنتجات النهائية (صور Docker) التي تجتاز الفحص تلقائيًا إلى الوكالة (بيئة الإنتاج).

1. ما ستتعلمه


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) المفاهيم الأساسية

100%
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) تعيين الفروع والبيئات

100%
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 كامل

YAML
# .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) ▶مثال: خط نشر الإنتاج

YAML
# .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 }}"

الناتج:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) خط تدفق CI/CD الكامل

100%
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) ▶مثال: حماية الفروع وقواعد حماية البيئات

YAML
# .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

الناتج:

TEXT
CI/CD pipeline loaded
Pipeline status: passed
Tests: 12 passed, 0 failed

❓أسئلة شائعة

س هل الحصة المجانية لـ GitHub Actions كافية؟
ج لا توجد حدود للمستودعات العامة. المستودعات الخاصة تحصل على 2,000 دقيقة مجانية شهريًا. خط CI لـ PriceTracker يستغرق حوالي 5 دقائق لكل تشغيل؛ مع 10 تشغيلات يوميًا، يكون المجموع حوالي 1,500 دقيقة، وهو كافٍ.
س كيف يجب التعامل مع ترحيلات قاعدة البيانات في CI؟
ج في اختبارات CI، استخدم Base.metadata.create_all() لإنشاء الجداول مباشرة (بدون Alembic). شغّل alembic upgrade head في سكريبت نشر CD.
س كيف تُخزن الأسرار بأمان؟
ج في مشروع GitHub الخاص بك، اذهب إلى Settings ← Secrets and variables ← Actions. تُخزن SERVER_SSH_KEY و DB_PASSWORD وغيرها هنا، ويمكنك الإشارة إليها في YAML باستخدام ${{ secrets.XXX }}.
س كيف أنشر فقط على الفرع main؟
ج استخدم on: push: tags: ['v*'] لتشغيل النشر فقط عند إنشاء tag. على فرع التطوير، شغّل الاختبارات فقط — لا تنشر.
س كيف أستفيد من تخزين Docker المؤقت؟
ج استخدم cache-from: type=gha للاستفادة من تخزين GitHub Actions المؤقت. البناء الأول يستغرق 5 دقائق؛ التغييرات اللاحقة تعيد بناء الطبقات المعدلة فقط، وتستغرق حوالي 1-2 دقيقة.
س كيف أتراجع بعد فشل النشر؟
ج docker compose down + docker compose pull <previous-tag> + docker compose up -d. احتفظ بعلامة الصورة من الإصدار السابق لتسهيل التراجع.

📖ملخص


📝تمارين

  1. تمرين أساسي (الصعوبة ⭐): أنشئ .github/workflows/ci.yml. إعداده بحيث يعمل ruff check و pytest تلقائيًا عند الدفع أو إنشاء PR، وتحقق من أن صفحة GitHub Actions تظهر علامة "Passed" خضراء. تلميح: on: push: branches: [main]
  2. تمرين متقدم (الصعوبة ⭐⭐): أضف حاويات خدمة PostgreSQL و Redis إلى مهمة الاختبار، إعداد متغيرات البيئة بحيث يمكن لـ pytest الاتصال بقاعدة بيانات الاختبار، وأضف خطوة بناء Docker للتحقق من نجاح بناء الصورة. تلميح: services: + options: --health-cmd
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أكمل CI/CD — خط CI يتضمن lint واختبار وفحص أمني وبناء؛ خط CD يدفع الصورة إلى GHCR عند إنشاء tag v* وينشرها إلى الخادم عبر SSH، باستخدام GitHub Secrets لتخزين المفاتيح. تلميح: docker/login-action + appleboy/ssh-action + ${{ secrets.XXX }}

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%