أذكرها وكأنها البارحة. كانت ليلة خميس، وأجواء المكتب بدأت تهدأ. الكل يجهز نفسه لعطلة نهاية الأسبوع، وأنا كنت أضع اللمسات الأخيرة على ميزة جديدة عملت عليها طويلاً. فجأة، علت أصوات الإشعارات من قناة “Slack” الخاصة بالفريق، تلك الإشعارات الحمراء التي لا تبشر بخير أبداً. “🔴 Build Failed on main”.
ساد صمت رهيب في المكتب. نظرنا إلى بعضنا البعض، وبدأ السؤال الذي يكرهه كل مدير فريق: “مين آخر واحد عمل push؟”. تبيّن أن زميلاً لنا، بنية حسنة طبعاً، قام بدفع “تعديل بسيط” قبل دقائق. تعديل لم يقم بتشغيل الاختبارات المحلية عليه، لأنه كان “مجرد تغيير في ملف نصي”. لكن هذا التغيير البسيط كسر أحد الاختبارات الحساسة التي تعتمد على هذا الملف. وهكذا، تبخرت أحلام بداية العطلة الهادئة، وقضينا الساعتين التاليتين في تتبع الخطأ وإصلاحه وإعادة النشر. في تلك اللحظة، قلت لنفسي: “خلص، بكفي! مش معقول نضل هيك. لازم نلاقي حل جذري لهالقصة”.
المشكلة ليست في المبرمج، بل في العملية
بعد تلك الحادثة، جلست مع الفريق. لم يكن الهدف إلقاء اللوم على أحد. الخطأ البشري وارد، وكلنا نرتكبه. أنا شخصياً، أبو عمر، نسيت أكثر من مرة أن أشغل الـ “linter” (مدقق الشيفرة) قبل أن ألتزم بالكود، مما أدى إلى مشاكل لاحقاً. المشكلة الحقيقية كانت في اعتمادنا الكلي على الذاكرة والانضباط الشخصي.
نحن نعتمد على أنفسنا لنتذكر قائمة طويلة من المهام قبل كل عملية git commit أو git push:
- هل قمت بتنسيق الكود (Code Formatting)؟
- هل قمت بتشغيل مدقق الشيفرة (Linting)؟
- هل قمت بتشغيل كافة الاختبارات (Unit Tests)؟
- هل رسالة الالتزام (Commit Message) تتبع المعايير المتفق عليها؟
هذه العملية اليدوية هي وصفة للكارثة. إنها مجرد مسألة وقت قبل أن ينسى أحدهم خطوة ما تحت ضغط العمل، وتنفجر “القنبلة الموقوتة” في نظام التكامل المستمر (CI/CD) لدينا.
الحل السحري: لنتعرف على خطافات Git (Git Hooks)
هنا يأتي دور البطل الصامت في عالم Git: “الخطافات” أو Git Hooks. ببساطة، خطافات Git هي عبارة عن سكربتات (scripts) برمجية يمكنك إعدادها لتعمل تلقائياً عند وقوع أحداث معينة في Git. فكر فيها كحارس أمن يقف على باب مستودعك البرمجي.
قبل أن تسمح لأي “التزام” (commit) بالدخول، يقوم هذا الحارس بفحصه والتأكد من مطابقته للقواعد. إذا لم يطابقها، يمنعه من الدخول ويرجعه لصاحبه ليقوم بتعديله.
أشهر أنواع الخطافات:
- pre-commit: يعمل هذا الخطاف قبل إنشاء الالتزام مباشرةً. إنه المكان المثالي لتشغيل مهام سريعة مثل تنسيق الكود والتحقق من الأخطاء البسيطة (linting). إذا فشل السكربت، يتم إلغاء عملية الالتزام.
- commit-msg: يعمل بعد كتابة رسالة الالتزام وقبل إنشائه فعلياً. رائع لفرض معايير محددة على رسائل الالتزام (مثل Conventional Commits).
- pre-push: يعمل قبل دفع (push) التزاماتك إلى المستودع البعيد (remote repository). هذا هو المكان الأنسب لتشغيل مهام أطول وأكثر شمولاً، مثل مجموعة الاختبارات الكاملة (test suite).
تبدو فكرة رائعة، أليس كذلك؟ لكن هناك مشكلة صغيرة. ملفات الخطافات هذه تعيش داخل مجلد .git/hooks، وهذا المجلد لا يتم تتبعه بواسطة Git. هذا يعني أن كل مطور في الفريق يجب أن يقوم بإعداد هذه الخطافات يدوياً على جهازه، وإذا قمنا بتحديث أحدها، يجب على الجميع تحديثه يدوياً أيضاً. “شغلانة” ما بعدها “شغلانة” وغير عملية بالمرة.
“بس يا أبو عمر، هالشغلانة معقدة!” – نقدم لكم Husky 🐺
لهذا السبب بالذات، ظهرت أدوات لتسهيل هذه العملية. وأشهرها وأروعها برأيي هي أداة اسمها Husky. Husky هي أداة بسيطة تجعل إدارة خطافات Git أمراً سهلاً للغاية وتسمح بمشاركتها مع كل أعضاء الفريق عبر package.json.
Husky يقوم بتوصيل خطافات Git بأوامر npm scripts، مما يعني أن الإعدادات تصبح جزءاً من المشروع نفسه، ويحصل عليها كل مطور تلقائياً بمجرد تثبيت تبعيات المشروع.
لنطبق الأمر عملياً: إعداد Husky خطوة بخطوة
لنفترض أن لدينا مشروع JavaScript/TypeScript ونريد أن نتأكد من أن كل كود يتم الالتزام به منسق وخالٍ من أخطاء الـ linting.
الخطوة 1: تثبيت Husky
# باستخدام npm
npm install husky --save-dev
# أو باستخدام yarn
yarn add husky --dev
الخطوة 2: تفعيل خطافات Git في مشروعك
بعد التثبيت، قم بتشغيل هذا الأمر لمرة واحدة:
npx husky install
هذا الأمر سيقوم بإنشاء مجلد .husky في مشروعك. لجعل هذه العملية تلقائية لكل من ينضم للفريق، أضف السكربت التالي إلى ملف package.json:
# هذا يضمن أن `husky install` يعمل تلقائياً بعد كل `npm install`
npm set-script prepare "husky install"
الخطوة 3: إنشاء أول خطاف (pre-commit)
لنفترض أن لدينا سكربت اسمه npm run lint. نريد تشغيله قبل كل عملية commit. الأمر بسيط:
npx husky add .husky/pre-commit "npm run lint"
هذا الأمر سينشئ ملفاً جديداً باسم pre-commit داخل مجلد .husky يحتوي على الأمر الذي حددناه. الآن، في كل مرة يحاول أي مطور في الفريق عمل git commit، سيتم تشغيل npm run lint تلقائياً. إذا فشل الأمر، ستفشل عملية الـ commit. رائع!
نصيحة من أبو عمر: تشغيل الـ linter على المشروع بأكمله في كل مرة قد يكون بطيئاً، خاصة في المشاريع الكبيرة. هذا قد يزعج المطورين ويدفعهم لتجاوز الخطاف. فما الحل؟
شريك Husky المفضل: `lint-staged`
هنا يأتي دور أداة أخرى عبقرية تعمل بتناغم تام مع Husky، وهي lint-staged. فكرتها بسيطة: لماذا نشغل الأوامر على كل ملفات المشروع، بينما يمكننا تشغيلها فقط على الملفات التي تم تعديلها وإضافتها إلى منطقة التجهيز (staged files)؟
الخطوة 1: تثبيت `lint-staged`
npm install lint-staged --save-dev
الخطوة 2: إعداد `lint-staged`
أضف الإعدادات الخاصة به إلى ملف package.json:
// في ملف package.json
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,md}": "prettier --write"
}
هذا الإعداد يخبر lint-staged بالآتي: لأي ملف JavaScript أو TypeScript جاهز للالتزام، قم بتشغيل eslint --fix ثم prettier --write عليه. ولأي ملفات أخرى مثل JSON أو CSS، قم فقط بتنسيقها باستخدام Prettier.
الخطوة 3: تحديث خطاف `pre-commit`
الآن، بدلاً من تشغيل npm run lint، سنجعل الخطاف يشغل lint-staged:
npx husky set .husky/pre-commit "npx lint-staged"
والآن، أصبحت العملية آلية وذكية وسريعة جداً. قبل كل commit، سيتم فحص وتنسيق الملفات المعدلة فقط. يا سلام! انتهى زمن الالتزامات البرمجية الفوضوية.
سيناريوهات متقدمة ونصائح من الميدان
بمجرد أن تتقن الأساسيات، يمكنك فعل الكثير مع Husky.
فرض معايير على رسائل الالتزام (Commit Messages)
تاريخ Git النظيف هو كنز لا يقدر بثمن. باستخدام أداة مثل commitlint مع خطاف commit-msg، يمكنك إجبار الفريق على اتباع نمط معين في كتابة الرسائل (مثل Conventional Commits)، مما يسهل تتبع التغييرات وإنشاء سجلات التغيير (Changelogs) تلقائياً.
# تثبيت الأدوات
npm install @commitlint/cli @commitlint/config-conventional --save-dev
# إنشاء ملف الإعدادات
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# إضافة الخطاف
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
تشغيل الاختبارات قبل الدفع (pre-push)
لتجنب كسر “البناء” على الفرع الرئيسي، يمكنك إضافة خطاف pre-push لتشغيل الاختبارات الأساسية.
npx husky add .husky/pre-push "npm test"
نصيحة عملية جداً من أبو عمر: كن حذراً مع خطاف
pre-push. إذا كانت مجموعة الاختبارات لديك تستغرق 5 أو 10 دقائق، فسيشعر المطورون بالإحباط وسيبحثون عن طرق لتجاوزها (مثل استخدامgit push --no-verify). القاعدة الذهبية هي: اجعل الخطافات المحلية سريعة. استخدمpre-pushللاختبارات السريعة والحرجة فقط، واترك الاختبارات الشاملة والطويلة لخادم الـ CI/CD. التوازن هو مفتاح النجاح.
الخلاصة: من قنابل موقوتة إلى التزامات واثقة 😌
رحلتنا بدأت من الفوضى والإحباط بسبب أخطاء كان من الممكن تفاديها بسهولة. المشكلة لم تكن في قدراتنا كمبرمجين، بل في اعتمادنا على عمليات يدوية هشة. من خلال تبني الأتمتة في أبسط صورها عبر خطافات Git، وبمساعدة أدوات مثل Husky و lint-staged، حولنا عملية الالتزام البرمجي من حقل ألغام إلى مسار آمن وموثوق.
النتائج كانت مذهلة:
- جودة كود أعلى بشكل ملحوظ.
- عدد شبه منعدم لحالات “كسر البناء” بسبب أخطاء بسيطة.
- وقت أقل ضائع في تصحيح الأخطاء، ووقت أكثر في كتابة الميزات الجديدة.
- قاعدة كود متسقة وواضحة بغض النظر عمن كتب الكود.
يا جماعة، استثمار بضع دقائق في إعداد هذه الأدوات سيوفر عليكم ساعات، بل أياماً، من وجع الراس لاحقاً. أتمتة هذه العمليات ليست رفاهية، بل هي من أساسيات الهندسة البرمجية الحديثة التي تفرق بين فريق هاوٍ وفريق محترف. ابدأوا اليوم، وسترون الفرق بأنفسكم. يلا، شدّوا حيلكم! 💪