Workflow di pelajaran GitHub Actions Dasar cuma punya satu job. Begitu catatan-api di-deploy lewat Actions, kamu butuh lima fitur lagi: matrix, cache, needs, environment, dan secrets. Bayangin dapur katering kita makin ramai. Satu resep dicicipi di beberapa kompor sekaligus (matrix), bumbu dasar dibuat sekali lalu disimpan di kulkas (cache), masakan baru boleh dikirim setelah semua cicipan lulus (needs), pengiriman ke pelanggan besar butuh tanda tangan kepala dapur (environment), dan kunci mobil pengantar disimpan di laci terkunci (secrets).
🧪 Tebakan cepat: matrix-nya
node: [20, 22]danos: [ubuntu-latest, macos-latest]. Berapa job test yang jalan, dan berurutan atau bersamaan? Tebak dulu, jawabannya ada di bagian "Matrix: satu job, banyak kombinasi".
Workflow catatan-api versi lengkap
Ini .github/workflows/ci.yml yang sama dengan di Lab: test di beberapa versi Node dan OS, lalu deploy ke web-1.
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
strategy:
matrix:
node: [20, 22]
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node${{ matrix.node }}-${{ hashFiles('package-lock.json') }}
- run: npm ci
- run: npm test
deploy:
needs: test
if: github.event_name == 'push'
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh ${{ github.sha }}
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
${{ ... }} adalah expression: GitHub mengganti isinya dengan nilai sebelum step jalan, misalnya ${{ matrix.node }} jadi 20.
Matrix: satu job, banyak kombinasi
strategy.matrix menjalankan job yang sama untuk setiap kombinasi nilai. Dua daftar berisi 2 dan 2 nilai menghasilkan 2 × 2 = 4 job, dan keempatnya jalan bersamaan (paralel), masing-masing di runner sendiri. Itu jawaban tebakannya.
| # | node | os | nama job di tab Actions |
|---|---|---|---|
| 1 | 20 | ubuntu-latest | test (20, ubuntu-latest) |
| 2 | 20 | macos-latest | test (20, macos-latest) |
| 3 | 22 | ubuntu-latest | test (22, ubuntu-latest) |
| 4 | 22 | macos-latest | test (22, macos-latest) |
Urutannya: kunci pertama (node) di luar, kunci berikutnya di dalam, persis seperti dua loop bersarang. Kumpulan semua pasangan ini disebut cartesian product, dan itulah yang dibuat fungsi di latihannya. Satu kunci dengan satu nilai, misalnya php: ["8.3"], menghasilkan satu job. Tambah satu kunci c: [true] gak menambah job, tapi setiap kombinasi dapat c: true. Di dalam job, nilainya dibaca lewat ${{ matrix.node }} dan ${{ matrix.os }}.
Cache: jangan unduh ulang yang sama
Setiap job mulai dari runner kosong, jadi npm ci mengunduh ratusan paket setiap kali. actions/cache menyimpan folder di antara run:
path: ~/.npm: folder yang disimpan, yaitu cache unduhan npm.key: nama simpanannya.hashFiles('package-lock.json')adalah sidik jari isi lockfile, jadi key-nya ikut berubah begitu dependency berubah.- Di awal job, action mencari key yang persis sama. Ketemu (cache hit): folder dipulihkan. Gak ketemu (cache miss): job jalan biasa, lalu di akhir job folder-nya disimpan dengan key itu.
Tiga run berturut-turut di job test (20, ubuntu-latest):
| Run | Yang terjadi | Log actions/cache | npm ci |
|---|---|---|---|
| 1 | pertama kali | Cache not found for input keys: Linux-node20-... lalu Cache saved with key: ... |
38 detik |
| 2 | push lagi, lockfile sama | Cache restored from key: Linux-node20-... |
6 detik |
| 3 | package-lock.json berubah |
Cache not found for input keys: ... (hash baru) |
38 detik |
Kenapa ~/.npm dan bukan node_modules? npm ci selalu menghapus node_modules sebelum memasang ulang, jadi cache node_modules terbuang percuma. Jalan pintasnya: actions/setup-node dengan cache: npm melakukan semua ini sendiri.
Satu aturan lagi: run di sebuah pull request bisa memakai cache dari main, tapi cache yang dibuat di pull request cuma berlaku untuk PR itu.
needs: deploy menunggu test
Tanpa needs, semua job jalan bersamaan. needs: test membuat deploy menunggu semua job matrix test lulus. Satu saja gagal, deploy dilewati. if: github.event_name == 'push' menambahkan syarat: deploy cuma untuk push ke main, gak pernah untuk pull request. Urutan lint, test, dan E2E dengan needs dibahas lebih dalam di pelajaran Testing di CI/CD Pipeline.
Environment: deploy butuh persetujuan
environment: production menghubungkan job ke environment bernama production. Di Settings, lalu Environments, lalu production, centang Required reviewers dan pilih siapa yang boleh menyetujui. Job deploy lalu berhenti dengan status Waiting dan pesan:
Waiting for review: production needs approval to start deploying changes.
Reviewer menekan "Review deployments", lalu "Approve and deploy", dan job-nya baru jalan. Inilah Continuous Delivery dari pelajaran Apa itu CI/CD, dalam bentuk GitHub Actions.
Secrets cuma untuk trigger tepercaya
Kunci SSH untuk masuk ke web-1 disimpan di Settings, lalu Secrets and variables, lalu Actions, dengan nama DEPLOY_SSH_KEY, lalu dibaca lewat context secrets: ${{ secrets.DEPLOY_SSH_KEY }}. Nilainya disamarkan jadi *** di log. Run yang dipicu push ke main atau PR dari branch di repo yang sama mendapat secret. Run dari PR dari fork (salinan repo milik orang lain) mendapat string kosong, karena kodenya belum kamu review. Cara menjaga secret lebih jauh ada di pelajaran CI/CD Pipeline Security.
⚠️ Jebakan yang sering ketemu
- Matrix yang membengkak. 3 versi Node × 3 OS = 9 job setiap push. Pilih kombinasi yang memang kamu dukung.
- Key cache tanpa
hashFiles. Key yang gak pernah berubah memulihkan cache lama terus walau dependency sudah ganti. - Cache
node_moduleslalunpm ci. Folder-nya dihapusnpm ci, jadi cache-nya gak berguna. Cache~/.npm. - Deploy tanpa
needs. Deploy jalan bersamaan dengan test, dan kode yang test-nya merah ikut terkirim. - Secret ditulis langsung di YAML. File workflow ikut ke git. Simpan di GitHub Secrets dan baca dengan
${{ secrets.NAMA }}.
TL;DR: strategy.matrix menjalankan satu job untuk setiap kombinasi nilai (cartesian product, kunci pertama di luar), semuanya paralel: node: [20, 22] × os: [ubuntu-latest, macos-latest] = 4 job. actions/cache dengan path: ~/.npm dan key berisi hashFiles('package-lock.json') memulihkan unduhan npm (hit) atau menyimpannya di akhir job (miss), dan lockfile yang berubah memberi key baru. needs: test membuat deploy menunggu semua job test lulus, environment: production dengan Required reviewers membuatnya menunggu approval, dan secret dibaca dengan ${{ secrets.NAMA }}, disamarkan ***, dan kosong untuk PR dari fork.