هوک‌های سمت سرور در گیت‌هاب (Git Server-Side Hooks) اسکریپت‌هایی هستند که روی سرور مخزن اجرا می‌شوند و به‌جای کاربر، سیاست‌های سازمانی، اعتبارسنجی و اتوماسیون را در لحظه دریافت یا ارسال داده اعمال می‌کنند.

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

هر push، یک نقطه ورود به مخزن اصلی است. اگر این نقطه ورود کنترل نشود، کیفیت و امنیت مخزن به رفتار کاربران وابسته می‌ماند. هوک سمت سرور، دقیقاً همین کنترل را ممکن می‌سازد.

هوک‌های سمت سرور چیست؟

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

در دستورات پرکاربرد Git که هر توسعه‌دهنده باید بداند به‌طور مفصل درباره چرخه حیات Git بحث کرده‌ام. Git در ویکی‌پدیا مرجع کاملی برای معماری داخلی آن ارائه می‌دهد.

چرا هوک سمت سرور مهم است؟

سه دلیل کلیدی وجود دارد:

  • غیرقابل دور زدن: برخلاف هوک سمت کلاینت، کاربر نمی‌تواند آن را با flag نادیده بگیرد.
  • سیاست‌محور: قوانین سازمانی در نقطه ورود اعمال می‌شوند، نه در دستگاه هر توسعه‌دهنده.
  • یکنواخت: همه اعضا، صرف‌نظر از تنظیمات محلی، تحت یک سیاست واحد قرار می‌گیرند.

معماری داخلی

Git هنگام دریافت push، یک سری اسکریپت را در پوشه hooks/ مخزن بررسی می‌کند. اگر اسکریپت با نام مشخص وجود داشته باشد و قابل اجرا باشد، Git آن را فراخوانی می‌کند. کد خروجی اسکریپت (Exit Code) تعیین‌کننده ادامه یا لغو عملیات است.

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

درک تفاوت این دو، برای انتخاب درست ضروری است.

ویژگی Client-Side Hook Server-Side Hook
محل اجرا دستگاه توسعه‌دهنده سرور مخزن
قابلیت دور زدن بله (--no-verify) خیر
نصب خودکار خیر بله
مناسب برای Lint، فرمت، بررسی محلی سیاست سازمانی، امنیت، اتوماسیون

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

هوک‌های سمت کلاینت (مثل pre-commit، commit-msg، pre-push) در پوشه .git/hooks/ قرار می‌گیرند و برای تجربه توسعه بهتر طراحی شده‌اند. اما هیچ تضمینی برای اجرای آن‌ها وجود ندارد. ابزارهای Git و GitHub برای تیم‌ها به ابزارهای مدیریتی این حوزه اشاره دارد.

هوک سمت سرور

هوک‌های سمت سرور (مثل pre-receive، update، post-receive) روی سرور اجرا می‌شوند و قابل دور زدن نیستند. این هوک‌ها، لایه دفاعی نهایی سازمان هستند.

ترکیب هر دو

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

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

Git سه هوک اصلی سمت سرور دارد که هرکدام در مرحله متفاوتی اجرا می‌شوند.

pre-receive

این هوک قبل از شروع به‌روزرسانی refها اجرا می‌شود. اگر Exit Code غیرصفر برگرداند، کل push رد می‌شود. مناسب برای اعتبارسنجی سراسری مانند بررسی محرمانه‌بودن فایل‌ها یا محدودیت اندازه.

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

while read oldrev newrev refname; do
  # بررسی اینکه کامیت‌ها امضاشده هستند
  if ! git verify-commit $newrev 2>/dev/null; then
    echo "ERROR: Commit $newrev is not signed"
    exit 1
  fi
done
exit 0

update

هوک update برای هر ref به‌صورت جداگانه اجرا می‌شود. اگر چندین شاخه در یک push به‌روزرسانی شوند، این هوک برای هرکدام یک‌بار اجرا می‌شود. مناسب برای سیاست‌های per-branch مانند محافظت از شاخه main.

#!/bin/bash
# .git/hooks/update
refname=$1

if [[ $refname == "refs/heads/main" ]]; then
  if ! git diff --check $2 $3; then
    echo "ERROR: Whitespace errors in main"
    exit 1
  fi
fi
exit 0

post-receive

این هوک پس از موفقیت به‌روزرسانی اجرا می‌شود. Exit Code آن push را رد نمی‌کند. مناسب برای اطلاع‌رسانی، دیپلوی خودکار و ایندکس‌گذاری. GitLab برای تیم‌های DevOps: راهنمای کامل از CI/CD تا امنیت و انتشار الگوهای مشابه را شرح می‌دهد.

post-update

نسخه‌ای دیگر از هوک پس از به‌روزرسانی، که تمام refهای به‌روزرسانی‌شده را دریافت می‌کند. کاربرد اصلی آن در Server-Side Git مانند Gitolite است.

جدول مقایسه هوک‌ها

هوک زمان اجرا قابلیت رد کردن کاربرد اصلی
pre-receive قبل از تمام refها بله اعتبارسنجی سراسری
update قبل از هر ref بله سیاست per-branch
post-receive پس از تمام refها خیر اطلاع‌رسانی، دیپلوی
post-update پس از تمام refها خیر Server-Side Git

هوک‌ها در GitHub: از Webhook تا GitHub Actions

GitHub به‌عنوان یک پلتفرم میزبانی Git، دسترسی مستقیم به پوشه hooks/ مخزن را نمی‌دهد. اما مکانیزم‌های جایگزین قدرتمندی ارائه می‌کند.

Webhook

Webhook، درخواست HTTP POST است که GitHub در رویدادهای مشخص (push، pull request، issue) به یک URL مشخص ارسال می‌کند. این مکانیزم، نقش هوک سمت سرور را در محیط GitHub ایفا می‌کند.

{
  "event": "push",
  "repository": "owner/repo",
  "commits": [...],
  "ref": "refs/heads/main"
}

GitHub Actions

GitHub Actions، مکانیزم مدرن‌تر است که امکان اجرای کد در رویدادهای مختلف را فراهم می‌کند. هم‌روندی در گیت‌هاب اکشنز به کنترل اجراهای همزمان اشاره دارد.

Branch Protection Rules

قوانین محافظت از شاخه، مکانیزم سطح بالاتری است که می‌تواند نیاز به برخی هوک‌ها را حذف کند. ترکیب این قوانین با GitHub Actions، جایگزین مدرن هوک‌های سمت سرور Git سنتی است. Pull Request در GitHub راهنمای حرفه‌ای به این موضوع می‌پردازد.

Self-Hosted Git و مقایسه

اگر روی سرور اختصاصی Git راه‌اندازی کنید، به هوک‌های سنتی دسترسی کامل خواهید داشت. تفاوت VPS و سرور اختصاصی چیست؟ به انتخاب زیرساخت مناسب کمک می‌کند. چگونه یک سرور اختصاصی راه‌اندازی کنیم؟ راهنمای عملی ارائه می‌دهد.

پیاده‌سازی عملی

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

سناریو اول: اجبار به امضای کامیت

در محیط‌های حساس، می‌توان اجبار کرد که همه کامیت‌ها با GPG امضا شوند. هوک pre-receive می‌تواند این سیاست را اعمال کند.

#!/bin/bash
# .git/hooks/pre-receive
while read oldrev newrev refname; do
  for commit in $(git rev-list $oldrev..$newrev); do
    if ! git verify-commit $commit; then
      echo "Commit $commit is not signed"
      exit 1
    fi
  done
done
exit 0

سناریو دوم: جلوگیری از فایل‌های حساس

بررسی اینکه فایل‌هایی مانند .env، credentials.json یا کلیدهای خصوصی وارد مخزن نشوند.

#!/bin/bash
# .git/hooks/pre-receive
BLOCKED_PATTERNS=".env$|credentials.json$|.pem$|.key$"
while read oldrev newrev refname; do
  files=$(git diff --name-only $oldrev $newrev)
  if echo "$files" | grep -E "$BLOCKED_PATTERNS"; then
    echo "Blocked sensitive file detected"
    exit 1
  fi
done
exit 0

سناریو سوم: دیپلوی خودکار

هوک post-receive می‌تواند پس از push روی main، به‌طور خودکار دیپلوی را آغاز کند. گیت در توسعه وردپرس راهنمای حرفه‌ای نمونه عملی این سناریو را نشان می‌دهد.

#!/bin/bash
# .git/hooks/post-receive
while read oldrev newrev refname; do
  if [[ $refname == "refs/heads/main" ]]; then
    cd /var/www/production
    git pull origin main
    npm ci
    npm run build
    systemctl reload nginx
  fi
done
exit 0

سناریو چهارم: اعتبارسنجی پیام کامیت

می‌توان اجبار کرد که پیام کامیت‌ها از الگوی Conventional Commits پیروی کنند.

#!/bin/bash
# .git/hooks/update
refname=$1
oldrev=$2
newrev=$3
PATTERN="^(feat|fix|docs|style|refactor|test|chore)((.+))?: .{1,}"
for commit in $(git rev-list $oldrev..$newrev); do
  msg=$(git log --format=%s -n 1 $commit)
  if ! [[ $msg =~ $PATTERN ]]; then
    echo "Invalid commit message: $msg"
    exit 1
  fi
done
exit 0

سیاست‌های امنیتی و اعتبارسنجی

هوک‌های سمت سرور، لایه اصلی اعمال سیاست‌های امنیتی در مخزن هستند.

جلوگیری از Force Push

Force Push روی شاخه‌های محافظت‌شده، تاریخچه را بازنویسی می‌کند و می‌تواند منجر به از دست رفتن کار سایر اعضا شود. هوک update می‌تواند این عمل را مسدود کند.

#!/bin/bash
# .git/hooks/update
refname=$1
oldrev=$2
newrev=$3

if [[ $refname == "refs/heads/main" ]]; then
  if ! git merge-base --is-ancestor $oldrev $newrev; then
    echo "Force push to main is not allowed"
    exit 1
  fi
fi
exit 0

محدودیت اندازه فایل

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

بررسی Secret

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

الگوهای شاخه

محدود کردن نام شاخه‌ها به الگوهای مشخص (مثل feature/، bugfix/، hotfix/)، نظم را در مخزن ایجاد می‌کند. برنچ در Git راهنمای مدیریت شاخه‌ها به استراتژی‌های نام‌گذاری می‌پردازد.

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

  • نادیده گرفتن هوک در GitHub: تصور اینکه GitHub اجازه نمی‌دهد. GitHub Actions و Webhook جایگزین‌های قدرتمندی هستند.
  • نبود لاگ‌گیری: هوک‌ها بدون لاگ، عیب‌یابی را دشوار می‌کنند.
  • هوک‌های کند: هوکی که چند ثانیه طول می‌کشد، تجربه push را خراب می‌کند.
  • عدم اطلاع به تیم: هوک بدون اطلاع اعضا، باعث سردرگمی می‌شود.
  • نادیده گرفتن Fail-Open: در برخی موارد، رد کردن push به دلیل خطای هوک، خطرناک است. باید سیاست Fail-Open یا Fail-Closed آگاهانه انتخاب شود.
  • اجرای اسکریپت‌های ناامن: هوکی که ورودی کاربر را بدون اعتبارسنجی اجرا می‌کند، خودش یک آسیب‌پذیری است.

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

تحلیل فنی پیشرفته

برای مهندسان ارشد و معماران زیرساخت، درک عمیق‌تر از هوک‌های سمت سرور ضروری است.

مکانیزم اجرای هوک در Git Core

Git Core هنگام دریافت push، از تابع run_receive_hook برای اجرای هوک‌ها استفاده می‌کند. اگر اسکریپت اجرایی نباشد، نادیده گرفته می‌شود. اگر اسکریپت Exit Code غیرصفر برگرداند، عملیات push لغو و rollback می‌شود. این rollback، اتمیک است و تمام refهای دریافت‌شده را تحت تأثیر قرار می‌دهد.

Atomicity در pre-receive

هوک pre-receive قبل از به‌روزرسانی refها اجرا می‌شود، پس اگر شکست بخورد، هیچ تغییری اعمال نمی‌شود. این ویژگی، آن را برای اعتبارسنجی‌های سراسری ایده‌آل می‌کند.

Concurrency در هوک‌ها

اگر چند push همزمان به یک مخزن برسند، Git آن‌ها را سریالی می‌کند. این بدان معناست که هوک‌های سمت سرور، به‌طور پیش‌فرض thread-safe نیستند و باید از قفل‌گذاری خارجی برای منابع مشترک استفاده کنند. هم‌روندی در گیت‌هاب اکشنز به الگوهای مشابه اشاره دارد.

هوک‌ها و Worktreeهای متعدد

در مخازن با چند Worktree، هوک‌ها از مخزن اصلی اجرا می‌شوند نه از Worktree. این نکته در طراحی سیاست‌های پیچیده اهمیت دارد.

هوک‌ها در Bare Repository

مخازن Bare (که در سرورها رایج‌اند) هوک‌ها را در پوشه hooks/ ریشه مخزن دارند. مسیر نسبت به Working Tree نیست، بلکه نسبت به ریشه مخزن است.

Fail-Open در برابر Fail-Closed

سیاست Fail-Closed (رد push در صورت خطای هوک) امن‌تر است اما می‌تواند مانع کار تیم شود. سیاست Fail-Open (اجازه push در صورت خطای هوک) خطرناک است اما کار را متوقف نمی‌کند. انتخاب بین این دو، تصمیمی معماری است که باید بر اساس سطح حساسیت پروژه گرفته شود.

Time-out در هوک‌ها

Git برای هوک‌ها Time-out داخلی ندارد. اگر هوک به‌طور نامحدود منتظر بماند، push معلق می‌ماند. برای همین، همیشه از timeout در اسکریپت‌های خارجی (مثل curl، ssh) استفاده کنید.

Audit Log و Compliance

برای سازمان‌های با الزامات Compliance، هوک post-receive می‌تواند لاگ‌های Audit تولید کند که شامل شناسه کاربر، زمان، ref و کامیت‌های تغییریافته باشد. این لاگ‌ها می‌توانند به سیستم SIEM ارسال شوند.

پیاده‌سازی با Server-Side Git

در Gitolite و GitLab Self-Managed، امکان تعریف هوک‌های سمت سرور در سطح Group و Instance وجود دارد. GitHub یا GitLab؛ کدام برای توسعه‌دهندگان بهتر است؟ و مهاجرت از GitHub به GitLab: تجربه عملی از تصمیم تا اجرا و مشکلات واقعی به این تفاوت‌ها اشاره دارند.

هوک‌ها و Zero-Trust

در معماری Zero-Trust، هیچ push بدون احراز هویت و اعتبارسنجی پذیرفته نمی‌شود. هوک‌های سمت سرور، ابزار اصلی پیاده‌سازی این رویکرد در لایه Git هستند.

هوک‌ها در Mono-Repo

در مخازن بزرگ با چند پروژه، هوک‌ها باید بتوانند مسیر تغییرات را تشخیص دهند و تنها سیاست‌های مربوط به پروژه تغییریافته را اعمال کنند. این نیازمند Parsing دقیق ساختار مسیرهاست.

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

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

اسکریپتی که روی سرور مخزن اجرا می‌شود و به‌جای کاربر، سیاست‌های سازمانی، امنیتی و اتوماسیون را در لحظه دریافت push اعمال می‌کند. این هوک‌ها قابل دور زدن نیستند.

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

هوک سمت کلاینت روی دستگاه توسعه‌دهنده اجرا می‌شود و با --no-verify قابل دور زدن است. هوک سمت سرور روی سرور مخزن اجرا می‌شود و غیرقابل دور زدن است.

چه هوک‌های سمت سرور در Git وجود دارد؟

سه هوک اصلی: pre-receive، update و post-receive. همچنین post-update در Server-Side Git کاربرد دارد.

آیا GitHub از هوک‌های سمت سرور پشتیبانی می‌کند؟

GitHub دسترسی مستقیم به پوشه hooks/ نمی‌دهد، اما جایگزین‌هایی مانند Webhook، GitHub Actions و Branch Protection Rules ارائه می‌کند که نقش مشابهی ایفا می‌کنند.

چگونه از Force Push در هوک جلوگیری کنیم؟

در هوک update، با دستور git merge-base --is-ancestor بررسی کنید که آیا کامیت قدیمی جد کامیت جدید است. اگر نباشد، Force Push رخ داده و باید رد شود.

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

هوک‌های سریع (کمتر از یک ثانیه) تأثیر محسوسی ندارند. هوک‌های کند (چند ثانیه) تجربه push را خراب می‌کنند. توصیه، انتقال بررسی‌های سنگین به CI/CD است.

آیا هوک‌های سمت سرور در مخازن Bare کار می‌کنند؟

بله. در مخازن Bare، هوک‌ها در پوشه hooks/ ریشه مخزن قرار می‌گیرند و نسبت به ریشه مخزن مسیر می‌گیرند.

چگونه هوک‌های سمت سرور را در تیم پیاده کنیم؟

ابتدا با تیم هماهنگ کنید، سپس هوک را در سطح سرور نصب کنید و لاگ‌گیری دقیق را فعال کنید. مدیریت پروژه با GitHub Projects: راهنمای عملی از راه‌اندازی تا بلوغ تیمی به هماهنگی تیمی اشاره دارد.

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