Membahas kebijakan rilis berbasis branch, bagaimana staging memproduksi versi kandidat berakhiran rc sementara main memproduksi versi stabil, serta menulis kondisi branch di GitHub Actions agar pipeline hanya berjalan pada jalur yang tepat.

Di episode 10 kita memverifikasi versi lewat dry run di kedua branch. Sekarang tiba waktunya mengunci kebijakan: rilis hanya boleh terjadi di branch yang tepat, dengan versi yang tepat pula. Kesalahan paling mahal di dunia release bukanlah versi yang salah — melainkan release yang berjalan di branch yang salah, meracuni registry dengan versi yang tidak seharusnya ada.
Episode ini membahas branch-based release conditions: kebijakan rilis per branch, cara menulis kondisi branch di GitHub Actions, dan bagaimana mengawinkannya dengan konfigurasi branches di semantic-release.
| Branch | Jenis rilis | Contoh versi |
|---|---|---|
main | stable | v1.3.0 |
staging | prerelease rc | v1.3.0-rc.1 |
rc | prerelease rc | v1.3.0-rc.1 |
| lainnya | tidak dirilis | tidak ada |
Pembagian ini memastikan setiap cabang punya arti yang jelas: main hanya menerima versi stabil, branch prerelease hanya menerima kandidat. Kebijakan ini diimplementasikan di dua tempat sekaligus — konfigurasi semantic-release dan kondisi workflow — sebagai pertahanan berlapis.
module.exports = {
branches: [
"main",
{ name: "staging", prerelease: "rc", channel: "rc" },
{ name: "rc", prerelease: "rc", channel: "rc" },
],
tagFormat: "v${version}",
};Branch main tanpa prerelease menghasilkan versi penuh. Branch staging dan rc memakai prerelease: "rc" sehingga setiap rilis diberi suffix rc dan nomor berjalan: commit pertama 1.3.0-rc.1, berikutnya 1.3.0-rc.2, dan seterusnya. Ketika staging di-merge ke main, versi stabil dihitung dari commit sejak tag stable terakhir.
Di GitHub Actions, setiap job punya akses ke konteks github. Untuk event push, github.ref berisi referensi lengkap branch seperti refs/heads/main, sedangkan github.ref_name hanya berisi nama branch seperti main.
name: Release
on:
push:
branches:
- main
- staging
- rc
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npx semantic-release
- name: Deploy ke pra-produksi
if: github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/rc'
run: npm run deploy:staging
- name: Deploy ke produksi
if: github.ref == 'refs/heads/main'
run: npm run deploy:prodWorkflow hanya dipicu oleh push ke tiga branch yang terdaftar di blok on, sehingga branch lain tidak pernah memicu rilis. Di dalam job, kondisi if memastikan tiap langkah deploy berjalan pada branch yang tepat. Rilis sendiri cukup satu perintah npx semantic-release — hasil versinya sudah ditentukan konfigurasi branch.
Warning
Perhatikan prefix refs/heads/. github.ref mengembalikan referensi
lengkap, jadi if: github.ref == 'main' tidak akan pernah benar dan
langkah akan selalu di-skip. Gunakan refs/heads/main, atau bandingkan
dengan github.ref_name == 'main' yang lebih pendek.
Release ganda sering terjadi saat push dan merge mendarat hampir bersamaan. Tambahkan concurrency agar run lama dibatalkan dan run terbaru yang dilanjutkan:
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: falsecancel-in-progress: false membuat release tidak dibatalkan di tengah jalan — yang tidak boleh terjadi karena bisa meninggalkan tag tanpa package atau sebaliknya. Run berikutnya menunggu run aktif selesai lebih dulu.
Tip
Jangan menaruh logika release pada lebih dari satu workflow. Satu file
workflow yang membaca branch, dikombinasikan dengan konfigurasi
branches, menjaga satu sumber kebenaran. Workflow kedua yang ikut
rilis hanya akan membuat versi bertabrakan.
Jika ingin memisahkan job rilis stable, gunakan kondisi di level job:
release-stable:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Semantic Release
run: npx semantic-releasePola yang sama berlaku untuk staging: cukup ubah perbandingan menjadi refs/heads/staging atau refs/heads/rc. Job yang kondisinya tidak terpenuhi akan ditandai "skipped" dan tidak memengaruhi status pipeline.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Bandingkan github.ref dengan main | Kondisi tidak pernah benar | Pakai refs/heads/main atau github.ref_name |
| Branch tidak terdaftar di config | Tidak ada rilis saat push | Daftarkan branch di branches |
| Release commit memicu loop | Rilis berulang terus | Sertakan [skip ci] pada commit rilis |
| Dua workflow release aktif | Versi bertabrakan | Satu workflow plus concurrency |
cancel-in-progress: true | Tag dibuat tanpa publish | Set false untuk job release |
Pada episode 11 ini kalian telah:
main stable, branch prerelease berakhiran rc.github.ref (referensi lengkap) dan github.ref_name (nama branch).on dan memakai if untuk langkah deploy per branch.concurrency dan cancel-in-progress: false.Di episode 12 kita akan mengamankan semuanya dengan GitHub Actions Security Best Practices — least privilege, pembatasan branch, dan pengamanan secret. Sampai jumpa di episode 12!