GIT-HOOKS PRE-COMMIT CODE-QUALITY DEVELOPER-EXPERIENCE AUTOMATION WEB-SECURITY SECRETS-MANAGEMENT LINTING FORMATTING CONVENTIONAL-COMMITS HUSKY LINT-STAGED GIT DEVOPS PRODUCTIVITY SECURITY-BEST-PRACTICES

Pre-commit Hooks Lanjutan: Otomatisasi Kualitas Kode dan Deteksi Rahasia di Setiap Commit

⏱️ 10 menit baca
👨‍💻

Pre-commit Hooks Lanjutan: Otomatisasi Kualitas Kode dan Deteksi Rahasia di Setiap Commit

1. Pendahuluan

Sebagai seorang developer, kita semua tahu pentingnya kualitas kode. Kode yang bersih, konsisten, dan bebas bug tidak hanya membuat tim lebih produktif, tetapi juga mengurangi potensi masalah di kemudian hari. Namun, menjaga kualitas ini secara manual seringkali menjadi tantangan. Kita bisa lupa menjalankan linter, terburu-buru melakukan commit dengan format yang berantakan, atau yang lebih parah, secara tidak sengaja memasukkan rahasia (seperti API key atau kredensial) ke dalam repositori Git. 😱

Di sinilah pre-commit hooks berperan sebagai penjaga gerbang pertama dalam alur kerja pengembangan kita. Jika Anda sudah familiar dengan konsep dasar pre-commit hooks untuk linting dan formatting, artikel ini akan membawa Anda ke level selanjutnya. Kita akan menggali bagaimana pre-commit hooks bisa menjadi lebih dari sekadar pembersih kode, melainkan juga perisai keamanan yang efektif dan penegak standar proyek secara otomatis, bahkan sebelum kode Anda menyentuh Git.

Mari kita ubah setiap git commit menjadi momen validasi otomatis yang kuat!

2. Mengapa Pre-commit Hooks Lanjutan Penting?

Artikel “Membangun Pipeline Kualitas Kode Lokal” sudah membahas dasar-dasar pre-commit hooks. Kali ini, kita akan fokus pada skenario yang lebih kompleks dan kritis:

📌 2.1 Kualitas Kode yang Konsisten dan Otomatis

Bayangkan Anda bekerja dalam tim dengan berbagai preferensi editor dan konfigurasi. Tanpa otomatisasi, kode bisa jadi campur aduk: ada yang pakai single quotes, ada yang double quotes, ada yang tab, ada yang space. Pre-commit hooks memastikan bahwa setiap kode yang di-commit sudah melewati standar linting (misalnya dengan ESLint) dan formatting (misalnya dengan Prettier) yang telah disepakati proyek. Ini mengurangi gesekan dalam code review dan menjaga codebase tetap bersih.

📌 2.2 Menjaga Sejarah Git yang Bersih dan Bermakna

Pernahkah Anda melihat pesan commit seperti “fix bug”, “update”, atau “asdfghjkl”? 🤦‍♂️ Pesan commit yang tidak deskriptif menyulitkan pelacakan perubahan, debugging, dan pembuatan changelog otomatis. Dengan pre-commit hooks, kita bisa menegakkan standar pesan commit (misalnya Conventional Commits) untuk memastikan setiap commit message informatif dan konsisten.

⚠️ 2.3 Perisai Keamanan: Deteksi Rahasia Sebelum Commit

Ini adalah salah satu aspek paling krusial dari pre-commit hooks lanjutan. Secara tidak sengaja meng-commit API key, kredensial database, atau token pribadi ke repositori publik (atau bahkan privat) adalah mimpi buruk keamanan. Pre-commit hooks dapat dikonfigurasi untuk memindai file yang di-staged dari pola-pola rahasia, mencegah kebocoran data sensitif sebelum terjadi. Ini adalah lapisan pertahanan pertama yang sangat penting dalam DevSecOps.

✅ 2.4 Mencegah Bug Sejak Awal dengan Validasi Cepat

Selain linting, Anda bisa mengintegrasikan validasi lain seperti type checking (untuk proyek TypeScript) atau bahkan menjalankan unit tests yang sangat cepat pada file yang berubah. Ini memberikan umpan balik instan kepada developer jika ada kesalahan kritis, jauh sebelum kode di-push ke remote repository atau melewati CI/CD.

3. Fondasi: Membangun Pre-commit Hooks dengan Husky dan lint-staged

Untuk mengimplementasikan pre-commit hooks secara efektif di proyek JavaScript/TypeScript, kita akan menggunakan dua alat populer: Husky dan lint-staged.

💡 3.1 Instalasi dan Konfigurasi Dasar

Pertama, instal Husky dan lint-staged:

npm install --save-dev husky lint-staged
# atau
yarn add --dev husky lint-staged

Kemudian, aktifkan Husky di proyek Anda:

npx husky install

Tambahkan perintah ini ke package.json Anda agar Husky otomatis terinstal saat npm install dijalankan oleh developer lain:

// package.json
{
  "name": "my-awesome-app",
  "version": "1.0.0",
  "scripts": {
    "prepare": "husky install" // Ini akan menjalankan husky install setelah npm install
  },
  "devDependencies": {
    "husky": "^8.0.0",
    "lint-staged": "^13.0.0"
  }
}

Sekarang, buat pre-commit hook menggunakan Husky:

npx husky add .husky/pre-commit "npx lint-staged"

Ini akan membuat file .husky/pre-commit yang berisi npx lint-staged.

Terakhir, konfigurasikan lint-staged di package.json Anda. Contoh untuk proyek React/TypeScript dengan ESLint dan Prettier:

// package.json
{
  "name": "my-awesome-app",
  "version": "1.0.0",
  "scripts": {
    "prepare": "husky install"
  },
  "devDependencies": {
    // ...
    "eslint": "^8.0.0",
    "prettier": "^2.0.0",
    "typescript": "^4.0.0"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,scss,md}": [
      "prettier --write"
    ]
  }
}

Dengan konfigurasi ini, setiap kali Anda git commit, Husky akan menjalankan lint-staged. lint-staged kemudian akan mencari file JavaScript/TypeScript yang di-staged dan menjalankannya melalui ESLint (dengan --fix untuk otomatis memperbaiki masalah) dan Prettier (untuk otomatis memformat). Jika ada error ESLint yang tidak bisa diperbaiki otomatis, commit akan gagal.

4. Menjaga Sejarah Git Tetap Rapi dengan commitlint

Pesan commit yang bersih adalah dokumentasi terbaik untuk sejarah proyek Anda. Conventional Commits adalah spesifikasi ringan untuk pesan commit, yang memungkinkan tooling otomatis untuk menghasilkan changelog, menentukan versi semantik, dan banyak lagi.

💡 4.1 Apa itu Conventional Commits?

Sebuah pesan commit Conventional Commits memiliki struktur seperti ini:

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Contoh:

💡 4.2 Integrasi commitlint dengan Husky

Untuk menegakkan standar ini, kita bisa menggunakan commitlint.

Instal commitlint dan konfigurasinya:

npm install --save-dev @commitlint/cli @commitlint/config-conventional
# atau
yarn add --dev @commitlint/cli @commitlint/config-conventional

Buat file konfigurasi commitlint.config.js di root proyek Anda:

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
};

Sekarang, tambahkan hook commit-msg ke Husky:

npx husky add .husky/commit-msg "npx commitlint --edit \$1"

Perintah ini akan membuat file .husky/commit-msg yang berisi npx commitlint --edit $1. $1 adalah argumen yang berisi path ke file commit message.

Sekarang, coba commit dengan pesan yang tidak sesuai standar:

git commit -m "oops, bad message"

Anda akan melihat error dari commitlint, dan commit akan gagal. Ini memaksa Anda untuk menulis pesan commit yang benar.

git commit -m "feat: add user profile page"

✅ Commit akan berhasil!

5. Perisai Keamanan: Deteksi Rahasia dengan git-secrets/TruffleHog

Ini adalah langkah advanced yang sangat penting. Meng-commit rahasia adalah salah satu penyebab umum pelanggaran keamanan.

⚠️ 5.1 Risiko Kebocoran Rahasia

Rahasia seperti kunci API, kredensial database, token autentikasi, dan kata sandi seringkali diperlukan dalam pengembangan. Namun, jika rahasia ini secara tidak sengaja masuk ke kontrol versi (terutama di repositori publik), mereka bisa diakses oleh pihak yang tidak bertanggung jawab. Meskipun repositori privat, praktik ini tetap berbahaya karena rahasia bisa tersebar ke lebih banyak developer dari yang seharusnya.

💡 5.2 Menggunakan git-secrets atau TruffleHog

Ada beberapa alat yang bisa membantu mendeteksi rahasia. Dua yang populer adalah:

Kita akan fokus pada git-secrets karena lebih mudah diintegrasikan ke pre-commit hook lokal.

Instalasi git-secrets

git-secrets tidak diinstal melalui npm, melainkan sebagai perintah Git.

Di macOS (dengan Homebrew):

brew install git-secrets

Di Linux (dengan apt):

sudo apt-get install git-secrets

Instalasi manual (dari source):

git clone https://github.com/awslabs/git-secrets.git
cd git-secrets
sudo make install

Setelah terinstal, Anda perlu menginstal git-secrets untuk repositori proyek Anda:

git secrets --install
git secrets --register-aws # Opsional: mendaftarkan pola rahasia AWS bawaan

git secrets --install akan menambahkan Git hooks (pre-commit, commit-msg, post-checkout, post-merge, post-rewrite) ke repositori lokal Anda. Namun, karena kita menggunakan Husky, kita akan mengintegrasikannya secara manual ke hook Husky.

Konfigurasi git-secrets

Anda bisa menambahkan pola kustom untuk mendeteksi rahasia spesifik proyek Anda. Misalnya, jika Anda memiliki pola untuk API key internal:

git secrets --add 'API_KEY_[A-Z0-9]{32}'

Atau, Anda bisa melarang file tertentu:

git secrets --add-provider -- cat .git_allowed_secrets

Kemudian buat file .git_allowed_secrets yang berisi daftar file yang boleh mengandung rahasia (misalnya, file .env.example yang hanya berisi placeholder).

Integrasi git-secrets ke Pre-commit Hook Husky

Karena git-secrets sudah punya hook-nya sendiri, kita perlu memastikan tidak ada konflik atau menjalankannya secara eksplisit. Cara termudah adalah dengan menambahkan perintah git secrets --scan ke dalam lint-staged atau sebagai script terpisah di Husky.

Opsi 1: Menambahkannya ke lint-staged (direkomendasikan untuk file yang di-staged)

// package.json
{
  // ...
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,scss,md}": [
      "prettier --write"
    ],
    // Tambahkan ini untuk memindai semua file yang di-staged
    "*": [
      "git secrets --scan"
    ]
  }
}

git secrets --scan akan memindai semua file yang di-staged. Jika ada rahasia yang terdeteksi, commit akan gagal.

Opsi 2: Sebagai Hook Husky terpisah

Jika Anda ingin kontrol lebih granular atau git-secrets tidak bekerja dengan baik di lint-staged, Anda bisa membuat hook terpisah:

npx husky add .husky/pre-commit-secrets "git secrets --scan"

Pastikan hook ini dijalankan sebelum npx lint-staged. Anda bisa menggabungkan pre-commit hook Husky menjadi satu file .husky/pre-commit yang memanggil lint-staged dan git secrets --scan.

# Edit .husky/pre-commit secara manual
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
git secrets --scan

Jika salah satu perintah gagal, commit akan dibatalkan.

🎯 5.3 Contoh Deteksi

Coba buat file baru config.js dan tambahkan rahasia:

// config.js
export const API_KEY = "sk_live_YOUR_SECRET_API_KEY_1234567890abcdef";
export const DB_PASSWORD = "supersecretpassword123";

Kemudian coba commit:

git add config.js
git commit -m "feat: add config file"

Jika git-secrets terkonfigurasi dengan baik, commit akan gagal dengan pesan seperti:

config.js:1: API_KEY detected
config.js:2: DB_PASSWORD detected
[ERROR] Some secrets were found in your commit, checking commit history.
[ERROR] Commit aborted.

Ini adalah penyelamat! Anda kemudian bisa menghapus rahasia dari file yang di-commit, menggunakan environment variables, atau alat secrets management lainnya.

6. Integrasi Checks Lainnya (Opsional tapi Kuat)

Selain yang di atas, Anda juga bisa menambahkan validasi lain ke pre-commit hooks untuk meningkatkan kualitas dan keamanan:

💡 6.1 Type Checking (TypeScript)

Untuk proyek TypeScript, menjalankan tsc --noEmit pada file yang di-staged dapat mendeteksi kesalahan tipe sebelum kompilasi.

// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write",
      "tsc --noEmit" // Menjalankan type checking
    ],
    // ...
  }
}

Ini memastikan bahwa setiap commit tidak merusak integritas tipe proyek Anda.

💡 6.2 Unit Tests Cepat

Meskipun tidak disarankan menjalankan semua unit test di pre-commit hook (karena bisa lambat), Anda bisa menjalankan subset kecil atau unit test yang sangat cepat pada file yang diubah.

// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write",
      "jest --findRelatedTests" // Atau vitest --run --changed
    ],
    // ...
  }
}

jest --findRelatedTests akan menjalankan test yang terkait dengan file yang berubah, memberikan umpan balik cepat.

💡 6.3 Dependency Vulnerability Scan

Anda bahkan bisa menjalankan pemindaian kerentanan dependensi menggunakan npm audit atau yarn audit pada pre-commit hook, meskipun ini mungkin memakan waktu:

// package.json
{
  "lint-staged": {
    "package.json": [
      "npm audit --production --json | npm_audit_hook" // Script kustom untuk memproses output
    ],
    // ...
  }
}

Anda mungkin perlu membuat script npm_audit_hook kustom untuk memproses output npm audit agar tidak memblokir commit jika hanya ada informational warnings.

Kesimpulan

Pre-commit hooks lanjutan adalah investasi kecil dengan dampak besar pada kualitas kode, konsistensi tim, dan yang paling penting, keamanan aplikasi Anda. Dengan mengintegrasikan alat seperti Husky, lint-staged, commitlint, dan git-secrets, Anda membangun pertahanan berlapis yang proaktif, menangkap masalah sebelum mereka menjadi sakit kepala yang lebih besar.

Ingat, tujuan utama adalah menciptakan alur kerja developer yang mulus namun ketat. Otomatisasi ini tidak hanya mencegah kesalahan, tetapi juga membebaskan developer untuk fokus pada fitur, bukan pada detail boilerplate atau potensi kebocoran rahasia. Mulailah mengimplementasikan ini di proyek Anda dan rasakan perbedaannya!

🔗 Baca Juga