Jika tim Anda memakai Laravel dan ingin mengurangi waktu tunggu saat review pull request, kombinasi Laravel + GitHub Actions adalah titik mulai yang sangat efektif. Tujuannya bukan membuat pipeline yang kompleks, tetapi memastikan setiap perubahan otomatis diperiksa dengan cepat: format code lewat Pint, eksekusi test lewat Pest atau PHPUnit, lalu simpan hasil coverage sebagai artefak atau ringkasan job.
Artikel ini fokus pada setup CI yang realistis untuk repo Laravel: Composer install dengan cache, job lint dan test yang dipisah, konfigurasi .env.testing, dan pilihan kapan memakai paralel atau fail-fast. Hasil akhirnya adalah pipeline yang cukup cepat untuk dipakai harian, tetapi tetap mudah dipahami dan dirawat oleh tim kecil.
Mengapa pipeline CI Laravel perlu dipisah antara lint dan test
Kesalahan style dan kegagalan test adalah dua jenis masalah yang berbeda. Jika keduanya dijalankan dalam satu job panjang, developer harus menunggu lebih lama hanya untuk mengetahui bahwa masalahnya sebenarnya sederhana, misalnya format code belum sesuai standar.
Karena itu, pendekatan yang umum dan praktis adalah:
- Job lint untuk menjalankan Laravel Pint.
- Job test untuk menjalankan Pest atau PHPUnit.
- Coverage dikumpulkan di job test, lalu diunggah sebagai artefak atau ditulis ke summary.
Pemisahan ini memberi beberapa keuntungan:
- Feedback lebih cepat untuk masalah style.
- Log lebih mudah dibaca karena tiap job punya tujuan tunggal.
- Retry lebih hemat, karena Anda tidak perlu mengulang seluruh pipeline jika hanya satu tahap yang gagal.
Untuk tim kecil, workflow sederhana yang mudah dipelihara hampir selalu lebih bernilai daripada pipeline yang sangat canggih tetapi sulit dipahami saat gagal.
Struktur minimum repo Laravel untuk CI
Sebelum menulis workflow, pastikan repo Laravel sudah memiliki komponen minimum berikut:
composer.jsondancomposer.lockyang konsisten.- Laravel Pint terpasang, biasanya sebagai dependency development.
- Pest atau PHPUnit sudah bisa dijalankan secara lokal.
- File
.env.testinguntuk konfigurasi environment test. - Jika test memakai database, siapkan konfigurasi yang cocok untuk CI, misalnya SQLite.
Contoh file .env.testing yang ringan untuk CI:
APP_ENV=testing
APP_KEY=base64:SomeGeneratedKeyHere=
APP_DEBUG=true
CACHE_DRIVER=array
QUEUE_CONNECTION=sync
SESSION_DRIVER=array
MAIL_MAILER=array
DB_CONNECTION=sqlite
DB_DATABASE=:memory:Poin penting dari file ini:
- Driver array/sync mengurangi dependensi eksternal saat test.
- SQLite in-memory sering menjadi pilihan paling cepat jika test Anda kompatibel.
- APP_KEY tetap perlu ada jika sebagian test menyentuh enkripsi, session, atau fitur Laravel tertentu.
Namun ada trade-off. Jika aplikasi Anda sangat bergantung pada perilaku database tertentu, SQLite bisa berbeda dengan MySQL atau PostgreSQL. Dalam kasus seperti itu, lebih aman memakai service database di CI meskipun durasinya lebih lama.
Workflow GitHub Actions yang praktis untuk Laravel + GitHub Actions
Berikut contoh workflow yang cukup realistis untuk banyak proyek Laravel. Workflow ini:
- Jalan saat push dan pull request.
- Memisahkan job lint dan test.
- Memakai cache untuk dependency Composer.
- Memakai matrix PHP sederhana pada job test.
- Menghasilkan file coverage dan mengunggahnya sebagai artefak.
- Menulis ringkasan coverage ke job summary bila file tersedia.
name: ci
on:
push:
branches:
- main
- develop
pull_request:
jobs:
lint:
name: Lint (Pint)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
coverage: none
tools: composer:v2
- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: Run Laravel Pint
run: ./vendor/bin/pint --test
test:
name: Test PHP ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3']
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: xdebug
tools: composer:v2
- name: Get Composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --no-progress
- name: Prepare environment
run: |
cp .env.testing .env
php artisan key:generate --env=testing
- name: Run migrations
run: php artisan migrate --env=testing --force
- name: Run tests with coverage
run: php artisan test --coverage-clover=coverage.xml
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-php-${{ matrix.php }}
path: coverage.xml
if-no-files-found: ignore
- name: Add coverage summary
if: always()
run: |
if [ -f coverage.xml ]; then
echo "## Coverage PHP ${{ matrix.php }}" >> $GITHUB_STEP_SUMMARY
echo "File coverage tersimpan sebagai artifact: coverage-php-${{ matrix.php }}" >> $GITHUB_STEP_SUMMARY
else
echo "## Coverage PHP ${{ matrix.php }}" >> $GITHUB_STEP_SUMMARY
echo "File coverage tidak ditemukan." >> $GITHUB_STEP_SUMMARY
fiKenapa workflow di atas cukup efektif
Ada beberapa keputusan penting di workflow tersebut:
- Lint tidak memakai coverage, karena coverage hanya memperlambat job yang sebenarnya tidak membutuhkannya.
- Matrix PHP hanya di job test, sehingga biaya runtime tetap terkendali.
- composer install --prefer-dist biasanya lebih cocok untuk CI karena mengambil distribusi paket yang lebih cepat dibanding source.
- Cache Composer membantu mengurangi waktu unduh dependency antar run.
- fail-fast: false pada matrix test berguna jika Anda ingin melihat hasil semua versi PHP sekaligus.
Jika Anda hanya mendukung satu versi PHP aktif di proyek, matrix bisa dihilangkan agar CI lebih cepat dan biaya eksekusi lebih rendah.
Memahami bagian penting YAML dan alasannya
Trigger: push dan pull_request
Menjalankan CI saat pull_request penting untuk validasi sebelum merge. Menjalankan juga saat push ke branch utama atau branch kerja memberi feedback lebih cepat untuk perubahan langsung.
Setup PHP
Action shivammathur/setup-php umum dipakai untuk workflow PHP karena mendukung setup versi PHP, extension, Composer, dan coverage driver. Dalam contoh di atas, lint tidak membutuhkan Xdebug, sedangkan test coverage membutuhkannya.
Trade-off-nya jelas: coverage hampir selalu menambah durasi test. Karena itu, beberapa tim memilih hanya menjalankan coverage di satu versi PHP saja, bukan di semua matrix.
Cache dependency Composer
Ada dua pendekatan cache yang sering dipakai:
- Cache direktori unduhan Composer, yaitu hasil paket yang diunduh oleh Composer.
- Cache folder vendor, yaitu hasil instalasi dependency.
Untuk banyak proyek, cache direktori Composer lebih aman dan lebih stabil. Alasannya:
- Lebih kecil risiko mismatch karena struktur vendor yang tidak cocok dengan environment runner.
- Tetap membiarkan
composer installmembangun foldervendorsecara bersih. - Lebih mudah di-invalidasi lewat perubahan
composer.lock.
Cache folder vendor bisa lebih cepat, tetapi lebih rentan menghasilkan perilaku aneh jika extension, platform PHP, atau file lock berubah. Untuk tim kecil, saya biasanya menyarankan mulai dari cache unduhan Composer dulu.
.env.testing dan environment test
Laravel mendukung environment testing, tetapi CI tetap perlu eksplisit. Menyalin .env.testing ke .env saat job berjalan membantu memastikan command artisan dan test memakai konfigurasi yang sesuai.
Kesalahan umum di sini adalah:
- Tidak menyiapkan APP_KEY, sehingga beberapa test gagal dengan pesan yang tampak tidak terkait.
- Masih memakai service eksternal seperti Redis, mail server, atau queue async padahal sebenarnya tidak diperlukan untuk unit/integration test.
- DB testing tidak sinkron dengan asumsi migration atau factory.
Fail-fast vs paralel: kapan dipakai
Di GitHub Actions, dua keputusan ini cukup memengaruhi pengalaman tim:
Paralel job
Jika lint dan test dipisah sebagai job terpisah, keduanya bisa berjalan paralel. Ini hampir selalu pilihan yang baik karena mempercepat feedback total tanpa menambah kompleksitas besar.
fail-fast pada matrix
Untuk matrix PHP, ada dua pendekatan:
- fail-fast: true — hentikan kombinasi lain saat salah satu gagal.
- fail-fast: false — biarkan semua kombinasi selesai.
Pilih fail-fast: true jika Anda ingin menghemat menit CI dan biasanya cukup peduli pada sinyal gagal pertama. Pilih fail-fast: false jika Anda ingin visibilitas penuh, misalnya untuk mengetahui bahwa bug hanya muncul di PHP tertentu.
Untuk tim kecil, kompromi yang masuk akal adalah:
- Lint dan test berjalan paralel.
- Matrix test dipakai hanya jika memang perlu.
- Coverage dijalankan di satu versi PHP saja bila durasi mulai terasa berat.
Integrasi minimum di repo Laravel
Supaya workflow di atas bisa langsung dipakai, langkah integrasi minimumnya biasanya seperti berikut:
- Pastikan Pint tersedia di project.
- Pastikan test dapat dijalankan lokal dengan
php artisan testatau./vendor/bin/pest. - Buat file
.env.testingyang aman untuk CI. - Simpan workflow ke
.github/workflows/ci.yml. - Buka pull request kecil untuk memvalidasi log CI pertama.
Contoh command lokal yang sebaiknya lolos sebelum mendorong ke CI:
composer install
./vendor/bin/pint --test
php artisan testJika Anda memakai Pest secara langsung, Anda juga bisa mengganti perintah test di workflow menjadi:
./vendor/bin/pest --coverage-clover=coverage.xmlPilih salah satu gaya yang paling konsisten dengan kebiasaan tim. Jika mayoritas developer terbiasa memakai php artisan test, gunakan itu agar command lokal dan command CI tidak berbeda terlalu jauh.
Bottleneck umum di CI Laravel dan cara menguranginya
1. composer install lambat
Penyebab paling umum adalah cache tidak efektif, dependency terlalu besar, atau lock file sering berubah. Pastikan:
- Cache key memakai hash dari
composer.lock. - Tidak ada langkah yang membuang cache secara tidak perlu.
- Anda memakai
--prefer-distuntuk CI.
2. Coverage membuat test jauh lebih lambat
Ini normal. Instrumentasi coverage menambah overhead. Solusi pragmatis:
- Jalankan coverage hanya pada satu versi PHP.
- Pisahkan job test biasa dan job coverage jika pipeline mulai berat.
- Jangan aktifkan coverage pada job lint.
3. Test database tidak stabil
Jika migration atau seeding tidak idempotent, CI akan gagal secara acak. Periksa apakah:
- Migration bisa dijalankan dari nol tanpa asumsi state lama.
- Factory tidak bergantung pada data global yang berubah-ubah.
- Setiap test membersihkan state dengan benar.
4. Perbedaan lokal vs CI
Masalah klasiknya adalah test lulus lokal tetapi gagal di GitHub Actions. Langkah debug yang biasanya membantu:
- Cek versi PHP dan extension yang dipakai lokal vs CI.
- Cek apakah
.env.testingbenar-benar dipakai. - Cetak informasi dasar saat debugging, misalnya driver database aktif.
- Jalankan command yang sama persis seperti di workflow.
5. Log sulit dibaca
Jika semua dijalankan dalam satu script panjang, sumber gagal sulit ditemukan. Itu sebabnya memisahkan langkah seperti install, prepare environment, migrate, dan test sangat membantu saat investigasi.
Kapan perlu matrix PHP, kapan tidak
Matrix PHP berguna jika aplikasi Anda memang mendukung lebih dari satu versi PHP secara aktif, atau Anda sedang transisi versi runtime. Namun matrix juga menggandakan waktu eksekusi.
Gunakan satu versi PHP jika:
- Tim kecil dan ingin feedback tercepat.
- Environment production hanya satu versi.
- Anda belum punya kebutuhan kompatibilitas lintas versi.
Gunakan matrix sederhana jika:
- Paket atau aplikasi perlu mendukung beberapa versi PHP.
- Anda ingin mendeteksi incompatibility lebih awal.
- Biaya runtime CI masih bisa diterima.
Pendekatan yang sering efektif adalah menjalankan lint sekali, test di beberapa versi PHP, dan coverage hanya di salah satu versi utama.
Checklist adopsi untuk tim kecil
Jika Anda ingin mulai tanpa membuat pipeline berlebihan, pakai checklist berikut:
- Sudah ada workflow CI dasar di
.github/workflows/ci.yml. - Lint dipisah dari test agar feedback style lebih cepat.
- Composer memakai cache berbasis
composer.lock. - .env.testing tersedia dan cocok untuk CI.
- Test bisa jalan tanpa dependensi eksternal yang tidak perlu.
- Coverage diunggah sebagai artifact atau minimal ditulis di job summary.
- Matrix PHP hanya dipakai jika ada kebutuhan nyata.
- Tim sepakat command lokal = command CI sejauh mungkin.
Penutup
Setup Laravel + GitHub Actions yang baik tidak harus rumit. Untuk sebagian besar proyek, kombinasi cache Composer, job lint dan test terpisah, .env.testing yang ringan, serta coverage sebagai artifact sudah cukup untuk mempercepat feedback tim secara signifikan.
Mulailah dari workflow kecil yang stabil, lalu optimalkan hanya saat ada bottleneck nyata. Dengan pendekatan itu, CI tetap cepat, mudah dipahami, dan tidak menjadi beban baru saat tim sedang fokus mengirim perubahan ke aplikasi Laravel Anda.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!