Setup CI/CD Pipeline dengan GitHub Actions: Auto Build, Test, dan Deploy ke Server

Lhuqita Fazry
Docker & DevOps GitHub Actions CI/CD Automation
Setup CI/CD Pipeline dengan GitHub Actions: Auto Build, Test, dan Deploy ke Server

CI/CD pipeline mengubah proses deployment dari manual dan rawan error menjadi otomatis dan konsisten. GitHub Actions menyediakan platform terintegrasi di dalam repository untuk menjalankan workflow build, test, dan deploy setiap kali kode berubah.

Memahami Konsep CI/CD Pipeline

Continuous Integration (CI) memastikan setiap perubahan kode diverifikasi secara otomatis melalui build dan test. Continuous Deployment (CD) melanjutkan proses dengan mendeploy aplikasi yang lolos test ke server secara otomatis. Kombinasi keduanya menjamin bahwa bug terdeteksi sejak awal dan deployment berjalan tanpa intervensi manual.

Workflow di GitHub Actions tersusun atas beberapa komponen: event trigger yang menentukan kapan workflow berjalan, jobs yang berisi langkah-langkah eksekusi, dan runners yang menyediakan environment eksekusi. Setiap job berjalan di environment terisolasi sehingga proses build dan test tidak saling mengganggu.

Struktur Dasar Workflow File

Workflow didefinisikan dalam format YAML di direktori .github/workflows/. File berikut menunjukkan konfigurasi dasar untuk build dan test aplikasi Node.js.

yamlyaml
name: CI Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout kode
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Jalankan lint
        run: npm run lint

      - name: Jalankan test
        run: npm test

      - name: Build aplikasi
        run: npm run build

Struktur di atas mengikuti alur standar: checkout kode, setup environment, install dependencies, lint, test, dan build. Setiap step berjalan secara berurutan — jika satu step gagal, seluruh job akan berhenti dan GitHub akan menampilkan notifikasi error.

Menambahkan Deployment Step

Setelah build dan test berhasil, langkah berikutnya adalah mendeploy ke server. Kita akan menggunakan SSH untuk menghubungkan runner ke server target dan menjalankan perintah deployment.

yamlyaml
name: CI/CD Pipeline

on:
  push:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout kode
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Jalankan lint
        run: npm run lint

      - name: Jalankan test
        run: npm test

      - name: Build aplikasi
        run: npm run build

  deploy:
    needs: build-and-test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Deploy ke server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /var/www/app
            git pull origin main
            npm ci --production
            npm run build
            pm2 restart app

Job deploy memiliki dependensi pada build-and-test melalui needs. Artinya deployment hanya berjalan jika semua test berhasil. Kondisi if: github.ref == 'refs/heads/main' memastikan deployment hanya dilakukan dari branch main, bukan dari pull request.

Secrets seperti SERVER_HOST, SERVER_USER, dan SSH_PRIVATE_KEY disimpan secara terenkripsi di bagian Settings > Secrets pada repository GitHub. Pendekatan ini menjaga kredensial tetap aman dan tidak terekspos di dalam file workflow.

Penting untuk dicatat: workflow di atas menggunakan npm ci alih-alih npm install. Perintah npm ci membaca langsung dari package-lock.json dan menghapus node_modules yang ada sebelum install ulang. Hasilnya lebih deterministic dan cepat dibandingkan npm install yang bisa menimbulkan perbedaan versi antara environment development dan produksi.

Mengelola Environment Variables

Environment variables memungkinkan konfigurasi yang berbeda antara staging dan production tanpa mengubah kode. GitHub Actions mendukung definisi environment variables di level workflow, job, maupun step.

yamlyaml
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    env:
      NODE_ENV: test
      DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}

    steps:
      - name: Checkout kode
        uses: actions/checkout@v4

      - name: Jalankan test dengan env
        run: npm test
        env:
          API_TIMEOUT: 5000

Variabel yang didefinisikan di level job berlaku untuk semua step dalam job tersebut. Step-level variables mengoverride job-level variables jika ada nama yang sama. Gunakan secrets untuk data sensitif seperti API key dan database URL — nilai secrets tidak pernah tampil di log.

Optimasi Workflow dengan Caching

Build berulang dapat memakan waktu jika dependencies diunduh ulang setiap kali. Caching mengatasi masalah ini dengan menyimpan data yang jarang berubah — seperti node_modules — agar tersedia langsung di eksekusi berikutnya.

yamlyaml
steps:
  - name: Checkout kode
    uses: actions/checkout@v4

  - name: Cache node_modules
    uses: actions/cache@v4
    with:
      path: ~/.npm
      key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
      restore-keys: |
        ${{ runner.os }}-npm-

  - name: Install dependencies
    run: npm ci

Key cache menggunakan hash dari package-lock.json. Jika file ini berubah (misalnya ada dependency baru), cache akan terbentuk ulang. Jika tidak berubah, runner akan mengambil cache yang sudah ada sehingga proses install lebih cepat. Parameter restore-keys menyediakan fallback jika hash tidak cocok — runner akan mencoba mencari cache terdekat yang tersedia.

Selain caching dependencies, artifact caching juga membantu untuk file build. Jika build menghasilkan file besar seperti Docker image atau compiled assets, kita dapat menggunakan actions/upload-artifact dan actions/download-artifact untuk membagi file antar job tanpa mengulang proses build. Pendekatan ini mempercepat pipeline secara signifikan pada repository dengan build time yang panjang.

Monitoring dan Notifikasi

Workflow yang baik memberikan feedback ketika terjadi kegagalan. GitHub Actions mendukung notifikasi melalui email secara default, tetapi integrasi Slack atau Discord memberikan visibilitas yang lebih baik untuk tim.

yamlyaml
  notify-on-failure:
    needs: [build-and-test, deploy]
    runs-on: ubuntu-latest
    if: failure()

    steps:
      - name: Kirim notifikasi Slack
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deployment gagal di ${{ github.repository }}\nCommit: ${{ github.sha }}\nLink: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Job notify-on-failure berjalan hanya ketika job yang terdaftar di needs gagal, berkat kondisi if: failure(). Notifikasi berisi informasi repository, commit hash, dan link langsung ke log workflow sehingga developer dapat segera menginvestigasi masalah.

Untuk pipeline yang lebih kompleks, pertimbangkan menambahkan step rollback otomatis. Jika deployment gagal setelah build berhasil, rollback dapat mengembalikan server ke versi terakhir yang stabil. Pendekatan sederhana: simpan versi terakhir yang berhasil di tag Git, lalu gunakan script git checkout ke versi tersebut saat failure() terdeteksi. Alternatifnya, gunakan fitur concurrency di GitHub Actions untuk membatalkan deployment yang sedang berjalan ketika commit baru masuk.

Mau memperdalam skill DevOps secara sistematis? Bergabunglah dengan Web Development Bootcamp di Rumah Coding. Kurikulum praktis dengan proyek real-world dan mentorship dari praktisi industri.

Artikel Terkait