0%
الدرس 10 من 10
اكتمل 100%

GitHub Actions وCI/CD

قم بإعداد سير عمل للتكامل المستمر والنشر المستمر مباشرةً داخل مستودعات GitHub الخاصة بك.

GitHub Actions وCI/CD

تُحدث GitHub Actions ثورة في أتمتة تطوير البرمجيات من خلال دمج CI/CD مباشرةً في مستودعاتك. سيعلّمك هذا الدليل كيفية إنشاء سير عمل قوية لأتمتة الاختبارات وعمليات البناء والنشر.

مقدمة إلى GitHub Actions

ما هي GitHub Actions؟

GitHub Actions منصة أتمتة تتيح لك:

  • تنفيذ سير العمل (workflows) عند وقوع أحداث Git
  • أتمتة الاختبارات وعمليات التحقق
  • نشر تطبيقاتك تلقائيًا
  • إدارة الإصدارات (releases) والإشعارات
Push/PRGitHub ActionsTestsBuildDeployNotifications

المفاهيم الأساسية

Workflow (سير العمل): عملية مؤتمتة يتم تعريفها بصيغة YAML Job (المهمة): مجموعة من الخطوات تُنفَّذ على runner Step (الخطوة): إجراء فردي داخل job Action (الإجراء): مكوّن قابل لإعادة الاستخدام Runner (المنفّذ): جهاز افتراضي ينفّذ المهام (jobs)

بنية سير العمل

الملف الأساسي

# .github/workflows/ci.yml
name: CI Pipeline

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run tests
        run: npm test

المحفّزات (Triggers)

# أحداث Git
on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]
  release:
    types: [published]

# أحداث مجدولة
on:
  schedule:
    - cron: '0 2 * * 1'  # كل يوم اثنين عند الساعة 2 صباحًا

# التشغيل اليدوي
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'

سير العمل الأساسية

CI أساسي لـ Node.js

# .github/workflows/node-ci.yml
name: Node.js CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16, 18, 20]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run linter
        run: npm run lint
        
      - name: Run tests
        run: npm test
        
      - name: Run build
        run: npm run build
        
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info

CI لـ Python

# .github/workflows/python-ci.yml
name: Python CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: [3.8, 3.9, '3.10', 3.11]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
          
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
          
      - name: Lint with flake8
        run: |
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127
          
      - name: Test with pytest
        run: |
          pytest --cov=./ --cov-report=xml
          
      - name: Upload coverage
        uses: codecov/codecov-action@v3

النشر التلقائي

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install and build
        run: |
          npm ci
          npm run build
          
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.ORG_ID }}
          vercel-project-id: ${{ secrets.PROJECT_ID }}
          vercel-args: '--prod'

إجراءات متقدمة

مصفوفة الاختبارات

jobs:
  test:
    runs-on: ${{ matrix.os }}
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
        include:
          - os: ubuntu-latest
            node-version: 20
            coverage: true
        exclude:
          - os: windows-latest
            node-version: 16
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          
      - name: Run tests
        run: npm test
        
      - name: Upload coverage
        if: matrix.coverage
        uses: codecov/codecov-action@v3

الشروط والتعبيرات

jobs:
  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    
    steps:
      - name: Deploy to staging
        if: contains(github.event.head_commit.message, '[staging]')
        run: echo "Deploying to staging"
        
      - name: Deploy to production
        if: startsWith(github.ref, 'refs/tags/v')
        run: echo "Deploying to production"
        
      - name: Notify on failure
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

الـ Artifacts والذاكرة المؤقتة (Cache)

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
            
      - name: Install and build
        run: |
          npm ci
          npm run build
          
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-files
          path: dist/
          
  deploy:
    needs: build
    runs-on: ubuntu-latest
    
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-files
          path: dist/
          
      - name: Deploy
        run: echo "Deploying files from dist/"

الأمان والأسرار (Secrets)

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

# إعداد الأسرار (secrets) في GitHub
# Settings > Secrets and variables > Actions

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Deploy to AWS
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          aws s3 sync ./dist s3://my-bucket

البيئات المحمية

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Deploy to production
        run: echo "Deploying to production environment"

صلاحيات GITHUB_TOKEN

name: Secure Workflow

on: [push]

permissions:
  contents: read
  issues: write
  pull-requests: write

jobs:
  security-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Run security scan
        uses: github/super-linter@v4
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

إجراءات مخصصة

إجراء JavaScript

// .github/actions/hello-world/index.js
const core = require('@actions/core');
const github = require('@actions/github');

try {
  const nameToGreet = core.getInput('who-to-greet');
  console.log(`Hello ${nameToGreet}!`);
  
  const time = (new Date()).toTimeString();
  core.setOutput("time", time);
  
  const payload = JSON.stringify(github.context.payload, undefined, 2);
  console.log(`The event payload: ${payload}`);
} catch (error) {
  core.setFailed(error.message);
}
# .github/actions/hello-world/action.yml
name: 'Hello World'
description: 'Greet someone and record the time'

inputs:
  who-to-greet:
    description: 'Who to greet'
    required: true
    default: 'World'

outputs:
  time:
    description: 'The time we greeted you'

runs:
  using: 'node20'
  main: 'index.js'

استخدام الإجراء

jobs:
  hello_world_job:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Hello world action step
        uses: ./.github/actions/hello-world
        id: hello
        with:
          who-to-greet: 'GitHub Actions'
          
      - name: Get the output time
        run: echo "The time was ${{ steps.hello.outputs.time }}"

سير عمل معقدة

خط أنابيب متعدد البيئات

# .github/workflows/multi-env-deploy.yml
name: Multi-Environment Deploy

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm test
  
  deploy-staging:
    needs: test
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: staging
    
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to staging
        run: echo "Deploying to staging"
        
  deploy-production:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: echo "Deploying to production"
        
  notify:
    needs: [deploy-staging, deploy-production]
    if: always()
    runs-on: ubuntu-latest
    
    steps:
      - name: Notify team
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

إصدار تلقائي (Release)

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build
        run: npm run build
        
      - name: Generate changelog
        id: changelog
        uses: metcalfc/changelog-generator@v4.0.1
        with:
          myToken: ${{ secrets.GITHUB_TOKEN }}
          
      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: ${{ steps.changelog.outputs.changelog }}
          draft: false
          prerelease: false
          
      - name: Upload Release Asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./dist/app.zip
          asset_name: app.zip
          asset_content_type: application/zip

تمارين عملية

التمرين 1: CI أساسي

# .github/workflows/basic-ci.yml
name: Basic CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: |
          echo '{"name": "test-app", "scripts": {"test": "echo \"Tests passed!\""}}' > package.json
          npm install
          
      - name: Run tests
        run: npm test
        
      - name: Success message
        run: echo "✅ All tests passed!"

التمرين 2: النشر المشروط

# .github/workflows/conditional-deploy.yml
name: Conditional Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to staging
        if: github.ref == 'refs/heads/staging'
        run: |
          echo "🚀 Deploying to staging environment"
          echo "Environment: staging"
          
      - name: Deploy to production
        if: github.ref == 'refs/heads/main'
        run: |
          echo "🚀 Deploying to production environment"
          echo "Environment: production"
          
      - name: Notify deployment
        run: |
          echo "📧 Sending notification"
          echo "Deployment completed for branch: ${{ github.ref_name }}"

التمرين 3: سير عمل مع Artifacts

# .github/workflows/artifacts-demo.yml
name: Artifacts Demo

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Create build artifacts
        run: |
          mkdir -p dist
          echo "Built application v1.0.0" > dist/app.txt
          echo "Build timestamp: $(date)" > dist/build-info.txt
          
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: dist/
          
  test:
    needs: build
    runs-on: ubuntu-latest
    
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-artifacts
          path: ./downloaded/
          
      - name: Test artifacts
        run: |
          echo "📦 Testing downloaded artifacts:"
          ls -la ./downloaded/
          cat ./downloaded/app.txt
          cat ./downloaded/build-info.txt

المراقبة وتصحيح الأخطاء

السجلات وتصحيح الأخطاء

jobs:
  debug:
    runs-on: ubuntu-latest
    
    steps:
      - name: Debug information
        run: |
          echo "🔍 Debug Information:"
          echo "Runner OS: ${{ runner.os }}"
          echo "GitHub Actor: ${{ github.actor }}"
          echo "Repository: ${{ github.repository }}"
          echo "Branch: ${{ github.ref_name }}"
          echo "Commit SHA: ${{ github.sha }}"
          
      - name: Enable debug logging
        run: echo "::debug::This is a debug message"
        
      - name: Set environment variable
        run: echo "CUSTOM_VAR=hello-world" >> $GITHUB_ENV
        
      - name: Use environment variable
        run: echo "Custom variable: $CUSTOM_VAR"

الإشعارات

jobs:
  notify:
    runs-on: ubuntu-latest
    if: always()
    
    steps:
      - name: Slack notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          channel: '#deployments'
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}
          
      - name: Email notification
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.gmail.com
          server_port: 465
          username: ${{ secrets.MAIL_USERNAME }}
          password: ${{ secrets.MAIL_PASSWORD }}
          subject: "Build ${{ job.status }}"
          body: "Build completed with status: ${{ job.status }}"
          to: team@company.com

أفضل الممارسات

تنظيم سير العمل (workflows)

.github/
├── workflows/
│   ├── ci.yml              # Tests et validation
│   ├── deploy-staging.yml  # Déploiement staging
│   ├── deploy-prod.yml     # Déploiement production
│   ├── release.yml         # Gestion des releases
│   └── security.yml        # Scans de sécurité
├── actions/
│   └── custom-action/      # Actions personnalisées
└── ISSUE_TEMPLATE/         # Templates d'issues

تحسين الأداء

jobs:
  optimized:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      # Cache des dépendances
      - name: Cache Node modules
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          
      # Parallélisation
      - name: Install dependencies
        run: npm ci --prefer-offline --no-audit
        
      # Éviter les étapes inutiles
      - name: Run tests
        if: github.event_name == 'pull_request'
        run: npm test

الأمان

# Permissions minimales
permissions:
  contents: read
  
jobs:
  secure:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      # Scan de sécurité
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          
      # Validation des secrets
      - name: Check for secrets
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./

موارد إضافية

Actions الشائعة

  • actions/checkout: جلب الشيفرة المصدرية
  • actions/setup-node: إعداد Node.js
  • actions/cache: التخزين المؤقت للاعتماديات
  • docker/build-push-action: بناء صور Docker ودفعها (build وpush)
  • peaceiris/actions-gh-pages: النشر على GitHub Pages

Marketplace

التوثيق

الخاتمة

يحوّل GitHub Actions مستودعك إلى منصة DevOps متكاملة. فبإتقان هذه المفاهيم، يمكنك أتمتة خط التطوير بأكمله، بدءًا من التكامل المستمر وصولًا إلى النشر في بيئة الإنتاج.

تهانينا! لقد أنهيت هذه الدورة الكاملة حول Git وGitHub. أصبحت تمتلك الآن جميع المهارات اللازمة لإدارة مشاريعك بفعالية باستخدام Git، والتعاون عبر GitHub، وأتمتة سير عمل التطوير لديك.


مدة القراءة المقدّرة: 40 دقيقة
المستوى: متقدم
المرحلة السابقة: Git sous-modules et sous-arbres
🎉 انتهت الدورة! أصبحت الآن تتقن Git وGitHub!