2026-08-13 · 8 min
Lighthouse CI di GitHub Actions: Enforce Performance Budget Sebelum Merge
Aplikasi bisa lolos semua unit test, lolos type check, dan masih bikin pengguna frustrasi karena loading 8 detik. Saya pasang Lighthouse CI sebagai required status check di GitHub Actions justru untuk menutup celah itu: kode yang menurunkan performa tidak bisa merge, titik.
Kenapa ini problem nyata
Di klien Jakarta yang kami tangani, ada periode tiga sprint di mana setiap release secara konsisten membuat Lighthouse Performance score turun 3-5 poin. Tidak ada yang sadar karena tidak ada yang mengukurnya secara otomatis. Ketika akhirnya ada yang memperhatikan, score sudah di angka 54 — padahal tiga bulan sebelumnya masih 78.
Root cause-nya tersebar: satu PR import library animasi besar tanpa tree-shaking, satu PR tambah font baru tanpa font-display: swap, satu PR tambah third-party script tanpa defer. Masing-masing terlihat sepele saat review, tapi efeknya kumulatif. Kalau ada enforcement otomatis sejak awal, paling tidak ada PR yang di-block dan ada diskusi terjadi.
Alternatif yang sering diusulkan adalah “cukup jalankan Lighthouse secara rutin dan kirim laporan ke Slack.” Masalahnya: laporan Slack jarang dibaca oleh orang yang punya wewenang fix, dan tidak ada konsekuensi langsung untuk yang membuat performa turun. Enforcement di level PR jauh lebih efektif daripada laporan monitoring pasif.
Setup dasar: LHCI di GitHub Actions
Anda butuh dua hal: package @lhci/cli dan file konfigurasi lighthouserc.js di root repo.
Install sebagai dev dependency:
npm install --save-dev @lhci/cli
Buat file konfigurasi di root:
// lighthouserc.js
module.exports = {
ci: {
collect: {
// Jalankan 3x per URL dan ambil median — meredam noise runner
numberOfRuns: 3,
// Start server lokal dulu sebelum audit
startServerCommand: 'npm run preview',
startServerReadyPattern: 'Local:',
startServerReadyTimeout: 30000,
url: [
'http://localhost:4321/',
'http://localhost:4321/dashboard',
'http://localhost:4321/pricing',
],
},
assert: {
preset: 'lighthouse:no-pwa',
assertions: {
// Core Web Vitals — threshold "Good" dari Google
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
'total-blocking-time': ['error', { maxNumericValue: 200 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
// Score keseluruhan — warn dulu, bukan error
'categories:performance': ['warn', { minScore: 0.85 }],
'categories:accessibility': ['error', { minScore: 0.90 }],
// Cegah resource besar masuk tanpa disadari
'resource-summary:script:size': ['error', { maxNumericValue: 350000 }],
'resource-summary:total:size': ['error', { maxNumericValue: 1500000 }],
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
Beberapa poin yang perlu diperhatikan di konfigurasi ini:
numberOfRuns: 3— LHCI otomatis ambil median dari 3 run. Tanpa ini, satu spike CPU di runner bisa memblokir PR yang sebenarnya tidak bermasalah.startServerCommand— audit dilakukan terhadap server lokal, bukan URL production. Ini penting: Anda mengaudit build yang akan di-deploy, bukan state production yang mungkin sudah berbeda.- Pisahkan antara
error(blokir) danwarn(tampil di log tapi tidak gagal). Score keseluruhan saya jadikanwarndulu karena lebih volatile; metric spesifik seperti LCP dan TBT saya jadikanerror.
Workflow GitHub Actions
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Run Lighthouse CI
run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
Kalau LHCI_GITHUB_APP_TOKEN di-set (dari GitHub App khusus LHCI), hasilnya muncul langsung sebagai status check di PR — bukan hanya di log Actions. Ini yang membuat enforcement benar-benar bekerja: reviewer bisa lihat badge “Lighthouse CI — Failed” di UI PR tanpa harus masuk ke tab Actions.
Untuk setup GitHub App-nya: install dari github.com/apps/lighthouse-ci, tambahkan ke repo Anda, dan simpan token yang diberikan sebagai repository secret LHCI_GITHUB_APP_TOKEN.
Jadikan required status check
Setup LHCI selesai, tapi ini belum memblokir merge. Langkah yang sering terlewat: jadikan job sebagai required status check di branch protection.
Masuk ke Settings → Branches → branch protection rules untuk main:
- Centang “Require status checks to pass before merging”
- Cari dan tambahkan “lighthouse” (nama job dari workflow Anda) ke daftar required checks
- Centang “Require branches to be up to date before merging”
Sekarang PR tidak bisa di-merge kalau job Lighthouse gagal. Tanpa langkah ini, LHCI hanya jadi laporan informatif yang bisa diabaikan.
Budget per-route dengan budget.json
Untuk aplikasi yang punya halaman dengan karakteristik berbeda — misalnya dashboard dengan charting library yang memang berat vs halaman landing yang harus ringan — definisikan budget terpisah per path.
// budget.json
[
{
"path": "/",
"resourceSizes": [
{ "resourceType": "script", "budget": 200 },
{ "resourceType": "total", "budget": 800 }
],
"timings": [
{ "metric": "largest-contentful-paint", "budget": 2000 },
{ "metric": "total-blocking-time", "budget": 150 }
]
},
{
"path": "/dashboard",
"resourceSizes": [
{ "resourceType": "script", "budget": 500 },
{ "resourceType": "total", "budget": 2000 }
],
"timings": [
{ "metric": "largest-contentful-paint", "budget": 3000 },
{ "metric": "total-blocking-time", "budget": 300 }
]
}
]
Referensikan dari lighthouserc.js:
assert: {
budgetsFile: './budget.json',
},
Pendekatan ini lebih jujur daripada satu threshold flat untuk semua halaman. Halaman landing yang melewati LCP 2500ms itu masalah serius; halaman dashboard yang butuh 3000ms untuk render chart bisa diterima selama ada justifikasi.
Trade-off yang perlu diketahui
Waktu CI bertambah. Dengan numberOfRuns: 3 dan tiga URL, Anda menambah sekitar 4-7 menit ke pipeline. Untuk tim kecil yang deploy beberapa kali sehari ini terasa. Mitigasinya: jalankan LHCI hanya pada PR ke main, bukan setiap push ke branch feature. Di workflow di atas sudah saya set on: pull_request: branches: [main].
Runner noise masih ada. Bahkan dengan median dari 3 run, sesekali ada fluke dari runner yang sangat sibuk. Kalau tim sering komplain tentang false positive, naikkan numberOfRuns ke 5 atau longgarkan threshold sedikit. Jangan langsung disable enforcement — cari tahu dulu apakah itu memang noise atau memang ada regresi.
Tidak menggantikan RUM (Real User Monitoring). Lighthouse CI mengaudit di kondisi lab terkontrol — Moto G4 emulated, throttling 4G. Angkanya berbeda dari pengalaman pengguna nyata di Jakarta yang pakai HP mid-range dengan jaringan bervariasi. LHCI adalah jaring pengaman untuk cegah regresi yang jelas; untuk memahami performa di lapangan tetap butuh RUM (Cloudflare Web Analytics, Sentry Performance, atau yang sejenisnya).
Build harus stabil. LHCI audit hasil build, bukan dev server. Kalau build sering gagal karena hal lain, job Lighthouse juga gagal — dan timnya mulai mengabaikan failure karena “pasti bukan masalah performa.” Pastikan step build sudah solid sebelum menambahkan LHCI.
Verdik
Lighthouse CI sebagai required status check itu worth it untuk hampir semua proyek web yang peduli dengan Core Web Vitals — dan di 2026, Core Web Vitals sudah jelas pengaruhnya ke ranking Google dan konversi. Biaya setupnya 2-3 jam engineer, biaya pemeliharaannya kecil, dan manfaatnya — mencegah regresi performa masuk production tanpa disadari — nyata.
Yang saya rekomendasikan untuk memulai: deploy dulu dengan threshold yang longgar (ambil nilai aktual sekarang minus 10 poin), lihat apakah ada PR yang segera langsung di-block, dan naikkan threshold secara bertahap. Jangan langsung pasang target ambisius yang langsung memblokir semua orang — itu akan berakhir dengan tim yang mematikan enforcement sambil berjanji “nanti diperbaiki.”
Satu catatan untuk stack Astro khusus: startServerCommand perlu disesuaikan. Astro preview server default berjalan di port 4321; kalau Anda menggunakan adapter yang berbeda atau port custom, sesuaikan juga url di konfigurasi LHCI. Selebihnya setup-nya sama persis.
Ditulis oleh Reza Pradipta