هل ما زلت تضغط زر 'دمج' يدوياً وتنتظر ساعة حتى ينتهي الـ CI؟ في 2025، GitHub Actions ليس مجرد أداة أتمتة، بل هو العقل الخفي وراء كل مشروع ناجح. اكتشف كيف تحول سير عمل التطوير إلى ماكينة سلسة تعمل حتى وأنت نائم، مع أمثلة حقيقية من مشاريع مفتوحة المصدر وأخطاء قاتلة يجب تجنبها.
في أحد أيام الجمعة الماضية، تلقى فريقنا إنذاراً من AWS مفاده أن فاتورة الـ EC2 تجاوزت ٥٠٠ دولار خلال يوم واحد. السبب؟ سيرفر CI/CD قديم كان يشغل اختبارات E2E على كل طلب دمج، حتى لو كان مجرد تعديل في ملف README. المشكلة الحقيقية لم تكن في التكلفة، بل في الوقت الضائع: المطورون كانوا ينتظرون ٤٥ دقيقة حتى تظهر نتائج الاختبارات، بينما كان بإمكانهم الحصول على نفس النتيجة خلال ٣ دقائق باستخدام GitHub Actions. هذا ليس مجرد توفير في الوقت، بل هو تغيير في ثقافة الفريق بأكمله - من انتظار النتائج إلى الثقة التامة في أن النظام سيعمل كما هو متوقع.
GitHub Actions في 2025 لم يعد مجرد أداة لتشغيل سكربتات بسيطة. إنه نظام متكامل لإدارة سير العمل، قادر على التعامل مع مهام معقدة مثل نشر تطبيقات Serverless، بناء صور Docker متعددة المنصات، وحتى تدريب نماذج الذكاء الاصطناعي. الفرق بين الفرق التي تستخدم Actions بشكل سطحي والفرق التي تستغله بشكل كامل هو نفس الفرق بين من يستخدم السيارة فقط للذهاب للعمل ومن يبني بها سباقات فورميلا 1. في هذا الدليل، سنغطي ليس فقط الأساسيات، بل أيضاً التفاصيل الدقيقة التي تجعل الفرق الحقيقية تتفوق على الآخرين.
دعونا نكون صريحين: Jenkins هو مثل تلك السيارة القديمة التي تعمل بشكل ما، لكنها تحتاج إلى ميكانيكي متفرغ لإبقائها على الطريق. نعم، هو مجاني ومفتوح المصدر، لكن تكلفة الصيانة والخبرات المطلوبة لتشغيله تفوق بكثير أي توفير مزعوم. من تجربتي الشخصية، رأيت فرقاً كاملة تضيع أسابيع في تكوين Jenkins فقط لتشغيل خط أنابيب بسيط، بينما نفس الخط يمكن إعداده على GitHub Actions في أقل من ساعة. الفرق الأساسي هنا هو التكامل: Actions جزء لا يتجزأ من GitHub، مما يعني أنه يفهم تلقائياً سياق المستودع، الفروع، الطلبات، وحتى تعليقات الكود.
أما بالنسبة لـ CircleCI، فهو بالتأكيد أفضل من Jenkins، لكنه يعاني من مشكلة أساسية: هو منتج منفصل يحتاج إلى تكامل مع GitHub. هذا يعني أنك ستضطر إلى إدارة مفاتيح الوصول، مزامنة الأحداث بين النظامين، والتعامل مع مشكلات مزامنة البيانات. في المقابل، GitHub Actions يعمل داخل بيئة GitHub نفسها، مما يعني أنه يحصل على جميع الأحداث في الوقت الفعلي وبدون تأخير. بالإضافة إلى ذلك، فإن نظام الـ Marketplace في GitHub يسمح لك بإضافة وظائف معقدة ببضع نقرات، بدلاً من كتابة سكربتات مخصصة لكل مهمة.
# مثال على workflow بسيط ولكنه قوي
name: CI Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
- run: npm run build
if: github.ref == 'refs/heads/main'
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: npm install -g aws-cdk
- run: cdk deploy --require-approval neverالخطأ الأول الذي يرتكبه معظم المطورين عند بدء استخدام GitHub Actions هو كتابة workflows طويلة ومعقدة في ملف واحد. هذا يشبه بناء ناطحة سحاب من قطعة واحدة من الخرسانة - قد تعمل في البداية، لكنها ستنهار عند أول زلزال. بدلاً من ذلك، يجب تقسيم الـ workflow إلى وحدات صغيرة ومستقلة، كل منها مسؤول عن مهمة محددة. مثلاً، بدلاً من كتابة workflow واحد يقوم بالبناء، الاختبار، والنشر، قم بإنشاء ثلاثة workflows منفصلة: واحد للبناء والاختبار، وآخر للنشر إلى بيئة staging، وثالث للنشر إلى الإنتاج. هذا ليس مجرد تنظيم أفضل، بل هو تصميم يسمح بإعادة الاستخدام والاستبدال السهل.
أحد المفاهيم الأساسية التي يجب فهمها هو الـ Event-Driven Architecture. GitHub Actions مبني على هذا المفهوم، حيث يتم تشغيل الـ workflows بناءً على أحداث محددة مثل push، pull request، أو حتى تعليق على issue. لكن المطورين غالباً ما ينسون أن هذه الأحداث يمكن تخصيصها بشكل دقيق جداً. مثلاً، يمكنك تشغيل workflow فقط عندما يتم تعديل ملفات معينة، أو عندما يتم دمج طلب معين من فرع معين. هذا النوع من التخصيص يمكن أن يوفر ساعات من وقت المعالجة ويقلل من تكاليف التشغيل بشكل كبير، خاصة في المشاريع الكبيرة.
# مثال على workflow ذكي يستجيب فقط للتغييرات ذات الصلة
name: Smart CI
on:
push:
paths:
- 'src/**'
- 'package.json'
- 'package-lock.json'
pull_request:
paths:
- 'src/**'
- 'package.json'
- 'package-lock.json'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
- name: Check for changes in specific files
id: changes
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'src/backend/**'
frontend:
- 'src/frontend/**'
- name: Run backend tests only if backend changed
if: steps.changes.outputs.backend == 'true'
run: npm run test:backend
- name: Run frontend tests only if frontend changed
if: steps.changes.outputs.fr= 'true'
run: npm run test:frontendإذا كنت لا تستخدم الـ caching في GitHub Actions، فأنت تضيع وقتاً ومالاً بشكل يومي. تخيل أنك تقوم ببناء مشروع Node.js كبير، وكل مرة تقوم فيها بتشغيل الـ CI، يتم تحميل جميع الـ dependencies من الصفر. هذا ليس فقط بطيء، بل هو أيضاً مكلف إذا كنت تستخدم runners مدفوعة. الحل هو استخدام الـ caching الذكي، الذي يخزن الـ dependencies بين الـ runs ويقلل وقت البناء بشكل كبير. لكن الحيلة هنا هي في كيفية إعداد الـ cache بشكل صحيح - الكثير من المطورين يستخدمون الـ cache بشكل سطحي، مما يؤدي إلى مشاكل مثل استخدام dependencies قديمة أو فقدان الـ cache بشكل متكرر.
الطريقة الصحيحة لاستخدام الـ caching تعتمد على نوع المشروع. بالنسبة لمشاريع Node.js، يجب تخزين مجلد node_modules بأكمله، بالإضافة إلى ملفات lock مثل package-lock.json أو yarn.lock. بالنسبة لمشاريع Python، يجب تخزين مجلد __pycache__ ومجلد site-packages. لكن الأهم من ذلك هو استخدام مفتاح cache متغير يعتمد على محتوى ملفات التبعية. هذا يضمن أن الـ cache يتم تحديثه فقط عندما تتغير الـ dependencies بالفعل، وليس مع كل تعديل في الكود. في أحد المشاريع التي عملت عليها، قمنا بتقليل وقت البناء من ١٢ دقيقة إلى ٤٥ ثانية فقط باستخدام هذه التقنية.
# مثال متقدم على استخدام الـ caching في Node.js
name: CI with Smart Caching
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Get npm cache directory
id: npm-cache-dir
run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
id: npm-cache
with:
path: ${{ steps.npm-cache-dir.outputs.dir }}
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm run build
- run: npm test
# مثال آخر لمشروع Python
python-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Get pip cache dir
id: pip-cache
run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- run: pip install -r requirements.txt
- run: python -m pytestالخطأ الأول والأكثر شيوعاً هو عدم استخدام secrets بشكل صحيح. الكثير من المطورين يضعون مفاتيح API ومعلومات حساسة مباشرة في ملفات workflow، مما يعرض المشروع لخطر كبير. المشكلة ليست فقط في الأمان، بل أيضاً في قابلية الصيانة - إذا احتجت لتغيير مفتاح معين، ستضطر لتعديل كل ملف workflow يستخدمه. الحل هو استخدام secrets في GitHub، التي تسمح بتخزين المعلومات الحساسة بشكل آمن وإعادة استخدامها في أي workflow. لكن حتى هنا، هناك أخطاء شائعة: مثل استخدام secrets في الأماكن الخطأ أو عدم تحديثها بشكل دوري.
الخطأ الثاني هو عدم فهم الفرق بين الـ jobs المتسلسلة والمتوازية. الكثير من المطورين يكتبون workflows حيث كل job يعتمد على السابق، حتى عندما لا يكون هناك حاجة لذلك. هذا يؤدي إلى تباطؤ كبير في التنفيذ. مثلاً، إذا كان لديك job لبناء الكود وآخر لتشغيل الاختبارات، يمكنك تشغيلهما في نفس الوقت بدلاً من الانتظار حتى ينتهي البناء. لكن الحذر هنا مهم: يجب أن تفهم بالضبط أي jobs يمكن تشغيلها بشكل متوازٍ وأيها يحتاج إلى التسلسل. في أحد المشاريع، قمنا بتقليل وقت التنفيذ من ٢٥ دقيقة إلى ٨ دقائق فقط عن طريق إعادة تنظيم الـ jobs بشكل ذكي.
في Microsoft، يستخدمون GitHub Actions لتشغيل أكثر من مليون workflow يومياً عبر آلاف المستودعات. السر وراء هذا الحجم الهائل هو استخدامهم لـ reusable workflows، وهي ميزة تسمح لك بتعريف workflow واحد واستخدامه في مشاريع متعددة. بدلاً من تكرار نفس الكود في كل مستودع، يمكنك إنشاء workflow مركزي وإعادة استخدامه في أي مكان آخر. هذا ليس فقط يوفر الوقت، بل أيضاً يضمن الاتساق عبر جميع المشاريع. مثلاً، لديهم workflow مركزي لتشغيل اختبارات الأمان يستخدم في جميع مشاريعهم المفتوحة المصدر.
شركة أخرى تستخدم GitHub Actions بشكل مبتكر هي Netflix. في أحد عروضهم التقنية، تحدثوا عن كيفية استخدامهم لـ Actions لأتمتة عملية نشر تطبيقاتهم على AWS. بدلاً من الاعتماد على أدوات نشر خارجية، قاموا ببناء workflow كامل داخل GitHub يقوم ببناء صور Docker، نشرها على ECS، وإجراء اختبارات الحمل تلقائياً. ما يميز نهجهم هو استخدامهم لـ dynamic environments، حيث يتم إنشاء بيئة جديدة لكل طلب دمج ويتم تدميرها تلقائياً بعد انتهاء الاختبارات. هذا يضمن أن كل تغيير يتم اختباره في بيئة نظيفة ومعزولة.
# مثال على reusable workflow يمكن استخدامه في مشاريع متعددة
# ملف: .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: true
type: string
test-command:
required: false
type: string
default: 'npm test'
secrets:
CODECOV_TOKEN:
required: false
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: ${{ inputs.test-command }}
- name: Upload coverage to Codecov
if: env.CODECOV_TOKEN != ''
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}إحدى الميزات القوية في GitHub Actions والتي لا يستخدمها الكثيرون هي القدرة على إنشاء بيئات ديناميكية تختفي تلقائياً بعد انتهاء الـ workflow. هذا مفيد بشكل خاص لاختبار التغييرات في بيئة مشابهة للإنتاج دون الحاجة إلى الحفاظ على بيئات ثابتة. على سبيل المثال، يمكنك إنشاء workflow يقوم بإنشاء بيئة AWS جديدة لكل طلب دمج، تشغيل الاختبارات فيها، ثم تدميرها تلقائياً بعد الانتهاء. هذا لا يوفر التكاليف فقط، بل أيضاً يضمن أن كل اختبار يعمل في بيئة نظيفة ومعزولة.
لتنفيذ هذا، ستحتاج إلى استخدام أدوات مثل Terraform أو AWS CDK لإنشاء البنية التحتية عند الحاجة. المفتاح هنا هو استخدام الـ outputs من الـ jobs لإنشاء بيئة فريدة لكل تشغيل. مثلاً، يمكنك استخدام الـ run ID أو الـ commit SHA كجزء من اسم البيئة لضمان التفرد. في أحد المشاريع، استخدمنا هذه التقنية لإنشاء بيئات اختبار كاملة لتطبيقنا الذي يعمل على Kubernetes، حيث يتم إنشاء namespace جديد لكل طلب دمج ويتم تدميره تلقائياً بعد ٢٤ ساعة.
# مثال على workflow ينشئ بيئة AWS مؤقتة
name: Dynamic Environment
on:
pull_request:
branches: [ main ]
jobs:
create-environment:
runs-on: ubuntu-latest
outputs:
environment-name: ${{ steps.env-name.outputs.name }}
steps:
- id: env-name
run: echo "name=pr-${{ github.event.pull_request.number }}-${{ github.run_id }}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: npm install -g aws-cdk
- run: cdk deploy --context envName=${{ steps.env-name.outputs.name }} --require-approval never
test-environment:
needs: create-environment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Testing environment ${{ needs.create-environment.outputs.environment-name }}"
# أضف هنا خطوات الاختبار الفعلية
destroy-environment:
needs: [create-environment, test-environment]
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- run: npm install -g aws-cdk
- run: cdk destroy --context envName=${{ needs.create-environment.outputs.environment-name }} --forceإذا كنت ستأخذ شيئاً واحداً من هذا الدليل، فليكن هذا: ابدأ صغيراً، ثم قم بالتوسيع بشكل ذكي. لا تحاول أتمتة كل شيء دفعة واحدة - بدلاً من ذلك، اختر عملية واحدة مؤلمة في سير عملك اليومي وقم بأتمتتها أولاً. مثلاً، إذا كنت تقضي الكثير من الوقت في التحقق من تنسيق الكود، قم بإنشاء workflow بسيط يقوم بتشغيل ESLint أو Prettier على كل طلب دمج. بمجرد أن تعتاد على هذه العملية، قم بإضافة المزيد من الخطوات تدريجياً. المفتاح هنا هو أن تجعل الأتمتة جزءاً طبيعياً من سير عملك، بدلاً من محاولة فرضها دفعة واحدة.
وأخيراً، تذكر أن GitHub Actions ليس مجرد أداة تقنية - إنه تغيير في طريقة تفكيرك في التطوير. بدلاً من التفكير في المهام كوحدات منفصلة، ابدأ في التفكير في كيفية ربطها معاً في سلسلة سلسة. عندما تصل إلى النقطة التي يمكنك فيها الضغط على زر 'دمج' وأنت واثق تماماً أن كل شيء سيعمل كما هو متوقع، عندها ستعرف أنك استخدمت Actions بالطريقة الصحيحة. هذا هو الفرق بين الفرق التي تعمل بجد والفرق التي تعمل بذكاء.