Untuk menjaga kualitas dan kecepatan delivery proyek Laravel, Anda perlu pipeline GitHub Actions yang mengotomatiskan linting, pengujian, dan release. Artikel ini langsung menjelaskan konfigurasi lengkap: lint PHP/JS, testing otomatis, release untuk staging/production, caching dependensi, validasi env secret, plus notifikasi atau rollback ringan agar tim selalu tahu status deploy.

Menyiapkan tantangan lint, test, dan release secara terintegrasi

Pendekatan minimal yang aman adalah memisahkan job linting, testing, dan release sambil memungkinkan mereka berjalan paralel jika tidak saling bergantung. Di Laravel, lint PHP (misalnya phpstan atau larastan) dan lint JS (menggunakan eslint) bisa dijalankan dalam job terpisah karena tidak membutuhkan artefak yang sama. Setelah lint selesai, job test (misalnya PHPUnit + Pest + Dusk) memberikan jaminan kualitas internal. Job release baru dijalankan jika lint dan test sukses, dengan strategi matrix agar bisa deploy ke staging dan production yang berbeda.

Lint PHP dan JavaScript secara paralel

Linting biasanya cepat dan cocok dijalankan lebih awal. Pastikan workflow memanfaatkan caching dependensi terkait (composer, npm) agar lint PHP/JS tidak mengunduh ulang paket setiap kali. Jika lint kehilangan waktu karena install dependensi, job menjadi bottleneck bagi seluruh pipeline.

Testing sebagai guard utama

Jalankan suite unit/integrasi dari Laravel (PHPUnit/Pest) setelah job lint selesai. Agar tidak menunggu lint JS selesai, Anda dapat membuat job test dengan needs: [lint-php] saja, sehingga job lain tetap bisa berjalan lebih awal. Gunakan caching vendor/composer dan node_modules untuk mempercepat instalasi.

Membangun pipeline GitHub Actions lengkap

Berikut contoh pipeline yang mencakup linting, testing, matrix release, env validation, caching, plus notifikasi/rollback berupa langkah ringan. Fokus ada pada DX tim dan debugging: setiap job mencetak log cache, hasil lint, dan memanfaatkan conditionals agar gagal lebih dini.

name: Laravel CI/CD

on:
  push:
    branches: [main, release/*]
  pull_request:
    branches: [main]

env:
  PHP_VERSION: '8.2'
  NODE_VERSION: '20'

jobs:
  lint-php:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ env.PHP_VERSION }}
      - name: Cache Composer
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
          restore-keys: composer-${{ runner.os }}-
      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-suggest
      - name: PHP Lint (phpstan / larastan)
        run: vendor/bin/phpstan analyse

  lint-js:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      - name: Install JS dependencies
        run: npm ci
      - name: JS Lint
        run: npm run lint

  test:
    needs: [lint-php]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ env.PHP_VERSION }}
      - name: Validate ENV secrets
        run: |
          required=(APP_ENV APP_KEY DEPLOY_KEY)
          for key in "${required[@]}"; do
            if [[ -z "${{ secrets[$key] }}" ]]; then
              echo "Secret $key tidak diset" && exit 1
            fi
          done
      - name: Cache Composer
        uses: actions/cache@v4
        with:
          path: vendor
          key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
          restore-keys: composer-${{ runner.os }}-
      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-suggest
      - name: Cache Node modules
        uses: actions/cache@v4
        with:
          path: node_modules
          key: node-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
          restore-keys: node-${{ runner.os }}-
      - name: Install frontend deps
        run: npm ci
      - name: Run tests
        run: vendor/bin/pest --parallel

Kami menggunakan validation step untuk memastikan secret penting ada sebelum tes berjalan: jika salah satu secret belum diset, CI gagal lebih awal. Ini menghindari situasi di mana job test gagal tanpa informasi yang jelas.

Job release dengan matrix environment dan notifikasi

Release job dijalankan setelah lint dan test sukses. Gunakan strategy.matrix untuk lingkungan staging dan production. Jika job release muncul untuk kedua lingkungan, trunking dengan deployment script (misalnya Ansible, Envoy, atau ssh) yang sama tapi dengan env config berbeda.

  release:
    needs: [lint-php, lint-js, test]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: [staging, production]
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ env.PHP_VERSION }}
      - name: Deploy ke ${{ matrix.target }}
        run: |
          export DEPLOY_TARGET=${{ matrix.target }}
          ./scripts/deploy.sh $DEPLOY_TARGET
      - name: Notifikasi sukses
        if: ${{ success() }}
        run: echo "Deploy ${{ matrix.target }} berhasil"
      - name: Notifikasi kegagalan dan rollback ringan
        if: ${{ failure() }}
        run: |
          echo "Deploy ${{ matrix.target }} gagal, mencoba rollback"
          ./scripts/rollback.sh ${{ matrix.target }}
          # bisa diganti dengan panggilan API atau Slack DM

Job notifikasi bisa diganti dengan action seperti rtCamp/action-slack-notify atau custom script `curl` ke webhook. Rollback ringan bisa berupa menjalankan ./scripts/rollback.sh yang menggunakan snapshot database atau memicu release versi sebelumnya.

Mempercepat feedback cycle dan debugging

Beberapa praktik tambahan yang meningkatkan developer experience:

  • Artifacts log lint/test: upload log ketika job gagal agar mudah melihat failure (gunakan actions/upload-artifact).
  • Cache invalidation strategis: gunakan hashFiles agar cache composer/npm hanya refresh saat lockfile berubah; hindari cache korup dengan restore-keys generik.
  • Debug step environment: tambahkan step seperti env | sort (dengan filter) dalam job debug untuk memastikan env secret benar.
  • Parallel jobs untuk linting: lint PHP/JS dan job type lain bisa berjalan bersamaan sehingga pengujian tidak menunggu lint JS selesai.

Pipeline ini mempersingkat delivery cycle karena lint/job test dijalankan otomatis pada setiap push/PR, caching mempercepat setup, dan release otomatis membuat tim tahu status deployment dengan notifikasi yang jelas. Jika ada failure, notifikasi plus rollback script memastikan penanganan cepat.