☀️Siang
DevOps & Cloud

CI/CD dengan GitHub Actions

TOKEN

Panduan lengkap membangun CI/CD pipeline dengan GitHub Actions — dari konsep dasar, workflow syntax, hingga deployment otomatis ke berbagai cloud provider

Artikel: Cicd Github Actions Artikel: Cicd Github Actions


1. Pengenalan CI/CD

CI/CD (Continuous Integration / Continuous Deployment) adalah praktik DevOps yang mengotomatiskan proses build, test, dan deployment aplikasi. GitHub Actions adalah platform CI/CD yang terintegrasi langsung dengan GitHub, memungkinkan Anda membuat workflow otomatis tanpa memerlukan tool eksternal.

Diperkenalkan pada November 2019, GitHub Actions dengan cepat menjadi salah satu platform CI/CD paling populer karena integrasinya yang mulus dengan repository GitHub, marketplace actions yang luas, dan kemudahan konfigurasi menggunakan YAML.

Konsep Dasar CI/CD

Konsep Penjelasan
Continuous Integration (CI)Developer sering merge kode ke branch utama. Setiap merge otomatis menjalankan build dan test untuk mendeteksi error lebih awal
Continuous Delivery (CD)Setiap perubahan yang lolos CI otomatis di-deploy ke staging environment. Deployment ke production masih butuh approval manual
Continuous Deployment (CD)Seperti Continuous Delivery, tapi deployment ke production juga otomatis tanpa approval manual

Mengapa GitHub Actions?

Keunggulan Penjelasan
Terintegrasi GitHubTidak perlu tool terpisah — langsung di repository GitHub Anda
Gratis untuk Public Repo2000 menit/bulan untuk private repo, unlimited untuk public
Marketplace LuasRibuan pre-built actions siap pakai dari komunitas
Multi-OSRunner tersedia untuk Ubuntu, Windows, macOS, dan ARM
Konfigurasi YAMLWorkflow didefinisikan dalam YAML — mudah di-version control
Matrix BuildsJalankan test di beberapa versi OS/bahasa secara paralel
Diagram: CI/CD Pipeline Flow
Diagram: CI/CD PIPELINE WITH GITHUB ACTIONS

TEST Unit + E2E

PRODUCTION (Cloud)

BUILD Code

git push



2. Workflow Syntax

Workflow GitHub Actions didefinisikan dalam file YAML yang disimpan di direktori .github/workflows/. Setiap file YAML merepresentasikan satu workflow yang bisa dipicu oleh berbagai events di repository.

Struktur File Workflow

Diagram: Job Dependencies
Job Dependencies

lint ► test coverage upload Parallel within job...

test lint unit coverage ► needs

build docker push ► deploy needs ssh deploy

⚠️ Peringatan

Setiap job berjalan di VM terpisah, sehingga file yang dihasilkan di satu job tidak tersedia di job lain. Gunakan actions/upload-artifact dan actions/download-artifact untuk berbagi file antar jobs.



4. Triggers: Events yang Memicu Workflow

GitHub Actions bisa dipicu oleh berbagai events di repository maupun secara manual. Kunci on di workflow YAML mendefinisikan kapan workflow akan dijalankan.

Berbagai Jenis Trigger

YAML — Trigger Examples
# === PUSH & PULL REQUEST ===
on:
  push:
    branches: [main, develop, 'release/**']
    paths: ['src/**', 'tests/**']  # Hanya jika file ini berubah
    paths-ignore: ['docs/**', '*.md']
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

# === SCHEDULE (CRON) ===
on:
  schedule:
    # Setiap hari jam 2 pagi UTC (cron syntax)
    - cron: '0 2 * * *'
    # Setiap Senin jam 9 pagi UTC
    - cron: '0 9 * * 1'

# === WORKFLOW DISPATCH (Manual) ===
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy target'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production
      version:
        description: 'Version tag'
        required: false

# === REPOSITORY DISPATCH ===
on:
  repository_dispatch:
    types: [deploy-command]

# === MULTIPLE TRIGGERS ===
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Setiap Minggu
  workflow_dispatch:

# === RELEASE ===
on:
  release:
    types: [published, edited]

# === ISSUE & COMMENTS ===
on:
  issues:
    types: [opened, edited, labeled]
  issue_comment:
    types: [created]

Filter dengan Paths dan Tags

YAML — Advanced Triggers
# Trigger hanya saat file di folder tertentu berubah
on:
  push:
    paths:
      - 'src/**'
      - 'package*.json'
      - '!src/**/*.test.js'  # Kecuali file test

# Trigger saat tag di-push (untuk release)
on:
  push:
    tags:
      - 'v*'  # Cocokkan v1.0, v2.3.1, dll

# Filter berdasarkan file yang diubah
on:
  pull_request:
    paths:
      - 'docs/**'
      - 'api/**'


5. Mengelola Secrets

GitHub Secrets memungkinkan Anda menyimpan data sensitif seperti API keys, password, dan tokens secara aman. Secrets tidak akan tampil di log workflow dan tidak bisa diakses oleh fork repository.

Menambahkan Secrets

  1. Buka repository di GitHub
  2. Klik SettingsSecrets and variablesActions
  3. Klik New repository secret
  4. Masukkan nama (misal: DOCKER_PASSWORD) dan value

Menggunakan Secrets di Workflow

YAML — Secrets Usage
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # Menggunakan secret sebagai environment variable
      - name: Deploy
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          echo "Deploying..."
          aws s3 sync ./dist s3://my-bucket

      # Menggunakan secret dengan action
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      # Menggunakan secret untuk SSH
      - 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 /opt/app && git pull && docker compose up -d

# === ENVIRONMENT SECRETS ===
# Secrets bisa dibatasi per environment
# (Settings → Environments → pilih environment → Add secret)
jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    environment: production  # Hanya akses secrets di environment ini
    steps:
      - run: echo "Deploying to ${{ secrets.DEPLOY_URL }}"
⚠️ Peringatan

Jangan pernah mencetak secrets ke log! Jangan gunakan echo ${{ secrets.MY_SECRET }} — meskipun GitHub akan otomatis menyensor output, lebih baik hindari sama sekali. Gunakan secrets hanya di environment variables atau input action.



6. Caching Dependencies

Setiap kali workflow berjalan, runner memulai dengan environment yang bersih — semua dependencies harus di-download ulang. Caching memungkinkan Anda menyimpan dependencies agar tidak perlu di-download setiap kali, mempercepat workflow secara signifikan.

Cara Cache dengan actions/cache

YAML — Caching
# === METHOD 1: Built-in cache di setup actions ===
# Node.js
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # Otomatis cache node_modules

# Python
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'  # Otomatis cache pip packages

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

# === METHOD 3: Docker layer caching ===
- name: Build Docker image
  uses: docker/build-push-action@v5
  with:
    context: .
    push: false
    tags: myapp:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

# === METHOD 4: Cache Gradle (Java) ===
- uses: actions/setup-java@v4
  with:
    java-version: '21'
    distribution: 'temurin'
    cache: 'gradle'

Strategi Cache Key

Strategi Key Pattern Kapan Cache Miss
Lockfile hash${{ hashFiles('**/package-lock.json') }}Saat dependencies berubah
Date-based${{ runner.os }}-${{ github.run_id }}Setiap run (tidak efektif)
Branch-specific${{ runner.os }}-${{ github.ref }}Per branch
FallbackGunakan restore-keysCache parsial jika miss


7. Deploy ke Cloud

Salah satu kekuatan utama GitHub Actions adalah kemampuan deploy ke berbagai cloud provider secara otomatis. Berikut contoh deployment ke beberapa platform populer:

Deploy ke AWS (S3 + CloudFront)

YAML — Deploy to AWS
name: Deploy Frontend to AWS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Build
        run: |
          npm ci
          npm run build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-southeast-1

      - name: Deploy to S3
        run: |
          aws s3 sync ./dist s3://my-frontend-bucket \
            --delete \
            --cache-control "max-age=31536000"

      - name: Invalidate CloudFront
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_ID }} \
            --paths "/*"

Deploy ke VPS dengan SSH

YAML — Deploy to VPS
name: Deploy to VPS

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          port: 22
          script: |
            set -e
            cd /opt/myapp

            # Pull kode terbaru
            git fetch origin main
            git reset --hard origin/main

            # Build dan restart
            docker compose build --no-cache
            docker compose down
            docker compose up -d

            # Cleanup
            docker system prune -f

            echo "✅ Deploy berhasil!"

Deploy ke Docker Hub + Server

YAML — Docker Hub Deploy
name: Build & Push Docker Image

on:
  push:
    tags: ['v*']  # Hanya saat tag version di-push

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_TOKEN }}

      - name: Extract version from tag
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64
          tags: |
            myuser/myapp:latest
            myuser/myapp:${{ steps.version.outputs.VERSION }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


8. Matrix Builds

Matrix builds memungkinkan Anda menjalankan job yang sama dengan beberapa kombinasi parameter (misalnya: berbagai versi Node.js, OS, atau database). Ini sangat berguna untuk memastikan kompatibilitas lintas platform.

Contoh Matrix Build

YAML — Matrix Build
name: Test Matrix

on: [push, pull_request]

jobs:
  test:
    name: Node ${{ matrix.node-version }} on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
        exclude:
          # Kecualikan kombinasi tertentu
          - node-version: 18
            os: windows-latest
        include:
          # Tambahkan kombinasi ekstra
          - node-version: 20
            os: macos-latest
            experimental: true
      fail-fast: false  # Jangan stop jika satu matrix gagal

    continue-on-error: ${{ matrix.experimental || false }}

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - run: npm ci
      - run: npm test

      # Upload coverage hanya untuk satu kombinasi
      - name: Upload coverage
        if: matrix.node-version == 20 && matrix.os == 'ubuntu-latest'
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

Contoh Matrix dengan Database

YAML — Matrix + Services
jobs:
  test:
    name: Test with ${{ matrix.db }}
    runs-on: ubuntu-latest

    strategy:
      matrix:
        db: [postgres, mysql]

    services:
      # Service container otomatis dijalankan
      postgres:
        image: postgres:16
        if: matrix.db == 'postgres'
        env:
          POSTGRES_DB: testdb
          POSTGRES_USER: testuser
          POSTGRES_PASSWORD: testpass
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

      mysql:
        image: mysql:8
        if: matrix.db == 'mysql'
        env:
          MYSQL_ROOT_PASSWORD: testpass
          MYSQL_DATABASE: testdb
        ports:
          - 3306:3306
        options: >-
          --health-cmd "mysqladmin ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - run: pip install -r requirements.txt
      - run: pytest tests/ -v
        env:
          DATABASE_TYPE: ${{ matrix.db }}


9. Best Practices GitHub Actions

Mengikuti best practices saat menggunakan GitHub Actions sangat penting untuk keamanan, performa, dan maintainability pipeline CI/CD Anda.

Keamanan

Praktik Alasan
Pin action versionsGunakan SHA commit, bukan tag — uses: actions/checkout@abc123
Least privilegeBerikan permissions hanya yang dibutuhkan: permissions: contents: read
Audit third-party actionsReview kode source sebelum menggunakan action dari marketplace
Environment secretsGunakan environment-level secrets untuk production credentials
Tidak print secretsJangan pernah echo atau print nilai secrets

Performa

YAML — Best Practices
# 1. Gunakan permissions minimal
permissions:
  contents: read
  packages: write

# 2. Set timeout untuk mencegah job berjalan terlalu lama
jobs:
  test:
    timeout-minutes: 15  # Maksimal 15 menit

# 3. Gunakan concurrency untuk cancel run lama
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# 4. Pisahkan workflow untuk CI dan CD
# .github/workflows/ci.yml  → Jalankan di setiap push/PR
# .github/workflows/deploy.yml → Hanya di merge ke main

# 5. Gunakan reusable workflows
# .github/workflows/reusable-deploy.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string

# 6. Conditional steps untuk efisiensi
steps:
  - name: Deploy only on main
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    run: ./deploy.sh
💡 Tips

Gunakan concurrency untuk membatalkan workflow yang sudah tidak relevan — misalnya saat Anda push commit baru ke PR yang sama, workflow dari commit sebelumnya otomatis dibatalkan. Ini menghemat menit GitHub Actions.



10. Quiz: Uji Pemahamanmu!

Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang CI/CD dengan GitHub Actions:

Pertanyaan 1: Di direktori mana file workflow GitHub Actions harus disimpan?

a) .github/actions/
b) .github/workflows/
c) .actions/workflows/
d) .ci/workflows/

Pertanyaan 2: Apa perbedaan antara Continuous Delivery dan Continuous Deployment?

a) Tidak ada perbedaan
b) Delivery otomatis, Deployment manual
c) Delivery butuh approval manual ke production, Deployment sepenuhnya otomatis
d) Delivery untuk frontend, Deployment untuk backend

Pertanyaan 3: Bagaimana cara menjalankan jobs secara sequential di GitHub Actions?

a) Gunakan keyword order: sequential
b) Gunakan needs untuk mendefinisikan dependensi
c) Jobs selalu berjalan sequential secara default
d) Gunakan depends_on

Pertanyaan 4: Apa fungsi dari concurrency di GitHub Actions workflow?

a) Menjalankan jobs secara bersamaan
b) Membatalkan workflow lama yang sudah tidak relevan
c) Mengatur jumlah runner
d) Mengenkripsi data workflow

Pertanyaan 5: Mengapa disarankan menggunakan SHA commit daripada tag untuk pin action versions?

a) SHA lebih pendek dari tag
b) SHA lebih cepat dieksekusi
c) Tag bisa diubah/di-overwrite oleh maintainer, SHA immutable
d) SHA menghemat menit GitHub Actions
🔍 Zoom
100%
🎨 Tema