CI/CD itu wajib kalau kamu mau deploy aplikasi secara profesional. GitHub Actions bikin semuanya gampang — cukup tulis workflow YAML, dan build-test-deploy jalan otomatis setiap kali push kode. Saya share cara setup dari nol.
Di sini saya bahas cara membuat CI/CD pipeline menggunakan GitHub Actions, mulai dari konsep dasar hingga deployment ke VPS production. Semua contoh dilengkapi dengan workflow YAML yang bisa langsung kamu gunakan.
CI/CD terdiri dari dua konsep utama yang bekerja bersama-sama:
Pipeline yang baik mengurangi human error, mempercepat release cycle, dan memberikan feedback cepat kepada developer tentang kualitas kode mereka.
GitHub Actions memiliki beberapa keunggulan dibanding tools CI/CD lain seperti Jenkins, GitLab CI, atau CircleCI:
Setiap workflow GitHub Actions didefinisikan dalam file YAML yang disimpan di direktori .github/workflows/. Berikut struktur dasarnya:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build project
run: npm run build
Penjelasan komponen utama:
Berikut workflow lengkap yang mencakup linting, testing, building, dan deployment:
name: Node.js CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_ENV: production
APP_NAME: my-app
jobs:
lint:
name: Code Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
-- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.node-version }}
path: coverage/
build:
name: Build Application
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
deploy:
name: Deploy to Server
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/html/${{ env.APP_NAME }}
git pull origin main
npm ci
--production
npm run build
pm2 restart ${{ env.APP_NAME }}
Untuk deploy ke VPS, kita menggunakan SSH action. Pertama, kamu perlu mengkonfigurasi secrets di repository GitHub:
Untuk generate SSH key pair jika belum punya:
ssh-keygen -t ed25519 -C "github-deploy" -f ~/.ssh/github_deploy -N ""
# Copy public key ke VPS
ssh-copy-id -i ~/.ssh/github_deploy.pub user@your-vps-ip
# Copy private key untuk disimpan di GitHub Secrets
cat ~/.ssh/github_deploy
Bagi pengguna PHP, berikut contoh pipeline untuk aplikasi CodeIgniter 4:
name: PHP CI/CD - CodeIgniter 4
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
extensions: mbstring, intl, json, mysql
coverage: xdebug
- name: Install Composer dependencies
run: composer install
--no-progress
--prefer-dist
- name: Copy env file
run: cp env .env
- name: Generate app key
run: php spark key:generate
- name: Run tests
run: vendor/bin/phpunit
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install dependencies
run: composer install
--no-dev
--optimize-autoloader
- name: Deploy to server
uses: SamKirkland/[email protected]
with:
server: ${{ secrets.FTP_SERVER }}
username: ${{ secrets.FTP_USERNAME }}
password: ${{ secrets.FTP_PASSWORD }}
local-dir: ./
server-dir: /var/www/html/
exclude: |
**/.git*
**/.env*
**/tests/**
**/.github/**
writable/debugbar/**
GitHub Actions menyediakan dua cara untuk menyimpan konfigurasi sensitif:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy with secrets
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "Deploying to production..."
# Gunakan environment variables dalam script deploy
Perbedaan antara Secrets dan Variables:
Caching dependencies bisa menghemat waktu build secara signifikan:
steps:
- uses: actions/checkout@v4
# Cache untuk Node.js
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Otomatis cache folder node_modules
# Cache untuk PHP/Composer
- name: Cache Composer packages
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-composer-
- name: Install dependencies
run: composer install
--no-progress
--prefer-dist
Selain trigger dari push atau pull request, kamu juga bisa menjalankan workflow secara terjadwal:
name: Scheduled Tasks
on:
schedule:
# Setiap hari jam 2 pagi UTC
- cron: '0 2 * * *'
# Setiap hari Senin jam 8 pagi UTC
- cron: '0 8 * * 1'
jobs:
database-backup:
runs-on: ubuntu-latest
steps:
- name: Backup database via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
mysqldump -u ${{ secrets.DB_USER }} -p${{ secrets.DB_PASSWORD }} ${{ secrets.DB_NAME }} > /backups/db_$(date +%Y%m%d).sql
gzip /backups/db_$(date +%Y%m%d).sql
Matrix strategy memungkinkan kamu menjalankan tests di berbagai versi atau konfigurasi secara paralel:
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.1', '8.2', '8.3']
database: ['mysql', 'postgres']
exclude:
- php-version: '8.1'
database: 'postgres'
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
- name: Run tests
run: vendor/bin/phpunit
env:
DB_TYPE: ${{ matrix.database }}
kamu bisa menambahkan notifikasi Slack untuk memberitahu tim tentang status deployment:
jobs:
notify:
runs-on: ubuntu-latest
needs: [test, deploy]
if: always()
steps:
- name: Slack notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
channel: '#deployments'
fields: repo,message,commit,author
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Untuk mendapatkan hasil maksimal dari CI/CD pipeline kamu, ikuti best practices berikut:
concurrency:
group: production-deploy
cancel-in-progress: true
Beberapa masalah umum yang sering terjadi saat setup CI/CD pipeline:
Dengan CI/CD pipeline yang terkonfigurasi dengan baik, proses development menjadi lebih efisien, konsisten, dan andal. Mulailah dengan pipeline sederhana, lalu tambahkan fitur seiring kebutuhan tim kamu berkembang.