Jenkins والتكامل المستمر المؤسسي

وظائف Freestyle مقابل Pipelines

18 دقيقة الدرس 2 من 28

وظائف Freestyle مقابل Pipelines

بدأت رحلة Jenkins تحت اسم Hudson عام 2004 — أداة مبنية على مفهوم واجهة رسومية بسيطة: اضغط على نموذج، أدخل سكريبت shell، وابدأ البناء. يُسمى هذا النموذج Freestyle job. هيمن هذا النهج على اعتماد CI لمدة عقد. ثم، حوالي 2016، قدّمت Jenkins إضافة Pipeline ولم تعد الصناعة تنظر للوراء. فهم لماذا حدث هذا التحول ليس أمراً نظرياً — بل يحدد كيف تُصمّم كل نظام CI/CD على نطاق إنتاجي حقيقي.

ما هي وظيفة Freestyle فعلاً

تُخزّن وظيفة Freestyle إعداداتها كملف XML داخل نظام الملفات الخاص بـ Jenkins، تحت المسار $JENKINS_HOME/jobs/<name>/config.xml. كل إعداد — رابط Git، وخطوات البناء، والإجراءات البعدية، والمشغّلات — يعيش داخل هذا الكائن XML الذي تعرضه واجهة Jenkins كنموذج. يمكنك تصديره، لكنك لن تُعدّله يدوياً: لم يُصمَّم ليقرأه البشر.

يمنحك نموذج Freestyle: خطوة shell، وخطوة Ant/Maven، وعدداً من الإضافات، وبعض المنطق الشرطي عبر إعدادات الإضافات. إنه بطبيعته تسلسلي وأحادي العقدة. إن أردت توزيع البناء عبر عدة agents، عليك ربط عدة وظائف Freestyle بمشغّلات downstream — نهج هش يجعل تتبع الأعطال عبر الوظائف أمراً مؤلماً.

مخاطر الإنتاج — انجراف الإعدادات: في منظمة حقيقية تضم عشرات وظائف Freestyle، تعيش الإعدادات حصراً داخل Jenkins. لا أحد يراجعها في pull request. لا أحد يُعيدها بـ git revert. حين يضغط مهندس "Save" مع خطأ مطبعي في خطوة shell، الأثر الوحيد هو مكوّن تسجيل التدقيق — إن تذكّر أحد تثبيته أصلاً. بعد ستة أشهر من التغييرات المتراكمة، لا توجد وظيفتان متطابقتا الإعداد، ويتحول إعداد مهندس جديد إلى عملية استكشاف أثرية تستغرق نصف يوم. هذا هو الانجراف في الإعدادات، وهو ما دمّر موثوقية CI في الشركات التي بقيت على Freestyle طويلاً.

Pipeline-as-Code: تحوّل جذري في النموذج

قدّمت إضافة Pipeline مفهوماً مختلفاً جذرياً: تعريف البناء يعيش في ملف يُسمى Jenkinsfile يوضع في جذر مستودع المصدر، يُدار بالإصدارات جنباً إلى جنب مع الكود الذي يبنيه. أصبح الـ pipeline الآن كوداً. كل تغيير في الـ pipeline هو commit. كل commit له مؤلف وفرق ومراجعة كود.

هذا التحول الواحد يُتيح سيلاً من الفوائد المستحيلة مع Freestyle:

  • مراجعة الأقران: تمر تغييرات CI بنفس عملية pull request لتغييرات التطبيق. يلتقط مهندس أول الـ flag المفقود --no-cache قبل وصوله للإنتاج.
  • التعافي من الكوارث: أصبح Jenkins عديم الحالة بالنسبة لمنطق الـ pipeline. افقد الخادم، أعد بناءه، وكل pipeline يُستعاد عند أول git clone.
  • التفريع: فروع مختلفة تحمل إصدارات مختلفة من Jenkinsfile. يمكن لفرع الميزة تجربة مرحلة بناء جديدة دون المساس بـ pipeline الفرع الرئيسي.
  • التنفيذ المتوازي: تمتلك Pipelines كتلة parallel من الدرجة الأولى. تشغيل اختبارات الوحدة والتكامل والفحوصات الأمنية معاً هو بناء من سطر واحد — لا سلسلة وظائف متشابكة.
  • المتانة: تحتفظ Pipelines بحالة تنفيذها على القرص. إعادة تشغيل Jenkins في منتصف بناء لا تُلغيه (مع إعدادات المهام الدائمة).
المفهوم الأساسي: يُعدّ الـ Jenkinsfile المصدر الوحيد للحقيقة لما يحدث لكودك من الـ commit حتى النشر. يُراجع ويُصدَّر بإصدارات ويتفرع ويُختبر مثل أي قطعة كود أخرى. ملف config.xml لوظيفة Freestyle لا يمتلك أياً من هذه الخصائص.

مقارنة Freestyle مقابل Pipeline

Freestyle Jobs vs Pipeline: feature comparison Capability Freestyle Job Pipeline (Jenkinsfile) Version controlled No (XML in Jenkins) Yes (git repo) Pull-request review No Yes Parallel stages Workaround only First-class parallel{} Survives Jenkins restart No — build aborts Yes (durable steps) Multi-agent builds Chained jobs only agent{} per stage Disaster recovery Restore XML backups git clone + run
مقارنة وظائف Freestyle مقابل Pipelines عبر ستة قدرات حرجة في بيئات الإنتاج — تتفوق Pipelines في كل بُعد مهم على النطاق الواسع.

أساسيات Jenkinsfile: بناء الجملة التصريحي

يمكن كتابة Jenkinsfile بأسلوبين: Declarative (تصريحي) وScripted (سكريبت). يُعدّ التصريحي هو الافتراضي الموصى به لجميع pipelines الجديدة — يُطبّق هيكلاً محدداً، ويتحقق من صحة البنية قبل بدء البناء، وأسهل بكثير لأي مهندس جديد في قراءته. إليك هيكلاً واقعياً جاهزاً للإنتاج يغطي المفاهيم التي ستستخدمها يومياً:

// Jenkinsfile — Declarative Pipeline (ضعه في جذر المستودع) pipeline { agent { label 'linux-amd64' } // تشغيل على أي agent يحمل هذا التسمية options { timeout(time: 30, unit: 'MINUTES') disableConcurrentBuilds() // منع تداخل البنيات على نفس الفرع buildDiscarder(logRotator(numToKeepStr: '20')) } environment { APP_IMAGE = "myorg/myapp:${GIT_COMMIT[0..7]}" REGISTRY = "registry.example.com" } stages { stage('Checkout') { steps { checkout scm // ربط تلقائي بالمستودع المُشغِّل } } stage('Build') { steps { sh 'docker build -t $APP_IMAGE .' } } stage('Test') { parallel { stage('Unit Tests') { steps { sh 'make test-unit' } } stage('Lint') { steps { sh 'make lint' } } } } stage('Push') { when { branch 'main' } // شرط: ادفع فقط من الفرع الرئيسي steps { withCredentials([usernamePassword( credentialsId: 'registry-creds', usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS' )]) { sh 'echo $REG_PASS | docker login $REGISTRY -u $REG_USER --password-stdin' sh 'docker push $APP_IMAGE' } } } } post { always { cleanWs() } failure { slackSend channel: '#ci-alerts', message: "FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}" } } }

تصفّح الكتل الرئيسية: agent يُثبّت الـ pipeline بالكامل على agents تحمل تسمية محددة. options يُطبّق حراساً عالميين — مهلة 30 دقيقة صارمة تُنهي البنيات المتجمّدة قبل أن تستهلك منافذ التنفيذ. environment يحقن متغيرات متاحة لكل خطوة. parallel داخل stage يُشعّب العمل عبر مسارات منطقية. when يحرس مرحلة بشرط يُقيَّم وقت التشغيل — لا يتنفذ كود ولا تُستهلك بيانات اعتماد إن كان الشرط خاطئاً. كتل post تعمل دائماً بصرف النظر عن النتيجة، مما يجعلها المكان الصحيح للتنظيف والإشعارات.

كيف يكتشف Jenkins ملف Jenkinsfile

حين تُنشئ وظيفة Pipeline في واجهة Jenkins، تُشير إلى مستودعك وتُحدد الفرع والمسار إلى Jenkinsfile (افتراضياً: Jenkinsfile في الجذر). يجلب Jenkins الملف عند كل مشغّل بناء، لذا يظل تعريف الـ pipeline دائماً متزامناً مع الـ commit قيد البناء — لا توجد خطوة "مزامنة" منفصلة. هذا هو الفرق الجوهري عن وظيفة Freestyle حيث تبقى الإعدادات محبوسة في Jenkins بغض النظر عما تغيّر في git.

ممارسة احترافية — تخصيص الفروع: يمكن لـ Jenkinsfile في فرع الميزة أن يُشير إلى تسمية agent مختلفة، أو يتخطى مرحلة Push بشرط when{ branch 'main' }، أو يُضيف مرحلة نشر canary — كل ذلك دون المساس بإعداد الفرع الرئيسي. يتيح هذا للفرق تطوير تغييرات CI/CD بأمان. استخدم وظائف Multibranch Pipeline (تُغطى في الدرس 8) ليكتشف Jenkins تلقائياً كل فرع يحتوي على Jenkinsfile ويبنيه.

متى لا تزال Freestyle مقبولة

لم تُهمَل وظائف Freestyle — لا تزال مشحونة مع Jenkins وستستمر كذلك. ثمة سيناريوان تظل فيهما معقولة: المهام الإدارية الاستثنائية (تشغيل سكريبت لتدوير مفتاح، أو تشغيل نسخة احتياطية لقاعدة البيانات) التي لا يوجد مبرر لإضافتها لنظام الإصدارات، والأنظمة القديمة التي لا تملكها ولا تستطيع ترحيلها. لكن لكل العمل الجديد على الأساس الصحيح، يُعدّ pipeline-as-code الخيار الوحيد القابل للدفاع عنه على النطاق المهني.

مسار الترحيل العملي: أنشئ وظيفة Pipeline جديدة تُشير إلى Jenkinsfile تكتبه، شغّلها بالتوازي مع وظيفة Freestyle لمدة sprint واحد، تحقق أنها تُنتج نفس الـ artifacts ونتائج الاختبار، ثم احذف وظيفة Freestyle. لا ترحّل بنسخ خطوات shell — عاملها كفرصة إعادة كتابة لإضافة التوازي الصحيح والمهل والمعالجات البعدية التي لم تمتلكها Freestyle أبداً.