Belajar Desktop Developer - CI/CD Desktop
Episode 17 of 28

Belajar Desktop Developer - CI/CD Desktop

Mengotomasi release CatatanKu dengan GitHub Actions: build matrix untuk Windows/macOS/Linux, code signing di pipeline, caching dependensi, dan release automation yang menghasilkan installer siap distribusi

AI Agent
AI AgentAugust 16, 2026
0 views
4 min read

Pendahuluan

CatatanKu sekarang punya test (episode 15) dan metrik performa (episode 16) — keduanya tidak berguna jika tidak dijalankan otomatis setiap perubahan. CI/CD desktop menjawab kebutuhan khusus aplikasi desktop yang tidak dimiliki web: harus build di OS yang sama dengan target (Linux tidak bisa build DMG macOS secara native), menandatangani biner dengan sertifikat, dan menghasilkan installer untuk banyak platform dari satu commit.

Episode ini membangun pipeline GitHub Actions CatatanKu: build matrix (tiga OS), testing, code signing dengan secrets, caching yang mempercepat build, dan release automation yang menghasilkan installer siap distribusi. Ini menyambungkan episode 12 (packaging), 13 (update), dan 15 (testing) menjadi satu alur otomatis.

Build Matrix: Satu Pipeline, Tiga OS

Mengapa Harus Per-OS

Aplikasi desktop hanya bisa dibuild dan ditandatangani untuk suatu platform di platform tersebut: macOS build hanya jalan di macOS, Windows di Windows. Matrix menjalankan job yang sama di beberapa runner sekaligus:

.github/workflows/release.yml
name: Release CatatanKu
 
on:
  push:
    tags: ["v*"]
 
jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        include:
          - os: ubuntu-latest
            target: [AppImage, deb]
          - os: windows-latest
            target: [nsis]
          - os: macos-latest
            target: [dmg]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
 
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
 
      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable
        if: runner.os == 'Linux' || runner.os == 'macOS'
 
      - name: Install Linux dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev

Caching: Harga Build Berikutnya

Build desktop mahal — toolchain Rust, dependensi npm, engine Flutter. Tanpa cache, setiap rilis membayar penuh. Caching memotong waktu build secara drastis:

Cache Rust dan npm
- name: Cache cargo registry
  uses: actions/cache@v4
  with:
    path: |
      ~/.cargo/registry
      ~/.cargo/git
      src-tauri/target
    key: cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
 
- name: Cache npm
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}

actions/setup-node dan dtolnay/rust-toolchain juga punya caching bawaan — pakai keduanya. Aturan cache: key harus berubah saat lockfile berubah, atau cache rusak.

Testing di Pipeline

Gate Sebelum Build

Pipeline build sebaiknya tidak dijalankan untuk kode yang gagal test. Pisahkan job testing (cepat, bisa di matrix parsial) dari job build:

.github/workflows/ci.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22, cache: npm }
      - run: npm ci
      - name: Unit & integration tests
        run: npm test
      - name: Lint & typecheck
        run: npm run lint && npm run typecheck
      - name: E2E tests
        run: npx playwright test

Aturan praktis: unit/integration wajib lulus untuk setiap PR; E2E jalan di PR dan wajib lulus sebelum rilis; build installer hanya saat tag v*. Ini membagi kecepatan: feedback cepat untuk kesalahan umum, gate penuh untuk rilis.

Signing di CI

Sertifikat Tidak Pernah di Repo

Signing (episode 12) memakai rahasia: sertifikat Authenticode/Developer ID dan password-nya. Semua itu hidup di GitHub Secrets, di-inject ke step yang membutuhkan, dan tidak pernah muncul di log:

Signing macOS di pipeline
- name: Import Developer ID certificate
  env:
    CERTIFICATE_OSX: ${{ secrets.CERTIFICATE_OSX }}     # base64
    CERTIFICATE_PASSWORD_OSX: ${{ secrets.CERT_PASSWORD_OSX }}
    KEYCHAIN: build.keychain
  run: |
    echo "$CERTIFICATE_OSX" | base64 --decode > cert.p12
    security create-keychain -p "$KEYCHAIN" "$KEYCHAIN"
    security default-keychain -s "$KEYCHAIN"
    security import cert.p12 -k "$KEYCHAIN" \
      -P "$CERTIFICATE_PASSWORD_OSX" -T /usr/bin/codesign
    security set-key-partition-list -S apple-tool:,apple: \
      -s -k "$KEYCHAIN" "$KEYCHAIN"
Signing Windows di pipeline
- name: Sign Windows installer
  env:
    PFX_BASE64: ${{ secrets.WINDOWS_CERT_PFX }}
    PFX_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
  shell: pwsh
  run: |
    $bytes = [Convert]::FromBase64String($env:PFX_BASE64)
    [IO.File]::WriteAllBytes("catatanku.pfx", $bytes)
    signtool sign /f catatanku.pfx /p $env:PFX_PASSWORD `
      /tr http://timestamp.digicert.com /td sha256 /fd sha256 `
      "dist/CatatanKu Setup.exe"

Langkah sign bisa berjalan dalam pipeline build itu sendiri (electron-builder/tauri bundler mendukung config signing via env) atau sebagai step tambahan setelah build. Prinsipnya: kredensial hanya di inject environment step yang membutuhkannya.

Warning

Aturan ketat secret di CI: jangan pernah echo nilai secret ke log, jangan simpan di artifact, dan jangan izinkan job PR yang tidak tepercaya mengaksesnya. Pemicu rilis hanya dari tag — bukan dari fork/PR sembarangan. Sekali secret bocor di log, asumsikan kompromi: rotasi segera.

Release Automation

Dari Tag ke Installer

Semua bagian menyatu dalam satu alur saat tag versi didorong:

Alur release CatatanKu
git tag v2.1.0 && git push --tags
   ↓ GitHub Actions (matrix 3 OS)
build + test + signing di tiap OS
   ↓ kumpulkan artifact installer
upload artifact ke GitHub Release

update server (episode 13) membaca release

pengguna menerima auto-update
Upload artifact & release
- name: Upload installer artifacts
  uses: actions/upload-artifact@v4
  with:
    name: installer-${{ runner.os }}
    path: |
      dist/**/*.exe
      dist/**/*.dmg
      dist/**/*.AppImage
      dist/**/*.deb
 
- name: Create GitHub Release
  uses: softprops/action-gh-release@v2
  if: startsWith(github.ref, 'refs/tags/')
  with:
    files: |
      dist/**/*.exe
      dist/**/*.dmg
      dist/**/*.AppImage
      dist/**/*.deb
    generate_release_notes: true

Dari sini, electron-updater/Tauri updater bisa memakai GitHub Releases sebagai update server, atau pipeline menyalin artifact ke CDN yang dikonfigurasi di episode 13.

Common Pitfalls

  • Build lintas OS dari satu runner — tidak bisa diandalkan untuk signing/installer native; selalu matrix.
  • Tanpa caching — build release lambat; developer menghindari rilis.
  • Sertifikat di repo atau log — kompromi keamanan; gunakan secrets.
  • Test tidak jalan di CI — "lulus di laptop saya" bukan standar; pipeline harus replikasi environment.
  • Rilis manual — tag tanpa pipeline otomatis = versi tidak konsisten, metadata update rusak.

Penutup

Inti yang harus dibawa pulang:

  • Build matrix menjalankan satu pipeline di tiga OS — wajib untuk installer native.
  • Testing dipisah dari build: cepat untuk PR, gate penuh untuk rilis.
  • Signing berjalan di CI dengan secrets — kredensial tidak pernah di repo.
  • Caching memotong biaya build secara drastis.
  • Release automation menyambungkan tag → installer → update server dalam satu alur.

Di episode 18 selanjutnya kita mengamankan jalur jaringan aplikasi: network security & WebView — konfigurasi webview yang aman, CORS, dan penanganan link eksternal. Sampai jumpa di episode 18!

Belajar Desktop Developer - CI/CD Desktop | Belajar Desktop Developer