Gerbang kualitas kini berdiri di mesin Budi — tetapi gerbang yang hanya teruji oleh kebetulan bukan gerbang, hanya keberuntungan. Bagian penutup mengerjakan dua pekerjaan terakhir. Pertama, seluruh pertahanan diuji sistematis: tujuh kasus uji disusun dalam tabel, dijalankan nyata, dan dicocokkan dengan hasil yang diharapkan. Kedua, hasil kerja dipasangkan ke mesin rekan tim: salinan di .git/hooks/ dibuang, digantikan core.hooksPath yang menunjuk folder versioned — lalu clone baru oleh Siti membuktikan celah sekaligus solusinya.

Tabel Kasus Uji #

Ujian ini memakai satu fitur baru sebagai kelinci percobaan: modul ulasan pelanggan pada src/ulasan.js. Tujuh kasusnya mencakup tiga pesan jelek, tiga isi berkas kotor, dan satu kasus sehat:

KasusYang diujiInputHasil yang diharapkan
K1pesan tanpa format sama sekaliupdateDITOLAK oleh commit-msg
K2pesan deskriptif tapi tanpa typeperbaiki bug ulasanDITOLAK oleh commit-msg
K3format benar, subjek 93 karakterfeat(ulasan): tambah modul ulasan beserta …DITOLAK oleh commit-msg
K4trailing whitespace pada berkas stagedsrc/ulasan.jsDITOLAK oleh pre-commit
K5jejak debug pada berkas stagedconsole.log di src/ulasan.jsDITOLAK oleh pre-commit
K6berkas melebihi 1 MiBaset/banner.binDITOLAK oleh pre-commit
K7pesan dan berkas sehatmodul ulasan bersihLULUS — commit lahir

Siapkan berkasnya, lalu mulai dari tiga pesan jelek — semuanya dijalankan tanpa mengubah isi staging area:

bash
printf '// Modul ulasan pelanggan untuk web-toko.\nfunction tampilkanUlasan(daftar) {\n  return daftar.slice(0, 10);\n}\n' > src/ulasan.js
git add src/ulasan.js
git commit -m "update"
git commit -m "perbaiki bug ulasan"
git commit -m "feat(ulasan): tambah modul ulasan beserta tampilan bintang filter kategori dan urutan terbaru"

Output yang Dihasilkan (perintah git commit -m "update"):

text
commit-msg: subjek ditolak: 'update'
  format: type(scope): deskripsi — scope opsional
  type: feat | fix | docs | chore | refactor | test
  contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'

Output yang Dihasilkan (perintah git commit -m "perbaiki bug ulasan"):

text
commit-msg: subjek ditolak: 'perbaiki bug ulasan'
  format: type(scope): deskripsi — scope opsional
  type: feat | fix | docs | chore | refactor | test
  contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'

Output yang Dihasilkan (perintah git commit -m "feat(ulasan): tambah …"):

text
commit-msg: subjek 93 karakter — maksimal 72: 'feat(ulasan): tambah modul ulasan beserta tampilan bintang filter kategori dan urutan terbaru'

Kasus K3 layak diperhatikan: formatnya sudah benar — type, scope, titik dua, spasi — tetapi subjek 93 karakter tetap ditolak karena melewati batas 72. Gerbang kita membaca pesan sampai tuntas, bukan sekadar mengenali awalnya.

Lanjut ke K4: tulis ulang berkas dengan dua spasi menggantung di ujung baris kedua:

bash
printf '// Modul ulasan pelanggan untuk web-toko.\nfunction tampilkanUlasan(daftar) {  \n  return daftar.slice(0, 10);\n}\n' > src/ulasan.js
git add src/ulasan.js
git commit -m "feat(ulasan): tambah modul ulasan"

Output yang Dihasilkan (perintah terakhir):

text
src/ulasan.js:2: trailing whitespace.
+function tampilkanUlasan(daftar) {  
pre-commit: whitespace error — lihat daftar di atas

Perbaiki barisnya, lalu masukkan K5 — jejak debug di baris ketiga:

bash
printf '// Modul ulasan pelanggan untuk web-toko.\nfunction tampilkanUlasan(daftar) {\n  return daftar.slice(0, 10);\n}\n' > src/ulasan.js
git add src/ulasan.js
printf '// Modul ulasan pelanggan untuk web-toko.\nfunction tampilkanUlasan(daftar) {\n  console.log("debug: daftar masuk");\n  return daftar.slice(0, 10);\n}\n' > src/ulasan.js
git add src/ulasan.js
git commit -m "feat(ulasan): tambah modul ulasan"

Output yang Dihasilkan (perintah terakhir):

text
3:  console.log("debug: daftar masuk");
pre-commit: jejak debug di src/ulasan.js — hapus sebelum commit

Bersihkan kembali, lalu K6 — berkas besar ikut di-staging bersama modul yang sehat:

bash
printf '// Modul ulasan pelanggan untuk web-toko.\nfunction tampilkanUlasan(daftar) {\n  return daftar.slice(0, 10);\n}\n' > src/ulasan.js
git add src/ulasan.js
mkdir aset
python3 -c "open('aset/banner.bin','wb').write(b'\x89PNG' + bytes(1200000))"
git add aset/banner.bin
git commit -m "feat(ulasan): tambah modul ulasan"
git reset aset/banner.bin
rm -rf aset

Output yang Dihasilkan (perintah git commit):

text
pre-commit: aset/banner.bin (1200004 byte) melebihi 1 MiB

Banner ditarik keluar dari staging area dan dihapus — aset besar diserahkan ke CDN. Terakhir, K7: commit dengan pesan dan isi yang sehat:

bash
git commit -m "feat(ulasan): tambah modul ulasan"

Output yang Dihasilkan:

text
[main d0e0183] feat(ulasan): tambah modul ulasan
 1 file changed, 4 insertions(+)
 create mode 100644 src/ulasan.js

Hasil akhirnya cocok satu per satu dengan tabel: K1–K6 ditolak dengan alasan yang tepat, K7 lahir sebagai commit d0e0183. Tujuh dari tujuh — gerbang ini boleh dipercaya.

Pemasangan Tim: core.hooksPath #

Sampai titik ini pemasangan kita masih menyalin berkas ke .git/hooks/ — salinan yang tidak ikut di-clone dan berumur pendek. Git punya jalur yang lebih rapi sejak versi 2.9: konfigurasi core.hooksPath mengarahkan Git ke folder hook mana pun, termasuk folder hooks/ yang hidup di dalam repository. Siapkan dokumentasinya dulu agar rekan tim tahu ritualnya:

bash
printf '# web-toko\n\nAplikasi toko web untuk latihan otomasi Git hooks — Course Git\nProyek 06.\n\n## Isi\n\n- `src/app.txt` — peta halaman aplikasi\n- `src/promo.txt` — halaman promo\n- `src/kupon.js` — modul kupon diskon\n- `src/ulasan.js` — modul ulasan pelanggan\n\n## Pemasangan Hooks\n\nRepository ini menjaga kualitas commit lewat hooks pada folder\nversioned `hooks/` dan `scripts/`. Setelah melakukan clone, jalankan\nsatu perintah:\n\n    git config core.hooksPath hooks\n\nPesan commit mengikuti Conventional Commits — aturannya berada\ndi `scripts/validate-commit-message.sh`.\n' > README.md
cat README.md
git add README.md

Output yang Dihasilkan (perintah cat README.md):

text
# web-toko

Aplikasi toko web untuk latihan otomasi Git hooks — Course Git
Proyek 06.

## Isi

- `src/app.txt` — peta halaman aplikasi
- `src/promo.txt` — halaman promo
- `src/kupon.js` — modul kupon diskon
- `src/ulasan.js` — modul ulasan pelanggan

## Pemasangan Hooks

Repository ini menjaga kualitas commit lewat hooks pada folder
versioned `hooks/` dan `scripts/`. Setelah melakukan clone, jalankan
satu perintah:

    git config core.hooksPath hooks

Pesan commit mengikuti Conventional Commits — aturannya berada
di `scripts/validate-commit-message.sh`.

Sekarang ujian yang membedakan core.hooksPath dari sekadar cp: hapus kedua salinan di .git/hooks/, pasang konfigurasinya, lalu buktikan hook tetap hidup:

bash
rm .git/hooks/commit-msg .git/hooks/pre-commit
ls .git/hooks/commit-msg .git/hooks/pre-commit
git config core.hooksPath hooks
git config core.hooksPath
git commit -m "update"
git commit -m "docs: dokumentasikan pemasangan hooks"

Output yang Dihasilkan (perintah ls .git/hooks/commit-msg .git/hooks/pre-commit):

text
ls: cannot access '.git/hooks/commit-msg': No such file or directory
ls: cannot access '.git/hooks/pre-commit': No such file or directory

Output yang Dihasilkan (perintah git config core.hooksPath):

text
hooks

Output yang Dihasilkan (perintah git commit -m "update"):

text
commit-msg: subjek ditolak: 'update'
  format: type(scope): deskripsi — scope opsional
  type: feat | fix | docs | chore | refactor | test
  contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'

Output yang Dihasilkan (perintah git commit -m "docs: …"):

text
[main f40f159] docs: dokumentasikan pemasangan hooks
 1 file changed, 14 insertions(+)

Rangkai buktinya: kedua salinan sudah tidak ada, git config core.hooksPath menjawab hooks, dan pesan "update" tetap ditolak. Git tidak lagi membaca .git/hooks/ — Git membaca folder hooks/ di dalam repository, folder yang sama yang ikut ter-clone dan ikut ter-review. Commit dokumentasi pun lahir, membawa riwayat ke sembilan:

bash
git log --oneline

Output yang Dihasilkan:

text
f40f159 docs: dokumentasikan pemasangan hooks
d0e0183 feat(ulasan): tambah modul ulasan
1a1337c refactor: pisahkan validasi pesan ke skrip inti
6f7aed7 chore: pasang pemeriksaan pre-commit
3e08c4d feat: tambah kupon diskon
0f3e0f1 feat: tambah halaman promo
668d6c2 chore: pasang validasi pesan commit
9643466 feat: tambah keranjang
e9d7cc7 feat: rintis web-toko

Clone Baru oleh Siti: Celah yang Terbukti #

Inilah momen kejujuran proyek: apa yang benar-benar diterima rekan baru? Simulasikan Siti yang meng-clone web-toko — keluar dari repository dulu:

bash
cd ..
git clone web-toko web-toko-siti
cd web-toko-siti
git config user.name "Siti Rahma"
git config user.email "[email protected]"
git config core.hooksPath
ls .git/hooks/commit-msg .git/hooks/pre-commit

Output yang Dihasilkan (perintah git clone web-toko web-toko-siti):

text
Cloning into 'web-toko-siti'...
done.

Perintah git config core.hooksPath pulang tanpa keluaran — dan dengan exit code 1, sinyal bahwa kunci itu belum ada di clone. Sementara itu dua hook juga tidak ada:

Output yang Dihasilkan (perintah ls .git/hooks/commit-msg .git/hooks/pre-commit):

text
ls: cannot access '.git/hooks/commit-msg': No such file or directory
ls: cannot access '.git/hooks/pre-commit': No such file or directory

Folder hooks/ dan scripts/ ada di clone — keduanya ikut ter-clone sebagai berkas biasa — tetapi tidak ada satu pun yang aktif. Siti belum tahu itu, dan seperti kebanyakan dari kita, ia langsung bekerja:

bash
printf '\nModul ulasan dikelola oleh Siti Rahma.\n' >> README.md
git add README.md
git commit -m "update readme"

Output yang Dihasilkan (perintah terakhir):

text
[main 79eb5e6] update readme
 1 file changed, 2 insertions(+)

Commit lahir — pesan tanpa type dan tanpa format lolos begitu saja. Inilah batas hooks sisi klien yang dibedah di Konsep Git Hooks : pencegahan, bukan jaminan. Yang untung, commit ini masih lokal di clone Siti, belum terdorong ke mana pun.

Siti membaca README yang barusan di-commit Budi, lalu menjalankan satu-satunya perintah yang dimintanya:

bash
git config core.hooksPath hooks

Perintah itu senyap — exit code 0, tanpa keluaran. Dengan satu baris, Git di mesin Siti kini membaca folder hooks/ yang sejak awal sudah ada di clone-nya. Pesan commit buruknya bisa dibenahi lewat git commit --amend — dan amend menjalankan ulang kedua hook, jadi pesan buruk tidak bisa menyelundup balik:

bash
git commit --amend -m "update"
git commit --amend -m "docs: dokumentasikan modul ulasan"

Output yang Dihasilkan (perintah git commit --amend -m "update"):

text
commit-msg: subjek ditolak: 'update'
  format: type(scope): deskripsi — scope opsional
  type: feat | fix | docs | chore | refactor | test
  contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'

Output yang Dihasilkan (perintah git commit --amend -m "docs: …"):

text
[main 2a4321a] docs: dokumentasikan modul ulasan
 Date: Tue Dec 15 11:30:00 2026 +0700
 1 file changed, 2 insertions(+)

Pesan "update" ditolak di pintu yang sama; pesan yang benar lolos, dan baris Date: menunjukkan author date asli commit Siti tetap terjaga — perilaku amend yang dibahas di Amend Commit . Riwayat di clone Siti kini sepuluh commit, dengan sembilan milik Budi yang dijaga penuh dan satu milik Siti yang sudah dibereskan:

bash
git log --oneline

Output yang Dihasilkan:

text
2a4321a docs: dokumentasikan modul ulasan
f40f159 docs: dokumentasikan pemasangan hooks
d0e0183 feat(ulasan): tambah modul ulasan
1a1337c refactor: pisahkan validasi pesan ke skrip inti
6f7aed7 chore: pasang pemeriksaan pre-commit
3e08c4d feat: tambah kupon diskon
0f3e0f1 feat: tambah halaman promo
668d6c2 chore: pasang validasi pesan commit
9643466 feat: tambah keranjang
e9d7cc7 feat: rintis web-toko

Strategi Distribusi Hooks #

Pola yang barusan kita jalankan hanyalah satu dari beberapa cara menutup celah distribusi. Perbandingan lengkapnya pernah diuji nyata di Mengelola Hooks ; ringkasannya:

StrategiSatu langkah per cloneMutakhir lewat git pullCocok untuk
hooks/ + salin manual / install.shcp atau ./install.shsalinan perlu dipasang ulangrepo pribadi, tim sangat kecil
core.hooksPath ke folder versionedgit config core.hooksPath hooksya — folder ikut ter-clonetim tanpa framework, Git ≥ 2.9
framework pre-commitpre-commit installya — konfigurasi versionedtim multi-bahasa, butuh hook siap pakai
template directorytidak perlu (hanya git init)tidak — salinan sekali jadistandar proyek baru di satu mesin

Web-toko memilih core.hooksPath karena paling sedikit bergerak: hooknya sudah di dalam repository, pemasangannya satu perintah, dan pembaruannya mengikuti git pull biasa. Lapisan penegakan berikutnya — yang tidak bisa dilewati siapa pun — berdiri di sisi server dan CI, dibahas di Git dalam CI/CD .

Ringkasan Proyek #

Mesin penjaga yang dijanjikan penutup Proyek 05 kini berdiri dan teruji:

PencapaianBukti pada sesi
Menguasai mekanisme hookcommit ditolak nyata saat exit 0 dibalik jadi exit 1; staging area terbukti utuh
Validasi pesan Conventional Commits"update" dan "perbaiki bug ulasan" ditolak dengan petunjuk; subjek 93 karakter ditolak; sembilan commit semuanya lolos pola
Pemeriksaan isi staging areatrailing whitespace, console.log, dan berkas 1.200.004 byte — masing-masing menolak commit secara nyata (K1–K7: tujuh dari tujuh)
Struktur yang bisa dipakai ulanghooks/commit-msg menjadi pembungkus scripts/validate-commit-message.sh; penolakan tetap bekerja pasca-refaktor
Pemasangan lintas mesincore.hooksPath aktif tanpa satu pun salinan; clone Siti membuktikan celah lalu pulih dengan satu perintah + --amend

Konsep yang barusan dilatih tangan ini memiliki kedalaman teorinya masing-masing: mekanisme exit code di Konsep Git Hooks , format pesan di Conventional Commits , kedua hooknya di Pre-Commit Hook dan Commit-Msg Hook , serta strategi distribusinya di Mengelola Hooks .

Kompetensi tim web-toko kini lengkap pada satu siklus: menulis, meninjau, memulihkan, dan mencegah. Satu ritual terakhir belum pernah dijalankan secara utuh: membungkus pekerjaan yang sudah terjaga ini menjadi rilis bernomor — menentukan versi, menandai titik rilis dengan tag, menyusun changelog dari riwayat yang barusan kita jaga formatnya. Riwayat web-toko yang rapi itu modal sempurna untuk ritual tersebut: Proyek 07: Simulasi Release .

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

scripts/validate-commit-message.sh

Unduh berkas
bash
#!/bin/sh
# validate-commit-message.sh — inti validasi pesan commit web-toko.
# Dipanggil oleh hooks/commit-msg dengan $1 = berkas pesan sementara
# (biasanya .git/COMMIT_EDITMSG). Aturan: Conventional Commits.

berkas_pesan="$1"
subjek="$(grep -v '^#' "$berkas_pesan" | sed '/^[[:space:]]*$/d' | head -n 1)"

pola='^(feat|fix|docs|chore|refactor|test)(\(.+\))?: .+'

if [ -z "$subjek" ]; then
  echo "commit-msg: pesan kosong — tulis subjek dengan format type(scope): deskripsi" >&2
  exit 1
fi

if ! printf '%s\n' "$subjek" | grep -qE "$pola"; then
  {
    echo "commit-msg: subjek ditolak: '$subjek'"
    echo "  format: type(scope): deskripsi — scope opsional"
    echo "  type: feat | fix | docs | chore | refactor | test"
    echo "  contoh: 'feat: tambah keranjang' atau 'fix(ui): benahi tombol bayar'"
  } >&2
  exit 1
fi

if [ "${#subjek}" -gt 72 ]; then
  echo "commit-msg: subjek ${#subjek} karakter — maksimal 72: '$subjek'" >&2
  exit 1
fi