Tag v1.6.0 telah duduk di origin, dan sejak chapter sebelumnya kita sudah memilih jalurnya: CI build dengan runner ephemeral. Kini tibalah pekerjaan yang sesungguhnya — menuliskan pipeline sebagai berkas yang hidup di dalam repositori, dibaca oleh GitHub Actions, dan dieksekusi di runner. Kabar baik untuk kita: seluruh kosakata yang dibutuhkan sudah dikuasai bertahap sepanjang course — build context, digest, tag, login registry — sehingga chapter ini bukan materi baru yang jatuh dari langit, melainkan penyusunan ulang kebiasaan lama ke dalam format YAML. Kita mulai dari anatomi layanannya, menulis workflow utuh build-backend.yml untuk image backend toko, membedahnya sebelas langkah, memilah empat sumber nilai yang dipakainya, lalu menyaksikan run pertamanya mendorong image ke ghcr.io.
Anatomi GitHub Actions #
GitHub Actions menyimpan seluruh pipeline di dalam repositori itu sendiri — berkas YAML di folder .github/workflows/, dengan satu berkas mewakili satu workflow. Struktur berkasnya ringkas: event menentukan kapan workflow dinyalakan, job adalah blok pekerjaan yang berjalan pada sebuah runner — mesin ephemeral yang disediakan GitHub dan sudah memasang Docker — dan setiap job tersusun dari step, yaitu langkah berurutan berupa skrip shell atau action siap pakai. Kerangka terkecil sebuah workflow terlihat seperti ini:
name: Halo Runner
on:
push:
branches: [main]
jobs:
sapa:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Halo dari runner"Mari kita telaah kerangka tersebut:
on:— eventpushke branchmainmenyalakan workflow; event lain — tag, pull request, jadwal — tinggal ditambahkan pada blok yang sama.jobs:— satu job bernamasapaberjalan pada runnerubuntu-latest; job inilah unit yang lulus atau gagal.steps:— dua langkah berurutan:actions/checkout@v4(action resmi untuk menyalin kode ke runner) danrun:untuk mengeksekusi perintah shell di atas runner tersebut.
Workflow Utuh: build-backend.yml #
Workflow pertama toko menuntaskan satu tugas: membangun image backend dari folder backend/ dan mendorongnya ke GHCR — registry image milik GitHub yang beralamat di ghcr.io. Budi menyimpan berkas berikut sebagai .github/workflows/build-backend.yml di akar repositori:
name: Build Backend
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}/backend
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Ambil kode ke runner
uses: actions/checkout@v4
- name: Siapkan Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login ke GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Susun nama image dengan huruf kecil
id: image
run: |
echo "name=${REGISTRY}/${IMAGE_NAME,,}" >> "$GITHUB_OUTPUT"
- name: Build dan push image backend
uses: docker/build-push-action@v6
with:
context: ./backend
platforms: linux/amd64
push: true
tags: |
${{ steps.image.outputs.name }}:${{ github.ref_name }}
${{ steps.image.outputs.name }}:sha-${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxBedah Sebelas Langkah #
Mari kita telaah workflow tersebut langkah demi langkah:
on: pushdenganbranches: [main]dantags: ['v*']— dua pemicu dari tabel pola pemicu chapter sebelumnya: push kemaindan setiap tag yang diawaliv, sehinggav1.6.0dan penerusnya sama-sama menyalakan workflow.env:— dua konstanta untuk seluruh workflow: alamat registryghcr.io, dan nama image yang disusun dari konteksgithub.repository(budi/toko) plus sufiks/backendkarena repositori toko menyimpan frontend dan backend sekaligus — hasil akhirnyaghcr.io/budi/toko/backend.permissions:— hak token dibatasi secara sadar:contents: readuntuk checkout danpackages: writeuntuk mendorong image ke GHCR; prinsip least privilege yang sama semangatnya dengan memangkas capabilities container pada Section Keamanan Docker.- Step checkout — action
actions/checkout@v4menyalin isi repositori ke runner; tanpa langkah ini, folderbackend/tidak pernah hadir di mesin. - Step setup-buildx —
docker/setup-buildx-action@v3menyiapkan builder Buildx dengan driverdocker-container, builder yang dibutuhkan untuk cacheghadan dorongan langsung ke registry. - Step login —
docker/login-action@v3mengisi kredensial registry pada runner, padanan otomatis daridocker loginmanual pada Chapter Login dan Authentication;github.actoradalah nama pengguna yang memicu run. - Step huruf kecil — nama image wajib huruf kecil, sedangkan
github.repositorydapat membawa huruf besar; ekspansi bash${IMAGE_NAME,,}menurunkan seluruh huruf, dan hasilnya diserahkan ke langkah berikutnya lewat berkas$GITHUB_OUTPUT. context: ./backend— Dockerfile dan build context diambil dari folderbackend/, bukan akar repositori;.dockerignoredi folder yang sama tetap menjaga context tetap ramping sebagaimana kebiasaan Chapter Build Context.platforms: linux/amd64— build satu platform yang sesuai arsitektur runner dan server produksi toko; membangun lintas arsitektur mengikuti pola dari Chapter Target Platform.tags:— dua tag dipasang pada setiap build:github.ref_name(bernilaimainsaat push branch,v1.6.0saat push tag) dansha-<commit>yang tidak pernah berganti isi — persis dua kolom tabel pola pemicu.cache-fromdancache-todengantype=gha— layer cache BuildKit disimpan pada cache GitHub Actions sehingga build berikutnya dapat meneruskan pekerjaan pendahulunya; pembahasan mendalamnya menyusul pada Chapter Build Cache di CI/CD.
Empat Sumber Nilai di Workflow #
Workflow di atas mengambil nilai dari empat sumber yang berbeda, dan membedakan keempatnya mencegah kebingungan yang paling sering dialami pemula Actions. Tabel berikut memetakan sumber nilainya dan wilayah berlakunya:
| Sumber | Dari mana nilainya | Scope dan sifatnya |
|---|---|---|
secrets.GITHUB_TOKEN | token otomatis yang dilahirkan GitHub pada setiap run | hidup hanya selama job; haknya mengikuti permissions:; tidak perlu disetel siapa pun |
secrets buatan sendiri, misal PAT | nilai yang kita simpan sendiri di pengaturan repositori | bertahan lintas run; dipakai bila butuh hak di luar repositori — misalnya login ke registry privat perusahaan |
konteks github.* | fakta peristiwa yang dibaca runner: repository, ref_name, sha, actor | baca-saja; sumber kebenaran tunggal tentang pemicu run |
env | konstanta yang kita definisikan sendiri: REGISTRY, IMAGE_NAME | berlaku untuk seluruh workflow; tempat pertama menaruh pengaturan yang mungkin berubah |
GITHUB_TOKEN layak mendapat perhatian khusus karena sifatnya yang unik: token ini lahir bersama setiap run, hanya mengenal repositori tempatnya dilahirkan, dan mati bersama job — tidak ada yang perlu menyimpan nilainya, dan tidak ada yang bisa dibocorkan dari berkas workflow. Personal access token (PAT) berperan sebaliknya: nilainya bertahan dan wilayahnya lebih luas, sehingga patut disimpan sebagai secret dan hanya dipakai ketika GITHUB_TOKEN memang tidak berwenang — contohnya login ke registry.example.com yang tidak mengenal token GitHub.
Tag Otomatis dengan metadata-action #
Bagian tags: pada workflow kita ditulis dengan tangan — dua baris yang kita pahami sepenuhnya. Ketika aturan penamaan tumbuh lebih rumit — tag semver yang melucuti awalan v, label 1.6 dan 1 yang mengikuti 1.6.0 otomatis — action resmi docker/metadata-action dapat mengambil alih pekerjaan itu: action ini membaca keadaan Git (branch, tag, event) lalu menghitung daftar tag image lengkap tanpa satu pun kondisi yang kita tulis sendiri. Pola pemicu sederhana toko belum membutuhkannya, tetapi ketika registry mulai penuh konvensi, action ini mengubah seluruh tabel pemicu chapter sebelumnya menjadi dua baris deklarasi. Kita bedah kemampuannya tuntas pada Chapter Version Tagging.
Pindah ke Registry Privat #
Workflow ini memilih ghcr.io karena image lahir di rumah yang sama dengan kodenya. Bagian penting yang harus dicatat: berpindah registry tidak mengubah struktur workflow — hanya dua titik yang bergeser. Titik pertama adalah nilai REGISTRY:
env:
REGISTRY: registry.example.comTitik kedua adalah langkah login: GITHUB_TOKEN tidak mengenal registry privat perusahaan, sehingga kredensialnya disimpan sebagai encrypted secrets dan dipanggil pada langkah login:
- name: Login ke registry privat
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}Seluruh mekanisme login dan penyimpanan kredensialnya mengikuti aturan yang sama dengan Chapter Login dan Authentication — hanya alamat dan pemilik kredensial yang berubah. Server produksi toko sendiri sudah terbiasa menarik dari registry.example.com sejak Section Deployment dan Production, sehingga dua registry dapat hidup berdampingan: GHCR untuk kandidat hasil pipeline, registry privat untuk image yang benar-benar dikerahkan.
Menyaksikan Run Pertama #
Budi menyerahkan workflow itu ke repositori dengan satu commit, dan push ke main itu sendiri menjadi pemicu run pertama — workflow baru yang membaca event push langsung menyalakan dirinya sendiri. Di tab Actions, job build berjalan hijau dari awal sampai akhir; ekor log pada langkah build-push terbaca sebagai berikut:
Output yang Dihasilkan (dipotong):
#1 [internal] load build definition from backend/Dockerfile
#2 [internal] load metadata for docker.io/library/node:22-alpine
#3 [auth] node:22-alpine pull token from registry-1.docker.io
#4 [internal] load .dockerignore
#5 [1/9] FROM docker.io/library/node:22-alpine
...
#14 exporting to image
#14 exporting layers 1.4s done
#14 writing image sha256:e7a1c4f8b2d6903e5a7c1b3d5f7a9c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a done
#14 naming to ghcr.io/budi/toko/backend:main done
#14 naming to ghcr.io/budi/toko/backend:sha-3f9c2e8a1b7d5c4e6f8a0b1c2d3e4f5a6b7c8d9e done
#14 pushing layers 6.8s done
#14 pushing manifest for ghcr.io/budi/toko/backend:main done
#14 pushing manifest for ghcr.io/budi/toko/backend:sha-3f9c2e8a1b7d5c4e6f8a0b1c2d3e4f5a6b7c8d9e done
digest: sha256:e7a1c4f8b2d6903e5a7c1b3d5f7a9c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4aMari kita telaah ekor log tersebut:
- Awalan
#N— nomor langkah BuildKit dengan format yang sama persis seperti build di laptop Budi; tidak ada satu pun perilaku build yang berubah karena pindah mesin. writing image sha256:...— image lahir di runner; 64 heksadesimal itu adalah image ID lokal runner yang akan hangus bersama mesin ephemeral-nya.naming to— dua tag dipasang pada satu image yang sama:mainyang bergulir dansha-3f9c...yang permanen — dua nama untuk satu objek, persis pola pemicu.pushing manifest— image terkirim keghcr.io; layer yang sudah ada di registry tidak dikirim ulang, mekanismeLayer already existsyang kita saksikan pada Chapter Push dan Pull Image.- Baris
digest:terakhir — identitas image di registry; formatnya sama persis denganRepoDigestsyang kita baca di chapter pembuka, sehingga pipeline kini dapat mencatat digest ini sebagai bukti rilis pada run log.
Workflow pertama toko sudah hidup: setiap push ke main dan setiap tag v* melahirkan image bertanda digest di ghcr.io tanpa satu ketikan manual. Namun mari kita jujur pada bentuknya — satu job yang langsung membangun dan mendorong. Belum ada test yang menjaga pintu masuk, belum ada scan yang memeriksa isi, dan belum ada satu pun titik di mana manusia dimintai persetujuan. Ritual checklist produksi yang lengkap menuntut empat job yang berbaris: test sebelum build, build sebelum scan, dan scan sebelum deploy. Merangkai barisan itu — lengkap dengan database sidecar untuk job test — adalah pekerjaan chapter berikutnya, Build, Test, dan Publish.
Referensi #
- Docker dan GitHub Actions — panduan resmi pola build-push di GitHub Actions yang menjadi acuan workflow ini.
- GitHub Actions — event, job, step, dan runner dari sumber aslinya.
- Autentikasi token otomatis
— cara kerja
GITHUB_TOKENdan pembatasanpermissions:.