Jika proyek Spring Boot hanya mengandalkan CI untuk mengecek format, lint, dan aturan commit, masalah yang sering muncul adalah feedback datang terlambat. Developer baru tahu ada pelanggaran style atau pesan commit tidak valid setelah push, pipeline gagal, lalu harus membuat commit perbaikan tambahan yang sebenarnya bisa dicegah sejak awal.
Pre-commit hook membantu memindahkan validasi ke mesin developer: format otomatis dijalankan sebelum commit, lint dasar dicek lebih cepat, dan commit message ditolak jika tidak sesuai aturan tim. Namun hook lokal bukan pengganti CI. Pendekatan yang aman adalah menjalankan validasi ringan di hook lokal untuk umpan balik cepat, lalu menjalankan validasi final di CI sebagai pagar terakhir.
Mengapa Spring Boot Perlu Workflow Pre-commit
Pada proyek Spring Boot yang aktif, masalah berikut sangat umum terjadi:
- Feedback lambat: format atau style baru diketahui salah setelah pipeline berjalan.
- Commit gagal di CI: developer sudah berpindah konteks, tetapi harus kembali memperbaiki hal kecil seperti impor, spasi, atau penamaan.
- Kualitas commit tidak seragam: pesan commit sulit dibaca, tidak konsisten, atau tidak cocok untuk release note dan audit perubahan.
Dengan workflow pre-commit, kita bisa membagi validasi menjadi dua lapisan:
- Lokal: cepat, fokus pada hal yang murah dijalankan seperti formatting, lint dasar, dan validasi pesan commit.
- CI: otoritatif, fokus pada build penuh, test suite, analisis tambahan, dan verifikasi bahwa tidak ada hook lokal yang dilewati.
Pendekatan ini bekerja karena masalah paling sering justru berasal dari aturan yang deterministik dan murah dicek. Formatting Java, aturan Checkstyle, atau pola pesan commit tidak perlu menunggu server CI.
Komponen Workflow yang Digunakan
Untuk artikel ini, kombinasi yang dipakai adalah:
- Git hook untuk menjalankan validasi sebelum commit dan saat memeriksa pesan commit.
- Spotless untuk formatting kode secara konsisten.
- Checkstyle untuk aturan style dan kualitas dasar yang lebih eksplisit.
- commit-msg hook untuk memvalidasi format pesan commit, misalnya gaya Conventional Commits.
Spotless dan Checkstyle punya peran berbeda. Spotless lebih cocok untuk hal-hal yang bisa diperbaiki otomatis, seperti format file. Checkstyle cocok untuk aturan yang perlu dilaporkan secara eksplisit, seperti panjang baris, penamaan, atau larangan pola tertentu.
Catatan: Jangan memindahkan seluruh beban validasi ke pre-commit. Menjalankan semua test integrasi atau analisis berat di hook lokal biasanya memperlambat developer dan mendorong orang memakai
--no-verify.
Struktur Proyek yang Disarankan
Agar hook mudah dibagikan lintas tim, simpan skrip di dalam repository, bukan hanya di .git/hooks lokal.
my-spring-boot-app/
├── .githooks/
│ ├── pre-commit
│ └── commit-msg
├── config/
│ └── checkstyle/
│ └── checkstyle.xml
├── gradle/
│ └── ...
├── src/
│ ├── main/java/...
│ └── test/java/...
├── build.gradle
├── settings.gradle
├── mvnw
├── mvnw.cmd
├── gradlew
└── gradlew.batJika tim memakai Maven, struktur dasarnya sama. Yang penting adalah:
- hook disimpan di repository,
- konfigurasi formatter dan linter ikut di-versioning,
- ada cara instalasi hook yang jelas untuk semua developer.
Konfigurasi Spotless dan Checkstyle di Spring Boot
Contoh Gradle
Berikut contoh minimal untuk proyek Spring Boot berbasis Gradle. Fokusnya bukan pada plugin tambahan, melainkan pada alur format dan lint yang stabil.
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.0' apply false
id 'com.diffplug.spotless' version '6.0.0'
id 'checkstyle'
}
repositories {
mavenCentral()
}
spotless {
java {
googleJavaFormat()
target 'src/*/java/**/*.java'
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
}
checkstyle {
toolVersion = '10.0'
configFile = file('config/checkstyle/checkstyle.xml')
}
tasks.withType(Checkstyle).configureEach {
reports {
xml.required = false
html.required = true
}
}Walaupun angka versi pada contoh di atas hanya ilustratif, pola konfigurasinya umum:
spotlessApplyuntuk memperbaiki format otomatis.spotlessCheckuntuk memverifikasi apakah hasil format sudah benar.checkstyleMaindancheckstyleTestuntuk mengecek aturan style.
Dalam hook lokal, lebih praktis menjalankan spotlessApply lebih dulu agar developer tidak perlu memperbaiki masalah format secara manual.
Contoh Maven
Jika tim menggunakan Maven, ide yang sama tetap berlaku: satu plugin untuk formatting dan satu plugin untuk style check. Contoh berikut menunjukkan bentuk konfigurasi yang sering dipakai.
<build>
<plugins>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<configuration>
<java>
<googleJavaFormat />
<removeUnusedImports />
</java>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>config/checkstyle/checkstyle.xml</configLocation>
</configuration>
</plugin>
</plugins>
</build>Di Maven, biasanya hook akan memanggil target seperti ./mvnw spotless:apply lalu ./mvnw checkstyle:check atau fase build yang sudah dikaitkan dengan plugin tersebut.
Membangun Git Hook untuk Format dan Lint
Hook pre-commit
Tujuan hook pre-commit adalah memastikan file yang akan dikomit sudah terformat dan lolos lint dasar. Pada praktiknya, ada dua strategi:
- Format otomatis lalu restage: paling nyaman untuk developer.
- Hanya cek dan gagal jika tidak sesuai: lebih ketat, tetapi menambah langkah manual.
Untuk Spring Boot, strategi pertama biasanya lebih ramah adopsi.
#!/usr/bin/env sh
set -eu
if [ -z "$(git diff --cached --name-only --diff-filter=ACM | grep '\.java$' || true)" ]; then
exit 0
fi
echo "[pre-commit] Menjalankan format kode..."
./gradlew spotlessApply
echo "[pre-commit] Menambahkan ulang file yang berubah..."
git add -A
echo "[pre-commit] Menjalankan Checkstyle..."
./gradlew checkstyleMain checkstyleTest
echo "[pre-commit] Selesai."Mengapa pola ini efektif?
spotlessApplymengurangi friksi karena developer tidak perlu memperbaiki hal remeh secara manual.git add -Amemastikan hasil formatting ikut masuk ke commit.- Checkstyle dijalankan setelah format, sehingga hasil lint tidak tercampur dengan noise dari masalah formatting.
Jika proyek besar dan hook terasa lambat, Anda bisa mengurangi cakupan validasi lokal. Misalnya, cukup jalankan formatting dan satu subset lint lokal, sedangkan lint penuh tetap di CI.
Varian untuk Maven
#!/usr/bin/env sh
set -eu
if [ -z "$(git diff --cached --name-only --diff-filter=ACM | grep '\.java$' || true)" ]; then
exit 0
fi
echo "[pre-commit] Menjalankan format kode..."
./mvnw spotless:apply
echo "[pre-commit] Menambahkan ulang file yang berubah..."
git add -A
echo "[pre-commit] Menjalankan Checkstyle..."
./mvnw checkstyle:check
echo "[pre-commit] Selesai."Hook di atas sengaja hanya fokus pada Java. Jika repository juga berisi YAML, Markdown, atau file konfigurasi lain, Anda bisa menambahkan formatter tambahan sesuai kebutuhan, tetapi hindari membuat hook menjadi terlalu berat di tahap awal rollout.
Validasi Commit Message dengan commit-msg Hook
Format pesan commit yang konsisten memudahkan review, pencarian riwayat, dan otomatisasi release. Pola yang umum adalah Conventional Commits, misalnya:
feat: tambah endpoint registrasi penggunafix: perbaiki null pointer pada service pembayaranrefactor: sederhanakan mapping DTO ke entity
Hook commit-msg menerima path file sementara yang berisi pesan commit. Kita bisa memvalidasinya dengan regex sederhana.
#!/usr/bin/env sh
set -eu
COMMIT_MSG_FILE="$1"
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
PATTERN='^(feat|fix|docs|style|refactor|test|chore)(\([a-zA-Z0-9._-]+\))?: .{1,}$'
if ! printf '%s' "$COMMIT_MSG" | grep -Eq "$PATTERN"; then
echo "Pesan commit tidak valid."
echo "Gunakan format: type(scope): deskripsi atau type: deskripsi"
echo "Contoh: feat(auth): tambah validasi token refresh"
exit 1
fiRegex tersebut cukup untuk banyak tim, tetapi ada keterbatasan:
- Ia memeriksa format dasar, bukan kualitas isi deskripsi.
- Jika tim memakai pesan multi-baris dengan body dan footer, validasi perlu disesuaikan.
- Beberapa workflow seperti merge commit atau revert commit mungkin perlu pengecualian.
Karena itu, implementasi awal sebaiknya sederhana dulu. Jangan mulai dari aturan yang terlalu kompleks sampai developer kesulitan melakukan commit normal.
Cara Mengaktifkan Hook Secara Konsisten di Seluruh Tim
Masalah klasik Git hook adalah file di .git/hooks tidak ikut version control. Solusi yang umum adalah memakai core.hooksPath agar Git membaca hook dari folder yang ada di repository.
git config core.hooksPath .githooks
chmod +x .githooks/pre-commit .githooks/commit-msgSupaya onboarding lebih mulus, tambahkan satu skrip instalasi, misalnya:
#!/usr/bin/env sh
set -eu
git config core.hooksPath .githooks
chmod +x .githooks/pre-commit .githooks/commit-msg
echo "Git hooks aktif dari .githooks"Simpan sebagai scripts/setup-git-hooks.sh, lalu dokumentasikan di README:
./scripts/setup-git-hooks.shUntuk tim lintas OS:
- macOS/Linux: shell script seperti di atas biasanya cukup.
- Windows: jika developer memakai Git Bash, skrip shell umumnya tetap bisa dipakai. Jika tidak, pertimbangkan wrapper tambahan atau dokumentasi khusus.
Intinya, pilih mekanisme instalasi yang paling dekat dengan toolchain tim saat ini. Jangan membuat proses setup hook lebih rumit daripada setup proyek itu sendiri.
Menjaga Hook dan CI Tetap Sinkron
Hook lokal membantu, tetapi tidak bisa dipercaya sebagai satu-satunya pagar. Developer bisa melewati hook dengan:
git commit --no-verifySelain itu, selalu ada kemungkinan hook belum terpasang, rusak, atau berbeda antar mesin. Karena itu CI tetap harus menjalankan validasi yang setara atau lebih ketat.
Contoh validasi di CI
Untuk Gradle, pipeline minimal biasanya tetap menjalankan:
./gradlew spotlessCheck checkstyleMain checkstyleTest testUntuk Maven:
./mvnw spotless:check checkstyle:check testDengan pola ini, hook lokal berfungsi sebagai early feedback, sedangkan CI tetap menjadi sumber kebenaran. Jika seseorang memakai --no-verify, pipeline masih akan menangkap pelanggaran.
Praktik yang disarankan: anggap hook lokal sebagai optimasi pengalaman developer, bukan kontrol keamanan utama. Kontrol utama tetap ada di CI.
Trade-off Hook Lokal vs Validasi di CI
Kelebihan hook lokal
- Feedback jauh lebih cepat.
- Mengurangi commit gagal karena masalah sepele.
- Mendorong konsistensi sebelum kode meninggalkan mesin developer.
Kekurangan hook lokal
- Bisa terasa lambat jika terlalu banyak task dijalankan.
- Bisa dilewati dengan
--no-verify. - Perlu setup tambahan dan disiplin dokumentasi.
Kelebihan validasi di CI
- Konsisten di environment yang terkontrol.
- Tidak bergantung pada setup lokal developer.
- Cocok untuk validasi berat seperti test lengkap, security scan, atau integrasi.
Kekurangan validasi di CI
- Feedback lebih lambat.
- Biaya konteks lebih tinggi karena developer baru memperbaiki setelah push.
- Pipeline bisa penuh oleh kegagalan yang sebenarnya mudah dicegah.
Pilihan yang paling realistis bukan salah satu, melainkan kombinasi keduanya. Hook lokal menangani validasi murah dan deterministik; CI menangani verifikasi final dan hal-hal yang lebih berat.
Kesalahan Umum Saat Implementasi
1. Menjalankan terlalu banyak task di pre-commit
Jika hook butuh waktu terlalu lama, developer akan frustrasi dan mencari cara untuk mematikannya. Hindari menjalankan seluruh test suite integrasi di fase ini.
2. Format otomatis tanpa restage
Ini bug workflow yang sering terjadi. Formatter mengubah file, tetapi hasilnya tidak ikut ke commit karena tidak di-git add ulang.
3. Aturan lokal dan CI berbeda
Jika hook menjalankan formatter A tetapi CI memeriksa formatter B, developer akan bingung karena hasil lokal dan pipeline tidak sinkron.
4. Regex commit message terlalu ketat sejak awal
Aturan yang terlalu rumit sering menolak kasus valid seperti merge commit, revert, atau hotfix darurat. Mulailah dari format dasar yang paling dibutuhkan.
5. Tidak ada dokumentasi onboarding
Hook yang bagus pun tidak akan terpakai jika anggota tim baru tidak tahu cara mengaktifkannya.
Tips Debugging Saat Hook Gagal
- Jalankan skrip hook secara manual untuk melihat error lebih jelas.
- Pastikan file hook punya izin eksekusi:
chmod +x .githooks/pre-commit .githooks/commit-msg. - Periksa apakah
core.hooksPathsudah mengarah ke folder yang benar. - Pastikan wrapper build seperti
./gradlewatau./mvnwtersedia dan bisa dijalankan. - Jika di Windows ada masalah line ending, cek apakah file shell memakai format yang kompatibel dengan Git Bash.
- Gunakan output yang jelas di skrip hook agar developer tahu langkah mana yang gagal.
Dalam banyak kasus, masalah bukan pada Spotless atau Checkstyle, tetapi pada instalasi hook yang tidak konsisten atau path yang berbeda antar environment.
Strategi Rollout Bertahap agar Tidak Mengganggu Produktivitas
Rollout yang terlalu agresif sering gagal, terutama pada repository lama dengan banyak pelanggaran style historis. Pendekatan bertahap biasanya lebih efektif:
Tahap 1: format otomatis lokal
Mulai dari spotlessApply di pre-commit. Ini memberi manfaat cepat dengan friksi minimal.
Tahap 2: tambahkan lint dasar
Setelah tim terbiasa, tambahkan Checkstyle untuk aturan yang memang disepakati bersama.
Tahap 3: validasi commit message
Jika tim sudah punya konvensi perubahan yang cukup matang, aktifkan commit-msg untuk menjaga kualitas riwayat commit.
Tahap 4: sinkronkan penuh dengan CI
Pastikan pipeline memverifikasi aturan yang sama, lalu dokumentasikan kapan pengecualian diperbolehkan.
Untuk codebase lama, pertimbangkan rollout per modul atau hanya untuk file yang berubah. Ini lebih realistis daripada memaksa seluruh repository bersih sekaligus.
Tip praktis: saat pertama kali memperkenalkan formatter, buat satu commit khusus untuk reformat massal. Setelah itu, review berikutnya akan lebih bersih karena noise format sudah berkurang.
Rekomendasi Workflow yang Seimbang
Jika Anda ingin workflow yang praktis untuk mayoritas proyek Spring Boot, kombinasi berikut biasanya cukup seimbang:
- pre-commit: jalankan
spotlessApply, lalucheckstyleMain/checkstyleTestatau ekuivalennya. - commit-msg: validasi format commit message sederhana.
- CI: jalankan
spotlessCheck, Checkstyle, test, dan validasi lain yang lebih berat. - Onboarding: sediakan satu skrip setup dan dokumentasi singkat di README.
Dengan model ini, tim mendapatkan feedback cepat tanpa kehilangan pagar keamanan dari CI. Hasil akhirnya bukan hanya kode yang lebih rapi, tetapi juga alur commit yang lebih stabil dan review yang lebih efisien.
Penutup
Spring Boot pre-commit hook untuk format, lint, dan commit aman paling efektif jika diposisikan sebagai lapisan validasi awal, bukan satu-satunya mekanisme kontrol. Gunakan Git hook untuk mempercepat umpan balik, Spotless untuk formatting otomatis, Checkstyle untuk aturan style yang eksplisit, dan commit-msg untuk menjaga kualitas riwayat commit.
Jaga agar aturan lokal tetap ringan, dokumentasikan instalasinya, dan pastikan CI tetap memverifikasi hal yang sama. Dengan begitu, tim bisa mengurangi kegagalan pipeline akibat masalah sepele tanpa mengorbankan produktivitas harian.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!