Pertemuan Pertemuan 27

Git Hooks & Husky

Pertemuan 27 / 32
Advanced Modul 3.3 60 menit
Pertemuan 27

Git Hooks & Husky

Tujuan Pembelajaran

  • Memahami konsep dan jenis-jenis Git hooks
  • Mampu membuat pre-commit hook manual
  • Mengenal Husky sebagai tool manajemen hooks modern
  • Memahami lint-staged untuk optimasi hooks

Materi Inti

A. Git Hooks — Script Otomatis

Git hooks adalah script yang berjalan otomatis saat event tertentu terjadi di Git. Hooks tersimpan di folder .git/hooks/ dan bisa digunakan untuk menjaga kualitas kode.

# Lokasi hooks
ls .git/hooks/
# pre-commit.sample, commit-msg.sample, pre-push.sample, dll

Jenis-jenis hooks yang sering digunakan:

HookKapan BerjalanKegunaan
pre-commitSebelum commit dibuatLinter, format kode, cek secrets
commit-msgSetelah pesan commit ditulisValidasi format pesan commit
pre-pushSebelum push ke remoteJalankan test, cek branch
post-mergeSetelah merge selesaiInstall dependencies baru
prepare-commit-msgSebelum editor pesan commit dibukaTemplate pesan commit

Aturan penting:

  • Hook harus executable (chmod +x)
  • Exit code 0 = sukses (lanjutkan operasi)
  • Exit code 1 (atau non-zero) = gagal (batalkan operasi)
  • Hooks di .git/hooks/ tidak ter-track oleh Git (tidak ikut di-push)

B. Membuat Pre-Commit Hook Manual

# Contoh pre-commit hook: cek console.log dan .env
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "=== Pre-commit Check ==="

# Cek 1: Apakah ada console.log yang tertinggal?
if grep -r "console.log" --include="*.js" .; then
    echo "ERROR: Masih ada console.log! Hapus sebelum commit."
    exit 1
fi

# Cek 2: Apakah file .env ikut ter-commit?
if git diff --cached --name-only | grep -q ".env"; then
    echo "ERROR: File .env tidak boleh di-commit!"
    exit 1
fi

echo "Semua pengecekan LULUS!"
exit 0
EOF
chmod +x .git/hooks/pre-commit

C. Commit-msg Hook

# Validasi format Conventional Commits
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/sh
commit_msg=$(cat "$1")

# Cek format: tipe: deskripsi
if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|ci|perf|build|revert)(\(.+\))?: .+"; then
    echo "ERROR: Pesan commit harus mengikuti format Conventional Commits!"
    echo "Contoh: feat: tambah halaman login"
    echo "Contoh: fix(auth): perbaiki validasi email"
    exit 1
fi
echo "Pesan commit valid!"
EOF
chmod +x .git/hooks/commit-msg

D. Setup Husky (Modern Approach)

Masalah hook manual: file di .git/hooks/ tidak ikut ter-track Git, sehingga tidak bisa di-share ke tim. Husky menyelesaikan masalah ini dengan menyimpan hooks di folder .husky/ yang bisa di-commit.

# Inisialisasi project Node.js
npm init -y

# Install Husky dan lint-staged
npm install --save-dev husky lint-staged

# Setup Husky
npx husky init

# Buat pre-commit hook via Husky
echo "npx lint-staged" > .husky/pre-commit

E. Konfigurasi lint-staged

lint-staged hanya menjalankan linter pada file yang ada di staging area (yang di-git add), bukan seluruh project. Ini jauh lebih cepat.

Tambahkan di package.json:

{
  "lint-staged": {
    "*.js": ["eslint --fix", "prettier --write"],
    "*.css": ["prettier --write"],
    "*.html": ["prettier --write"]
  }
}

Alur kerja dengan Husky + lint-staged:

git add file.js
git commit -m "feat: tambah fitur"
    |
    v
Husky trigger .husky/pre-commit
    |
    v
npx lint-staged
    |
    v
ESLint + Prettier hanya pada file.js (yang di-staged)
    |
    v
Jika LULUS -> commit berhasil
Jika GAGAL -> commit dibatalkan

Demonstrasi Live

mkdir proyek-hooks && cd proyek-hooks && git init
npm init -y

# Buat pre-commit hook manual
mkdir -p .git/hooks
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "=== Pre-commit Check ==="

# Cek file .env tidak ikut ter-commit
if git diff --cached --name-only | grep -q ".env"; then
    echo "ERROR: File .env tidak boleh di-commit!"
    exit 1
fi

# Cek tidak ada TODO yang tertinggal
if git diff --cached | grep -q "TODO"; then
    echo "WARNING: Ada TODO di kode yang akan di-commit"
    echo "(commit tetap dilanjutkan sebagai warning)"
fi

echo "Pre-commit check PASSED!"
EOF
chmod +x .git/hooks/pre-commit

# Test 1: Coba commit file .env (harus gagal)
echo "SECRET=abc123" > .env
git add .env
git commit -m "test: coba commit .env"
# Output: ERROR: File .env tidak boleh di-commit!

# Test 2: Commit file normal (harus berhasil)
git reset HEAD .env
echo ".env" > .gitignore
echo "console.log('hello')" > app.js
git add .gitignore app.js
git commit -m "feat: tambah .gitignore dan app.js"
# Output: Pre-commit check PASSED!

Latihan Praktik

Latihan: Setup Git Hooks Lengkap

mkdir proyek-hooks-latihan && cd proyek-hooks-latihan && git init
npm init -y

# Langkah 1: Buat pre-commit hook yang mengecek:
# - Tidak ada file .env
# - Tidak ada console.log di file .js
# - File tidak lebih dari 100 baris
mkdir -p .git/hooks
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "=== Pre-commit Checks ==="
PASS=true

# Cek 1: File .env
if git diff --cached --name-only | grep -q ".env"; then
    echo "GAGAL: File .env terdeteksi!"
    PASS=false
fi

# Cek 2: console.log
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.js$')
if [ -n "$FILES" ]; then
    for f in $FILES; do
        if grep -q "console.log" "$f"; then
            echo "GAGAL: console.log ditemukan di $f"
            PASS=false
        fi
    done
fi

if [ "$PASS" = true ]; then
    echo "LULUS: Semua pengecekan OK!"
    exit 0
else
    echo "Commit dibatalkan. Perbaiki error di atas."
    exit 1
fi
EOF
chmod +x .git/hooks/pre-commit

# Langkah 2: Buat commit-msg hook
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/sh
msg=$(cat "$1")
if ! echo "$msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore|ci): .+"; then
    echo "GAGAL: Format pesan commit salah!"
    echo "Gunakan: feat|fix|docs|style|refactor|test|chore|ci: deskripsi"
    exit 1
fi
EOF
chmod +x .git/hooks/commit-msg

# Langkah 3: Test hooks
echo ".env" > .gitignore
echo "function hello() { return 'hello'; }" > app.js
git add . && git commit -m "feat: setup project"
# Harus BERHASIL

echo "console.log('debug')" > debug.js
git add debug.js
git commit -m "feat: tambah debug"
# Harus GAGAL karena console.log

git reset HEAD debug.js && rm debug.js

Tugas Mandiri

  1. Buat pre-commit hook yang mengecek minimal 3 hal berbeda (misalnya: .env, console.log, file size)
  2. Buat commit-msg hook yang memvalidasi format Conventional Commits
  3. (Opsional) Setup Husky di project Node.js dan konfigurasi lint-staged
  4. Baca dokumentasi Husky: https://typicode.github.io/husky/