Cara Setup CI/CD Laravel dengan GitHub Actions

Cara Setup CI/CD Laravel dengan GitHub Actions


NEW
laravel ci-cd github-actions devops

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:

  1. Unit test atau feature test yang bisa dijalankan via php artisan test
  2. Repository GitHub yang sudah terhubung
  3. 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 server
  • SERVER_USER - username SSH (biasanya deploy atau www-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.

© 2026 D.N. Zaki Zamani