Cara Setup CI/CD Laravel dengan GitHub Actions
Cara setup CI/CD Laravel dengan GitHub Actions adalah skill penting yang wajib dikuasai developer modern. Dengan pipeline otomatis, setiap push ke repository langsung menjalankan test, linting, dan deployment tanpa intervensi manual. Artikel ini membahas langkah praktis dari nol sampai pipeline production-ready.
Kenapa CI/CD Penting untuk Project Laravel?
Banyak developer Laravel masih deploy manual lewat SSH atau FTP. Cara ini rawan human error, apalagi kalau tim sudah lebih dari satu orang. CI/CD menyelesaikan masalah ini dengan otomatisasi.
Keuntungan utama CI/CD:
- Deteksi bug lebih awal - test jalan otomatis setiap push
- Konsistensi deployment - tidak ada lagi “di laptop saya jalan”
- Percepat feedback loop - developer tahu dalam hitungan menit apakah kodenya aman
- Audit trail - setiap deployment tercatat di GitHub
Persiapan Sebelum Mulai
Sebelum setup pipeline, pastikan project Laravel kamu sudah punya:
- Unit test atau feature test yang bisa dijalankan via
php artisan test - Repository GitHub yang sudah terhubung
- Server deployment (VPS, shared hosting, atau platform seperti Forge/Vapor)
Kalau belum punya test sama sekali, minimal buat satu test sederhana dulu:
// tests/Feature/HealthCheckTest.php
<?php
namespace Tests\Feature;
use Tests\TestCase;
class HealthCheckTest extends TestCase
{
public function test_homepage_returns_200(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
Test sederhana ini sudah cukup untuk memvalidasi bahwa pipeline CI/CD kamu berjalan dengan benar.
Struktur Workflow File
GitHub Actions menggunakan file YAML di folder .github/workflows/. Buat file baru:
# .github/workflows/ci.yml
name: Laravel CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: testing
POSTGRES_PASSWORD: secret
POSTGRES_DB: testing
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
extensions: pdo_pgsql, mbstring, xml, bcmath
coverage: xdebug
- name: Cache Composer
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Install Dependencies
run: composer install --no-interaction --prefer-dist
- name: Prepare Environment
run: |
cp .env.example .env
php artisan key:generate
- name: Run Tests
env:
DB_CONNECTION: pgsql
DB_HOST: 127.0.0.1
DB_PORT: 5432
DB_DATABASE: testing
DB_USERNAME: testing
DB_PASSWORD: secret
run: php artisan test --coverage-min=60
Mari kita bedah bagian-bagian pentingnya.
Penjelasan Setiap Bagian Workflow
Trigger Events
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
Pipeline berjalan saat ada push ke branch main atau develop, dan saat ada pull request ke main. Ini memastikan setiap perubahan kode melewati validasi.
Service Container PostgreSQL
Blok services menjalankan PostgreSQL sebagai container Docker di dalam runner GitHub Actions. Health check memastikan database sudah siap sebelum test dimulai. Kalau project kamu pakai MySQL, tinggal ganti image-nya.
Caching Composer Dependencies
- name: Cache Composer
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
Cache ini mempercepat pipeline secara signifikan. Tanpa cache, composer install bisa makan waktu 1-2 menit. Dengan cache, turun jadi beberapa detik.
Coverage Minimum
Flag --coverage-min=60 memaksa minimal 60% code coverage. Kalau di bawah itu, pipeline gagal. Sesuaikan angkanya dengan kondisi project kamu.
Menambahkan Job Deployment
Setelah test lolos, tambahkan job untuk deploy otomatis ke server:
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Deploy to Server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/laravel-app
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart
Setup GitHub Secrets
Masuk ke Settings → Secrets and variables → Actions di repository GitHub kamu, lalu tambahkan:
SERVER_HOST- IP atau domain serverSERVER_USER- username SSH (biasanyadeployatauwww-data)SSH_PRIVATE_KEY- private key SSH (bukan public key)
Jangan pernah hardcode credentials di file workflow. Selalu gunakan GitHub Secrets.
Menambahkan Code Quality Check
Tambahkan step untuk Pint (Laravel code formatter) dan Larastan (static analysis):
- name: Check Code Style
run: vendor/bin/pint --test
- name: Run Static Analysis
run: vendor/bin/phpstan analyse --memory-limit=512M
Letakkan kedua step ini sebelum Run Tests. Kalau code style berantakan atau ada error tipe data, pipeline langsung gagal tanpa perlu menunggu test selesai.
Tips Optimasi Pipeline
Parallel Testing
Laravel mendukung parallel testing untuk mempercepat eksekusi:
- name: Run Tests
run: php artisan test --parallel --coverage-min=60
Pastikan sudah install package brianium/paratest via Composer.
Matrix Strategy untuk Multi-PHP Version
Kalau project harus support beberapa versi PHP:
strategy:
matrix:
php-version: ["8.2", "8.3"]
Lalu referensikan di step Setup PHP:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
Notification Slack/Discord
Tambahkan notifikasi di akhir pipeline agar tim tahu status deployment:
- name: Notify Discord
if: always()
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
status: ${{ job.status }}
Troubleshooting Masalah Umum
Pipeline Gagal di Step Migration
Biasanya karena database service belum ready. Pastikan ada health check di blok services dan tambahkan opsi --health-retries yang cukup.
Cache Tidak Bekerja
Periksa apakah key cache sudah benar. Gunakan hashFiles('composer.lock') bukan hashFiles('composer.json') karena lock file lebih spesifik.
Test Lokal Jalan Tapi di CI Gagal
Penyebab paling sering: environment variable berbeda. Pastikan .env di CI sudah dikonfigurasi dengan benar lewat env block di workflow, bukan dari file .env yang di-commit.
Timeout pada Test yang Lama
Tambahkan timeout di level job:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
Default timeout GitHub Actions adalah 360 menit. Terlalu lama untuk CI - set manual ke 10-15 menit agar pipeline yang hang cepat terdeteksi.
Workflow Lengkap Production-Ready
Berikut file workflow final yang menggabungkan semua elemen di atas:
name: Laravel CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: testing
POSTGRES_PASSWORD: secret
POSTGRES_DB: testing
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
extensions: pdo_pgsql, mbstring, xml, bcmath
- uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- run: composer install --no-interaction --prefer-dist
- run: cp .env.example .env && php artisan key:generate
- run: vendor/bin/pint --test
- name: Run Tests
env:
DB_CONNECTION: pgsql
DB_HOST: 127.0.0.1
DB_PORT: 5432
DB_DATABASE: testing
DB_USERNAME: testing
DB_PASSWORD: secret
run: php artisan test --parallel
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/laravel-app
git pull origin main
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan optimize
php artisan queue:restart
Kesimpulan
Setup CI/CD Laravel dengan GitHub Actions tidak serumit yang dibayangkan. Dengan workflow di atas, kamu sudah punya pipeline yang menjalankan test otomatis, mengecek code quality, dan deploy ke server setiap kali push ke branch main.
Langkah selanjutnya yang bisa kamu eksplorasi: integrasi dengan Laravel Forge untuk zero-downtime deployment, atau setup staging environment dengan branch develop yang deploy ke server terpisah.
Mulai dari pipeline sederhana dulu - satu job test dengan satu test file. Setelah terbiasa, tambahkan layer code quality, coverage, dan deployment secara bertahap.