أتمتة مهام الكود: دليل إضافة خطافات Git (Commit Hooks) باستخدام Husky
أتمتة مهام الكود: دليل إضافة خطافات Git (Commit Hooks) باستخدام Husky
في عالم تطوير البرمجيات الحديث، تتعدد الأدوات التي تساعدنا على أتمتة مهام الكود، مثل فحص الأخطاء النحوية باستخدام ESLint وتنسيق الكود باستخدام Prettier. ومع ذلك، قد لا يتذكر كل عضو في الفريق تشغيل هذه الأوامر في كل مرة يقوم فيها بالالتزام (commit) بالتغييرات. فكيف يمكننا استخدام Husky لإضافة خطافات Git لتشغيلها تلقائياً؟
ما هي خطافات Git (Git Hooks)؟
خطافات Git Hooks هي نصوص برمجية (scripts) يمكنك إعدادها للتشغيل في أحداث معينة ضمن دورة حياة Git. تشمل هذه الأحداث مراحل مختلفة من عملية الالتزام (commit)، مثل ما قبل الالتزام (pre-commit) وما بعد الالتزام (post-commit).
تُعد هذه الخطافات مفيدة للغاية، حيث تتيح للمطورين تشغيل مهام كود مخصصة أو حتى فرض معايير معينة عن طريق أتمتة تشغيل نصوص برمجية أخرى لتنفيذ تلك المهام. على سبيل المثال، يمكنك استخدام خطاف pre-commit للتأكد من أن الكود يتبع معايير تنسيق محددة قبل أن يتم الالتزام به، أو لمنع رفع مفاتيح الوصول السرية (secret access keys) إلى المستودع.
ما هو Husky؟
Husky هو أداة قوية وسهلة الاستخدام تتيح لنا إدارة خطافات Git وتشغيل النصوص البرمجية التي نريدها في المراحل المحددة. يعمل Husky عن طريق تضمين كائن خاص به مباشرة داخل ملف package.json الخاص بمشروعك، والذي يقوم بتهيئة Husky لتشغيل النصوص البرمجية التي نحددها. بعد ذلك، يتولى Husky إدارة النقطة التي سيتم فيها تشغيل نصوصنا البرمجية ضمن دورة حياة Git.
ماذا سنبني في هذا الدليل؟
سنقوم بإعداد مشروع بسيط يمكننا استخدامه لاختبار خطافات Git. على الرغم من أنه يمكنك متابعة هذا الدليل باستخدام أي مشروع تعمل عليه، إلا أننا سنستخدم Next.js كنقطة بداية لهذا المشروع، وذلك ببساطة لأنه يمكننا تشغيل أمر واحد لبدء المشروع.
أحد الاعتبارات عند متابعة هذا المشروع هو أننا سنستخدم Prettier كمثال لما يمكنك فعله باستخدام خطافات Git. Prettier هي أداة ستقوم بتنسيق الكود الخاص بنا تلقائياً، وهذا قد يسبب بعض التوتر إذا لم تكن تتوقع حدوثه. ستتيح لك المتابعة معي باستخدام مشروع Next.js اختبار ذلك دون إجراء أي تغييرات غير مقصودة.
بالنسبة لاختبار خطافات Git، سنبدأ بإضافة عبارة بسيطة في سطر الأوامر لنرى Husky يعمل. ولكننا سنختبر أيضاً إضافة Prettier، والذي سيقوم بتنسيق الكود الخاص بنا تلقائياً.
أخيراً، في وقت كتابة هذا الدليل، أصدر Husky إصداراً تجريبياً (v5 Alpha) لحل خطافات Git الخاص بهم. نظراً لأنه لا يزال إصداراً تجريبياً، سنمضي قدماً مع الإصدار v4، والذي يتيح لنا تثبيت Husky بسهولة باستخدام npm.
الخطوة 0: إعداد مشروع جديد
كما ذكرت، يمكنك حقاً اتباع نفس الخطوات هنا مع أي مشروع تتم إدارته بملف package.json. استخدام Next.js قد يكون مبالغاً فيه لهذا الشرح، ولكن الهدف هو تقليل الخطوات اللازمة للبدء بالعمل مع Husky.
إنشاء مشروع Next.js
للبدء بمشروع Next.js، انتقل إلى الدليل الذي تريد بدء مشروعك فيه وقم بتشغيل الأمر التالي:
yarn create next-app my-husky-project
# or npx create-next-app my-husky-project
ملاحظة: لا تتردد في استبدال my-husky-project بأي اسم ترغب في تسمية دليلك به. سيؤدي هذا إلى إنشاء مجلد جديد، وإنشاء مشروع Next.js جديد، وتثبيت جميع التبعيات.

بمجرد الانتهاء، انتقل إلى هذا المجلد الجديد، ويجب أن نكون جاهزين للبدء!
الخطوة 1: تثبيت Husky في المشروع
لتثبيت Husky، يمكننا استخدام yarn أو npm:
yarn add husky
# or npm install husky
ملاحظة: إذا كان تثبيت Husky في هذه المرحلة يقوم بتثبيت الإصدار v5، فهذا يعني أن الإصدار v5 قد تم إصداره رسمياً. يرجى مراجعة وثائق Husky المحدثة أو يمكنك تثبيت أحدث إصدار v4 عن طريق تحديد husky@4.3.0 (أو أياً كان أحدث إصدار) عند التثبيت.
بمجرد الانتهاء من تثبيت الحزمة، يجب أن نكون جاهزين للعمل مع Husky.
الخطوة 2: تهيئة Husky لتشغيل خطافات Git
بعد ذلك، سنقوم بإعداد Husky حتى نتمكن من استخدامه لخطافات Git الخاصة بنا.
إضافة خاصية Husky إلى ملف package.json
داخل ملف package.json الخاص بنا، أنشئ خاصية جديدة تسمى husky بكائن فارغ:
"husky": {}
يمكنك إضافة هذا في أي مكان تريده في ملف package.json، ولكننا سنضيفه مباشرة أسفل خاصية scripts لتسهيل إدارتها معاً.
تحديد الخطافات (Hooks)
داخل كائن husky، نريد إضافة خاصية أخرى تسمى hooks تحدد أيضاً كائناً فارغاً:
"husky": {
"hooks": {}
}
هنا سنضيف خطافات Git الخاصة بنا. يدعم Husky تقريباً جميع خطافات Git المحددة بواسطة Git، لذا يمكننا أن نكون مرنين قدر الإمكان ضمن تدفق أحداث Git الخاص بنا.
لاختبار هذا، قمت بإنشاء فرع جديد أضفت فيه حرفياً كل خطاف Git من تلك الصفحة، بما في ذلك نص برمجي يكتب ببساطة إلى الطرفية [Husky] event name.
ملاحظة: لا تشعر أنك بحاجة إلى فعل هذا إلا إذا كنت فضولياً. الهدف هو أن أتمكن من أن أريك بمثالي كيف يعمل.
"husky": {
"hooks": {
"applypatch-msg": "echo \"[Husky] applypatch-msg\"",
"pre-applypatch": "echo \"[Husky] pre-applypatch\"",
"post-applypatch": "echo \"[Husky] post-applypatch\"",
"pre-commit": "echo \"[Husky] pre-commit\"",
...
}
}
ما سيفعله هذا هو إخبار Husky أنه في كل مرحلة يُسمح لنا فيها بالتدخل في Git، أخبرنا! عندما أقوم بالالتزام بهذا التغيير، يمكننا أن نرى على الفور أن Husky يشغل بعض نصوصنا البرمجية.

هذه هي جميع الأحداث التي يسمح لنا Git بالتدخل فيها والتي تحدث أثناء عملية الالتزام (commit). وبالمثل، إذا قمت بدفع (push) هذه التغييرات إلى Github، يمكنني أن أرى أن عملية الدفع تشغل خطاف pre-push!

قد لا تستخدم معظم الخطافات التي يوفرها Husky و Git (رأينا عدداً قليلاً فقط بين هذين الأمرين). ولكن من الرائع أن نرى مدى قوة هذا، سواء كان ذلك لتشغيل كود يقوم بتنسيق الكود الخاص بنا، أو يمنع الالتزام بمفاتيح الوصول السرية، أو أي شيء آخر يمكن أن يساعد في أتمتة المهام الهامة لسير عملك.
يمكننا الآن أن نرى أنه يمكننا تهيئة Husky عن طريق تحديد التهيئة والخطافات مباشرة في ملف package.json الخاص بنا.
ملاحظة: إذا كنت ترغب في الاطلاع على الفرع الخاص بي الذي يتضمن كل خطاف Git للاختبار، يمكنك العثور عليه على Github.
الخطوة 3: استخدام Husky لتنسيق الكود باستخدام Prettier
أخيراً، لحالة استخدام واقعية، سنختبر استخدام Prettier لتنسيق الكود الخاص بنا تلقائياً. Prettier هي أداة تنسيق كود ذات رأي محدد تسمح لك بتنظيف الكود الخاص بك بسهولة لجعله يبدو وكأن شخصاً واحداً كتبه.
أهمية أدوات التنسيق مثل Prettier
عند العمل على الكود، خاصة مع فريق، من المهم الحفاظ على الاتساق حتى يعرف الجميع ما يمكن توقعه. سيساعد ذلك في منع الجدال حول الفاصلة المنقوطة في مراجعة الكود، ولكنه سيساعد أيضاً في اكتشاف أخطاء بناء الجملة ومنع الأخطاء البرمجية.
تحذير: تشغيل Prettier سيقوم بتنسيق جميع الكود الخاص بك تلقائياً. بينما سنختبر هذا قبل الالتزام بالتغييرات، بمجرد تطبيق هذا كخطاف Git، فإنه سيقوم بأتمتة هذه العملية.
تثبيت Prettier
للبدء مع Prettier، دعنا نثبته باستخدام مدير الحزم الخاص بنا:
yarn add prettier -D
# or npm install prettier --save-dev
ملاحظة: نقوم بتثبيت Prettier كـ devDependency حيث لا يحتاج تطبيقنا إلى هذا للتشغيل.
فحص الكود باستخدام Prettier
بعد ذلك، يمكننا إضافة نص برمجي جديد في ملف package.json الخاص بنا مما سيسهل تشغيل Prettier لاختبار هذا. داخل خاصية scripts، أضف:
"lint": "prettier --check ."
لهذا الاختبار الأول، سنقوم بتشغيله كـ “فحص” (check) مما سيسمح لنا برؤية الملفات التي ستتغير. قم بتشغيل ما يلي:
yarn lint
# or npm run lint
وبمجرد أن نفعل ذلك، يمكننا أن نرى أن Prettier يخبرنا أن الملفات المدرجة ستتغير.

في هذه المرحلة، سيبقى الكود الخاص بنا دون تغيير. ولكن إذا أردنا تشغيل Prettier حقاً لإجراء تلك التغييرات، يمكننا أولاً إضافة نص برمجي إضافي:
"format": "prettier --write ."
وإذا قمنا بتشغيل هذا النص البرمجي، فإنه سيقوم بتحديث جميع تلك الملفات لتنسيق الكود وفقاً لمواصفات Prettier.
تحذير: ملاحظة أخرى، تشغيل Prettier لكتابة التغييرات سيجري تغييرات في ملفاتك. هذه كلها تغييرات في نمط الكود لا ينبغي أن تؤثر على كيفية تشغيل الكود، ولكن على كيفية ظهور الكود. قبل تشغيل format، يجب عليك حفظ أي تغييرات عن طريق الالتزام (committing) باستخدام Git حتى تتمكن من التراجع عن التغييرات بسهولة إذا لم تكن راضياً عنها.
يمكنك الآن تشغيل النص البرمجي باستخدام:
yarn format
ويمكننا أن نرى أن Prettier قام بتحديث ملفاتنا!

دمج Prettier مع خطاف pre-commit في Husky
الآن الجزء ذو الصلة بهذا الشرح: يمكننا إضافة هذا كخطاف Git. بهذه الطريقة، عندما يحاول شخص ما الالتزام بالكود، يتم تشغيل Prettier قبل حفظ الكود. هذا يعني أننا سنحافظ دائماً على اتساق الكود مع نمط تنسيق Prettier.
داخل تهيئة خطافات Husky الخاصة بنا، دعنا نضيف:
"husky": {
"hooks": {
"pre-commit": "prettier --write . && git add -A ."
}
}
إذا لاحظت في خطاف pre-commit الخاص بنا، فإننا نضيف أيضاً git add -A .. عندما يعمل Husky، فإنه ببساطة يقوم بتشغيل النص البرمجي المقدم. عند تشغيل أمر Prettier الخاص بنا، فإننا نقوم فقط بتنسيق الكود، ولكننا لا نحفظ هذه التغييرات كجزء من العملية. لذلك نستخدم git add لتخزين جميع هذه التغييرات وتضمينها في الالتزام (commit).
لاختبار هذا، قمت بالتراجع عن التغييرات في جميع الملفات التي تم تنسيقها من قبل. إذا كنت تتابع نفس المشروع، يمكنك تشغيل:
git checkout pages
مما سيعيد تعيين جميع التغييرات في مجلد pages إلى آخر التزام (commit). الآن، دعنا نحاول إضافة جميع ملفاتنا باستخدام Git والالتزام بالتغييرات.

وبمجرد تشغيل أمر الالتزام (commit) الخاص بنا، يمكننا أن نرى أن خطاف pre-commit الخاص بـ Husky يبدأ بالفعل وينسق الكود الخاص بنا!
خطوات إضافية لتحسين سير العمل
استخدام lint-staged لتشغيل التنسيق على الملفات المتغيرة فقط
نحن نستخدم Prettier مباشرة في خطاف pre-commit الخاص بنا ونحدد . مما يعني أنه سيتم تشغيله على جميع الملفات في كل مرة. يمكننا استخدام أداة تسمى lint-staged، والتي تتيح لنا الاستمرار في تشغيل خطافات Git باستخدام Husky، ولكنها ستعمل فقط على الملفات التي تم تحديدها (staged).
على سبيل المثال، إذا أردنا القيام بذلك باستخدام Husky و Prettier، فقد تبدو تهيئتنا كما يلي:
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*": "prettier --write"
}
كجزء من كيفية تشغيل lint-staged، فإنه سيقوم بإرفاق الملفات المتغيرة بنهاية عبارة Prettier الخاصة بنا تلقائياً. ستلاحظ أيضاً أننا لم نقم بتضمين git add .. سيقوم lint-staged أيضاً بإضافة أي تغييرات إلى Git لنا تلقائياً.
إعداد ملف تهيئة Prettier لتخصيص قواعد التنسيق
Prettier ذات رأي محدد جداً. هناك بعض الأشياء التي لا أفضلها شخصياً وقد تشعر بنفس الشيء. لحسن الحظ، يسمح لك Prettier بإعداد ملف تهيئة يمكنه تجاوز بعض تلك الملفات لجعل الكود الخاص بك بالطريقة التي تريدها أنت وفريقك.
تجاهل الملفات باستخدام .prettierignore
ربما لا تريد أيضاً تشغيل Prettier على “كل شيء” (ربما تفعل). يسمح لك Prettier بإعداد ملف .prettierignore مباشرة داخل جذر المشروع بجانب package.json، على غرار .gitignore، والذي يسمح لك بإخبار Prettier بالملفات التي لا ينبغي تشغيلها عليها.
الخلاصة التقنية
تُعد أتمتة مهام الكود باستخدام خطافات Git وأداة مثل Husky خطوة حاسمة نحو تحسين جودة الكود وزيادة كفاءة فرق التطوير. فمن خلال فرض معايير التنسيق (مثل Prettier) أو إجراء فحوصات الجودة (مثل ESLint) تلقائياً قبل كل التزام (commit)، نضمن أن الكود المضاف إلى المستودع يلتزم بأفضل الممارسات، مما يقلل من الأخطاء ويوفر الوقت المستغرق في المراجعات اليدوية. إن دمج هذه الأدوات بسلاسة في سير عمل Git لا يعزز فقط الانضباط البرمجي، بل يساهم أيضاً في بيئة عمل أكثر إنتاجية وتناغماً، حيث يمكن للمطورين التركيز على كتابة ميزات جديدة بدلاً من تصحيح الأخطاء النمطية أو تذكر تشغيل الأوامر الروتينية.