Pipeline CI bertahap dengan Changesets adalah pendekatan praktis untuk mengelola rilis monorepo tanpa mengandalkan version bump manual dan changelog yang rawan tidak konsisten. Dengan pola ini, setiap pull request membawa metadata rilis dalam bentuk changeset, lalu CI memvalidasi perubahan lebih awal, membuat release PR, dan hanya melakukan publish dari branch utama setelah perubahan benar-benar disetujui.

Masalah yang sering muncul di monorepo biasanya sama: versi paket dinaikkan manual, changelog tertinggal, paket yang saling bergantung tidak ikut naik versi, dan tim akhirnya menumpuk banyak perubahan menjadi satu rilis besar. Changesets membantu memecah proses itu menjadi alur yang lebih kecil, lebih aman, dan lebih mudah diaudit.

Mengapa monorepo sering bermasalah saat rilis

Pada monorepo, satu repository bisa berisi banyak package yang dirilis secara terpisah tetapi saling bergantung. Tanpa mekanisme rilis yang disiplin, beberapa masalah berikut hampir pasti muncul:

  • Version bump manual mudah lupa atau tidak konsisten antar package.
  • Changelog ditulis belakangan, sehingga detail perubahan sering hilang.
  • Dependency internal tidak ikut diperbarui saat package lain berubah.
  • Rilis terlalu besar karena tim menunda publish sampai banyak perubahan menumpuk.
  • Publish tidak aman karena workflow dapat dipicu dari branch yang salah atau dari commit yang belum lolos validasi.

Changesets tidak menyelesaikan semua masalah DevOps, tetapi ia memberi satu sumber kebenaran untuk keputusan rilis: package mana yang berubah, seberapa besar perubahan versinya, dan catatan perubahan apa yang harus muncul di changelog.

Konsep dasar Changesets dalam monorepo

Apa itu changeset

Changeset adalah file markdown kecil yang disimpan di repository, biasanya di direktori .changeset/. File ini dibuat ketika sebuah PR mengubah package yang perlu dirilis. Isinya mendeskripsikan:

  • package yang terdampak,
  • jenis bump versi, seperti patch, minor, atau major,
  • ringkasan perubahan untuk changelog.

Dengan pendekatan ini, keputusan rilis dibuat saat perubahan dikembangkan, bukan di akhir ketika tim sudah lupa konteksnya.

Mengapa release PR penting

Alih-alih langsung publish setiap merge, Changesets umumnya dipakai untuk membuat release PR. PR ini berisi hasil agregasi semua changeset yang sudah masuk ke branch utama:

  • file package.json yang versinya dinaikkan,
  • pembaruan dependency internal antar package,
  • changelog yang dihasilkan otomatis.

Model ini aman karena publish tidak terjadi diam-diam. Tim masih bisa meninjau hasil akhir rilis sebelum benar-benar dipublikasikan.

Versioning paket terkait

Di monorepo, satu package sering bergantung pada package lain di repository yang sama. Jika @acme/utils berubah dan @acme/ui bergantung padanya, maka versi dependency internal perlu ikut disesuaikan. Inilah salah satu nilai utama Changesets: proses versioning paket terkait dilakukan secara konsisten saat release PR dibuat.

Aturan persisnya bergantung pada konfigurasi, tetapi prinsipnya tetap: perubahan pada package inti tidak boleh meninggalkan package downstream dalam keadaan memakai range versi internal yang sudah tidak sesuai.

Struktur repository yang masuk akal

Berikut contoh struktur monorepo yang sederhana tetapi realistis:

repo/
├─ .changeset/
│  ├─ config.json
│  └─ calm-buses-smile.md
├─ .github/
│  └─ workflows/
│     ├─ ci.yml
│     └─ release.yml
├─ packages/
│  ├─ utils/
│  │  ├─ package.json
│  │  └─ src/
│  ├─ ui/
│  │  ├─ package.json
│  │  └─ src/
│  └─ config/
│     ├─ package.json
│     └─ src/
├─ package.json
├─ pnpm-workspace.yaml
└─ README.md

Tidak wajib memakai packages/, tetapi pola ini memudahkan tooling workspace dan review perubahan. Yang penting, package internal memiliki metadata yang jelas dan dapat diidentifikasi oleh Changesets.

Konfigurasi Changesets yang relevan

Setelah inisialisasi, biasanya akan ada file .changeset/config.json. Contoh konfigurasi minimal yang umum dipakai:

{
  "$schema": "https://unpkg.com/@changesets/config/schema.json",
  "changelog": "@changesets/cli/changelog",
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "restricted",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": []
}

Beberapa poin penting:

  • baseBranch menentukan branch acuan saat validasi dan release PR. Jika branch utama Anda bukan main, nilai ini harus disesuaikan.
  • updateInternalDependencies membantu memastikan dependency internal ikut diperbarui saat package terkait dirilis.
  • access perlu disesuaikan jika Anda menerbitkan package publik atau private registry.

Hindari mengubah konfigurasi tanpa memahami dampaknya pada package internal. Kesalahan kecil di sini bisa membuat release PR tampak benar tetapi menghasilkan dependency graph yang tidak rapi.

Contoh file changeset

Contoh changeset untuk perubahan kecil pada package utilitas:

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

Menambahkan helper untuk normalisasi header HTTP dan memperbaiki perilaku default pada parser query.

Jika satu PR mengubah lebih dari satu package, satu changeset bisa mencantumkan beberapa package sekaligus. Ini lebih baik daripada membuat version bump manual di beberapa file package.json.

Desain pipeline CI bertahap

Pola yang aman biasanya dibagi menjadi empat tahap: validasi changeset saat PR, test/lint selektif, pembuatan release PR, dan publish setelah merge. Pemisahan ini penting karena kebutuhan validasi PR berbeda dengan kebutuhan publish.

1. Validasi changeset saat pull request

Tujuan tahap pertama adalah memastikan setiap PR yang mengubah package yang dapat dirilis juga menyertakan changeset. Ini memindahkan masalah dari akhir sprint ke awal review.

Aturan praktis yang umum dipakai:

  • Jika PR mengubah code package, harus ada file changeset.
  • Jika PR hanya mengubah dokumentasi, test, atau file CI, changeset boleh tidak ada.
  • Jika PR memang tidak perlu dirilis, tim harus punya pengecualian yang eksplisit.

Contoh workflow GitHub Actions untuk validasi dasar:

name: CI

on:
  pull_request:
    branches:
      - main

jobs:
  validate-changesets:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - name: Validate changesets
        run: pnpm changeset status --since=origin/main

Perintah exact yang digunakan tim bisa berbeda, tetapi idenya sama: CI harus gagal lebih awal bila ada perubahan release-worthy tanpa metadata rilis yang jelas.

2. Lint dan test selektif

Setelah validasi changeset, tahap berikutnya adalah menjalankan lint dan test hanya pada area yang relevan. Tujuannya bukan mengejar optimasi berlebihan, melainkan menghindari pipeline lambat yang membuat tim tergoda melewati proses rilis yang benar.

Contoh yang aman dan sederhana adalah membatasi scope berdasarkan workspace atau daftar file yang berubah. Anda tidak perlu membuat orkestrasi kompleks agar pola release dengan Changesets tetap efektif.

jobs:
  test-packages:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - name: Lint workspace
        run: pnpm -r lint
      - name: Test workspace
        run: pnpm -r test

Jika repository Anda cukup besar, Anda bisa menambah langkah untuk menghitung package yang terdampak dan hanya menjalankan test pada subset tersebut. Namun, jangan mengorbankan kejelasan pipeline jika tim belum siap memelihara logika deteksi perubahan yang kompleks.

3. Membuat release PR secara otomatis

Setelah changeset dari berbagai PR masuk ke branch utama, workflow terpisah dapat menggabungkan semua changeset menjadi release PR. Inilah titik ketika version bump aktual dan changelog digenerate.

Contoh workflow release:

name: Release PR

on:
  push:
    branches:
      - main

jobs:
  release-pr:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - name: Create Release PR
        uses: changesets/action@v1
        with:
          version: pnpm changeset version
          publish: pnpm changeset publish
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Dalam pola ini, action akan membuat atau memperbarui release PR bila ada changeset yang belum dirilis. Ketika release PR tersebut di-merge, workflow yang sama dapat melanjutkan ke proses publish, tergantung kondisinya.

Meski contoh di atas terlihat ringkas, secara operasional ada dua fase berbeda:

  1. version phase: menghasilkan perubahan versi dan changelog ke release PR,
  2. publish phase: menerbitkan package hanya setelah commit rilis masuk ke branch utama.

4. Publish hanya setelah merge ke branch utama

Guard terpenting adalah memastikan publish tidak pernah berjalan dari branch fitur, fork, atau PR yang belum di-merge. Ada beberapa lapisan pengaman yang sebaiknya dipasang sekaligus:

  • workflow publish hanya aktif pada push ke main, bukan pull_request,
  • cek eksplisit github.ref == 'refs/heads/main',
  • gunakan token registry hanya di job publish, bukan di seluruh pipeline CI,
  • lindungi branch utama dengan review wajib dan status check wajib.

Contoh job publish dengan guard tambahan:

jobs:
  publish:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          registry-url: https://registry.npmjs.org
      - run: corepack enable
      - run: pnpm install --frozen-lockfile
      - name: Publish packages
        run: pnpm changeset publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Pemisahan token seperti ini penting. Banyak insiden publish tidak sengaja terjadi bukan karena tool salah, tetapi karena credential tersedia pada konteks workflow yang terlalu luas.

Aturan branch yang disarankan

Agar alur rilis ini benar-benar aman, aturan branch harus memperkuat workflow CI, bukan malah membuka jalan pintas.

Aturan minimum yang layak

  • main adalah satu-satunya branch yang boleh memicu publish.
  • Semua perubahan masuk ke main lewat pull request.
  • Status check untuk validasi changeset, lint, dan test harus wajib lulus.
  • Release PR juga harus melewati review, terutama bila monorepo berisi package kritis.
  • Direct push ke main sebaiknya diblokir kecuali untuk akun otomasi yang memang diperlukan.

Kapan perlu branch rilis terpisah

Jika tim Anda punya kebutuhan cut release, hardening, atau dukungan patch untuk versi lama, branch rilis terpisah bisa masuk akal. Namun untuk banyak tim, menambahkan branch rilis terlalu awal justru memperumit. Untuk kasus umum, satu branch utama dan release PR otomatis sudah cukup.

Failure mode yang umum dan cara menanganinya

PR mengubah package tetapi tidak membawa changeset

Ini adalah kasus paling umum. Solusinya bukan mengandalkan reviewer untuk mengingatkan, tetapi menjadikannya aturan CI. Tambahkan pengecualian yang jelas hanya untuk perubahan non-release, misalnya dokumentasi atau refactor internal yang memang tidak mengubah perilaku publik.

Release PR gagal karena konflik

Ini sering terjadi jika banyak PR mengubah file changelog atau package yang sama dalam waktu berdekatan. Karena release PR adalah hasil generasi otomatis, pendekatan terbaik biasanya:

  • merge dulu PR yang lebih kecil,
  • biarkan workflow release membuat ulang commit versi,
  • hindari edit manual berlebihan pada file hasil generate.

Jika tim sering mengalami konflik release PR, biasanya masalahnya bukan di Changesets, melainkan terlalu banyak perubahan menumpuk sebelum dirilis.

Versi package internal tidak sesuai harapan

Periksa konfigurasi update dependency internal dan hubungan antar package. Kesalahan umum adalah menganggap perubahan internal tidak perlu memengaruhi package downstream, padahal package downstream mengunci atau mereferensikan versi tertentu.

Debugging yang berguna:

  • lihat hasil diff di release PR, bukan hanya commit fitur,
  • cek graph dependency internal secara eksplisit,
  • pastikan nama package di changeset persis sama dengan yang ada di package.json.

Publish gagal di registry setelah release PR merge

Penyebab paling umum:

  • token registry salah atau tidak punya izin publish,
  • versi sudah terlanjur terbit,
  • metadata package tidak valid,
  • registry sedang bermasalah.

Dalam kasus ini, jangan langsung mengubah versi manual di branch utama. Periksa dulu apakah release commit sudah benar. Jika kegagalan murni karena infrastruktur, biasanya lebih aman memperbaiki credential lalu menjalankan ulang job publish daripada membuat bump baru yang tidak perlu.

Strategi rollback yang realistis

Rollback pada package registry tidak sesederhana rollback deployment aplikasi. Setelah versi terbit, Anda sering kali tidak bisa menghapusnya dengan bebas, atau penghapusan justru menciptakan masalah baru bagi konsumen.

Pendekatan yang lebih aman:

  • anggap release immutable setelah publish,
  • jika ada bug, buat changeset baru dan rilis patch perbaikan secepat mungkin,
  • jika package bermasalah kritis, gunakan deprecate pada registry bila tersedia dan sesuai kebutuhan,
  • untuk aplikasi internal, pertimbangkan menahan konsumsi versi baru sampai verifikasi tambahan selesai.

Karena itu, release PR sangat berguna: ia memberi satu titik kontrol terakhir sebelum perubahan menyentuh registry.

Trade-off pendekatan ini

Kelebihan

  • Lebih aman karena publish dipisahkan dari pengembangan harian.
  • Lebih audit-friendly karena setiap PR membawa niat rilis yang eksplisit.
  • Changelog lebih konsisten karena ditulis saat perubahan masih segar.
  • Paket terkait lebih sinkron berkat proses versioning terpusat.
  • Rilis lebih kecil karena tim tidak perlu menunggu batch besar untuk menyiapkan changelog manual.

Kekurangan

  • Developer perlu disiplin menambahkan changeset untuk setiap perubahan yang relevan.
  • Release PR menambah satu langkah review sebelum publish.
  • Pada monorepo yang sangat aktif, konflik atau churn pada release PR bisa meningkat.
  • Jika banyak package sebenarnya tidak pernah dipublikasikan, overhead Changesets bisa terasa tidak perlu.

Kapan cocok dan kapan tidak

Cocok untuk

  • monorepo dengan beberapa package yang dirilis ke registry,
  • tim yang ingin changelog dan versi lebih konsisten tanpa proses manual,
  • organisasi yang butuh guard publish yang ketat dari branch utama,
  • repo yang sering mengalami ketidaksinkronan dependency internal.

Kurang cocok untuk

  • repo yang hanya berisi satu aplikasi dan tidak punya artefak package terpisah untuk dirilis,
  • tim yang merilis sangat jarang dan dapat mengelola versi secara manual tanpa risiko besar,
  • monorepo yang seluruh isinya bersifat private internal dan tidak memerlukan changelog versi per package.

Checklist implementasi yang bisa langsung dipakai

  1. Inisialisasi Changesets dan commit konfigurasi .changeset/config.json.
  2. Tetapkan main sebagai base branch dan satu-satunya branch publish.
  3. Tambahkan validasi changeset pada workflow pull_request.
  4. Jalankan lint dan test pada PR sebelum perubahan boleh di-merge.
  5. Tambahkan workflow release pada push ke main untuk membuat release PR.
  6. Simpan token registry hanya pada job publish.
  7. Aktifkan branch protection dan wajibkan status check.
  8. Dokumentasikan kapan changeset wajib, kapan boleh diabaikan, dan siapa yang meninjau release PR.

Penutup

Pipeline CI bertahap dengan Changesets cocok untuk tim yang ingin rilis monorepo lebih aman tanpa kembali ke version bump manual dan changelog yang ditulis di akhir. Intinya sederhana: deklarasikan niat rilis di setiap PR, validasi lebih awal, gabungkan menjadi release PR yang bisa ditinjau, lalu publish hanya dari branch utama dengan guard yang ketat.

Jika masalah Anda hari ini adalah versi package tidak sinkron, changelog berantakan, dan publish terasa menegangkan, pendekatan ini biasanya memberi perbaikan yang nyata. Bukan karena semua proses jadi otomatis sepenuhnya, tetapi karena keputusan rilis dipindahkan ke tempat yang tepat: sedekat mungkin dengan perubahan yang dibuat.