25. Automated Quality Control: Husky & lint-staged

Dalam pengembangan aplikasi modern berskala tim, menjaga konsistensi format kode (formatting), mendeteksi kesalahan penulisan (linting), serta memastikan keabsahan tipe (type checking) sejak dini sangatlah penting. Husky dan lint-staged berfungsi untuk memvalidasi kode di sisi lokal developer guna memastikan bahwa setiap perubahan yang akan dikirim ke repositori telah memenuhi standar minimal kualitas kode.


A. Konsep Utama & Mengapa Husky & lint-staged Diperlukan?

Sebelum adanya alat ini, kepatuhan terhadap standar kode (coding standards) sering kali terabaikan hingga kode mencapai pipeline CI/CD di server. Hal ini menyebabkan penumpukan build yang gagal (broken builds) di server, memicu penumpukan log error di pipeline repositori git, dan membuang waktu produktif tim untuk memperbaiki kesalahan formatting atau typo sintaksis sederhana yang seharusnya bisa ditangkap lebih awal.

1. Cara Kerja Git Hooks Tradisional vs Husky

Secara bawaan, Git menyediakan fitur Git Hooks, yaitu skrip khusus yang otomatis dijalankan Git pada saat terjadi event tertentu (seperti pre-commit, commit-msg, pre-push, dll). Skrip ini terletak di dalam direktori tersembunyi .git/hooks/. Namun, folder .git/ tidak didistribusikan saat melakukan push ke repositori (diabaikan oleh git demi alasan keamanan dan portabilitas). Akibatnya, tim pengembang tidak dapat membagikan atau memaksakan Git Hooks yang seragam kepada seluruh developer.

Husky memecahkan masalah ini dengan:

  • Memetakan konfigurasi Git hooks ke direktori proyek utama (misal .husky/) yang terikut ke sistem Git.
  • Mengotomatiskan registrasi hooks ke lokal mesin setiap developer saat pertama kali mereka melakukan instalasi dependensi (npm install).

2. Kebutuhan terhadap lint-staged

Menjalankan linting dan formatting pada seluruh berkas dalam proyek (eslint . atau prettier --write .) setiap kali melakukan commit sangat lambat dan tidak efisien seiring bertambahnya ukuran basis kode.

  • lint-staged menyaring daftar berkas dan hanya menjalankan perintah linting/formatting pada berkas-berkas yang sedang berada di area pementasan (staged files / berkas yang telah ditambahkan melalui git add).
  • Ini menjamin proses commit tetap berlangsung sangat cepat (kurang dari 3 detik) tanpa mengorbankan keamanan kualitas kode.

Tabel Perbandingan Alur Kerja

Metodologi / Aspek❌ Tanpa Git Hooks & Pemindaian Global💡 Menggunakan Husky & lint-staged
Kepatuhan DeveloperDeveloper sering lupa menjalankan npm run lint atau npm run format secara lokal.Pencegahan Otomatis: Git menolak commit jika terdeteksi kegagalan linter atau format yang fatal.
Beban Kerja Pipeline CI/CDPipeline sering gagal (red) hanya karena kesalahan kecil seperti semicolon hilang atau import tidak terpakai.Pipeline CI/CD selalu bersih (green) untuk masalah sintaksis, membiarkan CI fokus ke integrasi dan uji sistem.
Efisiensi Waktu PemindaianMenjalankan analisis statis global ke ratusan/ribuan file pada setiap commit (sangat lambat).Analisis Parsial: Hanya memindai berkas yang sedang diubah (staged files), selesai dalam hitungan detik.
Partial CommitsPerubahan yang belum di-stage bisa ikut terganggu atau ter-linting secara tidak sengaja.Proteksi Staging: Secara cerdas menyembunyikan (stash) perubahan tak ter-staged agar validasi presisi pada kode ter-staged saja.

B. Alur Validasi Pre-Commit Hook (Lifecycle)

Mermaid diagram di bawah mengilustrasikan alur pemrosesan yang terjadi di balik layar ketika developer mengetik perintah commit hingga akhirnya kode tersimpan atau ditolak di repositori lokal:

graph TD
    A[Developer mengetik: git commit -m '...'] --> B[Husky Memicu pre-commit Hook]
    B --> C[lint-staged Beraksi: Mengidentifikasi Staged Files]
    C --> D{Apakah ada file .ts / .tsx / .js / .jsx?}
    D -- Ya --> E[Jalankan eslint --fix & prettier --write]
    D -- Tidak --> F[Apakah ada file styling / markdown / json?]
    
    F -- Ya --> G[Jalankan prettier --write atau stylelint]
    F -- Tidak --> H[Lolos Pemindaian]

    E --> I{Apakah Verifikasi Lolos & Bebas Error?}
    G --> I
    H --> K[Commit Berhasil Disimpan]

    I -- Ya --> J[Tambahkan otomatis perubahan terformat ke Git Staging]
    J --> K
    I -- Tidak --> L[Proses Commit Dibatalkan & Tampilkan Log Kesalahan]
    
    L --> M[Developer Memperbaiki Masalah Secara Lokal]
    M --> A

Ketika proses di atas gagal (I -- Tidak), Git akan membatalkan pembuatan objek commit. Developer diharuskan memperbaiki baris kode yang ditunjuk oleh log error linter terlebih dahulu sebelum mengulangi proses commit.


C. Panduan Instalasi & Konfigurasi Langkah-demi-Langkah

Berikut adalah instalasi modern menggunakan Husky v9+ yang direkomendasikan untuk ekosistem React, Next.js, dan JavaScript/TypeScript modern.

Langkah 1: Pemasangan Paket Dependensi

Jalankan perintah instalasi di bawah ini untuk menambahkan Husky dan lint-staged ke dalam devDependencies proyek Anda:

npm install -D husky lint-staged

Langkah 2: Inisialisasi Otomatis Husky

Jalankan utilitas inisialisasi Husky untuk membuat folder konfigurasi secara instan:

npx husky init

Perintah di atas secara otomatis akan melakukan tiga hal berikut:

  1. Membuat direktori .husky/ di dalam root project Anda.
  2. Membuat berkas bawaan .husky/pre-commit yang berisikan perintah bawaan (biasanya npm test).
  3. Menambahkan skrip "prepare": "husky" di bagian "scripts" pada package.json Anda.

[!NOTE] Skrip "prepare": "husky" memastikan bahwa ketika developer lain melakukan clone repositori ini dan menjalankan npm install, Husky akan secara otomatis mengaktifkan Git Hooks di lokal mesin mereka tanpa perlu setup ulang secara manual.

Langkah 3: Konfigurasi Berkas pre-commit

Buka berkas .husky/pre-commit yang baru saja dibuat, lalu ubah isinya untuk memanggil lint-staged alih-alih npm test:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

Langkah 4: Menentukan Aturan lint-staged

Anda direkomendasikan untuk memisahkan konfigurasi lint-staged ke dalam berkas konfigurasi tersendiri agar file package.json tetap bersih. Buat berkas baru bernama .lintstagedrc.json di root directory proyek Anda:

{
  "*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{css,scss}": [
    "stylelint --fix",
    "prettier --write"
  ],
  "*.{json,md,html}": [
    "prettier --write"
  ]
}

Penjelasan Aturan:

  • eslint --fix: Mendeteksi kesalahan logika/sintaksis dan memperbaiki masalah yang dapat diselesaikan secara otomatis (seperti menghapus spasi berlebih atau meletakkan semicolon).
  • prettier --write: Merapikan format indentasi, tanda petik, dan gaya penulisan agar seragam.

D. Konfigurasi Hook Tingkat Lanjut

1. Hook pre-push: Validasi Build & Test Suite Global

Meskipun pre-commit memastikan file yang diubah telah lolos linting, ada kemungkinan perubahan tersebut merusak kompatibilitas aplikasi secara keseluruhan. Menjalankan integrasi berat seperti pemeriksaan kompilasi TypeScript global atau seluruh unit test di pre-commit akan sangat memperlambat proses pengerjaan harian.

Solusinya adalah memindahkannya ke tahapan pre-push hook, yang dijalankan hanya sekali sesaat sebelum kode dikirim ke GitHub/GitLab:

  1. Buat berkas .husky/pre-push di dalam direktori .husky/:
    touch .husky/pre-push
    
  2. Isi berkas tersebut dengan skrip pengujian:
    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"
    
    echo "🚀 Menjalankan verifikasi akhir sebelum push ke server..."
    
    # Jalankan build check & unit test
    npm run build && npm run test
    
  3. Berikan izin eksekusi (executable permissions) pada berkas tersebut:
    chmod +x .husky/pre-push
    

2. Hook commit-msg: Standardisasi Pesan Commit (Commitlint)

Untuk menjaga keterbacaan riwayat git (git history), Anda dapat memaksa developer menulis pesan commit dengan standar Conventional Commits (seperti feat: add new navbar, fix: resolve crash on home page).

  1. Instal dependensi commitlint:
    npm install -D @commitlint/config-conventional @commitlint/cli
    
  2. Buat berkas konfigurasi bernama commitlint.config.js di root folder:
    module.exports = { extends: ['@commitlint/config-conventional'] };
    
  3. Daftarkan hook commit-msg di Husky:
    echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg
    chmod +x .husky/commit-msg
    

Sekarang, jika ada developer mencoba melakukan commit dengan pesan acak (seperti git commit -m "fix bug" tanpa mengikuti konvensi), commit akan diblokir dengan pesan petunjuk kesalahan.


E. Penanganan Masalah Khusus TypeScript (tsc)

Menjalankan perintah TypeScript Compiler (tsc) langsung pada berkas staged di dalam lint-staged sering kali memunculkan masalah:

  1. tsc secara bawaan tidak mendukung penerimaan argumen berupa daftar berkas spesifik jika Anda mengandalkan konfigurasi tsconfig.json.
  2. Jika Anda memaksakan menjalankan tsc --noEmit pada staged files, ia akan mengabaikan relasi dependensi eksternal dari tipe berkas lain yang tidak ikut masuk ke staging.

Solusi 1: Menjalankan Global Check di pre-commit (Jika Proyek Kecil)

Jika ukuran proyek Anda tidak terlalu besar, Anda bisa menjalankan pemeriksaan tipe secara global pada setiap commit. Tambahkan skrip ini di .lintstagedrc.json:

{
  "*.{ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "**/*.ts?(x)": [
    "bash -c 'tsc --noEmit'"
  ]
}

Catatan: Penggunaan bash -c memaksa perintah untuk berjalan secara global tanpa meneruskan daftar staged files secara langsung ke tsc.

Solusi 2: Menggunakan Pustaka Tambahan (tsc-files)

Untuk proyek berskala menengah-besar di mana kompilasi global terlalu lama, Anda dapat menggunakan pustaka tsc-files yang berfungsi untuk meneruskan staged files ke tsc sembari tetap membaca pengaturan dari tsconfig.json:

npm install -D tsc-files

Lalu konfigurasi .lintstagedrc.json Anda menjadi:

{
  "*.{ts,tsx}": [
    "tsc-files --noEmit",
    "eslint --fix",
    "prettier --write"
  ]
}

F. Cara Kerja di Balik Layar: Partial Commits & Git Stashing

Fitur utama dari lint-staged adalah kemampuannya menangani staged vs unstaged changes pada file yang sama.

Misalkan Anda mengedit berkas Button.tsx:

  1. Anda memodifikasi fungsi klik dan melakukan git add Button.tsx.
  2. Setelah itu, Anda menambahkan komentar uji coba di baris bawah Button.tsx namun tidak melakukan git add pada baris tersebut.
  3. Anda menjalankan git commit.

Jika lint-staged memindai berkas Button.tsx apa adanya, ia akan memindai baris komentar yang belum di-stage. Jika linter menemukan kesalahan di baris komentar tersebut, commit akan dibatalkan padahal baris tersebut tidak berniat Anda commit.

Solusi Otomatis lint-staged:

  • Sebelum linter dijalankan, lint-staged akan membuat backup perubahan unstaged dengan melakukan Git Stash.
  • Berkas yang diperiksa linter murni hanya berisi perubahan yang telah Anda git add (staged).
  • Setelah linter berhasil dijalankan dan kode diformat ulang, lint-staged secara otomatis mengembalikan (restore) perubahan unstaged tadi kembali ke workspace Anda melalui perintah git internals.
  • Hal ini mencegah kode yang belum siap dideploy ikut masuk atau merusak alur validasi.

G. Panduan Praktis & Penanganan Masalah (Troubleshooting)

1. Menghindari Penggunaan Berlebihan (Limit pre-commit hooks)

  • Lakukan: Jalankan tugas cepat (< 5 detik) seperti linting (eslint), formatting (prettier), atau pemeriksaan tipe lokal (tsc-files).
  • Hindari: Menjalankan pengujian e2e (cypress run / playwright test) atau build global di dalam pre-commit hook. Tugas-tugas ini berdurasi panjang dan akan mengganggu ritme kerja developer lokal. Pindahkan ke tahapan pre-push atau biarkan dijalankan di server CI/CD.

2. Cara Mengabaikan Validasi untuk Kebutuhan Darurat (--no-verify)

Ada kalanya Anda perlu melewati Git Hooks karena situasi darurat (seperti melakukan commit perbaikan WIP pada branch pribadi atau menyelesaikan hotfix di bawah bimbingan langsung). Anda dapat menambahkan flag --no-verify di akhir perintah Git:

git commit -m "wip: commit cepat untuk checkpoint kerjaan" --no-verify
git push origin feature/wip-branch --no-verify

[!WARNING] Peringatan Penting! Mengabaikan validasi pre-commit secara sengaja pada branch kolaborasi utama (main / master / staging) sangat dilarang karena berpotensi besar merusak pipeline CI/CD bersama.

3. Masalah Izin Eksekusi di macOS / Linux (Permission Denied)

Jika Anda mendapatkan log error dari git yang menyatakan bahwa hook Husky diabaikan karena tidak memiliki izin eksekusi (hook was ignored because it's not set as executable), jalankan perintah berikut di terminal:

chmod +x .husky/pre-commit
chmod +x .husky/pre-push

4. Menonaktifkan Husky di Lingkungan CI/CD / Docker Build

Di lingkungan integrasi berkelanjutan (CI/CD) atau saat membangun kontainer Docker, proses npm install akan memicu skrip prepare yang mencoba menginisialisasi Husky. Hal ini dapat menyebabkan error karena lingkungan CI/CD sering kali tidak memiliki utilitas Git yang terkonfigurasi atau berjalan dalam mode non-interactive/headless.

Untuk menonaktifkan inisialisasi Husky di lingkungan tersebut, gunakan variabel environment HUSKY=0:

# Contoh instalasi di server CI
HUSKY=0 npm ci

# Contoh penulisan di Dockerfile
ENV HUSKY=0
RUN npm ci

5. Masalah Kompatibilitas GUI Git Client (VS Code, Sourcetree, GitKraken)

Beberapa aplikasi GUI Git Client terkadang gagal membaca environment variable dari sistem lokal Anda (seperti path instalasi Node.js) sehingga gagal mengeksekusi hooks Husky.

Jika Anda mengalami error seperti node: command not found di GUI client Anda, buat berkas penyeimbang environment bernama ~/.huskyrc (di direktori home mesin Anda) untuk memberi tahu path Node Anda:

# Tuliskan path node Anda di ~/.huskyrc
export PATH="/usr/local/bin:/usr/bin:/bin:$PATH"

H. Referensi Tambahan

  • Dokumentasi Resmi Husky: Panduan komprehensif mengenai opsi CLI terbaru, integrasi hooks, dan panduan migrasi dari versi lama.
  • Repository Resmi lint-staged: Dokumentasi parameter konfigurasi, penyaringan berbasis ekstensi file, dan resep konfigurasi tingkat lanjut.
  • Conventional Commits Specification: Spesifikasi penamaan pesan commit yang terstruktur dan mudah dibaca oleh mesin.
  • Buku Git Hooks SCM: Referensi dasar mengenai siklus hidup, tipe, dan mekanisme kerja Git hooks secara mendalam di level sistem operasi.

© 2026 Bit Group Asia. All rights reserved.

ArchitectureSecurityGuidelines