Chapter Continuous Integration di section Data Engineering Testing mengotomatiskan pertanyaan “apakah kode sehat?” — setiap push melewati lint dan test sebelum boleh merge. Pertanyaan kedua berbeda dan sering tertukar dengannya: “apakah rilis ini siap dipromosikan ke production?”. Continuous delivery (CD) mengambil alih setelah CI hijau: commit yang lolos uji dibangun menjadi artifact berversi, artifact yang sama dideploy ke staging, dan promosi ke production menunggu satu keputusan manusia. Chapter ini menyusun kedua bagian tersebut menjadi satu workflow GitHub Actions untuk pipeline data TokoKita.

CI Versus CD #

AspekContinuous IntegrationContinuous Delivery
Pertanyaan yang dijawabapakah kode sehat?apakah rilis siap dipromosikan?
Pemicusetiap push dan pull requestmerge ke branch main
Keluaranvonis hijau atau merahartifact berversi di staging
Keterlibatan manusiatidak ada — gerbang otomatissatu persetujuan untuk production

CI tanpa CD berhenti di merge; CD tanpa CI mempromosikan kode yang tidak teruji. Keduanya dirancang sebagai satu rantai supaya artifact yang sampai ke production adalah artifact yang lulus seluruh gerbang — rantai inilah yang membedakan deployment berulang yang membosankan dari penyalinan manual yang menegangkan.

Workflow GitHub Actions #

Workflow berikut melanjutkan gerbang dari chapter Continuous Integration, lalu menambahkan tiga tahap deployment dari chapter Deployment Pipeline: version stamp, build artifact, dan promote:

yaml
name: CI/CD Pipeline Data
on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:
    inputs:
      tujuan:
        description: "Tujuan deploy"
        required: true
        default: staging
        type: choice
        options: [staging, production]

jobs:
  uji:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip
      - run: pip install -r requirements.txt
      - name: Lint
        run: ruff check .
      - name: Test
        run: python3 -m pytest tests -q

  build:
    needs: uji
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # git describe butuh riwayat tag
      - name: Version stamp
        run: echo "VERSI=$(git describe --tags --always)" >> "$GITHUB_ENV"
      - name: Bangun artifact
        run: |
          python3 scripts/buat_manifest.py
          zip -r "tokokita-etl-${VERSI}.zip" src config MANIFEST.json
      - uses: actions/upload-artifact@v4
        with:
          name: tokokita-etl-${{ env.VERSI }}
          path: tokokita-etl-*.zip

  deploy-staging:
    needs: build
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: tokokita-etl-*
          merge-multiple: true
      - name: Promote artifact yang sama ke staging
        run: ./deploy.sh staging

  deploy-production:
    needs: build
    if: github.event_name == 'workflow_dispatch' && inputs.tujuan == 'production'
    runs-on: ubuntu-latest
    environment: production  # gerbang manual: required reviewers
    steps:
      - uses: actions/download-artifact@v4
        with:
          pattern: tokokita-etl-*
          merge-multiple: true
      - name: Promote artifact yang sama ke production
        run: ./deploy.sh production

Empat keputusan penting ada di dalamnya. Pertama, needs merangkai job berurutan — build hanya berjalan setelah uji hijau, dan kedua job deploy menunggu build. Kedua, job deploy mengunduh artifact dengan download-artifact, bukan membangun ulang; artifact yang dipromosikan ke production adalah byte yang sama dengan yang teruji di staging. Ketiga, environment: production menghubungkan job dengan lingkungan GitHub yang berisi required reviewers — gerbang manual berupa persetujuan sebelum job boleh berjalan. Keempat, workflow_dispatch dengan input tujuan membuat promosi ke production menjadi tindakan sadar lewat tombol, bukan efek samping push. Struktur lengkap workflow tervalidasi dengan yaml.safe_load:

python
"""Validasi struktur workflow CI/CD dengan yaml.safe_load."""
import yaml

wf = yaml.safe_load(open("cicd_pipeline.yaml", encoding="utf-8"))
# PyYAML mengikuti YAML 1.1: kunci 'on' terbaca sebagai boolean True
pemicu = wf.get("on") or wf[True]
print("pemicu:", sorted(pemicu))
for nama, job in wf["jobs"].items():
    gerbang = []
    if "needs" in job:
        gerbang.append(f"needs={job['needs']}")
    if job.get("environment"):
        gerbang.append(f"environment={job['environment']}")
    if job.get("if"):
        gerbang.append(f"if: {job['if']}")
    print(f"job {nama} ({'; '.join(gerbang) if gerbang else 'tanpa syarat'})")
    for langkah in job["steps"]:
        if "name" in langkah:
            print(f"  - {langkah['name']}")
        elif "run" in langkah:
            print(f"  - run: {langkah['run'].splitlines()[0]}")
        else:
            print(f"  - {langkah['uses']}")
assert list(wf["jobs"]) == ["uji", "build", "deploy-staging", "deploy-production"]
assert wf["jobs"]["deploy-production"]["environment"] == "production"
print("struktur valid: uji -> build -> deploy staging/production (manual gate)")

Output yang Dihasilkan:

text
pemicu: ['pull_request', 'push', 'workflow_dispatch']
job uji (tanpa syarat)
  - actions/checkout@v4
  - actions/setup-python@v5
  - run: pip install -r requirements.txt
  - Lint
  - Test
job build (needs=uji)
  - actions/checkout@v4
  - Version stamp
  - Bangun artifact
  - actions/upload-artifact@v4
job deploy-staging (needs=build; environment=staging; if: github.event_name == 'push' && github.ref == 'refs/heads/main')
  - actions/download-artifact@v4
  - Promote artifact yang sama ke staging
job deploy-production (needs=build; environment=production; if: github.event_name == 'workflow_dispatch' && inputs.tujuan == 'production')
  - actions/download-artifact@v4
  - Promote artifact yang sama ke production
struktur valid: uji -> build -> deploy staging/production (manual gate)

Keluaran di atas adalah peta rantai dalam bentuk ringkas: empat job dengan urutan needs yang jelas, gerbang if dan environment tercetak per job, dan dua asersi terakhir memastikan urutan job serta keberadaan gerbang manual production tidak berubah tanpa sengaja. Validasi semacam ini murah dijalankan di CI sehingga setiap perubahan workflow ikut diperiksa, bukan hanya dilihat sekilas saat review.

Strategi Deploy untuk Pipeline Batch #

Aplikasi web melakukan deploy berkelanjutan: beberapa kali sehari, versi baru langsung melayani trafik. Pipeline batch berjalan berbeda — versi baru hanya terasa saat run berikutnya dieksekusi — sehingga pilihan strateginya juga berbeda:

StrategiCara kerjaCocok untuk
Redeploy saat jendela runartifact baru dipromosikan menjelang jadwal run berikutnyapipeline batch harian seperti TokoKita
Blue/greendua versi terpasang berdampingan; symlink current dipindah setelah verifikasipipeline yang tidak boleh melewatkan satu jendela pun
Canary runversi baru memproses satu partisi kecil dulu, hasilnya dibandingkan dengan versi lamatransformasi yang sensitif terhadap angka

Pola symlink current dari chapter Deployment Pipeline membuat blue/green hampir gratis: versi lama tetap terpasang di foldernya, dan satu rename mengembalikan segalanya. Canary run memanfaatkan sifat partisi: jalankan versi baru untuk satu tanggal, bandingkan dengan hasil versi lama, baru lanjutkan sisa rentang.

Bagi mayoritas pipeline batch, pilihan pertama sudah memadai: promosi dilakukan pada jam kerja, verifikasi dilakukan di staging dengan data tiruan, dan run tengah malam berikutnya memakai versi baru dengan tenang. Strategi yang lebih berat baru layak ketika konsekuensi satu run gagal benar-benar mahal — misalnya laporan keuangan yang harus terbit pagi.

Workflow otomatis mengantar artifact sampai ke server, tetapi pipeline yang dideploy tetap hidup di jadwalnya sendiri — dan disiplin berjalan di production menuntut pembukuan yang rapi. Kita susun run ledger beserta keputusan catch-up dan guard concurrency pada chapter Scheduling dan Production Runs.