البرمجة متوسط 13 دقيقة

كيفية إعداد CI/CD باستخدام GitHub Actions

تلتقط بيبلاين CI/CD الـ commits المعطوبة قبل وصولها للإنتاج وتنشر الكود الناجح تلقائياً. GitHub Actions مجاني للمستودعات العامة وسخي للخاصة، وملف YAML للـ workflow يسكن في مستودعك جنباً إلى جنب مع الكود الذي يختبره. هذا الدليل يبني بيبلاين من وظيفتين: الاختبارات أولاً، والنشر فقط إن نجحت.

الخطوات

  1. 1

    أنشئ مجلد الـ workflow والملف

    يبحث GitHub Actions عن ملفات الـ workflow في .github/workflows/. أي ملف .yml هناك يُلتقط تلقائياً. ملف واحد = workflow واحد.

    bash
    mkdir -p .github/workflows
    touch .github/workflows/deploy.yml
  2. 2

    اكتب trigger ووظيفة الاختبار

    شغَّل عند الـ push إلى main. وظيفة test تسحب الكود وتثبّت التبعيات وتشغّل مجموعة الاختبارات. هذا المثال يستخدم PHP/Laravel — استبدل action الإعداد وأمر التثبيت لـ Node أو Python وغيرها.

    yaml
    name: CI/CD
    
    on:
      push:
        branches: [main]
      pull_request:
        branches: [main]
    
    jobs:
      test:
        runs-on: ubuntu-latest
    
        services:
          mysql:
            image: mysql:8.4
            env:
              MYSQL_ROOT_PASSWORD: secret
              MYSQL_DATABASE: laravel_test
            ports:
              - 3306:3306
            options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    
        steps:
          - uses: actions/checkout@v4
    
          - name: Set up PHP
            uses: shivammathur/setup-php@v2
            with:
              php-version: "8.3"
              extensions: mbstring, pdo_mysql, zip, bcmath
              coverage: none
    
          - name: Cache Composer packages
            uses: actions/cache@v4
            with:
              path: vendor
              key: ${{ runner.os }}-php-${{ hashFiles("**/composer.lock") }}
              restore-keys: ${{ runner.os }}-php-
    
          - name: Install dependencies
            run: composer install --no-dev --optimize-autoloader --no-interaction
    
          - name: Copy .env
            run: cp .env.example .env && php artisan key:generate
    
          - name: Run tests
            env:
              DB_CONNECTION: mysql
              DB_HOST: 127.0.0.1
              DB_PORT: 3306
              DB_DATABASE: laravel_test
              DB_USERNAME: root
              DB_PASSWORD: secret
            run: php artisan test --parallel
  3. 3

    أضف وظيفة النشر عبر SSH

    تعمل وظيفة deploy فقط إن نجحت test (needs: test) وعند الـ push إلى main فقط (لا عند الـ PRs). تتصل بخادمك عبر SSH وتشغّل أوامر النشر.

    yaml
      deploy:
        runs-on: ubuntu-latest
        needs: test
        if: github.ref == "refs/heads/main" && github.event_name == "push"
    
        steps:
          - name: Deploy to server
            uses: appleboy/ssh-action@v1
            with:
              host: ${{ secrets.SSH_HOST }}
              username: ${{ secrets.SSH_USER }}
              key: ${{ secrets.SSH_KEY }}
              port: 22
              script: |
                cd /var/www/myapp
                git pull origin main
                composer install --no-dev --optimize-autoloader
                php artisan migrate --force
                php artisan optimize
                php artisan view:cache
                chown -R www-data:www-data /var/www/myapp
  4. 4

    احفظ الأسرار في GitHub

    لا تضع أي بيانات اعتماد في ملف YAML. احفظها كأسرار للمستودع وأشر إليها بـ ${{ secrets.NAME }}. انتقل إلى مستودعك ← Settings ← Secrets and variables ← Actions ← New repository secret.

    bash
    # الأسرار التي يجب إنشاؤها في GitHub:
    # SSH_HOST     — عنوان IP الخادم أو اسم المضيف
    # SSH_USER     — مثل root أو deploy
    # SSH_KEY      — المفتاح الخاص (المحتوى الكامل لـ ~/.ssh/id_ed25519)
    
    # أنشئ مفتاح نشر مخصصاً (لا تعيد استخدام مفتاحك الشخصي)
    ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/deploy_key -N ""
    
    # أضف المفتاح العام إلى الخادم
    ssh-copy-id -i ~/.ssh/deploy_key.pub user@your-server
    
    # الصق محتوى المفتاح الخاص في SSH_KEY في GitHub
    cat ~/.ssh/deploy_key
  5. 5

    استخدم أسرار البيئة للـ staging مقابل الإنتاج

    لبيئات متعددة، أنشئ Environments في GitHub (Settings ← Environments). يمكن لكل بيئة أن يكون لها أسرارها الخاصة وقواعد الحماية (مراجعون مطلوبون) وقيود فروع النشر.

    yaml
      deploy:
        runs-on: ubuntu-latest
        needs: test
        environment: production          # يرتبط بـ GitHub Environment
        if: github.ref == "refs/heads/main" && github.event_name == "push"
    
        steps:
          - name: Deploy to production
            uses: appleboy/ssh-action@v1
            with:
              host: ${{ secrets.SSH_HOST }}     # محدود ببيئة "production"
              username: ${{ secrets.SSH_USER }}
              key: ${{ secrets.SSH_KEY }}
              script: cd /var/www/myapp && git pull && php artisan optimize
  6. 6

    مرّر ملفات البناء بين الوظائف

    إن كانت خطوة البناء تنتج ملفاً مجمّعاً (أصول Vite أو ملف ثنائي أو digest لصورة Docker)، ارفعه في وظيفة test وحمّله في deploy — لا تُعد البناء في وظيفة النشر.

    yaml
        # في وظيفة test — ارفع الأصول المجمّعة
          - name: Build frontend
            run: npm ci && npm run build
    
          - uses: actions/upload-artifact@v4
            with:
              name: frontend-build
              path: public/build/
    
      deploy:
        needs: test
        runs-on: ubuntu-latest
        steps:
          - uses: actions/download-artifact@v4
            with:
              name: frontend-build
              path: public/build/
    
          # ثم انقل مجلد public/build/ إلى الخادم بـ rsync أو scp
  7. 7

    أضف بناء matrix لإصدارات متعددة

    تشغّل بنية الـ matrix نفس الوظيفة عبر إصدارات متعددة بالتوازي — مفيد للمكتبات أو ضمانات دعم إصدارات متعددة. كل مجموعة وظيفة منفصلة في واجهة Actions.

    yaml
      test:
        runs-on: ubuntu-latest
        strategy:
          matrix:
            php: ["8.2", "8.3"]
            # node: [18, 20, 22]   # استبدل لمشاريع Node
    
        steps:
          - uses: actions/checkout@v4
          - uses: shivammathur/setup-php@v2
            with:
              php-version: ${{ matrix.php }}
          - run: composer install --no-interaction
          - run: php artisan test
  8. 8

    استكشف أخطاء الـ workflow وأصلحها

    معظم الفشل يأتي من ثلاثة أماكن. تحقق منها بالترتيب:

    • سر مفقود — سر فارغ يسبب خطأ SSH غامضاً. أعد التحقق من اسم السر يطابق تماماً (حساس لحالة الأحرف).
    • صيغة مفتاح SSH — يجب أن يتضمن المفتاح أسطر -----BEGIN...----- و-----END...-----. الصقه خاماً بدون مسافات إضافية.
    • خطأ صلاحية الخادم — يجب أن يملك مستخدم النشر مجلد التطبيق أو يكون في مجموعة www-data. شغّل chown -R deploy:deploy /var/www/myapp مرة واحدة.
    bash
    # أعد تشغيل workflow فاشل من سطر الأوامر
    gh run list --limit 5
    gh run rerun <run-id>
    
    # راقب run مباشرة
    gh run watch <run-id>
    
    # حمّل سجلات run فاشل
    gh run view <run-id> --log-failed

نصائح ومحاذير

  • استخدم مجموعات <code>concurrency</code> لإلغاء الـ runs الجارية عند وصول push جديد — يمنع تراكم عمليات نشر متعددة من commits سريعة: <code>concurrency: { group: deploy, cancel-in-progress: true }</code>
  • أضف trigger من نوع <code>workflow_dispatch</code> حتى تتمكن من تشغيل النشر يدوياً من واجهة GitHub دون push: <code>on: [push, workflow_dispatch]</code>
  • خزّن تبعيات مدير الحزم مؤقتاً بـ <code>actions/cache@v4</code> مُفهرَسة بـ hash ملف القفل. ذاكرة مؤقتة دافئة تختصر وقت التثبيت من 60 ثانية لأقل من 5 ثوانٍ.
  • حافظ على سرعة وظيفة النشر — شغّل <code>php artisan migrate --pretend</code> في وظيفة الاختبار لاكتشاف أخطاء الـ migrations قبل الاتصال بالخادم.
  • استخدم <code>actions/checkout@v4</code> مع <code>fetch-depth: 0</code> إن كانت اختباراتك أو أدوات إصدار الإصدارات تحتاج تاريخ git كاملاً.

خاتمة

لديك الآن بيبلاين تشغّل الاختبارات عند كل push، وتحجب النشر عند فشلها، وتشحن للإنتاج عبر SSH حين تكون الفرع main والاختبارات خضراء. وسّعها تدريجياً — أضف فحوصات جودة الكود (phpstan، eslint)، أو إشعارات (Slack، بريد إلكتروني)، أو بوابة بيئة staging قبل الإنتاج.

#CI/CD #GitHub Actions #Deployment
العودة إلى جميع الأدلة

هل تحتاج مساعدة في مشروعك؟

احجز استشارة مجانية لمدة 30 دقيقة لمناقشة تحدياتك التقنية واستكشاف الحلول معًا.