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
- Settings → Secrets and variables → Actions
- New repository secret
- Name (مثلاً
DEPLOY_TOKEN) - 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:


Self-hosted Runners
میتوانید روی سرور خودتان runner داشته باشید (مفید برای دسترسی به سیستمهای داخلی):
- Settings → Actions → Runners → New self-hosted runner
- دستورات نصب را در سرور اجرا کنید
- در 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.