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

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
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    CI/CD PIPELINE WITH GITHUB ACTIONS             β”‚
β”‚                                                                   β”‚
β”‚  Developer                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                    β”‚
β”‚  β”‚  git push │──── Trigger β”€β”€β”€β”€β–Ίβ”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚   GitHub Actions Workflow     β”‚ β”‚
β”‚                                 β”‚                              β”‚ β”‚
β”‚                                 β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚ β”‚
β”‚                                 β”‚  β”‚  BUILD │──►│   TEST    β”‚  β”‚ β”‚
β”‚                                 β”‚  β”‚  Code  β”‚  β”‚  Unit +   β”‚  β”‚ β”‚
β”‚                                 β”‚  β”‚        β”‚  β”‚  E2E      β”‚  β”‚ β”‚
β”‚                                 β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜  β”‚ β”‚
β”‚                                 β”‚                    β”‚         β”‚ β”‚
β”‚                                 β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚ β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚         β–Ό                    β”‚ β”‚
β”‚         β”‚  PRODUCTION   │◄──────│  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚ β”‚
β”‚         β”‚  (Cloud)      β”‚Deploy β”‚  β”‚  DEPLOY   β”‚               β”‚ β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚ β”‚
β”‚                                 β”‚                              β”‚ β”‚
β”‚                                 β”‚  βœ… Pass β†’ Deploy             β”‚ β”‚
β”‚                                 β”‚  ❌ Fail β†’ Notify             β”‚ β”‚
β”‚                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

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

YAML β€” .github/workflows/ci.yml
# .github/workflows/ci.yml
# CI Pipeline dengan GitHub Actions

name: CI Pipeline  # Nama workflow (tampil di tab Actions)

# Trigger workflow
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

# Environment variables (aksesible di semua jobs)
env:
  NODE_VERSION: '20'
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

# Jobs yang akan dijalankan
jobs:
  lint-and-test:
    name: Lint & Test
    runs-on: ubuntu-latest  # Runner OS

    steps:
      # 1. Checkout kode dari repository
      - name: Checkout code
        uses: actions/checkout@v4

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

      # 3. Install dependencies
      - name: Install dependencies
        run: npm ci

      # 4. Jalankan linter
      - name: Run ESLint
        run: npm run lint

      # 5. Jalankan unit tests
      - name: Run tests
        run: npm test -- --coverage
        env:
          CI: true

      # 6. Upload coverage report
      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

Komponen Utama Workflow

Komponen Penjelasan
nameNama workflow β€” tampil di GitHub UI
onEvent trigger β€” kapan workflow dijalankan
envEnvironment variables global
jobsKumpulan jobs yang dijalankan (paralel atau sequential)
stepsLangkah-langkah dalam satu job
usesMenjalankan pre-built action dari marketplace
runMenjalankan shell command
withInput parameter untuk action
needsDependency antar jobs
πŸ’‘ Tips

Gunakan act (https://github.com/nektos/act) untuk menjalankan workflow GitHub Actions secara lokal saat development. Ini menghemat waktu karena tidak perlu push ke GitHub setiap kali ingin test workflow.

3. Jobs dan Steps

Jobs adalah unit dasar eksekusi dalam GitHub Actions. Setiap job berjalan di environment runner yang terpisah (VM terpisah). Secara default, jobs berjalan secara paralel, tetapi Anda bisa membuatnya sequential dengan needs.

Steps adalah langkah-langkah individual dalam satu job. Steps berjalan secara sequential β€” step berikutnya hanya dijalankan setelah step sebelumnya berhasil.

Contoh Multi-Job dengan Dependencies

YAML β€” Multi-Job Workflow
name: Full CI/CD Pipeline

on:
  push:
    branches: [main]

jobs:
  # === JOB 1: Test (berjalan dulu) ===
  test:
    name: Run Tests
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt

      - name: Run linting
        run: flake8 src/ --max-line-length=100

      - name: Run unit tests
        run: pytest tests/ -v --cov=src --cov-report=xml

      - name: Upload test coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage.xml

  # === JOB 2: Build (butuh test selesai) ===
  build:
    name: Build Docker Image
    runs-on: ubuntu-latest
    needs: test  # Hanya jalan setelah test selesai

    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_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            myuser/myapp:latest
            myuser/myapp:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # === JOB 3: Deploy (butuh build selesai) ===
  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: build  # Hanya jalan setelah build selesai
    if: github.ref == 'refs/heads/main'  # Hanya di main branch

    environment: production  # Butuh approval manual

    steps:
      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/myapp
            docker compose pull
            docker compose up -d
            docker system prune -f
Diagram: Job Dependencies
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  test   │────►│  build  │────►│   deploy   β”‚
β”‚         β”‚needsβ”‚         β”‚needsβ”‚            β”‚
β”‚ lint    β”‚     β”‚ docker  β”‚     β”‚ ssh deploy β”‚
β”‚ unit    β”‚     β”‚ push    β”‚     β”‚            β”‚
β”‚ coverageβ”‚     β”‚         β”‚     β”‚            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Parallel within job:    Sequential between jobs:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    test β†’ build β†’ deploy
β”‚ lint ─► test     β”‚
β”‚ coverage upload  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
⚠️ 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 Settings β†’ Secrets and variables β†’ Actions
  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