404 Not Found

404 Not Found


nginx

CI/CD

يستمر Bob في ارتكاب الأخطاء في كل مرة ينشر فيها يدويًا—ينسى تشغيل الترحيلات، يكوّن متغيرات البيئة بشكل خاطئ، ويدفع الكود إلى الإنتاج قبل اجتياز الاختبارات. يحتاج Charlie إلى أتمتة CI/CD: اختبار تلقائي عند دفع الكود، معاينة تلقائية لطلبات السحب، ونشر تلقائي للإنتاج من فرع main، كل ذلك بدون تدخل يدوي.

1. ما ستتعلمه


2. قصة حقيقية لمسؤول

(1) نقطة الألم: أخطاء متكررة أثناء النشر اليدوي

ينشر Bob MegaShop يدويًا. الخطوات: 1) استنساخ المستودع 2) تشغيل npm install 3) تشغيل الاختبارات (نسي) 4) البناء 5) الرفع 6) تشغيل الترحيلات (نسي) 7) إعادة تشغيل الخدمة. تفويت خطوة واحدة يسبب مشكلة، مما ينتج عنه متوسط حادثي نشر شهريًا.

(2) حل باستخدام GitHub Actions CI/CD

تعمل العملية الكاملة تلقائيًا مع كل دفع:

YAML
# .github/workflows/deploy.yml
on: push
jobs:
  test:    → lint + typecheck + vitest
  build:   → npm run build
  deploy:  → docker compose up -d

(3) الفوائد: صفر جهد + صفر حوادث

اختبار وبناء ونشر مؤتمت بالكامل بعد دفع الكود؛ تولد طلبات السحب بيئات معاينة تلقائيًا؛ عمليات دمج الكود في الفرع الرئيسي تُنشر تلقائيًا؛ وتنخفض حوادث النشر إلى الصفر.


3. سير عمل GitHub Actions

(1) مراحل خط أنابيب CI/CD

100%
flowchart LR
    A[Push / PR] --> B[Lint + TypeCheck]
    B --> C[اختبارات الوحدة]
    C --> D[البناء]
    D --> E{الفرع؟}
    E -->|PR| F[نشر المعاينة]
    E -->|main| G[نشر Staging]
    G --> H[اختبار الدخان]
    H --> I[نشر الإنتاج]
    I --> J[فحص الصحة]
    J --> K[تم ✅]

(1) ▶ مثال: سير عمل CI كامل

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

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

jobs:
  lint:
    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: npx nuxi typecheck

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: megashop_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    env:
      DATABASE_URL: postgresql://test:test@localhost:5432/megashop_test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx prisma migrate deploy
      - run: npm run test:coverage
      - name: رفع التغطية
        uses: codecov/codecov-action@v4

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx prisma generate
      - run: npm run build
      - name: رفع مخرجات البناء
        uses: actions/upload-artifact@v4
        with:
          name: nuxt-build
          path: .output/

الناتج:

TEXT
تم تحميل خط أنابيب CI/CD
حالة الخط الأنابيب: ناجح
الاختبارات: 12 ناجح، 0 فاشل

4. مراقبة جودة الكود

(1) تكوين التحكم في الوصول للجودة

(1) ▶ مثال: تكوين ESLint + Prettier

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/eslint'],

  eslint: {
    config: {
      stylistic: {
        indent: 2,
        quotes: 'single',
        semi: false
      }
    }
  }
})

الناتج:

TEXT
// التنفيذ ناجح

(2) ▶ مثال: نصوص package.json

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

الناتج:

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

(2) مقاييس التحكم في الوصول للجودة

المقياس الأداة عتبة الوصول الوصف
نمط الكود ESLint 0 أخطاء نمط كود متسق
التنسيق Prettier 0 تحذيرات تنسيق تلقائي
فحص النوع vue-tsc 0 أخطاء أمان نوع TypeScript
اختبارات الوحدة Vitest تغطية ≥ 80% تغطية المنطق الأساسي
اختبار E2E Playwright جميعها ناجحة التحقق من التدفقات الرئيسية
حجم الحزمة rollup-plugin < 200 KB gzip منع تضخم الحجم

5. إدارة البيئات المتعددة

(1) ▶ مثال: ملف تكوين البيئة

TEXT
# .env.development
DATABASE_URL=postgresql://dev:dev@localhost:5432/megashop_dev
REDIS_URL=redis://localhost:6379
JWT_ACCESS_SECRET=dev-access-secret

# .env.staging
DATABASE_URL=postgresql://staging:xxx@staging-db.internal:5432/megashop_staging
REDIS_URL=redis://staging-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.STAGING_JWT_SECRET }}

# .env.production
DATABASE_URL=postgresql://prod:xxx@prod-db.internal:5432/megashop
REDIS_URL=redis://prod-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.PROD_JWT_SECRET }}

(2) ▶ مثال: سير عمل نشر Staging

YAML
# .github/workflows/deploy-staging.yml
name: Deploy Staging

on:
  push:
    branches: [main]

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    needs: [lint, test]
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - name: نشر إلى staging
        run: |
          ssh staging-server << 'EOF'
          cd /opt/megashop
          git pull origin main
          docker compose up -d --build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: اختبار الدخان
        run: |
          sleep 10
          curl -f https://staging.megashop.com/api/products?limit=1 || exit 1

      - name: إبلاغ الفريق
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {"text": "Staging deployment failed!"}

الناتج:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(1) مقارنة البيئات

البيئة الغرض قاعدة البيانات طريقة النشر صلاحيات الوصول
development تطوير محلي PostgreSQL محلي npm run dev المطورون
staging اختبار التكامل PostgreSQL مستقل مؤتمت (دفع main) الفريق
production بيئة الإنتاج PostgreSQL للإنتاج تلقائي (tag/release) جميع المستخدمين

6. استراتيجية النشر

(1) مقارنة استراتيجيات النشر

الاستراتيجية المبدأ وقت التوقف سرعة التراجع التعقيد
الاستبدال المباشر إيقاف القديم وتشغيل الجديد 🔴 نعم (5-30 ثانية) 🟡 إعادة النشر 🟢 بسيط
النشر الأزرق-الأخضر التبديل بين بيئتين 🟢 لا 🟢 تبديل بالثواني 🟡 متوسط
التحديث المتدرج استبدال النسخ واحدة تلو الأخرى 🟢 لا 🟡 تراجع واحدة تلو الأخرى 🟡 متوسط
الإطلاق الكناري التحقق مع قاعدة مستخدمين صغيرة أولاً 🟢 لا 🟢 تراجع فوري 🔴 معقد

(1) ▶ مثال: نص النشر الأزرق-الأخضر

BASH
#!/bin/bash
# deploy-blue-green.sh

CURRENT=$(docker compose ps --format '{{.Name}}' | grep -o 'blue\|green' | head -1)
if [ "$CURRENT" = "blue" ]; then
  NEXT="green"
else
  NEXT="blue"
fi

echo "Deploying to $NEXT environment..."

# بناء وبدء البيئة التالية
docker compose -f docker-compose.yml -f docker-compose.$NEXT.yml up -d --build

# انتظار فحص الصحة
for i in {1..30}; do
  if curl -sf http://localhost:3001/health; then
    echo "Health check passed"
    break
  fi
  sleep 2
done

# تبديل Nginx إلى البيئة الجديدة
sed "s/$CURRENT/$NEXT/g" nginx.conf > /tmp/nginx.conf
docker compose exec nginx nginx -s reload

echo "Switched from $CURRENT to $NEXT"

الناتج:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours

7. مثال شامل: عملية CI/CD الكاملة لـ MegaShop

YAML
# .github/workflows/deploy-production.yml
name: Deploy Production

on:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: بناء صورة Docker
        run: docker build -t megashop:${{ github.sha }} .

      - name: الدفع إلى السجل
        run: |
          docker tag megashop:${{ github.sha }} ghcr.io/megashop/megashop:latest
          echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u $ --password-stdin
          docker push ghcr.io/megashop/megashop:latest

      - name: نشر إلى الإنتاج
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker pull ghcr.io/megashop/megashop:latest
          docker compose up -d --no-build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: فحص الصحة
        run: |
          for i in {1..10}; do
            if curl -sf https://megashop.com/api/health; then exit 0; fi
            sleep 5
          done
          exit 1

      - name: تراجع عند الفشل
        if: failure()
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker compose down
          docker tag megashop:previous megashop:latest
          docker compose up -d
          EOF

❓ أسئلة شائعة

س هل الحصة المجانية لـ GitHub Actions كافية؟
ج المستودعات العامة لديها دقائق غير محدودة. المستودعات الخاصة لها حد شهري 2000 دقيقة. تشغيل CI واحد لـ MegaShop يستغرق حوالي 10 دقائق، لذا 10 تشغيلات يوميًا ستكون حوالي 100 دقيقة—وهو كافٍ.
س كيف يتم إعداد بيئة معاينة PR؟
ج يتم توليدها تلقائيًا باستخدام Vercel Preview. كل PR له رابط URL الخاص به (pr-123-megashop.vercel.app)، الذي يُحذف تلقائيًا بعد دمج طلب السحب.
س كيف أشغل ترحيلات قاعدة البيانات في CI؟
ج في CI، استخدم prisma migrate deploy (الذي يطبق الترحيلات الموجودة فقط ولا ينشئ ترحيلات جديدة). أثناء التطوير، استخدم prisma migrate dev لإنشاء ملفات الترحيل والالتزام بها في Git.
س كيف يمكن تحقيق نشر بدون توقف؟
ج النشر الأزرق-الأخضر (التبديل بين بيئتين) أو إعادة تحميل كتلة PM2 (استبدال العمال واحدًا تلو الآخر). لـ Docker، استخدم التحديثات المتدرجة (docker compose rolling update).
س كيف أقوم بالتراجع؟
ج استخدم git revert وأعد النشر. لـ Docker، أعد التشغيل باستخدام علامة الصورة السابقة. لـ PM2، استخدم pm2 stop ثم ابدأ النسخة القديمة. لأتمتة التراجع، أضف منطق معالجة فحوصات الصحة الفاشلة إلى نص النشر.
س كيف يجب إدارة الأسرار بأمان؟
ج خزّن الأسرار في GitHub Secrets كمتغيرات بيئة؛ لا تلتزم بها أبدًا في Git. الحقول الخاصة في runtimeConfig هي من جانب الخادم فقط. في CI، أشر إليها باستخدام ${{ secrets.XXX }}.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): إنشاء سير عمل CI في GitHub Actions يشغل تلقائيًا الفحص والتحقق من النوع والاختبارات عند دفع التزام.
  2. تمرين متقدم (الصعوبة ⭐⭐): إضافة نشر تلقائي إلى staging—عند دفع فرع main، النشر تلقائيًا إلى خادم staging وتشغيل اختبار دخان بعد النشر.
  3. تحدي (الصعوبة: ⭐⭐⭐): تنفيذ نص نشر أزرق-أخضر + تراجع تلقائي—التبديل تلقائيًا إلى النسخة السابقة عند فشل فحص الصحة

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%