Membongkar commit analyzer, otak semantic-release yang mengubah setiap pesan commit menjadi keputusan versi. Termasuk pemetaan default preset angular, kustomisasi release rules dan parserOpts, serta urutan evaluasi aturan yang menentukan hasil akhir.

Di episode 3 kita mengenal format Conventional Commits seperti feat(scope): subject. Di episode 7 kita melihat bagaimana branch main dan staging menghasilkan versi berbeda. Penghubung keduanya adalah commit analyzer: komponen yang membaca pesan commit dan memutuskan apakah versi naik major, minor, patch, atau tidak sama sekali.
Kenapa commit docs(readme): perbaiki typo tidak boleh menaikkan versi? Kenapa tim lain justru ingin chore(deps) memicu patch? Episode ini menjawab keduanya — bagaimana aturan default bekerja dan bagaimana menyesuaikannya.
semantic-release menjalankan plugin dalam urutan pipeline: verifyConditions, analyzeCommits, verifyRelease, generateNotes, prepare, publish, dan fail. Commit analyzer bekerja di tahap analyzeCommits: ia mengambil seluruh commit sejak tag terakhir, menguji satu per satu, lalu mengembalikan satu release type.
Hasil akhirnya adalah level perubahan tertinggi yang ditemukan. Satu feat di antara banyak fix berarti rilis minor, bukan patch.
Tanpa konfigurasi apa pun, semantic-release memakai preset angular dengan pemetaan berikut:
| Commit type | Release type | Contoh hasil |
|---|---|---|
feat | minor | 1.2.0 -> 1.3.0 |
fix | patch | 1.2.0 -> 1.2.1 |
perf | patch | 1.2.0 -> 1.2.1 |
revert | patch | 1.2.0 -> 1.2.1 |
| breaking change | major | 1.2.0 -> 2.0.0 |
docs, style, test, chore, build, ci | tidak ada | tidak dirilis |
Breaking change terdeteksi dari footer BREAKING CHANGE: atau tanda seru pada subject seperti feat!: hapus dukungan Node 16. Tanpa aturan khusus, tipe seperti docs dan chore dianggap tidak layak rilis.
Aturan default bersifat konservatif: hanya feat dan fix yang memicu rilis. Padahal banyak tim ingin perbaikan kecil ikut dirilis:
build(deps): update esbuild — memperbarui tooling build sering berarti memperbaiki bug atau celah keamanan.chore(deps): bump lodash — kenaikan versi dependensi juga bisa bersifat patch.Sebaliknya, sebagian tim ingin docs tetap tidak memicu rilis agar changelog tidak penuh noise. Di sinilah releaseRules berperan: aturan custom kalian dievaluasi lebih dulu dan menimpa preset.
Aturan di releaseRules dievaluasi berurutan dari atas ke bawah, dan aturan pertama yang cocok langsung menentukan hasil. Taruh aturan paling spesifik lebih dulu — misalnya breaking: true harus berada di atas agar commit bertanda breaking selalu jadi major, apa pun tipenya.
Sebelum aturan diterapkan, commit dipecah dulu oleh parser. Dengan parserOpts kalian bisa menyesuaikan kata kunci note dan pola header:
parserOpts: {
noteKeywords: ["BREAKING CHANGE", "BREAKING-CHANGE"],
headerPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$/,
breakingHeaderPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?!: (.*)$/,
}noteKeywords menentukan footer mana yang dianggap breaking. headerPattern dan breakingHeaderPattern mendefinisikan struktur type(scope): subject, termasuk dukungan tanda !. Kalau tim memakai format commit custom, di sinilah tempat menyesuaikannya.
const config = {
branches: ["main", { name: "staging", prerelease: "rc" }],
plugins: [
[
"@semantic-release/commit-analyzer",
{
preset: "angular",
parserOpts: {
noteKeywords: ["BREAKING CHANGE", "BREAKING-CHANGE"],
headerPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?: (.*)$/,
breakingHeaderPattern: /^(\w*)(?:\(([\w$.\-* ]*)\))?!: (.*)$/,
},
releaseRules: [
{ breaking: true, release: "major" },
{ type: "feat", release: "minor" },
{ type: "fix", release: "patch" },
{ type: "perf", release: "patch" },
{ type: "build", release: "patch" },
{ type: "chore", release: "patch" },
{ type: "revert", release: "patch" },
{ type: "docs", release: false },
{ type: "style", release: false },
{ type: "test", release: false },
],
},
],
"@semantic-release/release-notes-generator",
"@semantic-release/github",
],
};
module.exports = config;Perubahan utama dibanding default: build dan chore kini memicu patch, sedangkan docs, style, dan test tetap tidak memicu rilis. revert tetap patch. Preset angular tetap dipakai sebagai fondasi, lalu aturan custom menimpa perilakunya.
Warning
Memetakan chore menjadi patch bisa menjebak kalian dalam release loop.
Commit rilis yang dibuat @semantic-release/git bertipe chore(release):
dan akan memicu rilis lagi. Solusinya: sertakan [skip ci] pada pesan
commit rilis, karena semantic-release mengabaikan commit pemicu yang
memuat kata kunci itu.
Setelah mengubah aturan, verifikasi sebelum benar-benar rilis:
git log --oneline -5
npx semantic-release --dry-run --no-ciBaca baris yang menyebut "next release version". Kalau commit terakhir docs dan tidak ada feat atau fix, hasilnya tidak ada rilis. Kalau ada chore(deps), versi akan naik patch — sesuai aturan yang kalian tulis.
| Kesalahan | Gejala | Solusi |
|---|---|---|
breaking ditaruh paling bawah | Breaking jadi minor/patch | Taruh aturan breaking: true paling atas |
| Tipe commit tidak dikenal | Tidak ada rilis | Pilih preset sesuai konvensi atau tambahkan rule |
chore memicu loop rilis | Rilis berulang terus | Tambahkan [skip ci] pada commit rilis |
| Salah tulis nama preset | Error saat analisis commit | Pastikan preset yang dipakai sudah terpasang |
Pada episode 8 ini kalian telah:
analyzeCommits dalam pipeline.angular: feat minor, fix/perf/revert patch, breaking major, lainnya tidak rilis.releaseRules dengan urutan first-match-wins dan parserOpts untuk format commit custom.npx semantic-release --dry-run --no-ci.Di episode 9 kita akan menyusun Linting & Build Automation — gerbang kualitas sebelum rilis, supaya hanya kode yang sudah teruji yang sampai ke tangan pengguna. Sampai jumpa di episode 9!