GitHub Actions وCI/CD
قم بإعداد سير عمل للتكامل المستمر والنشر المستمر مباشرةً داخل مستودعات GitHub الخاصة بك.
GitHub Actions وCI/CD
تُحدث GitHub Actions ثورة في أتمتة تطوير البرمجيات من خلال دمج CI/CD مباشرةً في مستودعاتك. سيعلّمك هذا الدليل كيفية إنشاء سير عمل قوية لأتمتة الاختبارات وعمليات البناء والنشر.
مقدمة إلى GitHub Actions
ما هي GitHub Actions؟
GitHub Actions منصة أتمتة تتيح لك:
- تنفيذ سير العمل (workflows) عند وقوع أحداث Git
- أتمتة الاختبارات وعمليات التحقق
- نشر تطبيقاتك تلقائيًا
- إدارة الإصدارات (releases) والإشعارات
المفاهيم الأساسية
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!