Banyak tim kecil masih men-deploy aplikasi dengan cara yang sama: login ke server, git pull, jalankan beberapa perintah artisan, lalu berharap tidak ada yang lupa. Cara ini berjalan sampai suatu hari ada test yang rusak tetapi tetap naik ke produksi, atau migrasi lupa dijalankan sehingga halaman checkout error selama setengah hari. CI/CD menjadikan langkah-langkah itu otomatis dan selalu sama setiap kali.
Artikel ini membangun pipeline sederhana untuk aplikasi Laravel memakai GitHub Actions: setiap push dan pull request menjalankan test serta pengecekan gaya kode, dan setiap perubahan di branch main yang lulus test otomatis di-deploy ke server lewat SSH.
CI dan CD, Apa Bedanya?
| Istilah | Artinya | Contoh di proyek Laravel |
|---|---|---|
| CI (Continuous Integration) | Setiap perubahan kode otomatis diuji | php artisan test dan Pint berjalan di setiap push |
| CD (Continuous Delivery/Deployment) | Kode yang lulus uji otomatis disiapkan atau langsung dirilis | Branch main yang lulus test otomatis di-deploy ke server |
Anda tidak harus langsung menerapkan keduanya. Memulai dengan CI saja sudah memberi manfaat besar, karena kode yang rusak ketahuan sebelum di-merge.
Prasyarat
- Kode proyek tersimpan di repository GitHub.
- Proyek punya test, minimal beberapa feature test untuk alur penting. Jika belum, mulai dari panduan testing otomatis di Laravel untuk pemula.
- Untuk tahap deploy: server yang bisa diakses lewat SSH (VPS atau hosting yang menyediakan akses SSH). Jika aplikasi belum pernah di-deploy, ikuti dulu panduan deploy Laravel ke shared hosting.
Langkah 1: Workflow CI untuk Test dan Gaya Kode
Buat file .github/workflows/ci.yml di root repository:
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Siapkan PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, pdo_sqlite
coverage: none
- name: Cache dependency Composer
uses: actions/cache@v4
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Install dependency
run: composer install --no-interaction --prefer-dist --no-progress
- name: Siapkan environment
run: |
cp .env.example .env
php artisan key:generate
- name: Cek gaya kode
run: vendor/bin/pint --test
- name: Jalankan test
run: php artisan test
Penjelasan bagian-bagian pentingnya:
on: workflow berjalan setiap ada push kemaindan setiap pull request.actions/cache: foldervendordisimpan berdasarkan isicomposer.lock, sehingga run berikutnya jauh lebih cepat selama dependency tidak berubah.- Database test:
phpunit.xmlbawaan Laravel 11 sudah mengarahkan test ke SQLite in-memory, jadi tidak perlu menyiapkan MySQL. Jika aplikasi Anda memakai fitur khusus MySQL, tambahkan blokservices: mysqlpada job dan sesuaikan variabelDB_*. pint --test: Laravel Pint (sudah terpasang di proyek Laravel baru) mengecek format kode tanpa mengubahnya, dan gagal bila ada file yang tidak sesuai standar.
Commit dan push file tersebut. Buka tab Actions di repository untuk melihat setiap langkah berjalan beserta log-nya.
Langkah 2: Wajibkan CI Lulus Sebelum Merge
CI hanya berguna jika hasilnya dihormati. Di Settings → Branches, tambahkan aturan proteksi untuk branch main: wajibkan pull request dan centang status check test sebagai syarat. Dengan begitu kode yang membuat test gagal tidak bisa masuk ke main, termasuk oleh pemilik repository yang sedang terburu-buru.
Langkah 3: Simpan Kredensial Server di GitHub Secrets
Untuk deploy otomatis, GitHub perlu login ke server. Jangan pernah menulis password atau private key di file workflow. Buat pasangan kunci SSH khusus untuk deploy:
ssh-keygen -t ed25519 -C "github-deploy" -f ~/.ssh/github_deploy
# Salin isi github_deploy.pub ke ~/.ssh/authorized_keys di server
Lalu di Settings → Secrets and variables → Actions, tambahkan secret SSH_HOST, SSH_PORT, SSH_USER, dan SSH_KEY (isi file private key github_deploy). Prinsip yang sama dengan file .env berlaku di sini: secret tidak pernah ditulis di repository, dan kunci deploy ini hanya dipakai untuk satu keperluan sehingga mudah dicabut bila bocor.
Langkah 4: Tambahkan Job Deploy
Tambahkan job berikut di file yang sama, sejajar dengan job test:
deploy:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- name: Deploy lewat SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SSH_HOST }}
port: ${{ secrets.SSH_PORT }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
set -e
cd ~/apps/toko
git pull origin main
composer install --no-dev --optimize-autoloader --no-interaction
php artisan migrate --force
php artisan optimize
Dua baris kunci di job ini:
needs: testmemastikan deploy hanya berjalan bila job test lulus.if:memastikan deploy hanya terjadi pada push kemain, bukan pada pull request.
Perintah set -e di awal script menghentikan proses pada perintah pertama yang gagal, sehingga Anda tidak berakhir dengan kode baru yang berjalan tanpa migrasinya. Perintah php artisan optimize di akhir meng-cache konfigurasi dan route; langkah ini penting untuk performa, seperti dibahas di artikel caching di Laravel untuk performa.
Langkah 5: Pertimbangkan Mode Maintenance
Untuk perubahan yang menyentuh struktur database, Anda bisa mengapit proses dengan mode maintenance agar pengguna tidak melihat error di tengah deploy:
php artisan down --retry=60
# ... git pull, composer install, migrate, optimize ...
php artisan up
Hati-hati: bila salah satu perintah di tengah gagal dan set -e aktif, aplikasi tetap dalam mode maintenance sampai Anda menjalankan php artisan up secara manual. Untuk deploy kecil yang tidak mengubah database, mode maintenance biasanya tidak diperlukan.
Masalah yang Sering Muncul
| Gejala | Penyebab umum | Solusi |
|---|---|---|
| Test lulus di laptop, gagal di CI | Test bergantung pada data atau file lokal, atau versi PHP berbeda | Samakan php-version, gunakan factory dan RefreshDatabase |
Permission denied (publickey) saat deploy | Public key belum ada di authorized_keys server, atau secret terpotong | Salin ulang kunci lengkap termasuk baris BEGIN/END |
git pull gagal karena perubahan lokal | Ada file yang diedit langsung di server | Jangan mengedit kode di server; semua perubahan lewat Git |
index.lock: File exists | Proses git sebelumnya terhenti di tengah jalan | Pastikan tidak ada proses git lain, hapus .git/index.lock, ulangi |
| Perubahan .env tidak terbaca | Konfigurasi masih di-cache | Jalankan php artisan optimize ulang setelah mengubah .env |
Checklist Pipeline
- Workflow CI berjalan di setiap push dan pull request.
- Branch
maindiproteksi: merge hanya bila CI lulus. - Kredensial deploy tersimpan di GitHub Secrets dengan kunci SSH khusus.
- Job deploy bergantung pada job test (
needs: test). - Script deploy memakai
set -e, menjalankan migrasi dengan--force, dan diakhiriphp artisan optimize. - Tidak ada lagi perubahan kode yang dilakukan langsung di server.
Pipeline ini sengaja dibuat sederhana. Setelah berjalan stabil, Anda bisa menambahkan composer audit sebagai langkah CI, sebagai bagian dari best practice keamanan aplikasi web, sehingga paket yang rentan langsung ketahuan sebelum rilis.