Conventional Commits dan semantic-release adalah kombinasi yang umum dipakai untuk mengotomatiskan proses rilis: menentukan kenaikan versi dari isi commit, membuat changelog, lalu mem-publish release tanpa hitung manual. Untuk tim yang sering merilis perubahan kecil atau bekerja paralel di banyak branch, pendekatan ini membantu mengurangi human error seperti salah bump version, changelog yang tertinggal, atau release yang tidak konsisten.
Dalam praktiknya, otomasi ini hanya efektif jika tim punya aturan commit yang konsisten, validasi yang dijalankan sejak awal, dan pipeline CI yang jelas. Artikel ini membahas implementasi yang realistis: format commit, validasi dengan commitlint, konfigurasi semantic-release, integrasi GitHub Actions untuk branch utama, strategi dry-run di staging, serta jebakan umum seperti squash merge, commit yang tidak konsisten, dan prerelease.
Mengapa Conventional Commits dan semantic-release cocok untuk alur rilis tim
Masalah utama pada proses rilis manual biasanya bukan pada satu langkah besar, melainkan akumulasi detail kecil:
- versi dinaikkan dengan tipe yang salah,
- perubahan penting tidak masuk changelog,
- rilis dibuat dari commit yang belum sesuai standar,
- maintainer harus menilai satu per satu apakah perubahan termasuk patch, minor, atau major.
Conventional Commits memberi format commit yang bisa diparsing mesin. semantic-release membaca commit tersebut untuk memutuskan apakah rilis berikutnya adalah patch, minor, atau major, lalu menjalankan langkah lanjutan seperti membuat Git tag, GitHub Release, memperbarui changelog, atau publish paket.
Secara umum, pemetaan yang sering dipakai adalah:
fix:→ patchfeat:→ minorBREAKING CHANGE:atau tanda breaking change → major
Contoh commit:
feat(auth): tambah refresh token endpoint
fix(api): perbaiki validasi header authorization
feat!: ubah format respons error
BREAKING CHANGE: field error_code diganti menjadi codeDengan pola ini, tim tidak perlu lagi menebak versi saat release. Keputusan diambil dari histori commit yang sudah distandardisasi.
Dasar adopsi di repository tim
Sebelum menyalakan semantic-release, pastikan repository memenuhi beberapa syarat minimum. Jika tidak, hasilnya biasanya tidak stabil.
1. Aturan branch yang jelas
Tentukan branch mana yang benar-benar dianggap sumber rilis, misalnya main atau master. semantic-release biasanya dijalankan hanya pada branch tersebut agar tidak membuat release dari branch fitur atau branch eksperimen.
2. Commit message harus konsisten
Kalau tim masih bebas menulis commit seperti update, fix bug, atau final, semantic-release tidak bisa menentukan versi dengan benar. Ini sebabnya validasi commit perlu diterapkan, bukan hanya didokumentasikan.
3. Riwayat git harus bisa dibaca CI
Pipeline release memerlukan akses ke tag dan histori commit. Pada CI, kesalahan umum adalah clone dangkal sehingga semantic-release tidak menemukan tag sebelumnya. Di GitHub Actions, gunakan fetch-depth: 0.
4. Token untuk publish release
Jika ingin membuat GitHub Release atau publish paket ke registry, pipeline harus memiliki token yang sesuai. Hak akses token perlu dibatasi secukupnya, tetapi tetap cukup untuk membuat release atau publish artefak.
5. Kesepakatan tipe commit dalam tim
Minimal, dokumentasikan tipe yang dipakai bersama. Misalnya:
feat: fitur barufix: perbaikan bugdocs: perubahan dokumentasirefactor: refactor tanpa perubahan perilaku eksternaltest: perubahan testchore: maintenance non-fungsionalbuild: perubahan build/dependencyci: perubahan pipeline CI/CD
Tidak semua tipe menghasilkan release. Umumnya hanya feat, fix, dan breaking changes yang memicu bump versi, tergantung konfigurasi.
Standar format Conventional Commits yang sebaiknya dipakai
Format dasarnya:
<type>(<scope>): <subject>Contoh yang baik:
feat(payments): tambah dukungan virtual account
fix(users): tangani null pada profil
docs(readme): perbarui langkah instalasi
refactor(cache): sederhanakan pembacaan konfigurasiJika ada breaking change, Anda bisa menandainya di header:
feat(api)!: ubah struktur payload loginAtau menambah footer:
feat(api): ubah struktur payload login
BREAKING CHANGE: field user diganti menjadi dataPraktik yang memudahkan tim
- Gunakan scope bila proyek cukup besar. Ini membantu changelog lebih mudah dibaca.
- Jaga subject singkat dan spesifik. Hindari subjek generik seperti
fix issue. - Gunakan breaking change secara disiplin. Jika perubahan memaksa pengguna mengubah integrasi, tandai dengan jelas.
Kesalahan umum dalam penulisan commit
feat: update→ terlalu umumfixed auth→ tidak sesuai formatchore: upgrade dependencypadahal mengubah perilaku publik → tipe salah, versi bisa tidak naikfeatuntuk perbaikan bug → minor release terpicu padahal seharusnya patch
Validasi commit message dengan commitlint
Dokumentasi saja tidak cukup. Agar format commit benar-benar dipatuhi, validasi harus otomatis. Cara umum adalah memakai commitlint, lalu menghubungkannya ke Git hook agar commit yang tidak valid ditolak sebelum masuk repository.
Contoh konfigurasi commitlint
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['feat', 'fix', 'docs', 'refactor', 'test', 'chore', 'build', 'ci', 'perf', 'revert']
]
}
};Konfigurasi di atas memakai aturan Conventional Commits yang umum, lalu membatasi tipe commit yang diizinkan.
Menjalankan validasi di lokal
Validasi commit biasanya dipasang lewat Git hooks, misalnya pada hook commit-msg. Implementasinya bisa memakai alat seperti Husky atau mekanisme hook lain yang dipakai tim. Prinsipnya sama: setiap commit diperiksa sebelum disimpan.
npx commitlint --edit $1Jika tim belum siap memasang validasi di lokal, minimal jalankan commitlint di CI pada pull request. Ini tidak seketat hook lokal, tetapi tetap mencegah commit buruk masuk ke branch utama.
Mengapa commitlint penting meski sudah ada code review
Reviewer manusia sering fokus pada kode, bukan format commit. Akibatnya commit message yang tidak sesuai lolos, lalu semantic-release gagal menentukan versi atau melewatkan release. commitlint menutup celah ini dengan validasi deterministik.
Konfigurasi semantic-release yang ringkas dan praktis
semantic-release membaca histori commit sejak tag release terakhir, menghitung versi berikutnya, membuat catatan rilis, lalu mem-publish hasilnya melalui plugin yang dipilih. Anda tidak perlu menulis versi secara manual di commit release.
Contoh file konfigurasi semantic-release
// .releaserc.json
{
"branches": [
"main",
{ "name": "beta", "prerelease": true }
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}Penjelasan singkat plugin:
@semantic-release/commit-analyzermembaca commit untuk menentukan level release.@semantic-release/release-notes-generatormembuat catatan rilis dari commit yang relevan.@semantic-release/changelogmemperbarui fileCHANGELOG.md.@semantic-release/gitmelakukan commit terhadap file yang diperbarui, misalnya changelog.@semantic-release/githubmembuat GitHub Release.
Jika proyek Anda adalah paket yang dipublish ke registry, tambahkan plugin publish yang sesuai untuk ekosistem tersebut. Jika tidak, konfigurasi di atas sudah cukup untuk mengelola tag, changelog, dan GitHub Release.
Mengapa semantic-release bekerja
semantic-release tidak menebak dari nama branch fitur atau label pull request. Ia membaca commit sejak release terakhir, lalu menerapkan aturan analisis commit. Dengan demikian, sumber kebenaran ada di histori git, bukan pada spreadsheet release atau ingatan maintainer.
Integrasi GitHub Actions untuk trigger setelah merge ke branch utama
Tempat paling aman menjalankan semantic-release adalah di CI setelah perubahan sudah masuk ke branch utama. Ini mencegah release dibuat dari branch yang belum final.
Workflow GitHub Actions
name: release
on:
push:
branches:
- main
- beta
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-releaseAda beberapa detail penting pada workflow ini:
- Trigger pada push ke branch utama: artinya release terjadi setelah merge selesai.
fetch-depth: 0: histori dan tag lengkap diperlukan untuk menghitung release berikutnya.- Test dijalankan sebelum release: jangan publish release jika build atau test gagal.
- Permission ditetapkan eksplisit: membantu menghindari kegagalan saat membuat release.
Apakah lebih baik trigger di pull_request atau push?
Untuk release final, push ke branch utama biasanya pilihan yang tepat. Trigger pada pull_request cocok untuk validasi, bukan publish. Jika semantic-release dijalankan terlalu awal, Anda berisiko membuat release dari kode yang belum benar-benar masuk ke branch produksi.
Strategi dry-run di staging sebelum produksi
Sebelum mengaktifkan release otomatis di branch produksi, lakukan simulasi di staging atau branch uji. Tujuannya bukan sekadar melihat apakah command berhasil, tetapi memverifikasi bahwa:
- commit yang ada menghasilkan bump versi yang benar,
- changelog terbentuk sesuai ekspektasi,
- token CI memiliki izin yang cukup,
- workflow hanya berjalan pada branch yang dimaksud.
Cara menjalankan dry-run
semantic-release menyediakan mode simulasi yang menampilkan apa yang akan dilakukan tanpa benar-benar membuat release. Ini cocok untuk branch staging atau untuk pengujian awal di CI.
npx semantic-release --dry-runAnda bisa menambahkan workflow terpisah untuk branch staging:
name: release-dry-run
on:
push:
branches:
- staging
jobs:
dry-run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- run: npm ci
- run: npx semantic-release --dry-runApa yang perlu diperiksa saat dry-run
- Apakah semantic-release mendeteksi release
patch,minor, ataumajorsesuai commit yang ada? - Apakah commit seperti
docsatauchoretidak memicu release jika memang itu yang diinginkan? - Apakah breaking change benar-benar terdeteksi?
- Apakah branch prerelease seperti
betadiperlakukan berbeda darimain?
Dry-run sebaiknya dilakukan beberapa kali dengan skenario commit yang berbeda sebelum pipeline produksi diaktifkan penuh.
Handling prerelease untuk branch beta atau candidate
Tidak semua tim ingin setiap fitur baru langsung menjadi release stabil. Jika Anda punya branch seperti beta, next, atau rc, semantic-release dapat dipakai untuk menghasilkan versi prerelease.
Pada contoh konfigurasi sebelumnya:
{ "name": "beta", "prerelease": true }Artinya branch beta akan menghasilkan versi prerelease, bukan release stabil. Ini berguna untuk:
- uji internal sebelum merge ke
main, - distribusi kandidat rilis ke QA,
- menguji perubahan breaking besar tanpa langsung merilis stabil.
Hal yang perlu diwaspadai pada prerelease
- Jangan campur branch prerelease dan stable tanpa aturan merge yang jelas.
- Pastikan konsumen internal paham bahwa versi beta bukan untuk produksi.
- Jika publish ke registry, atur channel atau dist-tag sesuai kebutuhan agar versi prerelease tidak menjadi default.
Jebakan umum saat adopsi di tim
1. Squash merge mengubah histori commit
Ini salah satu sumber masalah paling sering. Jika tim menggunakan squash merge, semantic-release biasanya hanya melihat satu commit hasil squash, bukan seluruh commit dalam pull request. Artinya, yang penting bukan commit individual di branch fitur, melainkan pesan commit hasil squash.
Jika pesan squash ditulis asal-asalan seperti update feature branch, maka release bisa tidak terpicu atau bump versinya salah.
Solusinya:
- wajibkan format Conventional Commits pada pesan squash merge,
- atau hindari squash merge jika tim ingin mempertahankan commit granular yang sudah rapi,
- tambahkan panduan untuk editor merge di platform Git hosting.
2. Commit tidak konsisten antar anggota tim
Jika sebagian tim memakai feat dan sebagian lagi menulis bebas, hasil release menjadi tidak dapat diprediksi. commitlint membantu, tetapi tim tetap perlu memahami makna tiap tipe commit.
3. Salah menandai breaking change
Perubahan yang merusak kompatibilitas publik tetapi ditulis sebagai fix atau feat biasa akan menghasilkan versi yang terlalu rendah. Akibatnya pengguna bisa menerima perubahan breaking tanpa kenaikan major version.
4. CI tidak mengambil seluruh tag
Jika checkout dangkal dipakai, semantic-release bisa gagal menentukan release terakhir atau menganggap seluruh histori sebagai perubahan baru. Gejalanya sering berupa versi yang tidak sesuai atau error terkait tag.
5. Mencampur commit maintenance dan fitur besar dalam satu commit
Satu commit yang terlalu banyak isi menyulitkan klasifikasi. Pisahkan perubahan besar agar histori release tetap bermakna dan changelog mudah dibaca.
6. Mengandalkan changelog manual bersamaan dengan changelog otomatis
Jika tim masih mengedit changelog manual tanpa aturan jelas, konflik merge dan inkonsistensi mudah terjadi. Tentukan apakah changelog sepenuhnya otomatis atau hanya ada bagian tertentu yang ditulis manual.
Tips debugging saat release tidak berjalan sesuai harapan
- Periksa log CI secara lengkap: semantic-release biasanya menjelaskan mengapa tidak ada release yang dibuat.
- Pastikan branch cocok dengan konfigurasi: jika branch tidak terdaftar, release akan dilewati.
- Cek format commit sejak tag terakhir: satu commit valid di masa lalu tidak cukup; semantic-release menganalisis rentang commit terbaru.
- Verifikasi token dan permission: khususnya jika GitHub Release gagal dibuat.
- Jalankan dry-run secara lokal atau di staging: ini membantu melihat keputusan release tanpa efek samping publish.
- Periksa pesan squash merge: sering kali di sinilah akar masalah sebenarnya.
Jika semantic-release mengatakan tidak ada release baru, itu belum tentu error. Bisa jadi memang tidak ada commit yang termasuk tipe pemicu release sejak tag terakhir.
Trade-off dan batasan yang perlu dipahami
Pendekatan ini sangat berguna, tetapi bukan tanpa kompromi.
- Disiplin commit menjadi syarat utama: tanpa itu, hasil otomasi akan salah.
- Perlu sedikit overhead awal: tim harus belajar format commit, menyiapkan hook, dan menyesuaikan workflow CI.
- Tidak semua jenis perubahan mudah dipetakan: kadang perubahan internal besar tidak memengaruhi publik API, sehingga tipe commit harus dipilih dengan hati-hati.
- Release notes otomatis tetap perlu ditinjau: bagus untuk konsistensi, tetapi belum tentu cukup naratif untuk pengumuman produk atau migrasi kompleks.
Meski demikian, untuk banyak tim engineering, trade-off ini layak karena mengurangi pekerjaan manual yang berulang dan rawan salah.
Checklist implementasi
- Tentukan branch rilis utama, misalnya
main. - Sepakati tipe commit yang dipakai tim.
- Tambahkan dokumentasi singkat Conventional Commits di repository.
- Pasang
commitlintuntuk validasi commit message. - Tambahkan validasi di hook lokal atau minimal di CI pull request.
- Konfigurasikan
semantic-releasedengan plugin yang dibutuhkan. - Pastikan CI mengambil histori dan tag penuh dengan
fetch-depth: 0. - Jalankan test sebelum langkah release.
- Siapkan token dan permission untuk membuat release atau publish artefak.
- Uji
--dry-rundi branch staging. - Definisikan strategi prerelease jika ada branch
betaatau sejenisnya. - Putuskan aturan squash merge dan pastikan pesan merge tetap mengikuti Conventional Commits.
Penutup
Conventional Commits dan semantic-release untuk rilis otomatis bukan sekadar alat bantu CI, tetapi mekanisme untuk menjadikan proses release lebih konsisten, bisa diaudit, dan tidak tergantung pada satu maintainer. Kunci keberhasilannya ada pada disiplin format commit, validasi sejak awal dengan commitlint, serta workflow CI yang hanya merilis dari branch yang memang disepakati.
Jika Anda menerapkannya bertahap, mulailah dari standarisasi commit dan dry-run di staging. Setelah pola commit tim stabil dan hasil simulasi sesuai harapan, baru aktifkan publish release otomatis di branch produksi. Pendekatan ini jauh lebih aman daripada langsung mengotomatiskan release pada repository yang histori commit-nya masih berantakan.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!