~/icsd.ir — bash
SYSTEM_ONLINE

GitHub Actions و CI/CD

GitHub Actions پلتفرم CI/CD داخلی GitHub است. می‌توانید با هر push، PR، یا event دیگر، خودکار تست کنید، build کنید و deploy کنید. در این فصل از صفر تا workflow‌های حرفه‌ای پیش می‌رویم.

GitHub Actions پلتفرم CI/CD داخلی GitHub است. می‌توانید با هر push، PR، یا event دیگر، خودکار تست کنید، build کنید و deploy کنید. در این فصل از صفر تا workflow‌های حرفه‌ای پیش می‌رویم.

CI/CD چیست؟

اصطلاح توضیح
CI – Continuous Integration هر تغییر خودکار تست و build می‌شود
CD – Continuous Delivery هر تغییر آماده deploy است (با تأیید)
CD – Continuous Deployment هر تغییر خودکار deploy می‌شود

مزایا: کشف زودهنگام باگ، اعتماد به deploy، اتوماسیون کامل، سرعت بالا.

مفاهیم اصلی

Workflow (.github/workflows/test.yml)
  └─ triggered by: push, PR, schedule, ...
     └─ Job: test
         └─ runs-on: ubuntu-latest
             └─ Steps:
                 ├─ Checkout code
                 ├─ Setup Python
                 ├─ Install deps
                 └─ Run tests

  └─ Job: deploy (depends on: test)
      └─ Steps: ...
مفهوم توضیح
Workflow فایل YAML در .github/workflows/
Event چه چیزی workflow را trigger می‌کند
Job مجموعه step‌ها که روی یک runner اجرا می‌شوند
Runner سرور (GitHub-hosted یا self-hosted) که job‌ها روی آن اجرا می‌شوند
Step یک command یا action
Action کامپوننت قابل استفاده مجدد

اولین Workflow

# .github/workflows/hello.yml
name: Hello World

on: [push]

jobs:
  greet:
    runs-on: ubuntu-latest
    steps:
      - name: Say hello
        run: echo "Hello, GitHub Actions!"
      
      - name: Show date
        run: date

commit + push کنید. به tab Actions در GitHub بروید – workflow اجرا شد!

Events – چه چیزی trigger می‌کند؟

# یک event
on: push

# چند event
on: [push, pull_request]

# با فیلتر
on:
  push:
    branches: [main, develop]
    paths:
      - 'src/**'
      - '!docs/**'        # exclude
    tags:
      - 'v*'

  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # زمان‌بندی شده (cron)
  schedule:
    - cron: '0 0 * * *'   # هر روز نیمه شب UTC

  # دستی
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment'
        required: true
        type: choice
        options: [staging, production]

  # event‌های دیگر
  release:
    types: [published]
  
  issues:
    types: [opened, labeled]
  
  issue_comment:
    types: [created]

استفاده از Actions موجود

هزاران Action آماده در Marketplace موجود است:

steps:
  # checkout - تقریباً همه workflow‌ها لازم دارند
  - uses: actions/checkout@v4

  # setup زبان‌ها
  - uses: actions/setup-python@v5
    with:
      python-version: '3.12'

  - uses: actions/setup-node@v4
    with:
      node-version: '20'
      cache: 'npm'

  - uses: actions/setup-go@v5
    with:
      go-version: '1.22'

  # caching
  - uses: actions/cache@v4
    with:
      path: ~/.cache/pip
      key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

  # upload/download artifacts
  - uses: actions/upload-artifact@v4
    with:
      name: build
      path: dist/

  - uses: actions/download-artifact@v4
    with:
      name: build

مثال: تست Python

# .github/workflows/python-test.yml
name: Python Tests

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pytest pytest-cov flake8 black
      
      - name: Lint with flake8
        run: flake8 . --count --max-line-length=100 --statistics
      
      - name: Format check with black
        run: black --check .
      
      - name: Run tests
        run: pytest --cov=. --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

Matrix – تست روی چند نسخه

برای پروژه‌های open-source، تست روی چند Python version و OS:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false       # نه‌ی یکی fail شد، بقیه را stop نکن
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.10', '3.11', '3.12']
        # exclude خاص
        exclude:
          - os: windows-latest
            python-version: '3.10'
        # شامل خاص
        include:
          - os: ubuntu-latest
            python-version: '3.12'
            experimental: false
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - run: pytest

# نتیجه: 3 OS × 3 version - 1 exclude = 8 job موازی

Secrets و Variables

برای اطلاعات حساس مثل API key‌ها:

تنظیم Secret

  1. Settings → Secrets and variables → Actions
  2. New repository secret
  3. Name (مثلاً DEPLOY_TOKEN)
  4. Value (یک‌بار قابل دیدن، بعد پنهان)

استفاده در Workflow

steps:
  - name: Deploy
    env:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
      DB_URL: ${{ secrets.DB_URL }}
    run: |
      echo "Deploying with token..."
      ./deploy.sh

  # یا inline
  - name: API call
    run: curl -H "Authorization: Bearer ${{ secrets.API_KEY }}" ...

Environment Secrets

Secret‌های مختلف برای staging و production:

jobs:
  deploy-staging:
    environment: staging          # secrets از environment "staging"
    runs-on: ubuntu-latest
    steps:
      - run: deploy --token ${{ secrets.DEPLOY_TOKEN }}

  deploy-prod:
    environment: production       # نیاز به approval
    runs-on: ubuntu-latest
    needs: deploy-staging
    steps:
      - run: deploy --token ${{ secrets.DEPLOY_TOKEN }}

Variables (متغیرهای غیرحساس)

env:
  APP_NAME: ${{ vars.APP_NAME }}
  REGION: ${{ vars.AWS_REGION }}

شرط‌ها

jobs:
  deploy:
    if: github.ref == 'refs/heads/main'      # فقط روی main
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # فقط برای PR
      - name: Run on PR
        if: github.event_name == 'pull_request'
        run: echo "PR #${{ github.event.pull_request.number }}"
      
      # نه برای fork
      - name: Deploy
        if: github.event.pull_request.head.repo.full_name == github.repository
        run: ./deploy.sh
      
      # ادامه حتی اگر step قبلی fail شد
      - name: Cleanup
        if: always()
        run: ./cleanup.sh
      
      # فقط در failure
      - name: Notify on failure
        if: failure()
        run: ./notify.sh

Job Dependencies

jobs:
  test:
    runs-on: ubuntu-latest
    steps: [...]
  
  build:
    needs: test                    # بعد از test
    runs-on: ubuntu-latest
    steps: [...]
  
  deploy:
    needs: [test, build]           # بعد از هر دو
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps: [...]
test ──→ build ──┐
                  ├──→ deploy
test ─────────────┘

مثال: Build و Push Docker

name: Docker Build

on:
  push:
    branches: [main]
    tags: ['v*']

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: myuser/myapp
          tags: |
            type=ref,event=branch
            type=ref,event=tag
            type=sha
      
      - name: Build and push
        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

مثال: Deploy به VPS با SSH

name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup SSH key
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
      
      - name: Add server to known_hosts
        run: |
          ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts
      
      - name: Deploy
        run: |
          ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} '
            cd /var/www/myapp
            git pull origin main
            source venv/bin/activate
            pip install -r requirements.txt
            python manage.py migrate
            python manage.py collectstatic --noinput
            sudo systemctl restart gunicorn
          '
      
      - name: Notify on Slack
        if: always()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deploy ${{ job.status }}: ${{ github.sha }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Services – دیتابیس برای تست

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: --health-cmd pg_isready
      
      redis:
        image: redis:7
        ports:
          - 6379:6379
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
      
      - name: Run tests
        env:
          DATABASE_URL: postgres://postgres:test@localhost:5432/testdb
          REDIS_URL: redis://localhost:6379
        run: pytest

Scheduled Workflows

on:
  schedule:
    # هر روز ساعت 3 صبح UTC
    - cron: '0 3 * * *'
    
    # هر دوشنبه ساعت 9
    - cron: '0 9 * * 1'
    
    # هر 6 ساعت
    - cron: '0 */6 * * *'

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Daily cleanup..."

  weekly-report:
    runs-on: ubuntu-latest
    if: github.event.schedule == '0 9 * * 1'
    steps:
      - run: ./generate-report.sh

Reusable Workflows

می‌توانید workflow‌ها را call کنید از workflow دیگر:

# .github/workflows/reusable-deploy.yml
name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      DEPLOY_TOKEN:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to ${{ inputs.environment }}
        run: ./deploy.sh ${{ inputs.environment }}
        env:
          TOKEN: ${{ secrets.DEPLOY_TOKEN }}
# .github/workflows/main.yml
jobs:
  deploy-staging:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: staging
    secrets:
      DEPLOY_TOKEN: ${{ secrets.STAGING_TOKEN }}
  
  deploy-prod:
    needs: deploy-staging
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: production
    secrets:
      DEPLOY_TOKEN: ${{ secrets.PROD_TOKEN }}

Status Badges

در README، badge وضعیت CI:

![Tests](https://github.com/USER/repo/actions/workflows/test.yml/badge.svg)
![Build](https://github.com/USER/repo/actions/workflows/build.yml/badge.svg?branch=main)

Self-hosted Runners

می‌توانید روی سرور خودتان runner داشته باشید (مفید برای دسترسی به سیستم‌های داخلی):

  1. Settings → Actions → Runners → New self-hosted runner
  2. دستورات نصب را در سرور اجرا کنید
  3. در workflow:
jobs:
  deploy:
    runs-on: self-hosted
    steps: [...]

بهترین شیوه‌ها

  • هر workflow کوچک و focused
  • caching برای سرعت (pip، npm، docker layers)
  • matrix برای تست multi-version
  • secrets برای اطلاعات حساس – هرگز در کد
  • environment‌ها برای approval در deploy
  • concurrent runs را با cancel-in-progress مدیریت کنید
  • fail-fast: false برای دیدن همه نتایج
  • artifact‌ها برای پاس دادن بین job‌ها
  • Reusable workflow برای DRY
  • GitHub-hosted runner مگر نیاز خاص

concurrency

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true       # cancel اجراهای قبلی همان branch

# نتیجه: اگر دو push پشت سر هم بزنید، اولی cancel می‌شود

قیمت‌گذاری

  • Public repos: کاملاً رایگان، نامحدود
  • Private repos (Free): ۲۰۰۰ دقیقه/ماه ubuntu، ۵۰۰ MB storage
  • Pro: ۳۰۰۰ دقیقه/ماه
  • Team: ۳۰۰۰ + minutes اضافه
  • Windows runner: ۲× minutes، Mac runner: ۱۰× minutes
  • self-hosted: همیشه رایگان

جمع‌بندی

  • GitHub Actions = CI/CD رایگان داخلی GitHub
  • workflow‌های YAML در .github/workflows/
  • events: push، pull_request، schedule، workflow_dispatch
  • actions: setup-python، checkout، cache، upload-artifact
  • matrix برای تست multi-version
  • secrets برای اطلاعات حساس
  • job dependencies با needs
  • environment‌ها برای approval
  • reusable workflow برای DRY
  • self-hosted runner برای موارد خاص

در فصل بعد، امنیت در GitHub – Dependabot، Secret scanning، Code scanning و signed commits.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.