Membedah alur rilis dua jalur: staging sebagai tempat release candidate berakhiran rc dan main sebagai cabang stabil tanpa akhiran. Termasuk perbandingan dry run dengan actual release serta workflow yang melayani kedua jalur sekaligus.

Di episode 6 workflow CI sudah berdiri: job ci memvalidasi setiap push dan pull request, job release berjalan hanya di branch main dan staging. Sekarang pertanyaannya: kalau kedua branch memicu workflow yang sama, apakah hasil rilisnya juga sama?
Jawabannya tidak. Rilis dari staging dan rilis dari main adalah dua barang berbeda: yang pertama kandidat, yang kedua produk final. Episode ini membedah staging vs production release flow — bagaimana satu konfigurasi semantic-release melayani dua jalur, perbedaan dry run dengan actual release, dan mengapa pola ini menyelamatkan kalian dari rilis rusak di produksi.
Bayangkan restoran: staging adalah dapur percobaan tempat menu baru dicicipi, main adalah menu yang disajikan ke pelanggan. Tidak masuk akal menyajikan hidangan yang belum diuji. Dalam release, staging menampung release candidate — versi hampir final yang siap diuji di pra-produksi — sementara main hanya menerima versi stabil.
Pembeda keduanya terlihat dari nomor versi:
staging keluar v1.2.0-rc.1 — ada akhiran rc sebagai tanda kandidat.main keluar v1.2.0 — versi penuh, tanpa akhiran apa pun.Akhiran rc bukan sekadar kosmetik. Menurut aturan SemVer, 1.2.0-rc.1 punya precedence lebih rendah daripada 1.2.0, dan berlaku urutan 1.2.0-rc.1 < 1.2.0-rc.2 < 1.2.0. Kandidat tidak pernah dianggap menimpa versi stabil, dan urutan antar kandidat tetap teratur.
┌──────────────┐ merge ┌──────────────┐
│ staging │──────────►│ main │
│ (prerelease) │ │ (stable) │
└──────────────┘ └──────────────┘
│ │
▼ ▼
v1.2.0-rc.1 v1.2.0-rc.2 v1.2.0 v1.3.0Fitur dikerjakan di branch feature, di-merge ke staging, lalu menghasilkan rc yang diuji tim QA. Setelah disetujui, staging di-merge ke main dan semantic-release menghitung versi stabil berikutnya dari seluruh commit sejak tag stabil terakhir.
| Branch | Contoh versi | Sifat |
|---|---|---|
main | v1.2.0 | Stabil, siap produksi |
staging | v1.2.0-rc.1 | Release candidate, pra-produksi |
feature/* | tidak dirilis | Development |
Branch feature/* tidak perlu didaftarkan — semantic-release hanya memproses branch yang terdaftar di konfigurasi branches. Push ke branch lain dianggap bukan momen release dan tidak menghasilkan versi apa pun.
Dry run adalah gladi resik: seluruh pipeline berjalan, versi dihitung, changelog disusun — tetapi tanpa efek samping. Actual release melakukan semuanya hingga tuntas.
npx semantic-release --dry-run --no-ci
npx semantic-releaseBendera --no-ci diperlukan saat berjalan di mesin lokal karena semantic-release menolak berjalan di luar environment CI. Dry run menjadi alat verifikasi terbaik sebelum rilis asli — dibahas lebih dalam di episode 10.
| Aspek | Dry run | Actual release |
|---|---|---|
| Hitung versi berikutnya | ya | ya |
| Susun changelog | ya | ya |
| Buat tag git | tidak | ya |
| Publish ke npm | tidak | ya |
| Buat GitHub release | tidak | ya |
| Commit CHANGELOG.md | tidak | ya |
module.exports = {
branches: [
"main",
{ name: "staging", prerelease: "rc", channel: "rc" },
{ name: "rc", prerelease: "rc", channel: "rc" },
],
tagFormat: "v${version}",
};"main" berarti stable tanpa akhiran. Baris dengan prerelease: "rc" berarti setiap rilis dari branch itu diberi suffix rc dengan nomor berjalan — commit pertama menghasilkan 1.2.0-rc.1, berikutnya 1.2.0-rc.2. Opsi channel: "rc" menandai package npm dengan dist-tag terpisah sehingga konsumen versi stabil tidak ikut ter-update oleh kandidat.
name: Release
on:
push:
branches: [main, staging]
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-releasePerhatikan: workflow-nya satu, hasilnya dua jenis. Dari staging keluar v1.2.0-rc.1, dari main keluar v1.2.0. Semua keputusan ada di konfigurasi branches, bukan di workflow — satu sumber kebenaran untuk logika rilis.
Tip
Jangan pernah membuat tag versi secara manual setelah workflow berjalan. Tag manual akan dianggap sebagai rilis terakhir oleh semantic-release dan bisa membuat versi berikutnya tidak pernah terbit. Biarkan alat ini yang membuat tag, biarkan workflow yang mengelola seluruh siklusnya.
| Kesalahan | Gejala | Solusi |
|---|---|---|
| Push tag manual | Versi bertabrakan dengan hasil SemVer | Biarkan semantic-release membuat tag |
prerelease tidak dikonfigurasi | Rilis dari staging ikut versi stabil | Daftarkan branch dengan prerelease |
Branch tidak ada di branches | Tidak ada rilis saat push | Tambahkan branch ke konfigurasi |
| Trigger workflow terlalu luas | Rilis terjadi dari branch lain | Batasi branches di event push |
Pada episode 7 ini kalian telah:
main menghasilkan versi stabil tanpa suffix dan staging/rc menghasilkan release candidate berakhiran rc.branches melayani dua jalur tanpa menduplikasi workflow.rc tidak pernah mengalahkan versi stable menurut aturan SemVer.Di episode 8 kita akan membongkar Commit Analyzer & Release Rules: cara menentukan tipe commit mana yang menaikkan major, minor, atau patch, dan bagaimana menyesuaikan aturannya untuk kebutuhan tim. Sampai jumpa di episode 8!