Studi kasus lengkap arsitektur pipeline produksi dari nol: gerbang kualitas saat pull request, auto versioning, build image multi-arch, scan Trivy, deploy staging, E2E otomatis, approval gate manual, dan deploy produksi tanpa downtime. Ditutup dengan checklist kesiapan produksi.

Dari episode 0 sampai 19 kita merakit pipa-pipa CI/CD satu per satu: workflow dasar, variabel, matrix, artifact, secret, OIDC, reusable workflow, Docker, deployment, self-hosted runner, kualitas kode, security scan, environment protection, release management, dan debugging. Di episode terakhir ini kita menyatukan semuanya menjadi satu arsitektur produksi yang utuh — mulai developer menekan tombol merge sampai pelanggan menggunakan fitur baru, semuanya otomatis dengan pengamanan berlapis.
Di episode ini kita membahas:
Bayangkan alur seperti jalur boarding pesawat: setiap gerbang memeriksa dokumen sebelum penumpang naik. Kalau ada satu gerbang yang dilewati, risiko menyebar ke seluruh penumpang. Pipeline kita punya lima gerbang utama:
Dua workflow terpisah menjaga pemisahan tanggung jawab: ci.yml untuk pull request (feedback cepat), cd.yml untuk main (rilis penuh).
Job lint, unit test, CodeQL, dan audit berjalan paralel. Semua wajib lulus sebelum branch protection mengizinkan merge — kita tidak pernah membiarkan kode rusak masuk ke main:
name: CI - Pull Request
on:
pull_request:
permissions:
contents: read
security-events: write
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx eslint . --max-warnings 0
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test
codeql:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ['javascript-typescript']
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
dependency-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm audit --audit-level=highEmpat job ini adalah tembok pertama. lint menjaga gaya dan bug kecil, unit-test membuktikan logika bekerja, codeql mencari kerentanan statis, dan dependency-audit memastikan tidak ada CVE level high pada dependensi. Audit dependensi otomatis tambahan dari Dependabot (episode 16) menutup celah di luar jam kerja.
Inilah workflow paling penting. Perhatikan rantai needs — setiap job menunggu kualitas job sebelumnya. Nilai image dipakai dari job version lewat output, sehingga seluruh pipeline memakai tag yang sama:
name: Production Pipeline
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
contents: write
packages: write
security-events: write
jobs:
version:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout kode
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Auto semantic versioning
id: release
uses: google-github-actions/release-please-action@v4
with:
release-type: simple
token: ${{ secrets.GITHUB_TOKEN }}
- name: Tentukan tag rilis
id: version
env:
RELEASE_TAG: ${{ steps.release.outputs.tag_name }}
run: |
tag="$RELEASE_TAG"
if [ -z "$tag" ]; then
tag=$(git tag --sort=-v:refname | head -1)
fi
echo "tag=${tag:-v0.0.0}" >> "$GITHUB_OUTPUT"
build-push:
needs: version
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push multi-arch image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
scan:
needs: build-push
runs-on: ubuntu-latest
steps:
- name: Scan image dengan Trivy
uses: aquasecurity/trivy-action@0.24.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
- name: Upload hasil scan ke Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
deploy-staging:
needs: [build-push, scan]
runs-on: ubuntu-latest
environment: staging
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Deploy staging (rolling update)
uses: azure/k8s-deploy@v5
with:
namespace: staging
manifests: k8s/staging.yaml
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}
e2e:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependensi
run: npm ci
- name: Jalankan E2E Playwright
run: npx playwright test
env:
BASE_URL: https://staging.example.com
deploy-production:
needs: e2e
runs-on: ubuntu-latest
environment: production
concurrency:
group: production
cancel-in-progress: false
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Deploy zero-downtime ke production
uses: azure/k8s-deploy@v5
with:
namespace: production
manifests: k8s/production.yaml
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.version.outputs.tag }}
strategy: canary
action: deploy
release:
needs: [version, deploy-production]
runs-on: ubuntu-latest
if: needs.version.outputs.tag != ''
steps:
- name: Buat GitHub Release dengan changelog
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.version.outputs.tag }}
generate_release_notes: true
notify:
needs: [version, deploy-production, release]
runs-on: ubuntu-latest
if: always()
steps:
- name: Kirim notifikasi Slack
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
VERSION_TAG: ${{ needs.version.outputs.tag }}
run: |
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"Rilis ${VERSION_TAG} berhasil dideploy\"}"Catatan penting dari workflow di atas:
version menghitung versi dari Conventional Commits lewat release-please. Nilai context steps.release.outputs.tag_name dikirim lewat env, bukan langsung ke run — praktik anti script injection dari episode 9.build-push membangun image dua arsitektur sekaligus dengan cache GitHub Actions (type=gha), lalu scan memeriksa image dengan Trivy dan menulis hasil SARIF ke Security tab.environment, sehingga protection rules staging dan production berlaku otomatis.Tiga mekanisme terakhir membuat pipeline ini "production-grade":
BASE_URL staging, menangkap regresi yang lolos unit test seperti flow login lintas service.deploy-production memakai environment production dengan required reviewers (episode 17). GitHub menghentikan job dan meminta persetujuan Lead Engineer sebelum step apa pun berjalan — momen otomasi menyerahkan kendali ke manusia.concurrency memastikan hanya satu deployment produksi berjalan dalam satu waktu.Warning
Kalau memakai self-hosted runner di repository public, siapa pun bisa menjalankan kode arbitrer di runner kalian. Pipeline produksi seperti ini hanya aman di repository privat, atau dengan runner GitHub-hosted.
Sebelum mengaku pipeline kalian "production-grade", cek semua lapisan berikut:
| Lapisan | Alat di pipeline | Standar minimal |
|---|---|---|
| Kualitas kode | ESLint, unit test | Wajib lulus sebelum merge |
| SAST | CodeQL | Scan setiap pull request |
| Audit dependensi | npm audit, Dependabot | Tidak ada CVE high terbuka |
| Versioning | release-please | SemVer dari Conventional Commits |
| Build image | Buildx multi-arch | amd64 dan arm64 |
| Image security | Trivy | Block jika HIGH/CRITICAL |
| Registry | GHCR | Push otomatis, package permission |
| Deploy staging | Kubernetes rolling | Otomatis setelah build lulus |
| E2E | Playwright | Semua spec lulus |
| Gate produksi | Environment production | Required reviewers aktif |
| Deploy produksi | Zero-downtime/canary | Strategi rollback tersedia |
| Rilis & komunikasi | GitHub Release, Slack | Notifikasi otomatis per rilis |
Kalau semua lapisan sudah ada, pipeline bukan lagi pengumpul tugas manual — ia menjadi sistem yang menjaga kualitas, keamanan, dan kecepatan rilis secara bersamaan.
Di episode pamungkas ini kita merangkai seluruh seri:
Selamat — kalian telah menyelesaikan perjalanan dari nol sampai pipeline produksi penuh. Jangan berhenti di sini: coba terapkan pada proyek nyata, pelajari failure mode yang tidak terduga, dan terus ukur waktu dari commit ke production. Selamat berlatih, dan sampai jumpa di seri berikutnya!