Release otomatis SemVer dengan Changesets dan GitHub Actions cocok ketika tim mulai kewalahan menjaga versi paket, menulis changelog manual, dan melakukan publish yang mudah salah langkah. Dengan pola ini, perubahan versi dan catatan rilis ditentukan saat PR dibuat, lalu proses pembuatan version PR, tagging, dan publish dijalankan otomatis dari branch yang sudah ditentukan.

Untuk tim JavaScript/TypeScript, pendekatan ini bekerja baik pada single package repository maupun monorepo. Kuncinya adalah memisahkan dua tahap: menentukan perubahan versi saat fitur digabung, dan menerbitkan rilis hanya dari branch release yang aman. Hasilnya adalah alur CI/CD yang lebih dapat diaudit, lebih konsisten, dan lebih sulit rusak karena human error.

Kapan workflow ini layak dipakai

Repo tunggal

Untuk satu paket NPM atau satu library internal, Changesets membantu tim mendisiplinkan kenaikan versi dengan format yang konsisten. Ini berguna jika masalah yang sering muncul adalah:

  • versi di package.json sering lupa dinaikkan,
  • changelog ditulis belakangan dan tidak akurat,
  • publish dilakukan manual dari laptop developer,
  • tag Git tidak sinkron dengan versi paket.

Monorepo

Pada monorepo, manfaatnya biasanya lebih besar. Changesets bisa mencatat paket mana yang berubah, lalu menghitung kebutuhan kenaikan versi antar paket yang saling bergantung. Ini penting ketika satu perubahan di package inti harus memicu bump di package lain yang menjadi dependennya.

Workflow ini paling cocok jika:

  • repo berisi beberapa package JavaScript/TypeScript,
  • tim memakai pull request sebagai titik review utama,
  • publish dilakukan ke registry seperti npm registry atau registry privat yang kompatibel,
  • tim ingin release bisa direproduksi dari CI, bukan dari mesin lokal.

Workflow ini mungkin terlalu berat jika repo sangat kecil, release sangat jarang, dan hanya ada satu maintainer yang nyaman mengelola versi secara manual. Namun begitu ada lebih dari satu kontributor aktif, otomatisasi biasanya cepat terasa manfaatnya.

Konsep alur release otomatis SemVer

Secara umum, alurnya seperti ini:

  1. Developer membuat perubahan kode di branch fitur.
  2. Developer menambahkan file changeset yang menjelaskan major, minor, atau patch untuk package yang terdampak.
  3. Pull request direview, termasuk review isi changeset.
  4. Setelah PR digabung ke branch utama, GitHub Actions menjalankan Changesets untuk membuat atau memperbarui version PR.
  5. Version PR berisi perubahan versi paket, pembaruan changelog, dan perubahan lockfile jika ada.
  6. Saat version PR digabung, workflow publish berjalan dari branch yang diizinkan, membuat tag Git, lalu menerbitkan package.

Pemisahan antara PR fitur dan version PR penting karena memberi satu titik audit yang jelas. Tim bisa melihat dengan eksplisit paket apa yang akan dirilis, versi akhirnya, dan catatan perubahan yang akan dipublikasikan.

Memahami struktur changeset

Changesets menyimpan metadata perubahan dalam file Markdown di folder .changeset. File ini biasanya dibuat dengan perintah interaktif:

npx changeset

Contoh isi file changeset:

---
"@acme/utils": minor
"@acme/web": patch
---

Menambahkan helper validasi baru pada utils dan memperbaiki penggunaan util tersebut di web.

Bagian atas adalah frontmatter yang menentukan package mana yang berubah dan jenis bump versi:

  • patch: perbaikan bug atau perubahan kompatibel kecil,
  • minor: fitur baru yang tetap kompatibel,
  • major: perubahan yang memutus kompatibilitas.

Deskripsi di bawah frontmatter menjadi bahan changelog. Kualitas teks ini penting. Jangan isi dengan pesan terlalu umum seperti update code atau fix bug. Tulis dampak perubahan dari sudut pandang pengguna paket.

Catatan: untuk monorepo, diskusikan aturan bump lintas paket sejak awal. Misalnya, jika package A berubah secara minor dan package B bergantung langsung pada A, apakah B juga ikut di-bump. Changesets mendukung pengelolaan dependensi, tetapi kebijakan tim tetap harus jelas.

Setup dasar Changesets di repo JavaScript/TypeScript

Instal Changesets sebagai dev dependency, lalu inisialisasi konfigurasi:

npm install -D @changesets/cli
npx changeset init

Perintah init akan membuat folder .changeset dan file konfigurasi dasar. Pada banyak tim, setup minimal sudah cukup, lalu aturan tambahan ditempatkan di dokumentasi internal atau workflow CI.

Beberapa praktik yang disarankan:

  • simpan file changeset di PR yang sama dengan perubahan kode,
  • wajibkan setiap PR yang memengaruhi package publik untuk punya changeset,
  • izinkan pengecualian untuk PR internal seperti perubahan CI, dokumentasi non-release, atau refactor yang benar-benar tidak mengubah paket terdistribusi,
  • tambahkan pengecekan CI agar PR tanpa changeset gagal jika memang diwajibkan.

Struktur repo yang umum

Untuk repo tunggal:

.
├── .changeset/
├── .github/workflows/
├── src/
├── package.json
└── CHANGELOG.md

Untuk monorepo:

.
├── .changeset/
├── .github/workflows/
├── packages/
│   ├── core/
│   │   └── package.json
│   ├── ui/
│   │   └── package.json
│   └── utils/
│       └── package.json
├── package.json
└── pnpm-workspace.yaml

Manajer paket bisa berupa npm, pnpm, atau yarn workspaces. Prinsip workflow tetap sama, tetapi perintah install dan publish perlu disesuaikan dengan tool yang dipakai tim.

Proses review PR yang efektif

Kesalahan paling umum bukan pada tool, melainkan pada disiplin review. File changeset sebaiknya diperlakukan sebagai bagian penting dari perubahan, bukan formalitas.

Yang perlu dicek reviewer

  • Apakah package yang dipilih sudah benar?
  • Apakah bump versinya sesuai dampak perubahan?
  • Apakah deskripsi changeset cukup jelas untuk changelog publik?
  • Apakah ada perubahan breaking yang salah diberi label patch atau minor?
  • Apakah PR internal yang tidak perlu release memang tidak membawa changeset?

Kesalahan umum

  • Major tersamar sebagai minor: API berubah, tetapi tim menganggap perubahan kecil karena implementasinya sederhana.
  • Terlalu banyak package ikut di-bump: biasanya karena belum jelas package mana yang benar-benar terdampak.
  • Deskripsi terlalu teknis internal: cocok untuk commit message, tetapi buruk untuk changelog pengguna.

Jika tim sering berdebat soal major/minor/patch, buat panduan singkat internal berisi contoh nyata dari produk sendiri. Ini jauh lebih efektif dibanding hanya mengutip definisi SemVer secara umum.

Membangun version PR dengan GitHub Actions

Langkah berikutnya adalah membuat workflow yang berjalan saat perubahan masuk ke branch utama, lalu membuka atau memperbarui version PR. Actions ini tidak langsung publish. Ia hanya menyiapkan perubahan versi dan changelog agar bisa direview lagi.

Contoh workflow:

name: Version Packages

on:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write

jobs:
  version:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Create or update version PR
        uses: changesets/action@v1
        with:
          version: npm run version-packages
          commit: "chore: version packages"
          title: "chore: version packages"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Script version-packages biasanya memanggil:

{
  "scripts": {
    "version-packages": "changeset version"
  }
}

Mengapa workflow ini efektif:

  • changeset version membaca semua file changeset yang belum dikonsumsi.
  • Versi di package.json diperbarui secara konsisten.
  • Changelog dihasilkan atau diperbarui otomatis.
  • Perubahan dirangkum ke version PR sehingga tim bisa meninjau sebelum publish.

Di beberapa tim, version PR langsung di-merge otomatis jika seluruh pemeriksaan lolos. Namun untuk awal adopsi, lebih aman menahannya sebagai PR biasa agar maintainer bisa meninjau hasil bump versi.

Workflow publish package dan tagging Git

Setelah version PR digabung, barulah workflow publish dijalankan. Di tahap ini, package diterbitkan ke registry dan tag Git dibuat. Penting untuk membatasi publish hanya dari branch tertentu, misalnya main atau branch release khusus.

name: Publish Packages

on:
  push:
    branches:
      - main

permissions:
  contents: write
  packages: write

jobs:
  publish:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://registry.npmjs.org/
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Publish packages
        uses: changesets/action@v1
        with:
          publish: npm run release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Script release misalnya:

{
  "scripts": {
    "release": "changeset publish"
  }
}

Pada workflow ini, changeset publish akan menerbitkan package yang versinya sudah dinaikkan pada commit sebelumnya. Biasanya tool ini juga menangani pembuatan tag versi sesuai package yang dipublish, selama permission repository memadai.

Guard agar rilis hanya dari branch tertentu

Jangan andalkan kebiasaan tim semata. Tambahkan guard eksplisit:

  • trigger workflow hanya pada branch yang disetujui,
  • gunakan kondisi if pada job untuk memastikan branch benar,
  • aktifkan protection rule pada branch utama,
  • batasi siapa yang boleh mengubah workflow release,
  • jika perlu, gunakan GitHub Environment dengan approval untuk tahap publish.

Untuk organisasi yang lebih ketat, branch release bisa dipisahkan dari branch development. Misalnya, PR digabung ke main, tetapi publish hanya terjadi dari release setelah promosi terkontrol. Trade-off-nya adalah alur lebih aman, tetapi lebih kompleks.

Strategi secret dan token yang aman

Kesalahan umum pada release otomatis adalah penggunaan token yang terlalu luas atau disimpan sembarangan. Prinsip minimasi hak akses sangat penting.

Rekomendasi praktis

  • Gunakan GITHUB_TOKEN bawaan untuk operasi repository seperti membuat PR, commit, atau tag, jika permission-nya cukup.
  • Gunakan token registry terpisah, misalnya NPM_TOKEN, hanya untuk publish.
  • Simpan token di GitHub Actions Secrets, bukan di file repo atau variabel plaintext.
  • Jika registry mendukung token granular, batasi hanya untuk publish package yang relevan.
  • Rotasi token secara berkala, terutama jika maintainer berganti.

Hal yang perlu dihindari

  • menggunakan personal access token dengan scope luas tanpa kebutuhan jelas,
  • membagikan token lintas environment development dan production,
  • menjalankan publish dari workflow yang bisa dipicu bebas oleh pull request dari fork.

Praktik aman: jangan publish pada event pull_request. Jalankan publish hanya pada push ke branch terpercaya agar secret tidak terekspos ke kontribusi eksternal.

Menangani prerelease

Prerelease berguna untuk menguji kandidat rilis tanpa mengganggu kanal stabil. Contohnya untuk versi alpha, beta, atau rc sebelum paket dipublikasikan sebagai versi final.

Pendekatan umumnya:

  • gunakan branch terpisah untuk prerelease, misalnya next,
  • aktifkan mode prerelease pada Changesets sesuai kebutuhan tim,
  • publish dengan dist-tag non-default seperti next agar tidak menjadi versi stabil utama.

Secara operasional, tim biasanya punya dua jalur:

  • main untuk stable release,
  • next untuk prerelease.

Manfaatnya adalah pengguna awal bisa mencoba perubahan besar lebih cepat, sementara pengguna umum tetap menerima versi stabil. Trade-off-nya, tim harus disiplin menjaga branch dan tag agar tidak saling tercampur.

Hal yang sering keliru adalah menganggap prerelease hanya soal suffix versi. Sebenarnya yang lebih penting adalah isolasi alur publish, penamaan dist-tag yang benar, dan komunikasi internal bahwa versi prerelease tidak otomatis aman untuk production.

Rollback sederhana saat rilis bermasalah

Pada registry package, rollback tidak selalu berarti menghapus versi yang sudah terbit. Banyak registry membatasi unpublish atau membuatnya berisiko untuk konsumen. Karena itu, strategi rollback paling aman biasanya adalah forward fix: terbitkan versi baru yang memperbaiki masalah.

Langkah praktis jika publish gagal sebagian

  1. Periksa log Actions: apakah gagal saat build, autentikasi, atau publish.
  2. Pastikan versi mana yang sudah benar-benar terbit di registry.
  3. Jika hanya sebagian package terpublish di monorepo, hindari mengedit versi manual tanpa rencana yang jelas.
  4. Buat changeset baru untuk perbaikan, lalu rilis versi berikutnya.
  5. Jika tag Git sudah terbuat tetapi publish gagal, sinkronkan kondisi repo dan registry sebelum mencoba ulang.

Kapan perlu intervensi manual

Intervensi manual kadang diperlukan jika:

  • tag Git dibuat tetapi package belum terpublish,
  • workflow publish dijalankan dua kali untuk commit yang sama,
  • konfigurasi registry salah sehingga publish menuju namespace yang keliru.

Dalam kasus seperti ini, dokumentasikan langkah koreksi tim. Jangan hanya memperbaiki sekali lalu melupakannya, karena masalah serupa cenderung muncul lagi.

Debugging masalah yang sering muncul

Version PR tidak muncul

  • Pastikan workflow berjalan pada branch yang benar.
  • Periksa apakah memang ada file changeset baru yang belum dikonsumsi.
  • Pastikan permission contents: write dan pull-requests: write tersedia.
  • Cek apakah GITHUB_TOKEN dibatasi oleh pengaturan repository atau organisasi.

Publish gagal autentikasi

  • Verifikasi secret NPM_TOKEN masih valid.
  • Pastikan workflow memakai registry-url yang sesuai dengan registry target.
  • Cek apakah package bersifat public atau restricted, lalu samakan dengan pengaturan publish tim.

Versi tidak sesuai ekspektasi

  • Lihat semua file changeset yang masuk sejak rilis terakhir.
  • Pastikan tidak ada changeset lama yang tertinggal dan ikut dikonsumsi.
  • Review kembali kebijakan dependency bump pada monorepo.

Tag Git tidak sinkron

  • Gunakan fetch-depth: 0 saat checkout agar riwayat dan tag tersedia penuh.
  • Pastikan workflow publish tidak berjalan paralel secara konflik untuk commit yang sama.

Trade-off dan batasan pendekatan ini

  • Lebih disiplin, lebih banyak proses: tim harus terbiasa menulis changeset di setiap PR yang relevan.
  • Butuh review yang matang: tool tidak bisa menggantikan keputusan produk soal breaking change.
  • Monorepo perlu kebijakan dependensi: tanpa aturan jelas, bump versi antar package bisa membingungkan.
  • Release jadi lebih terstruktur, tetapi setup awal sedikit lebih kompleks dibanding publish manual.

Walau ada overhead, keuntungan utamanya adalah jejak audit yang rapi dan pengurangan kesalahan operasional. Untuk banyak tim, itu lebih berharga daripada kecepatan semu dari rilis manual.

Checklist adopsi untuk tim kecil

  1. Tentukan branch mana yang menjadi sumber release stabil.
  2. Pasang Changesets dan inisialisasi konfigurasi dasar.
  3. Tambahkan aturan PR: perubahan yang memengaruhi package harus memiliki changeset.
  4. Buat workflow version PR terpisah dari workflow publish.
  5. Simpan NPM_TOKEN atau token registry setara di secrets repository atau environment.
  6. Aktifkan branch protection untuk branch release.
  7. Sepakati aturan major, minor, dan patch dengan contoh nyata dari codebase sendiri.
  8. Tentukan apakah tim perlu jalur prerelease seperti next.
  9. Uji end-to-end pada package percobaan sebelum diterapkan ke semua package.
  10. Dokumentasikan prosedur gagal rilis dan langkah koreksi sederhana.

Penutup

Release otomatis SemVer dengan Changesets dan GitHub Actions bukan sekadar otomatisasi publish, tetapi cara membuat proses rilis lebih dapat diprediksi. Tim bisa menentukan bump versi saat PR masih hangat, menghasilkan changelog dari konteks perubahan yang benar, lalu menerbitkan paket hanya dari branch yang aman dengan token yang terkontrol.

Jika Anda mengelola repo JavaScript/TypeScript yang mulai sering merilis package, terutama dalam monorepo, pola ini memberi keseimbangan yang baik antara otomatisasi dan kontrol. Mulailah dari alur sederhana: wajibkan changeset, buat version PR, publish hanya dari branch utama, lalu tambahkan prerelease dan guard lanjutan ketika kebutuhan tim berkembang.