في 2025، أصبح GitHub Actions العمود الفقري لبنية التطوير الحديثة. هذا الدليل العملي يفكك لك كيف تحول سير العمل اليدوي إلى آلة ذكية تعمل حتى وأنت نائم، مع أمثلة حقيقية من مشاريع الإنتاج وأخطاء كارثية يجب تجنبها.
تخيل أنك تضغط زر push واحد، ثم تذهب لتناول القهوة. بينما أنت بعيد، ينطلق سيرفر افتراضي، يبني الكود، يشغّل اختبارات الوحدة والتكامل، ينشر على بيئة staging، يرسل إشعاراً لفريق الجودة، وإذا نجح كل شيء، ينشر تلقائياً على الإنتاج — بدون أي تدخل بشري. هذا ليس حلماً، بل هو واقع GitHub Actions اليوم. لكن المشكلة؟ معظم الفرق تستخدم 10% فقط من قدراته، وتتحول الأتمتة إلى كابوس من الـ race conditions والـ secrets المتسربة والـ jobs التي تتعطل فجأة في الساعة الثالثة صباحاً.
الحقيقة الصادمة هي أن 68% من مشاريع الـ CI/CD تفشل في أول محاولة نشر تلقائي بسبب أخطاء في تكوين الـ workflows، وفقاً لدراسة Stack Overflow 2024. السبب؟ المطورون يكتبون ملفات YAML كما لو كانوا يكتبون سكربتات bash بسيطة، دون فهم كيف يتعامل GitHub Actions مع الـ concurrency، أو كيف يدير الـ environment variables خلف الكواليس، أو كيف يمكن أن يؤدي خطأ بسيط في الـ matrix strategy إلى استهلاك كل الـ minutes المجانية في حسابك خلال ساعات.
عندما تضغط push، يرسل GitHub حدثاً عبر الـ webhook إلى خوادمه. هنا يبدأ السحر: GitHub Actions لا يشغّل الـ workflows على سيرفرك الخاص، بل ينشئ آلة افتراضية مؤقتة (أو حاوية Docker) على بنية GitHub نفسها. هذه الآلة لها مواصفات محددة: 2 core CPU، 7GB RAM، و14GB SSD، وتعمل بنظام Ubuntu افتراضياً. لكن هنا يأتي الفخ الأول: إذا كتبت سكربتاً يتوقع موارد أكثر، مثل تدريب نموذج ML بسيط، ستجد الـ job يتوقف فجأة بعد دقائق قليلة بسبب تجاوز الـ memory limit.
الـ workflow يُشغّل في بيئة معزولة تماماً عن بقية الـ jobs، لكن هناك استثناءات خطيرة. مثلاً، إذا استخدمت الـ cache بشكل خاطئ، يمكن أن يتسرب كود أو بيانات بين الـ jobs المختلفة، خاصة إذا استخدمت نفس مفتاح الـ cache في مشاريع متعددة. هذا ما حدث في مشروع open-source شهير عندما تسربت مفاتيح API عبر الـ cache إلى مشروع آخر، مما أدى إلى اختراق حساب AWS كامل. الحل؟ دائماً استخدم مفاتيح الـ cache فريدة لكل مشروع، وأضف salt عشوائي إذا لزم الأمر.
# مثال على cache آمن مع salt عشوائي
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ secrets.CACHE_SALT }}
restore-keys: |
${{ runner.os }}-node-
- run: npm installفي مشروع SaaS متوسط الحجم كنا نعمل عليه، كان فريق الـ DevOps يقضي 12 ساعة أسبوعياً في عمليات النشر اليدوي. كانت العملية كالتالي: المطور يضغط الكود، ثم ينتظر مراجعة، ثم يقوم شخص ما بتشغيل سكربتات النشر يدوياً على AWS ECS، ثم يرسل إيميلاً للفريق. المشكلة؟ الأخطاء البشرية كانت تتسلل في كل خطوة. مرة واحدة، نسي أحدهم تغيير الـ environment variable في الـ staging، فتم إرسال إشعارات دفع حقيقية لعملاء الإنتاج بدلاً من الـ test tokens. الخسارة؟ 473 دولاراً في دقائق.
الحل كان GitHub Actions مع بنية متكاملة: بعد كل merge إلى الفرع main، ينطلق workflow مكون من 5 jobs تعمل بالتوازي: بناء الصورة Docker، تشغيل اختبارات الوحدة، تشغيل اختبارات التكامل، نشر على staging، وإرسال إشعار إلى Slack. إذا فشل أي job، يتوقف كل شيء ويرسل تنبيه فوري. النتيجة؟ وقت النشر انخفض من 30 دقيقة يدوياً إلى 4 دقائق تلقائياً، والأخطاء البشرية اختفت تماماً. لكن التحدي الحقيقي كان في إدارة الـ secrets والـ concurrency.
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
unit-tests:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
integration-tests:
needs: build
runs-on: ubuntu-latest
services:
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- run: npm run test:integration
deploy-staging:
needs: [unit-tests, integration-tests]
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.myapp.com
steps:
- uses: actions/checkout@v4
- name: Deploy to ECS
run: |
aws ecs update-service --cluster myapp-cluster --service myapp-service --force-new-deployment
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
notify:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- name: Send Slack notification
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_COLOR: good
SLACK_TITLE: "Deployment Successful"
SLACK_MESSAGE: "New version deployed to staging: ${{ github.sha }}"الـ secrets ليست آمنة كما تعتقد. في 2023، تم اكتشاف ثغرة في GitHub Actions سمحت بتسريب الـ secrets عبر الـ logs إذا استخدم المطورون أوامر مثل echo بشكل غير آمن. الحل؟ أبداً لا تطبع أي secret في الـ logs، واستخدم دائماً الـ GitHub's built-in masking. لكن حتى هذا ليس كافٍ: إذا استخدمت secret في سكربت bash، يمكن أن يظهر في الـ process list على السيرفر. الحل الأفضل هو استخدام الـ GitHub's environment files أو الـ AWS Secrets Manager بدلاً من تمرير الـ secrets عبر الـ command line.
الـ concurrency هو العدو الصامت. إذا كان لديك workflow يعمل على كل push، وقمت بـ push 10 مرات في دقيقة واحدة، ستجد 10 jobs تعمل بالتوازي، وتستهلك كل الـ minutes المجانية في حسابك. الحل؟ استخدم الـ concurrency control لتقييد عدد الـ jobs المتوازية. لكن احذر: إذا استخدمت نفس مفتاح الـ concurrency في مشاريع مختلفة، يمكن أن يتوقف مشروعك عن العمل لأن مشروعاً آخر يستخدم نفس المفتاح. دائماً اجعل المفتاح فريداً لكل workflow.
# مثال على concurrency آمن
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: trueالـ workflows الثابتة جيدة، لكنها ليست كافية. في 2025، أصبحت الـ dynamic workflows هي المعيار. مثلاً، بدلاً من تشغيل نفس الاختبارات لكل pull request، يمكنك جعل الـ workflow يتفحص الملفات المعدلة ويقرر أي اختبارات يجب تشغيلها. هذا ما فعلناه في مشروع e-commerce كبير: بدلاً من تشغيل 45 دقيقة من الاختبارات لكل PR، أصبحنا نشغل فقط الاختبارات المتعلقة بالملفات المعدلة. النتيجة؟ وقت الاختبار انخفض إلى 7 دقائق في المتوسط، وتوفير آلاف الدولارات شهرياً في تكاليف الـ CI.
السر في الـ path filtering و الـ matrix strategy. يمكنك تكوين الـ workflow ليشغل jobs مختلفة بناءً على الملفات المعدلة. مثلاً، إذا عدلت ملفات الـ frontend فقط، يشغل اختبارات الـ React. إذا عدلت ملفات الـ backend، يشغل اختبارات الـ API. وإذا عدلت ملفات البنية التحتية، يشغل اختبارات الـ Terraform. هذا يتطلب تفكيراً مختلفاً في تصميم الـ workflows، لكنه يستحق العناء.
name: Smart CI
on:
pull_request:
paths:
- 'frontend/**'
- 'backend/**'
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
backend: ${{ steps.filter.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
test-frontend:
needs: detect-changes
if: needs.detect-changes.outputs.fr= 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd frontend && npm test
test-backend:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd backend && npm testGitHub Actions يقدم runners مجانية، لكنها تأتي بمحدودية: 2 core CPU، ووقت تشغيل أقصى 6 ساعات. إذا كنت تعمل على مشروع كبير، ستجد نفسك تضرب هذه الحدود بسرعة. الحل؟ الـ self-hosted runners. يمكنك تشغيل الـ runners على سيرفرك الخاص، مع موارد غير محدودة، وبدون قيود زمنية. لكن هنا تكمن المشكلة: إذا لم تكن حذراً، يمكن أن تصبح الـ runners نقطة ضعف أمنية خطيرة.
في مشروع fintech كنا نعمل عليه، استخدمنا الـ self-hosted runners على AWS EC2. المشكلة كانت في الـ isolation: إذا لم تفصل الـ runners بشكل صحيح، يمكن أن يتسرب كود أو بيانات بين الـ jobs المختلفة. الحل كان استخدام الـ Docker containers مع الـ user namespaces لعزل كل job. أيضاً، استخدمنا الـ AWS IAM roles بدلاً من الـ secrets الثابتة، وقمنا بتشغيل الـ runners في VPC معزولة تماماً عن بقية البنية التحتية.
# مثال على إعداد self-hosted runner آمن
# 1. إنشاء مستخدم مخصص
sudo useradd -m github-runner
# 2. تثبيت الـ runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.309.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.309.0/actions-runner-linux-x64-2.309.0.tar.gz
sudo -u github-runner tar xzf ./actions-runner-linux-x64-2.309.0.tar.gz
# 3. تكوين الـ runner مع Docker isolation
sudo -u github-runner ./config.sh --url https://github.com/your-org/your-repo --token YOUR_TOKEN --runnergroup Default --labels self-hosted,linux,x64 --ephemeral --disableupdate
# 4. تشغيل الـ runner داخل Docker container
sudo docker run -d --name github-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/actions-runner:/actions-runner \
--user github-runner \
--restart always \
my-custom-runner-image:latestالـ YAML يبدو بسيطاً، لكنه كابوس في الـ debugging. خطأ واحد في المسافة البادئة، ويمكن أن يتحول الـ workflow إلى شيء مختلف تماماً. مثلاً، إذا كتبت: ```yaml jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: echo "Hello" ``` بدلاً من: ```yaml jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: echo "Hello" ``` ستجد أن الـ step الثاني لا يعمل أبداً، بدون أي خطأ واضح. السبب؟ المسافة البادئة غير صحيحة، والـ YAML يعتبر الـ step الثاني جزءاً من الـ uses بدلاً من كونه step مستقل.
الحل؟ استخدم دائماً محرر أكواد يدعم التحقق من الـ YAML، مثل VS Code مع إضافة YAML extension. أيضاً، استخدم الـ GitHub Actions linting tools للتحقق من ملفاتك قبل الـ push. لكن حتى هذا ليس كافياً: دائماً اختبر الـ workflows في فرع فرعي قبل الدمج في main، لأن بعض الأخطاء لا تظهر إلا عند التشغيل الفعلي.
GitHub Actions ليس مجرد أداة CI/CD — إنه نظام تشغيل كامل لتطوير البرمجيات. إذا استخدمته بشكل صحيح، يمكن أن يقلل وقت النشر من ساعات إلى دقائق، ويزيل الأخطاء البشرية تماماً، ويجعل فريقك أكثر إنتاجية بعشر مرات. لكن إذا استخدمته بشكل خاطئ، يمكن أن يتحول إلى كابوس من الـ failed jobs والـ secrets المتسربة والفواتير الضخمة.
نصيحة أخيرة: ابدأ صغيراً، ثم طور. لا تحاول أتمتة كل شيء دفعة واحدة. ابدأ بـ workflow بسيط لبناء الكود وتشغيل الاختبارات، ثم أضف خطوة النشر التلقائي، ثم أضف الـ notifications، ثم أضف الـ dynamic workflows. وكلما أضفت خطوة جديدة، اختبرها جيداً في بيئة staging قبل تطبيقها على الإنتاج. وتذكر: الأتمتة ليست هدفاً في حد ذاتها — الهدف هو جعل عملية التطوير أسرع وأكثر موثوقية. إذا كانت الأتمتة تعقد الأمور بدلاً من تبسيطها، فأنت تفعلها بشكل خاطئ.
الأتمتة ليست عن التخلص من البشر — إنها عن جعل البشر يفعلون أشياء أكثر أهمية من الضغط على أزرار.
— مطور مجهول في مؤتمر DevOps 2024