CI/CD
يستمر Bob في ارتكاب الأخطاء في كل مرة ينشر فيها يدويًا—ينسى تشغيل الترحيلات، يكوّن متغيرات البيئة بشكل خاطئ، ويدفع الكود إلى الإنتاج قبل اجتياز الاختبارات. يحتاج Charlie إلى أتمتة CI/CD: اختبار تلقائي عند دفع الكود، معاينة تلقائية لطلبات السحب، ونشر تلقائي للإنتاج من فرع main، كل ذلك بدون تدخل يدوي.
1. ما ستتعلمه
- سير عمل GitHub Actions: lint → test → build → deploy
- فحوصات جودة الكود: ESLint + Prettier + TypeCheck + تغطية كود Vitest
- إدارة البيئات: development → staging → production
- استراتيجية النشر: نشر أزرق-أخضر + تحديثات متدرجة + تراجع
- معاينة تلقائية لـ PR في MegaShop + نشر تلقائي من main
2. قصة حقيقية لمسؤول
(1) نقطة الألم: أخطاء متكررة أثناء النشر اليدوي
ينشر Bob MegaShop يدويًا. الخطوات: 1) استنساخ المستودع 2) تشغيل npm install 3) تشغيل الاختبارات (نسي) 4) البناء 5) الرفع 6) تشغيل الترحيلات (نسي) 7) إعادة تشغيل الخدمة. تفويت خطوة واحدة يسبب مشكلة، مما ينتج عنه متوسط حادثي نشر شهريًا.
(2) حل باستخدام GitHub Actions CI/CD
تعمل العملية الكاملة تلقائيًا مع كل دفع:
# .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
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 كامل
# .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/
الناتج:
تم تحميل خط أنابيب CI/CD
حالة الخط الأنابيب: ناجح
الاختبارات: 12 ناجح، 0 فاشل
4. مراقبة جودة الكود
(1) تكوين التحكم في الوصول للجودة
(1) ▶ مثال: تكوين ESLint + Prettier
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/eslint'],
eslint: {
config: {
stylistic: {
indent: 2,
quotes: 'single',
semi: false
}
}
}
})
الناتج:
// التنفيذ ناجح
(2) ▶ مثال: نصوص package.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"
}
}
الناتج:
{
"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) ▶ مثال: ملف تكوين البيئة
# .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
# .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!"}
الناتج:
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) ▶ مثال: نص النشر الأزرق-الأخضر
#!/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"
الناتج:
CONTAINER ID IMAGE STATUS
abc123 latest Up 2 hours
7. مثال شامل: عملية CI/CD الكاملة لـ MegaShop
# .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
❓ أسئلة شائعة
prisma migrate deploy (الذي يطبق الترحيلات الموجودة فقط ولا ينشئ ترحيلات جديدة). أثناء التطوير، استخدم prisma migrate dev لإنشاء ملفات الترحيل والالتزام بها في Git.git revert وأعد النشر. لـ Docker، أعد التشغيل باستخدام علامة الصورة السابقة. لـ PM2، استخدم pm2 stop ثم ابدأ النسخة القديمة. لأتمتة التراجع، أضف منطق معالجة فحوصات الصحة الفاشلة إلى نص النشر.runtimeConfig هي من جانب الخادم فقط. في CI، أشر إليها باستخدام ${{ secrets.XXX }}.📖 ملخص
- تنفيذ خط أنابيب مؤتمت بالكامل (lint → test → build → deploy) باستخدام GitHub Actions
- متطلبات جودة الكود: ESLint + TypeCheck + تغطية Vitest ≥ 80%
- إدارة ثلاث بيئات: development (محلي) → staging (اختبار التكامل) → production (مباشر)
- النشر الأزرق-الأخضر يضمن عدم وجود توقف؛ تراجع تلقائي عند فشل فحص الصحة
- MegaShop: معاينة تلقائية لـ PRs + staging تلقائي لـ "main" + نشر تلقائي للإنتاج لـ "release"
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): إنشاء سير عمل CI في GitHub Actions يشغل تلقائيًا الفحص والتحقق من النوع والاختبارات عند دفع التزام.
- تمرين متقدم (الصعوبة ⭐⭐): إضافة نشر تلقائي إلى staging—عند دفع فرع main، النشر تلقائيًا إلى خادم staging وتشغيل اختبار دخان بعد النشر.
- تحدي (الصعوبة: ⭐⭐⭐): تنفيذ نص نشر أزرق-أخضر + تراجع تلقائي—التبديل تلقائيًا إلى النسخة السابقة عند فشل فحص الصحة
---|



