انتقل إلى المحتوى الرئيسي
العودة إلى الفئة

النشر التلقائي مع GitHub Actions: إعداد CI/CD Pipeline

إنشاء CI/CD pipelines مع GitHub Actions: بناء ونشر Docker، النشر على الخادم عبر SSH، أتمتة الاختبارات، إدارة الأسرار وتحسين سير العمل.

وقت القراءة: 18 دقيقة DevOps والأتمتة
github-actionsci/cddeploymentdevopsأتمتةdockerssh

جدول المحتويات

النشر التلقائي مع GitHub Actions: إعداد CI/CD Pipeline

GitHub Actions منصة CI/CD قوية تختبر تغييرات الكود تلقائياً وتبنيها وتنشرها على الخادم. في هذا الدليل سنبني pipeline جاهزاً للإنتاج من الصفر.

ما هو GitHub Actions؟

GitHub Actions منصة أتمتة مدمجة في مستودعات GitHub:

  • يستجيب لأحداث مثل push وPR والجدولة
  • يشغّل سير عمل متوازية ومتسلسلة
  • 2,000 دقيقة/شهر مجاناً (غير محدود للمستودعات العامة)
  • قابل للتوسع بآلاف الـ actions الجاهزة

تُخزَّن ملفات سير عمل GitHub Actions بتنسيق YAML في مجلد .github/workflows/.

هيكل سير العمل الأساسي

hljs yaml
# .github/workflows/deploy.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: إعداد Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
      - name: تثبيت التبعيات
        run: npm ci
      - name: تشغيل الاختبارات
        run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: النشر
        run: echo "خطوات النشر هنا"

هيكل on / jobs / steps

المحفزات (on)

hljs yaml
on:
  # Push إلى فرع
  push:
    branches: [main, develop]
    paths:
      - 'src/**'
      - 'package.json'

  # Pull request
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # تشغيل مجدول (cron)
  schedule:
    - cron: '0 2 * * *'  # كل ليلة الساعة 02:00

  # تشغيل يدوي
  workflow_dispatch:
    inputs:
      environment:
        description: 'بيئة النشر'
        required: true
        default: 'staging'
        type: choice
        options: [staging, production]

  # عند اكتمال سير عمل آخر
  workflow_run:
    workflows: ["Build"]
    types: [completed]

تكوين المهمة

hljs yaml
jobs:
  build:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: test
    timeout-minutes: 30
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}

بناء ونشر Docker

النشر إلى Docker Hub

hljs yaml
jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: بيانات Docker الوصفية
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: username/myapp
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha,prefix=sha-

      - name: إعداد Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: تسجيل الدخول إلى Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: البناء والنشر
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

GitHub Container Registry (GHCR)

hljs yaml
      - name: تسجيل الدخول إلى GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: البناء والنشر (GHCR)
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:latest

النشر على الخادم عبر SSH

استخدام appleboy/ssh-action

hljs yaml
  deploy:
    needs: docker
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: النشر عبر SSH
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: ${{ secrets.SERVER_PORT }}
          script: |
            cd /root/myapp
            git pull origin main
            docker compose pull
            docker compose up -d --remove-orphans
            docker image prune -f
            echo "اكتمل النشر: $(date)"

النشر مع Docker Compose

hljs yaml
      - name: نشر Docker Compose
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # سحب الصورة الجديدة
            docker pull ghcr.io/${{ github.repository }}:latest

            # نشر بدون توقف
            docker compose -f /root/app/docker-compose.yml up -d \
              --no-deps \
              --build \
              --remove-orphans

            # تنظيف الصور القديمة
            docker image prune -af --filter "until=24h"

            # فحص الصحة
            sleep 10
            curl -f http://localhost:3000/health || exit 1

إدارة الأسرار

يخزّن GitHub Secrets المعلومات الحساسة بأمان:

hljs yaml
steps:
  - name: تكوين التطبيق
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
      JWT_SECRET: ${{ secrets.JWT_SECRET }}
      API_KEY: ${{ secrets.API_KEY }}
    run: |
      echo "تم تحميل التكوين"
      # لا تُطبع الأسرار في السجلات، تُخفى تلقائياً

الأسرار المطلوبة:

  • SERVER_HOST: عنوان IP للخادم
  • SERVER_USER: اسم مستخدم SSH
  • SSH_PRIVATE_KEY: المفتاح الخاص لـ SSH
  • DOCKERHUB_USERNAME: اسم مستخدم Docker Hub
  • DOCKERHUB_TOKEN: رمز وصول Docker Hub

استخدام التخزين المؤقت

hljs yaml
      - name: تخزين تبعيات Node.js مؤقتاً
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: npm ci (مع التخزين المؤقت)
        run: npm ci

      # تخزين طبقات Docker مؤقتاً
      - name: البناء والنشر
        uses: docker/build-push-action@v5
        with:
          cache-from: type=gha
          cache-to: type=gha,mode=max

Matrix Builds

hljs yaml
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        node: [18, 20]
        os: [ubuntu-latest, macos-latest]
        include:
          - node: 20
            os: ubuntu-latest
            coverage: true
        exclude:
          - node: 18
            os: macos-latest
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test
      - name: تقرير التغطية
        if: matrix.coverage
        run: npm run coverage

قواعد حماية البيئة

hljs yaml
jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    steps:
      - name: نشر الإنتاج
        run: echo "النشر إلى الإنتاج"

في GitHub تحت Settings > Environments > production:

  • Required reviewers (يتطلب موافقة)
  • Wait timer (وقت الانتظار)
  • Deployment branches (main فقط)

مثال كامل لـ CI/CD Pipeline

hljs yaml
name: Full CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm test -- --coverage
      - name: رفع التغطية
        uses: codecov/codecov-action@v4

  build:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
      - uses: docker/build-push-action@v5
        with:
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://example.com
    steps:
      - uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
            docker compose -f /root/app/docker-compose.yml up -d
            docker image prune -f

الخلاصة

بإعداد CI/CD pipeline مؤتمت بالكامل مع GitHub Actions، يمكنك تحسين جودة الكود وتقليل أخطاء النشر وتسريع عملية التطوير. مع إدارة الأسرار وتحسين التخزين المؤقت وقواعد حماية البيئة، ستحصل على pipeline بمستوى الإنتاج.