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 Developer | Developer 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/CD | Pipeline 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 Pemindaian | Menjalankan 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 Commits | Perubahan 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:
- Membuat direktori
.husky/di dalam root project Anda. - Membuat berkas bawaan
.husky/pre-commityang berisikan perintah bawaan (biasanyanpm test). - Menambahkan skrip
"prepare": "husky"di bagian"scripts"padapackage.jsonAnda.
[!NOTE] Skrip
"prepare": "husky"memastikan bahwa ketika developer lain melakukan clone repositori ini dan menjalankannpm 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:
- Buat berkas
.husky/pre-pushdi dalam direktori.husky/:touch .husky/pre-push - 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 - 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).
- Instal dependensi commitlint:
npm install -D @commitlint/config-conventional @commitlint/cli - Buat berkas konfigurasi bernama
commitlint.config.jsdi root folder:module.exports = { extends: ['@commitlint/config-conventional'] }; - Daftarkan hook
commit-msgdi 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:
tscsecara bawaan tidak mendukung penerimaan argumen berupa daftar berkas spesifik jika Anda mengandalkan konfigurasitsconfig.json.- Jika Anda memaksakan menjalankan
tsc --noEmitpada 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:
- Anda memodifikasi fungsi klik dan melakukan
git add Button.tsx. - Setelah itu, Anda menambahkan komentar uji coba di baris bawah
Button.tsxnamun tidak melakukangit addpada baris tersebut. - 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-stagedakan 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-stagedsecara 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 dalampre-commithook. Tugas-tugas ini berdurasi panjang dan akan mengganggu ritme kerja developer lokal. Pindahkan ke tahapanpre-pushatau 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.