Kode yang berhasil dibuild belum tentu berkualitas dan aman. Di episode ini kita mengotomasi unit test, integration test, dan upload coverage ke Codecov, lalu mengintegrasikan ESLint, ShellCheck, dan SonarQube untuk menjaga kualitas. Kita juga mengaktifkan SAST dengan CodeQL dan Dependabot agar kerentanan pada kode dan dependensi terdeteksi lebih awal.

Di episode 15 kita mengelola self-hosted runner dan memahami kapan membutuhkannya. Sekarang kita masuk fase paling krusial dari CI/CD: membuktikan bahwa kode benar-benar layak di-release. Build yang sukses hanya membuktikan kode bisa dikompilasi; kualitas, keamanan, dan perilaku runtime masih belum teruji. Pipeline produksi yang baik harus menjawab empat pertanyaan: apakah fungsi bekerja (test), seberapa luas diuji (coverage), apakah kode bersih (static analysis), dan apakah ada kerentanan (security scanning).
Di episode ini kita membahas:
Bayangkan pipeline seperti jalur produksi pabrik. Setiap stasiun punya tugas memeriksa produk sebelum menyerahkannya ke stasiun berikutnya. Kalau satu stasiun lolos tanpa memeriksa, produk cacat akan terus mengalir sampai ke tangan pelanggan. Demikian pula pipeline: setiap job adalah stasiun pemeriksa yang menghentikan alur begitu menemukan masalah.
Urutan idealnya: lint → unit test → coverage → static analysis → security scan. Semua dijalankan saat pull request dibuat, sehingga masalah ditemukan sebelum kode masuk ke main.
Prasyaratnya adalah script npm yang menghasilkan laporan coverage dalam format yang bisa dibaca alat eksternal. Untuk project Node.js dengan Vitest, contohnya:
{
"scripts": {
"test": "vitest run",
"test:coverage": "vitest run --coverage"
}
}Command test:coverage menghasilkan file coverage/lcov.info plus laporan HTML. Pisahkan unit test (cepat, terisolasi, tanpa jaringan) dari integration test (memakai database atau HTTP nyata) supaya feedback lebih cepat — developer tidak perlu menunggu integration test yang lambat untuk setiap commit kecil.
Workflow berikut menjalankan test lalu mengunggah laporan coverage ke Codecov, yang menampilkan badge dan bisa diatur untuk gagal kalau coverage turun di bawah threshold:
name: Test & Coverage
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Setup Node.js 20
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install dependensi
run: npm ci
- name: Jalankan unit & integration test
run: npm run test:coverage
- name: Upload coverage ke Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/lcov.info
fail_ci_if_error: trueBeberapa hal penting:
npm ci melakukan install deterministik dari lockfile — lebih andal daripada npm install karena hasilnya selalu identik.fail_ci_if_error: true membuat workflow gagal jika upload coverage error, jadi kalian tidak pernah diam-diam kehilangan laporan.coverallsapp/github-action@v2 dengan file yang sama.Tip
Pasang branch protection di main yang mewajibkan job test lulus. Dengan begitu test dan coverage menjadi gerbang wajib sebelum pull request digabungkan.
Linter menangkap error yang tidak ditemukan compiler: variabel tak terpakai, kondisi yang salah, atau pola berbahaya. Workflow terpisah membuat hasilnya jelas per job:
name: Lint & Static Analysis
on:
pull_request:
permissions:
contents: read
jobs:
lint:
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: ESLint
run: npx eslint . --max-warnings 0
shellcheck:
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Jalankan ShellCheck
uses: ludeeus/action-shellcheck@masterFlag --max-warnings 0 membuat lint gagal walaupun hanya ada warning — standar yang tepat untuk tim yang ingin disiplin. Job shellcheck memakai action community untuk memeriksa semua script *.sh di repository.
Linter memeriksa file demi file; SonarQube menambahkan analisis lintas-modul: duplikasi kode, complexity, dan reliability bugs. SonarCloud adalah versi cloud-nya — gratis untuk project open source dan menyimpan hasil analisis di cloud tanpa perlu server sendiri:
name: SonarCloud Analysis
on:
pull_request:
jobs:
sonar:
runs-on: ubuntu-latest
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Analisis SonarCloud
uses: SonarSource/sonarcloud-scan-action@v5
with:
args: >
-Dsonar.organization=my-org
-Dsonar.projectKey=my-org_my-app
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}Hasil scan diposting sebagai komentar di pull request dan dihitung sebagai check. Kalau quality gate di dashboard SonarCloud tidak lolos, pull request bisa diatur untuk gagal.
Warning
Jangan pernah menuliskan token Sonar langsung di file YAML. Seluruh token diambil lewat context secrets, misal secrets.SONAR_TOKEN, bukan diketik manual ke dalam workflow.
CodeQL menganalisis kode secara statis (SAST) dan mencari pola kerentanan seperti SQL injection, path traversal, atau unsafe deserialization. Action resminya butuh tiga langkah: init, autobuild, dan analyze.
name: CodeQL Analysis
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 3 * * 1'
permissions:
security-events: write
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
timeout-minutes: 360
strategy:
fail-fast: false
matrix:
language: ['javascript-typescript']
steps:
- name: Checkout kode
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v3
- name: Perform analysis
uses: github/codeql-action/analyze@v3Penjelasan penting:
security-events: write dibutuhkan agar hasil scan bisa ditulis ke Security tab.schedule dengan cron 0 3 * * 1 (setiap Senin 03:00 UTC) menjalankan scan penuh berkala; pada pull request hanya delta perubahan yang dianalisis.autobuild menebak cara build project; untuk bahasa yang tidak terdeteksi, ganti dengan langkah build manual.Hasilnya muncul di tab Security → Code scanning sebagai alert, lengkap dengan severity, lokasi, dan rekomendasi perbaikan.
Kode kalian aman, tapi dependensinya belum tentu. Dependabot memantau package manager (npm, pip, Maven, dan lainnya) lalu membuka pull request otomatis ketika ada versi yang mengatasi CVE. Konfigurasinya disimpan di .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
time: '09:00'
timezone: Asia/Jakarta
open-pull-requests-limit: 10
labels:
- dependencies
- security
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']Dependabot membedakan security updates (dipicu CVE, prioritas tinggi, tidak menunggu schedule) dari version updates (jadwal rutin di atas). Keduanya sama-sama menghasilkan pull request, jadi beri label jelas dan wajibkan review sebelum merge.
Di episode ini kita membangun gerbang kualitas dan keamanan:
Pipeline yang menguji segalanya bisa terasa menakutkan ketika terjadi kecelakaan di production. Di episode 17 kita membahas environment protection rules & approval gateways — cara menambahkan persetujuan manual dan kontrol lingkungan agar production tidak bisa dideploy sembarangan. Sampai jumpa!