Tạm biệt Husky: Tại sao mình chuyển sang Lefthook để quản lý Git Hooks?

Git tutorial - IT technology blog
Git tutorial - IT technology blog

Bối cảnh: Cú sập CI/CD lúc 2 giờ sáng

2 giờ sáng, Slack réo liên hồi vì build pipeline trên production bị lỗi. Nguyên nhân khá hy hữu: một dev trong team dùng máy Windows đẩy code Go nhưng quên chạy linting. Lỗi này đáng lẽ phải được chặn ngay từ máy cá nhân bằng Git Hooks.

Trước đây, mình trung thành với Husky. Tuy nhiên, rắc rối nảy sinh khi dự án phình to thành Monorepo chứa cả Python, Go và Rust. Việc ép các dev backend cài Node.js chỉ để chạy một cái Git Hook là yêu cầu rất vô lý. Chưa kể, Husky chạy tuần tự nên cực kỳ chậm. Với những commit lớn, mình từng phải đợi hơn 30 giây chỉ để linter kiểm tra xong — một khoảng nghỉ đủ lâu để mình mất tập trung và bấm nhầm lệnh git push --force tai hại.

Đó là lý do mình tìm đến Lefthook. Được viết bằng Go, Lefthook là một file binary duy nhất, siêu nhẹ và không phụ thuộc vào môi trường Node.js.

Cài đặt Lefthook: Linh hoạt cho mọi workflow

Lefthook không kén chọn môi trường. Bạn có thể tích hợp nó vào dự án theo nhiều cách khác nhau.

Cách 1: Dành cho dự án Node.js

npm install lefthook --save-dev
# Hoặc
yarn add -D lefthook

Cách 2: Cài đặt độc lập (Cho Go, Python, Rust…)

Nếu dự án không dùng package.json, bạn hãy cài trực tiếp vào hệ thống:

# macOS (Homebrew)
brew install lefthook

# Cài qua Go
go install github.com/evilmartians/lefthook@latest

Sau khi cài đặt, hãy khởi tạo bằng lệnh: lefthook install. Một file lefthook.yml sẽ xuất hiện ở thư mục gốc. Đây là nơi bạn kiểm soát toàn bộ quy trình commit.

Cấu hình: Sức mạnh từ việc chạy song song

Điểm “ăn tiền” nhất của Lefthook là file cấu hình YAML cực kỳ tường minh. Dưới đây là cách mình thiết lập để vừa check linter cho JS, vừa format code Go cùng lúc:

# lefthook.yml

pre-commit:
  parallel: true # Chạy các lệnh cùng lúc để tiết kiệm thời gian
  commands:
    linter-js:
      glob: "*.{js,ts,tsx}" # Chỉ lọc các file thay đổi
      run: npx eslint {staged_files} --fix
    formatter-go:
      root: "backend/" 
      glob: "*.go"
      run: go fmt {staged_files} && git add {staged_files}
    check-secret:
      run: gitleaks detect --source . --verbose

Tại sao cấu hình này tối ưu hơn Husky?

  • parallel: true: Husky chạy từng lệnh một. Lefthook tận dụng tối đa CPU để chạy song song. Nếu bạn có 5 task, thời gian chờ sẽ giảm từ 30 giây xuống còn khoảng 5-7 giây.
  • {staged_files}: Thay vì quét toàn bộ project, Lefthook chỉ truyền danh sách file bạn vừa git add vào lệnh. Tốc độ xử lý nhờ đó nhanh hơn gấp nhiều lần.
  • Bộ lọc glob: Bạn có thể chỉ định chính xác linter nào chạy cho loại file nào, tránh lãng phí tài nguyên.

Xử lý tình huống thực tế

Đôi khi bạn cần commit gấp để xử lý sự cố (hotfix) mà không muốn đợi linter kiểm tra những lỗi style nhỏ nhặt.

Bỏ qua hook một cách chuyên nghiệp

Thay vì dùng flag --no-verify thiếu an toàn, Lefthook cho phép bạn bỏ qua các lệnh cụ thể qua biến môi trường:

LEFTHOOK=0 git commit -m "Fix gấp cứu hỏa"

Hoặc chỉ bỏ qua bước check secret nếu bạn chắc chắn:

SKIP=check-secret git commit -m "Commit tạm thời"

Đảm bảo mọi dev trong team đều tuân thủ

Để tránh việc đồng nghiệp “quên” cài hook, hãy thêm script này vào package.json:

{
  "scripts": {
    "postinstall": "lefthook install"
  }
}

Mỗi khi chạy npm install, Lefthook sẽ tự động thiết lập lại các hook trong .git/hooks. Không ai có thể “né” được việc kiểm tra code trước khi đẩy lên server.

Kinh nghiệm thực chiến: Đừng để mất tiền vì lộ Secret

Bạn có thể chạy thử cấu hình bất cứ lúc nào bằng lệnh: lefthook run pre-commit. Lệnh này giả lập quá trình commit và in ra log chi tiết. Nếu có lỗi, Lefthook sẽ chỉ rõ lệnh nào thất bại thay vì quăng ra một mớ log hỗn độn.

Một bài học xương máu của mình: Hãy luôn cài gitleaks vào bước pre-commit. Mình từng vô tình đẩy AWS Secret Key lên GitHub và nhận cảnh báo từ Amazon chỉ sau 5 phút. Từ đó, bước check-secret là bắt buộc trong mọi dự án của mình.

Kết luận

Husky vẫn tốt cho các dự án nhỏ thuần JavaScript. Nhưng nếu bạn muốn tốc độ, sự linh hoạt cho Monorepo và sự ổn định cho team đông người, Lefthook là lựa chọn không thể thay thế. Việc chuyển đổi chỉ mất 15 phút nhưng sẽ giúp bạn tiết kiệm hàng giờ chờ đợi và tránh được những pha vỡ build không đáng có.

Share: