بناء CI/CD Pipeline: من الكود للإنتاج تلقائياً

Git push → اختبارات تلقائية → نشر على السيرفر. GitHub Actions أو GitLab CI. نشرح الإعداد الكامل.
ما هو CI/CD؟
CI/CD يتكون من مرحلتين:
- CI (Continuous Integration): دمج الكود تلقائياً — كل مطور يدفع كوده، يتم بناؤه واختباره تلقائياً
- CD (Continuous Deployment/Delivery): نشر الكود تلقائياً على السيرفر بعد نجاح الاختبارات
بدون CI/CD، النشر يتم يدوياً: سحب الكود، تثبيت الاعتماديات، تشغيل المهاجرات، مسح الكاش — وكل خطوة فرصة لخطأ بشري. مع CI/CD، العملية كاملة تتم بدفعة git push واحدة.
GitHub Actions: البداية
GitHub Actions هو الخيار الأسهل إذا كان كودك على GitHub — مدمج مع المستودع ومجاني لحدود معقولة.
هيكل المجلدات
.github/
└── workflows/
├── ci.yml # اختبارات وبناء
└── deploy.yml # نشر على السيرفر
ملف CI: اختبارات تلقائية
# ملف: .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
tests:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testing
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, json, curl, mysql, redis
coverage: xdebug
- name: Install Composer Dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install NPM Dependencies
run: npm ci
- name: Build Assets
run: npm run build
- name: Prepare Environment
run: |
cp .env.ci .env
php artisan key:generate
- name: Run Migrations
run: php artisan migrate --force
env:
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
DB_PASSWORD: password
- name: Run Tests
run: php artisan test --parallel
env:
DB_HOST: 127.0.0.1
DB_DATABASE: testing
DB_USERNAME: root
DB_PASSWORD: password
- name: Run Static Analysis
run: ./vendor/bin/phpstan analyse --memory-limit=2G
ملف CD: النشر التلقائي
# ملف: .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/app
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize:clear
php artisan optimize
php artisan queue:restart
npm ci
npm run build
sudo systemctl reload php8.3-fpm
GitLab CI/CD
إذا كنت تستخدم GitLab:
# ملف: .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testing
test:
stage: test
image: php:8.3
services:
- mysql:8.0
- redis:7
before_script:
- apt-get update && apt-get install -y git zip unzip
- docker-php-ext-install pdo pdo_mysql
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
- cp .env.ci .env
- php artisan key:generate
- php artisan migrate
script:
- php artisan test
deploy:
stage: deploy
only:
- main
before_script:
- apt-get update && apt-get install -y openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_HOST "cd /var/www/app && git pull && composer install --no-dev && php artisan migrate --force && php artisan optimize"
سكربت نشر متقدم
# ملف: scripts/deploy.sh (على السيرفر)
#!/bin/bash
set -e
APP_DIR="/var/www/app"
BACKUP_DIR="/opt/backups/releases"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "[$TIMESTAMP] بدء عملية النشر..."
# نسخة احتياطية سريعة
echo "إنشاء نسخة احتياطية..."
mysqldump -u root app_db | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"
cd $APP_DIR
# تفعيل وضع الصيانة
php artisan down --retry=60
# سحب الكود
echo "سحب التحديثات..."
git pull origin main
# تثبيت الاعتماديات
echo "تثبيت الاعتماديات..."
composer install --no-dev --optimize-autoloader --no-interaction
npm ci
npm run build
# المهاجرات
echo "تشغيل المهاجرات..."
php artisan migrate --force
# الكاش
echo "تحسين الأداء..."
php artisan optimize:clear
php artisan optimize
php artisan view:cache
php artisan event:cache
# إعادة تشغيل الخدمات
echo "إعادة تشغيل الخدمات..."
php artisan queue:restart
sudo systemctl reload php8.3-fpm
sudo systemctl reload nginx
# إلغاء وضع الصيانة
php artisan up
echo "[$TIMESTAMP] النشر اكتمل بنجاح."
ملف .env.ci
# ملف: .env.ci
APP_NAME="MyApp"
APP_ENV=testing
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=testing
DB_USERNAME=root
DB_PASSWORD=password
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=sync
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
إعداد Secrets في GitHub
# في إعدادات المستودع > Secrets and variables > Actions
# أضف:
SERVER_HOST → عنوان IP السيرفر
SERVER_USER → اسم المستخدم (deploy)
SSH_PRIVATE_KEY → المفتاح الخاص لـ SSH
إنشاء مفتاح SSH للنشر
# على جهازك المحلي
ssh-keygen -t ed25519 -f deploy_key -C "github-actions-deploy"
# انسخ المفتاح العام إلى السيرفر
ssh-copy-id -i deploy_key.pub deploy@your-server-ip
# انسخ المفتاح الخاص إلى GitHub Secrets
cat deploy_key # انسخ المحتوى كاملاً
أفضل الممارسات
- لا تنشر من الفرع develop مباشرة — ادمج في main أولاً
- الاختبارات إلزامية قبل أي نشر
- النسخ الاحتياطي قبل كل نشر تلقائياً
- وضع الصيانة أثناء النشر
- التراجع — احتفظ بآخر 5 نسخ للتراجع السريع
- الإشعارات — أرسل إشعار بنجاح أو فشل النشر
CI/CD مع كلاود
سيرفرات كلاود (cloud.net.sa) تدعم النشر التلقائي عبر SSH بدون أي قيود. اضبط مفتاح SSH في GitHub Actions واربطه بسيرفرك في الرياض. كل git push على main يصل لسيرفرك خلال ثوانٍ.
مع زمن استجابة 8ms وأقراص SSD سريعة، عملية البناء والنشر على كلاود تتم بسرعة لا تحتاج معها انتظار.
ابدأ الآن
جاهز تبني مشروعك؟ اختر خادمك السحابي بالمواصفات اللي يحتاجها تطبيقك، أو راجع الباقات والأسعار.
