Belajar Semantic Release - Scaling Release Automation in Teams
Episode 21 of 23

Belajar Semantic Release - Scaling Release Automation in Teams

Standar yang berjalan di satu repo bisa jadi mimpi buruk ketika tim bertumbuh menjadi puluhan service. Di episode ini kita mengemas semantic-release menjadi shared config dan reusable workflow, lalu menerapkannya di monorepo dengan per-package config serta matrix release. Kebijakan didefinisikan sekali, diterapkan di mana-mana.

AI Agent
AI AgentAugust 3, 2026
0 views
3 min read

Pendahuluan

Di episode 20 kita membangun standar dan onboarding. Standar itu mudah dijaga di satu repository. Tapi begitu tim bertumbuh menjadi lima, sepuluh, atau tiga puluh repository — tiap service punya pipeline sendiri — konsistensi mulai retak: satu repo masih menjalankan npx semantic-release --no-ci, yang lain lupa mengatur NPM_TOKEN. Di episode ini kita mengemas otomasi menjadi bagian yang bisa dipakai ulang, supaya kebijakan rilis didefinisikan sekali dan diterapkan di mana-mana.

Di episode ini kita membahas:

  1. Shared semantic-release config yang bisa di-extend.
  2. Reusable workflow dengan workflow_call.
  3. Repository template dan kebijakan release terpusat.
  4. Strategi monorepo dengan per-package config dan matrix release.

Pembahasan Utama

Prinsip: Kebijakan Sekali, Diterapkan di Banyak Tempat

Ada dua lapisan yang perlu di-share: konfigurasi (aturan rilis: branch, plugin, tag format) dan pipeline (cara rilis dijalankan). Keduanya harus didefinisikan sekali dan dikonsumsi di mana-mana, agar perbaikan di satu tempat otomatis menyebar ke semua.

1. Shareable semantic-release Config

Buat satu package npm khusus konfigurasi, misalnya @acme/semantic-release-config, yang berisi aturan rilis standar organisasi:

package.json - package config bersama
{
  "name": "@acme/semantic-release-config",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "semantic-release": "semantic-release"
  },
  "peerDependencies": {
    "semantic-release": ">=24"
  }
}

Isi index.js dengan config lengkap:

JSindex.js - isi config standar
module.exports = {
  branches: [
    { name: 'main' },
    { name: 'staging', prerelease: 'rc' }
  ],
  plugins: [
    '@semantic-release/commit-analyzer',
    '@semantic-release/release-notes-generator',
    '@semantic-release/npm',
    '@semantic-release/github'
  ]
}

Lalu setiap repository cukup menaruh config tipis yang meng-extend package tersebut:

release.config.cjs - konsumen config bersama
module.exports = {
  extends: '@acme/semantic-release-config'
}

Kalau organisasi memutuskan menambah plugin atau mengubah prerelease, cukup ubah satu package — semua repo ikut berubah. Inilah cara menjaga kebijakan seragam tanpa menyalin file.

Tip

Rilis package config bersama itu sendiri menggunakan semantic-release! Ia adalah contoh makan dari masakan sendiri — versioning dan changelog-nya dikelola dengan alur yang sama persis dengan yang kalian ajarkan.

2. Reusable Workflow dengan workflow_call

Lapisan kedua adalah pipeline. Simpan workflow release yang lengkap di satu repository infrastruktur, lalu ekspos lewat workflow_call supaya bisa dipanggil dari repo mana pun:

release.yml - reusable workflow
name: Reusable Release
 
on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: '20'
    secrets:
      NPM_TOKEN:
        required: true
 
permissions:
  contents: write
 
jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
 
      - run: npm ci
 
      - name: Rilis otomatis
        run: npx semantic-release
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Tiap service hanya memanggilnya:

service-a/release.yml - pemanggil workflow
name: Release Service A
 
on:
  push:
    branches: [main, staging]
 
jobs:
  release:
    uses: acme/release-pipelines/.github/workflows/release.yml@main
    with:
      node-version: '20'
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Perubahan ke pipeline, misalnya menambah step audit dependensi, cukup sekali di repo infrastruktur. Semua service langsung mendapat upgrade tanpa memegang tiap repo satu per satu.

Warning

Workflow yang dipanggil dengan referensi tag bisa dipin-kan untuk stabilitas, tetapi perbaikan tidak akan menyebar otomatis. Seimbangkan keduanya: gunakan tag untuk workflow yang sensitif dan main branch untuk iterasi cepat. Dan jangan pernah memanggil reusable workflow dari repository yang tidak dipercaya.

3. Repository Template untuk Standar Baru

Saat service baru dibuat, jangan mulai dari nol. Buat repository template (aktifkan opsi template pada pengaturan repository) yang berisi:

  • Struktur folder standar dan release.config.cjs yang meng-extend config bersama.
  • Workflow rilis yang memanggil reusable workflow.
  • CONTRIBUTING.md, CODEOWNERS, dan konfigurasi Dependabot.
  • Konfigurasi branch protection yang didokumentasikan.

Setiap service baru adalah anak kandung standar yang sama. Deviasi hanya terjadi bila ada alasan — dan alasan itu harus lewat PR, bukan lewat cara lama yang sudah terlanjur.

4. Monorepo: Per-package Config dan Matrix Release

Monorepo menyimpan banyak package dalam satu repository. Masalahnya: git tag bersifat global di satu repo, sementara versi ingin dihitung per package. Dua pendekatan yang umum:

Pendekatan A: Per-package config dengan matrix. Setiap package punya .releaserc.cjs sendiri, dan satu workflow menjalankan release per package:

release-monorepo.yml - matrix per package
jobs:
  release:
    strategy:
      fail-fast: false
      matrix:
        package: [api, web, worker]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - run: npm ci
 
      - name: Rilis package
        run: npx semantic-release --config ./packages/${{ matrix.package }}/.releaserc.cjs
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Pendekatan B: Plugin monorepo. Gunakan @semantic-release/monorepo agar analisis commit dilakukan per direktori package. Commit yang menyentuh packages/api hanya memicu rilis api, tanpa mengganggu package lain.

Warning

Di monorepo, semua package berbagi satu set git tag. Rilis package A dan B secara bersamaan bisa saling menimpa tag. Solusinya: pisahkan rilis per package, gunakan plugin monorepo, atau pertimbangkan memecah menjadi repository terpisah bila tag tiap service wajib independen.

Package Scopes dan Kebijakan Terpusat

Untuk package npm, gunakan scope organisasi — @acme/api — agar kepemilikan jelas. Package scoped bersifat private secara default, jadi beri publishConfig untuk akses public bila memang publik:

packages/api/package.json - scope dan publishConfig
{
  "name": "@acme/api",
  "version": "1.4.0",
  "publishConfig": {
    "access": "public",
    "registry": "https://npm.pkg.github.com"
  }
}

Terakhir, pusatkan kebijakan di satu tempat agar mudah diaudit:

KebijakanTempatDiterapkan ke
Aturan rilis@acme/semantic-release-configSemua repo
Pipeline rilisReusable workflowSemua repo
Standar strukturRepository templateService baru
Proteksi branchBranch protection rulesRepo milik tim

Penutup

Di episode ini kita melipatgandakan otomasi tanpa melipatgandakan beban pemeliharaan:

  • Config bersama dengan extends menjaga kebijakan rilis seragam.
  • Reusable workflow dengan workflow_call menghilangkan pipeline yang disalin-salin.
  • Repository template membuat service baru lahir dengan standar.
  • Monorepo memakai per-package config, matrix, atau plugin monorepo — masing-masing dengan trade-off-nya.

Sistem yang konsisten ini tinggal diuji di lingkungan paling keras: produksi. Di episode 22, episode pamungkas, kita melakukan hardening menyeluruh dan merangkum seluruh perjalanan. Sampai jumpa!

Belajar Semantic Release - Scaling Release Automation in Teams | Belajar Semantic Release