هوک‌های سمت کلاینت در گیت‌هاب (Git Client-Side Hooks) اسکریپت‌هایی هستند که روی دستگاه توسعه‌دهنده و در نقاط مشخصی از چرخه حیات Git اجرا می‌شوند و هدف اصلی آن‌ها، کشف خطا پیش از رسیدن کد به مخزن مرکزی است.

هوک‌های سمت کلاینت، اولین خط دفاعی در برابر کامیت‌های معیوب هستند. اما این خط دفاعی، یک ضعف بنیادی دارد: با یک فلگ ساده مانند --no-verify قابل دور زدن است. در پروژه‌های متعددی که زیرساخت Git را برای تیم‌ها راه‌اندازی کرده‌ام، دیده‌ام که تیم‌ها تنها بر هوک‌های سمت کلاینت تکیه می‌کنند و در نتیجه، کیفیت مخزن به رفتار اعضا وابسته می‌ماند. این نوشتار، هوک‌های سمت کلاینت را از پایه تا سطح پیشرفته بررسی می‌کند و نشان می‌دهد چگونه باید این هوک‌ها را با لایه‌های دیگر ترکیب کرد.

هوک سمت کلاینت، یک چراغ قرمز محلی است. اگر آن را نادیده بگیرید، چراغ قرمز بعدی در CI روشن می‌شود. بهترین رویکرد، ترکیب این دو چراغ است.

هوک سمت کلاینت چیست؟

هوک (Hook) در Git، اسکریپتی است که در نقاط مشخصی از چرخه حیات مخزن اجرا می‌شود. هوک‌های سمت کلاینت، در پوشه .git/hooks/ روی دستگاه توسعه‌دهنده قرار می‌گیرند و در رویدادهای محلی اجرا می‌شوند. Git در ویکی‌پدیا مرور جامعی از معماری داخلی آن ارائه می‌دهد.

چرا هوک سمت کلاینت مهم است؟

  • بازخورد سریع: خطا در چند ثانیه کشف می‌شود، نه پس از push و انتظار برای CI.
  • کاهش بار CI: کامیت‌های معیوب، به CI نمی‌رسند.
  • آموزش تیم: هوک، استانداردها را به‌طور عملی آموزش می‌دهد.
  • تجربه توسعه‌دهنده: کشف زودهنگام خطا، تجربه بهتری می‌سازد.

محل قرارگیری هوک‌ها

هوک‌ها در .git/hooks/ قرار می‌گیرند. اما این پوشه، در مخزن Git ردیابی نمی‌شود. یعنی هوک‌ها به‌طور خودکار بین اعضا توزیع نمی‌شوند. این محدودیت، یکی از دلایل اصلی شکل‌گیری ابزارهای خودکارسازی است.

زبان هوک‌ها

هوک‌ها می‌توانند هر اسکریپت اجرایی باشند: Bash، Python، Node.js. تنها شرط، اجرایی بودن فایل است (chmod +x).

انواع هوک‌های سمت کلاینت

Git مجموعه‌ای از هوک‌های سمت کلاینت دارد که در نقاط مختلف چرخه حیات اجرا می‌شوند.

هوک زمان اجرا کاربرد اصلی
pre-commit قبل از ثبت کامیت Lint، فرمت، تست سریع
prepare-commit-msg قبل از باز شدن ادیتور پیام تولید پیام پیش‌فرض
commit-msg پس از نوشتن پیام اعتبارسنجی الگوی پیام
post-commit پس از ثبت کامیت اعلان، لاگ
pre-push قبل از push تست، اعتبارسنجی
pre-rebase قبل از rebase جلوگیری از rebase روی کامیت‌های منتشرشده
post-checkout پس از checkout راه‌اندازی محیط، نصب وابستگی
post-merge پس از merge به‌روزرسانی وابستگی‌ها
pre-auto-gc قبل از Garbage Collection شرایط اجرای GC

هوک‌های پرکاربرد در تیم‌ها

در عمل، سه هوک بیشترین کاربرد را دارند: pre-commit، commit-msg و pre-push. دستورات پرکاربرد Git که هر توسعه‌دهنده باید بداند به این چرخه اشاره دارد.

هوک‌های کم‌کاربرد اما مفید

هوک‌هایی مانند post-checkout و post-merge در تیم‌هایی که محیط توسعه پیچیده دارند، ارزشمندند. مثلاً نصب خودکار وابستگی‌ها پس از هر checkout.

هوک pre-commit به تفصیل

هوک pre-commit، پرکاربردترین هوک سمت کلاینت است. این هوک قبل از باز شدن ادیتور پیام کامیت اجرا می‌شود و اگر Exit Code غیرصفر برگرداند، کامیت لغو می‌شود.

کاربردهای pre-commit

  • Lint کد (ESLint، Pylint، PHP_CodeSniffer)
  • فرمت‌دهی خودکار (Prettier، Black، PHP-CS-Fixer)
  • تست سریع واحد
  • بررسی فایل‌های حساس (Secrets)
  • بررسی خطای Whitespace

مثال pre-commit ساده

#!/bin/bash
# .git/hooks/pre-commit

# اجرای ESLint روی فایل‌های تغییریافته
files=$(git diff --cached --name-only --diff-filter=ACM | grep "\.js$")
if [ -n "$files" ]; then
  npx eslint $files
  if [ $? -ne 0 ]; then
    echo "ESLint found errors. Commit aborted."
    exit 1
  fi
fi
exit 0

مثال pre-commit با Prettier

#!/bin/bash
# .git/hooks/pre-commit

files=$(git diff --cached --name-only --diff-filter=ACM | grep -E "\.(js|ts|css|md)$")
for file in $files; do
  npx prettier --write "$file"
  git add "$file"
done
exit 0

در ESLint یا Prettier؛ کدام برای کیفیت کد مهم‌تر است؟ تفاوت این دو ابزار بررسی شده است. اصول کدنویسی تمیز در پروژه‌های وردپرس نیز به استانداردهای کیفیت کد اشاره دارد. استفاده از WordPress Coding Standards در پروژه‌ها نیز به این موضوع می‌پردازد.

بررسی Secrets در pre-commit

ابزارهایی مانند gitleaks و trufflehog می‌توانند در pre-commit اجرا شوند و از کامیت شدن کلیدهای API جلوگیری کنند. این لایه دفاعی، از فاش شدن تصادفی اعتبارنامه‌ها جلوگیری می‌کند. امنیت در PHP به این موضوع می‌پردازد.

Speed و بهینه‌سازی

هوک pre-commit باید سریع باشد (کمتر از ۳ ثانیه). اگر کند باشد، توسعه‌دهندگان آن را دور می‌زنند. راهکار: اجرای Lint فقط روی فایل‌های تغییریافته، نه کل پروژه.

هوک commit-msg

هوک commit-msg پس از نوشتن پیام کامیت و پیش از ثبت آن اجرا می‌شود. این هوک، برای اعتبارسنجی الگوی پیام استفاده می‌شود.

الگوی Conventional Commits

الگوی رایج، Conventional Commits است:

<type>(<scope>): <description>

انواع رایج: feat، fix، docs، style، refactor، test، chore.

مثال commit-msg

#!/bin/bash
# .git/hooks/commit-msg

msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,}"

if ! [[ "$msg" =~ $pattern ]]; then
  echo "Invalid commit message. Use Conventional Commits format."
  echo "Example: feat(auth): add login validation"
  exit 1
fi
exit 0

مزایای پیام استاندارد

  • تولید خودکار Changelog
  • نسخه‌بندی خودکار (Semantic Versioning)
  • جستجوی آسان‌تر در تاریخچه
  • درک بهتر هدف تغییرات

در مرج در Git: چگونه تغییرات شاخه‌ها را درست ادغام کنیم؟ به اهمیت پیام‌های استاندارد در فرآیند مرج اشاره کرده‌ام. برنچ در Git راهنمای مدیریت شاخه‌ها نیز به این موضوع می‌پردازد.

هوک pre-push

هوک pre-push قبل از push به مخزن ریموت اجرا می‌شود. اگر Exit Code غیرصفر برگرداند، push لغو می‌شود.

کاربردهای pre-push

  • اجرای تست‌های واحد
  • بررسی Build
  • اعتبارسنجی نسخه
  • بررسی عدم push مستقیم به main

مثال pre-push برای جلوگیری از push به main

#!/bin/bash
# .git/hooks/pre-push

remote="$1"
url="$2"

while read local_ref local_sha remote_ref remote_sha; do
  if [[ "$remote_ref" == "refs/heads/main" ]]; then
    echo "Direct push to main is not allowed. Use a Pull Request."
    exit 1
  fi
done
exit 0

مثال pre-push با تست

#!/bin/bash
# .git/hooks/pre-push

echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
  echo "Tests failed. Push aborted."
  exit 1
fi
exit 0

هزینه و بهینه‌سازی

اجرای تست کامل در pre-push می‌تواند کند باشد. راهکار: اجرای تست‌های سریع (Smoke Tests) در pre-push و تست‌های کامل در CI. GitHub Actions راهنمای خودکارسازی گردش کار به این تقسیم‌بندی اشاره دارد.

سایر هوک‌ها

هوک‌های دیگری نیز وجود دارند که در سناریوهای خاص مفیدند.

prepare-commit-msg

این هوک پیش از باز شدن ادیتور پیام کامیت اجرا می‌شود و می‌تواند پیام پیش‌فرض تولید کند. مثلاً افزودن شماره Issue به‌طور خودکار:

#!/bin/bash
# .git/hooks/prepare-commit-msg

branch=$(git symbolic-ref --short HEAD)
if [[ $branch =~ ^(feature|bugfix)/([A-Z]+-[0-9]+) ]]; then
  issue="${BASH_REMATCH[2]}"
  echo "[$issue] $(cat $1)" > "$1"
fi
exit 0

post-commit

پس از ثبت کامیت اجرا می‌شود. برای اعلان، لاگ یا به‌روزرسانی سیستم‌های جانبی. این هوک، Exit Code را نمی‌تواند برای لغو استفاده کند.

post-checkout

پس از هر checkout اجرا می‌شود. کاربرد اصلی: نصب خودکار وابستگی‌ها، پاک‌سازی کش، به‌روزرسانی فایل‌های محیطی. توسعه وردپرس با محیط لوکال چگونه انجام می‌شود؟ به این سناریو اشاره دارد.

#!/bin/bash
# .git/hooks/post-checkout

if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
  npm install
fi
exit 0

post-merge

پس از merge اجرا می‌شود. مناسب برای به‌روزرسانی وابستگی‌ها یا اجرای Migration. چگونه سایت وردپرسی را به هاست جدید منتقل کنیم؟ به Migration اشاره دارد.

pre-rebase

قبل از rebase اجرا می‌شود. برای جلوگیری از rebase روی کامیت‌های منتشرشده. چگونه تعارض گیت را بدون از دست دادن کدها حل کنیم؟ به این موضوع می‌پردازد.

نصب خودکار و ابزارها

چالش اصلی هوک‌های سمت کلاینت، توزیع آن‌ها بین اعضای تیم است.

مشکل توزیع

پوشه .git/hooks/ در مخزن ردیابی نمی‌شود. پس هر عضو باید هوک‌ها را به‌طور دستی نصب کند. این کار در تیم‌های بزرگ، دشوار و مستعد خطاست.

راهکار اول: Husky

Husky ابزاری برای پروژه‌های Node.js است که هوک‌ها را در پوشه .husky/ قرار می‌دهد و آن‌ها را در مخزن ردیابی می‌کند. با نصب وابستگی‌ها، هوک‌ها به‌طور خودکار در .git/hooks/ نصب می‌شوند.

npx husky init
echo "npx lint-staged" > .husky/pre-commit
echo "npm test" > .husky/pre-push

راهکار دوم: pre-commit framework

فریم‌ورک pre-commit (پایتون) امکان تعریف هوک‌ها در یک فایل YAML را فراهم می‌کند:

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml

  - repo: https://github.com/pre-commit/mirrors-eslint
    rev: v8.56.0
    hooks:
      - id: eslint
        files: \.js$

راهکار سوم: Git Templates

می‌توانید یک پوشه Template برای Git تعریف کنید که هنگام git init یا git clone کپی شود:

git config --global init.templateDir ~/.git-templates
mkdir -p ~/.git-templates/hooks
cp my-hooks/* ~/.git-templates/hooks/

راهکار چهارم: Makefile یا اسکریپت نصب

یک اسکریپت setup.sh در مخزن قرار دهید که هوک‌ها را نصب کند. در README، به اجرای آن اشاره کنید.

#!/bin/bash
# setup.sh
cp scripts/hooks/* .git/hooks/
chmod +x .git/hooks/*
echo "Hooks installed."

راهکار پنجم: npm scripts با postinstall

در پروژه‌های Node.js، می‌توانید در package.json از postinstall برای نصب خودکار هوک‌ها استفاده کنید:

{
  "scripts": {
    "postinstall": "node scripts/install-hooks.js"
  }
}

در ابزارهای Git و GitHub برای تیم‌ها به ابزارهای مشابه اشاره کرده‌ام. تفاوت GitHub و GitLab برای تیم توسعه نرم‌افزار نیز به این ابزارها اشاره دارد.

محدودیت‌ها و دور زدن

هوک‌های سمت کلاینت، محدودیت‌های بنیادی دارند که باید شناخت.

دور زدن با --no-verify

هر توسعه‌دهنده می‌تواند با فلگ --no-verify هوک‌ها را نادیده بگیرد:

git commit --no-verify -m "quick fix"
git push --no-verify

این فلگ، در مواقع اضطراری مفید است اما اگر به رویه تبدیل شود، کل سیستم کیفیت را بی‌اثر می‌کند.

دور زدن با تنظیمات Git

با تنظیم core.hooksPath می‌توان مسیر هوک‌ها را تغییر داد یا غیرفعال کرد:

git config core.hooksPath /dev/null

دور زدن با Git GUI

برخی ابزارهای GUI ممکن است هوک‌ها را به‌طور کامل اجرا نکنند. این ناسازگاری، بر رفتار هوک اثر می‌گذارد.

ناسازگاری سیستم‌عامل

اسکریپت Bash روی ویندوز (بدون WSL) اجرا نمی‌شود. راهکار: استفاده از اسکریپت‌های Cross-Platform مانند Node.js یا Python.

نتیجه محدودیت‌ها

هوک سمت کلاینت، یک لایه بازخورد است، نه یک لایه اجبار. برای اجبار، باید از هوک سمت سرور، GitHub Actions یا Branch Protection استفاده کرد. هوک‌های سمت سرور در گیت‌هاب: چرا بیشتر تیم‌ها پتانسیل امنیتی و اتوماسیون آن را نادیده می‌گیرند؟ به این موضوع می‌پردازد.

ترکیب با لایه‌های دیگر

هوک سمت کلاینت، تنها زمانی مؤثر است که با لایه‌های دیگر ترکیب شود.

لایه‌بندی دفاعی

لایه ابزار هدف
IDE Linter، Formatter بازخورد فوری
Client Hook pre-commit، commit-msg بازخورد سریع محلی
CI GitHub Actions اعتبارسنجی جامع
Server Hook pre-receive، update سیاست نهایی
Branch Protection GitHub Rules اجبار سیاست

مثال ترکیب کامل

  • در IDE: ESLint و Prettier فعال
  • در pre-commit: Prettier روی فایل‌های تغییریافته
  • در pre-push: تست‌های سریع
  • در GitHub Actions: تست کامل، Build، Security Scan
  • در Branch Protection: اجبار PR و Review

هماهنگی تیم

ترکیب لایه‌ها باید با تیم هماهنگ شود. هوک‌های سخت‌گیرانه بدون توضیح، اعضا را فراری می‌دهند. همدلی در رهبری تیم به این هماهنگی اشاره دارد. همکاری بین‌تیمی در سازمان نیز به این موضوع می‌پردازد.

مستندسازی

هر هوک باید در README یا CONTRIBUTING.md مستند شود. اعضا باید بدانند چه هوکی وجود دارد، چه می‌کند و چگونه می‌توانند آن را نصب کنند. مدیریت پروژه با GitHub Projects: راهنمای عملی از راه‌اندازی تا بلوغ تیمی به این موضوع می‌پردازد.

اشتباهات رایج

  • تکیه تنها بر هوک سمت کلاینت: این هوک‌ها قابل دور زدن هستند.
  • نبود مستندسازی: اعضا نمی‌دانند چه هوکی وجود دارد.
  • هوک‌های کند: تجربه توسعه را خراب می‌کنند.
  • نبود نصب خودکار: هر عضو باید دستی نصب کند.
  • هوک‌های سخت‌گیرانه بدون توضیح: اعضا را فراری می‌دهند.
  • نادیده گرفتن Cross-Platform: اسکریپت Bash روی ویندوز اجرا نمی‌شود.
  • نبود پیام خطای مفید: اعضا نمی‌دانند چه باید بکنند.
  • نبود هماهنگی با CI: هوک و CI استانداردهای متفاوتی دارند.

در رفع خطاهای رایج Git راهنمای کاربردی به خطاهای مشابه اشاره کرده‌ام. Git یا SVN؛ کدام برای کنترل نسخه بهتر است؟ نیز به تفاوت‌های ابزارها می‌پردازد.

اشتباهات پیشرفته

  • نبود Fallback: اگر هوک شکست بخورد، پروسه باید به‌درستی گزارش دهد.
  • نبود Timeout: هوک‌های بدون timeout می‌توانند پروسه را معلق کنند.
  • نبود Skip در اضطراری: گاهی نیاز به دور زدن آگاهانه است. باید مستند باشد.
  • نادیده گرفتن Windows Line Endings: CRLF و LF می‌توانند اسکریپت‌ها را خراب کنند.
  • نبود Integration با CI: اگر CI استانداردهای متفاوتی داشته باشد، هوک محلی بی‌اثر می‌شود.

پرسش‌های پرتکرار درباره هوک‌های سمت کلاینت

هوک سمت کلاینت در Git چیست؟

اسکریپتی که روی دستگاه توسعه‌دهنده و در نقاط مشخصی از چرخه حیات Git اجرا می‌شود. این هوک‌ها قابل دور زدن با --no-verify هستند.

تفاوت هوک سمت کلاینت و سمت سرور چیست؟

هوک سمت کلاینت روی دستگاه توسعه‌دهنده و در پوشه .git/hooks/ اجرا می‌شود. هوک سمت سرور روی سرور مخزن و غیرقابل دور زدن است.

مهم‌ترین هوک‌های سمت کلاینت کدامند؟

سه هوک پرکاربرد: pre-commit برای Lint و فرمت، commit-msg برای اعتبارسنجی پیام و pre-push برای تست.

چگونه هوک‌ها را بین اعضای تیم توزیع کنیم؟

با ابزارهایی مانند Husky، pre-commit framework، Git Templates یا اسکریپت نصب. این ابزارها هوک‌ها را در مخزن ردیابی و نصب خودکار را ممکن می‌کنند.

آیا هوک‌های سمت کلاینت قابل دور زدن هستند؟

بله. با --no-verify یا تنظیم core.hooksPath. برای اجبار، باید از هوک سمت سرور یا Branch Protection استفاده کرد.

سرعت هوک‌های سمت کلاینت چقدر مهم است؟

هوک باید سریع باشد (کمتر از ۳ ثانیه برای pre-commit). هوک کند، توسعه‌دهندگان را به دور زدن ترغیب می‌کند.

چگونه هوک Cross-Platform بسازیم؟

با نوشتن هوک با Node.js یا Python به‌جای Bash. این زبان‌ها روی ویندوز، macOS و Linux یکسان اجرا می‌شوند.

آیا هوک‌های سمت کلاینت جایگزین CI هستند؟

خیر. هوک سمت کلاینت بازخورد سریع محلی می‌دهد، اما CI اعتبارسنجی جامع و غیرقابل دور زدن است. بهترین رویکرد، ترکیب هر دو است.

چگونه هوک pre-commit را برای بررسی Secrets بسازیم؟

با ابزارهایی مانند gitleaks یا trufflehog که در pre-commit فراخوانی می‌شوند و از کامیت شدن کلیدهای API جلوگیری می‌کنند.

آیا هوک‌ها در مخازن GitLab هم کار می‌کنند؟

بله. هوک‌های سمت کلاینت، بخشی از Git Core هستند و مستقل از پلتفرم میزبانی کار می‌کنند. GitHub یا GitLab؛ کدام برای توسعه‌دهندگان بهتر است؟ مقایسه‌ای عملی ارائه می‌دهد.

هوک‌های سمت کلاینت، ابزاری ساده اما قدرتمند در چرخه کیفیت کد هستند. اگر تجربه‌ای در پیاده‌سازی این هوک‌ها دارید، به‌خصوص اگر با چالش Cross-Platform یا توزیع تیمی مواجه شده‌اید، آن را در دیدگاه‌ها بنویسید. تجربه شما می‌تواند راهنمای دیگران باشد. 🪝⚙️