11
Otomasi Repository: Husky, Commitlint, dan CI
Template ini menahan kesalahan masuk lewat dua lapis. Lapis pertama berjalan di mesin pengembang: tiga hook Husky memeriksa berkas dan pesan commit sebelum commit itu tercatat .husky/pre-commit:1-4. Lapis kedua berjalan di server: workflow GitHub Actions mengulang pemeriksaan yang sama setelah kode di-push ke main atau dijadikan pull request .github/workflows/lint.yml:1-61. Keduanya memakai script yang sama dari package.json, sehingga kegagalan lokal hampir selalu berarti kegagalan CI juga.
Tiga hook lokal
Setiap berkas hook memakai pola identik: memuat _/husky.sh pada baris kedua, lalu menjalankan tepat satu perintah.
| Hook | Perintah | Cakupan |
|---|---|---|
pre-commit | npx lint-staged | hanya berkas yang sudah di-stage .husky/pre-commit:1-4 |
commit-msg | npx --no-install commitlint --edit "$1" | pesan commit hasil penyuntingan .husky/commit-msg:1-4 |
post-merge | pnpm install | menyamakan node_modules dengan pnpm-lock.yaml setelah merge .husky/post-merge:1-4 |
Berkas _/husky.sh tidak disimpan di repository; direktori .husky/ hanya memuat tiga berkas hook di atas. Hook baru aktif setelah husky install dijalankan oleh script prepare package.json:182.
⚠️ Clone tanpa
pnpm installmembuat ketiga hook tidak pernah terpasang, dan tidak ada peringatan apa pun saatcommitdibuat. README menegaskan pemakaian pnpm justru supaya hook Husky bekerja sebagaimana mestinya README.md:793.
Opsi --no-install pada commit-msg melarang npx mengunduh paket, sehingga commitlint wajib sudah ada di node_modules. Ketiga alat itu masuk devDependencies: @commitlint/cli dan @commitlint/config-conventional package.json:31-324, sedangkan husky dan lint-staged package.json:48-505.
Alur commit dari hook lokal sampai CI
Diagram berikut menelusuri satu commit dari git commit sampai status check di GitHub. Perhatikan bahwa pemeriksaan yang sama dijalankan dua kali: sekali oleh hook, sekali oleh CI.
sequenceDiagram
autonumber
actor Dev as Developer
participant PC as pre-commit
participant LS as lint-staged
participant CM as commit-msg
participant CL as commitlint
participant CI as Code Check
Dev->>PC: git commit
PC->>LS: npx lint-staged
LS->>LS: eslint --max-warnings=0 pada berkas staged
LS->>LS: prettier -w
alt ada berkas yang gagal
LS-->>Dev: commit dibatalkan
else semua lolos
PC->>CM: pesan commit tersedia
CM->>CL: npx --no-install commitlint --edit
alt type di luar type-enum
CL-->>Dev: commit ditolak
else type valid
Dev->>CI: push ke main atau pull request
CI->>CI: lint:strict dan typecheck
CI->>CI: format:check dan test
CI-->>Dev: status check selesai
end
end
%% fig: Alur commit dari hook lokal sampai Code Check
%% desc: pre-commit menjalankan lint-staged lalu commit-msg menjalankan commitlint; setelah push atau pull request workflow Code Check mengulang seluruh pemeriksaan.lint-staged dan aturan commitlint
lint-staged dipilih berdasarkan pola berkas, bukan seluruh proyek, sehingga hanya berkas yang berubah yang diperiksa package.json:59-676.
| Pola berkas | Perintah berurutan |
|---|---|
**/*.{js,jsx,ts,tsx} | eslint --max-warnings=0, lalu prettier -w |
**/*.{json,css,scss,md,webmanifest} | prettier -w |
Perhatikan bahwa prettier -w menulis ulang berkas, bukan sekadar memeriksanya. Pemeriksaan tanpa menulis hanya terjadi di CI lewat format:check package.json:9-167.
commitlint.config.js mewarisi @commitlint/config-conventional lalu mempersempit daftar type commitlint.config.js:1-38. Ada sebelas nilai yang diterima commitlint.config.js:6-229.
type | Dipakai untuk |
|---|---|
feat | fitur baru |
fix | perbaikan bug |
docs | dokumentasi |
chore | pekerjaan pemeliharaan |
style | format tanpa mengubah perilaku |
refactor | penyusunan ulang kode tanpa mengubah perilaku |
ci | perubahan konfigurasi CI |
test | perubahan test |
perf | peningkatan performa |
revert | pembatalan commit sebelumnya |
vercel | penyesuaian deploy Vercel, lihat SEO, Sitemap, dan Deploy |
Aturan scope-enum sengaja masih dikomentari, jadi scope bebas dipakai selama formatnya type(scope): subject commitlint.config.js:4-510. Pesan seperti update atau fix bug tidak punya type yang sah dan ditolak sebelum commit tercatat.
Empat workflow GitHub Actions
Setiap workflow dipicu event yang berbeda, seperti terlihat pada peta berikut.
graph LR
PUSH["push ke main"] --> CODE["Code Check<br/>lint.yml"]
PR["pull_request"] --> CODE
PR --> LINK["Issue Autolink<br/>issue-autolink.yml"]
ASSIGN["issues assigned"] --> BRANCH["Create Branch<br/>create-branch.yml"]
DISPATCH["workflow_dispatch"] --> REL["release-please<br/>release-please.yml"]
BRANCH --> NAME["branchName i nomor issue"]
LINK --> PREFIX["branch-prefix i"]
CODE --> FAIL["check gagal"]
style CODE fill:#2d7d46,color:#fff
style FAIL fill:#dc2626,color:#fff
style BRANCH fill:#8b5cf6,color:#fff
style LINK fill:#8b5cf6,color:#fff
style NAME fill:#8b5cf6,color:#fff
style PREFIX fill:#8b5cf6,color:#fff
style REL fill:#f59e0b,color:#000
%% fig: Peta trigger workflow GitHub Actions
%% desc: Setiap workflow dipicu event berbeda; hanya Code Check yang menjaga push ke main dan pull request, sedangkan release-please masih manual.| Workflow | Berkas | Trigger | Langkah kunci |
|---|---|---|---|
| Code Check | lint.yml | push ke main, pull_request .github/workflows/lint.yml:2-611 | pnpm 9 dan Node 20 dengan cache pnpm, pnpm install --frozen-lockfile, lalu lint:strict, typecheck, format:check, test .github/workflows/lint.yml:16-4412 |
| release-please | release-please.yml | workflow_dispatch .github/workflows/release-please.yml:1-913 | google-github-actions/release-please-action@v3 dengan release-type: node .github/workflows/release-please.yml:10-1714 |
| Create Branch from Issue | create-branch.yml | issues tipe assigned .github/workflows/create-branch.yml:1-515 | robvanderleek/create-issue-branch@main .github/workflows/create-branch.yml:7-1316 |
| Issue Autolink | issue-autolink.yml | pull_request tipe opened .github/workflows/issue-autolink.yml:1-417 | tkt-actions/[email protected] dengan branch-prefix: 'i' dan resolve: 'true' .github/workflows/issue-autolink.yml:6-1618 |
Workflow Code Check juga memasang concurrency dengan group ${{ github.job }}-${{ github.ref }} dan cancel-in-progress: true, jadi push baru pada ref yang sama membatalkan run yang masih berjalan .github/workflows/lint.yml:8-1019.
Langkah CI memanggil script yang sudah ada di package.json, bukan perintah baru package.json:9-167. Karena itu kegagalan test di CI bisa ditelusuri lewat Pengujian dengan Jest, dan aturan lint serta TypeScript-nya dibahas di Konfigurasi Build dan Alat Pengembangan.
Cabang otomatis dan autolink issue
Saat sebuah issue di-assign, create-issue-branch membuat branch dengan nama dari .github/issue-branch.yml. Pola namanya i${issue.number}-${issue.title,}, dan issue berlabel epic atau debt dilewati .github/issue-branch.yml:1-920. Di sisi lain, add-issue-links memakai branch-prefix: 'i', sehingga prefix huruf i yang sama menghubungkan branch hasil otomasi dengan pull request yang dibuka .github/workflows/issue-autolink.yml:12-1621.
⚠️ Label
epicdandebttidak akan menghasilkanbranchotomatis. Kalaubranchtetap dibuat manual dengan nama di luar polai<nomor>, autolink tidak menemukan issue pasangannya.
release-please dan changelog
Workflow release-please sengaja hanya bisa dijalankan manual. Trigger otomatis push ke main tersedia di berkas itu, tetapi masih dikomentari .github/workflows/release-please.yml:3-922. Nilai package-name diisi release-please-action, bukan nama paket repository ini yang tertulis di package.json ts-nextjs-tailwind-starter package.json:223, jadi ganti nilai itu sebelum workflow dipakai sungguhan .github/workflows/release-please.yml:14-1724.
⚠️ Selama trigger hanya
workflow_dispatch,CHANGELOG.mdtidak bertambah sendiri setelahmergekemain. Riwayat rilis baru bergerak ketika seseorang menjalankan workflow ini dari tab Actions.