GitHub Actions - Membangun CI Pipeline Aplikasi React.js
CI Pipeline ini nantinya akan menjalankan beberapa jobs workflow seperti PR Build Check, Automatic tag & release dengan semantic versioning, dan melakukan Build dan Push Container Image ke GitHub Container Registry (GHCR)
Pada blog kali ini, kita akan coba membangun CI Pipeline menggunakan GitHub Actions untuk aplikasi yang dibuat menggunakan teknologi React.js, dimana tujuan akhirnya adalah menjalankan automation mulai dari:
Build & Test Pull Request Check
Automatic tagging & release menggunakan semantic versioning berdasarkan rule conventional commit
Melakukan Build & Push Container Image ke GitHub Container Registry (GHCR)
Integrasi GitHub Repository dengan Discord menggunakan WebHook
Namun sebelum mencoba membuat kita akan bahas dulu pengertian-pengertian dari masing-masing yang akan kita bangun:
CI/CD Pipeline singkata dari Continuous Integration (CI) dan Continuous Delivery/Deployment (CD) adalah sebuah proses automation yang melancarkan pengembangan software dari perubahan kode ke deployment. Proses ini mengotomatiskan langkah-langkah build, test, dan deployment, memungkinkan developer untuk melakukan release perubahan kode secara lebih sering, andal, dan aman. Pipeline ini berjalan secara otomatis setiap kali kode diperbarui di central repository, dan terus berlanjut melalui berbagai tahap hingga kode tersebut aktif di lingkungan production. Proses akan berhenti dan memberi tahu tim jika ada tahap yang gagal ataupun berhasil.
Catatan: Untuk referensi bacaan terkait pembahasan Semantic Versioning, Conventional Commit, dan Git Flow (SDLC), saya sudah sempat posting di LinkedIn dan melakukan implementasi di GitLab Repository.
Semantic versioning atau disingkat SemVer adalah standar penomoran versi software yang didefinisikan pada semver.org. Tujuannya adalah untuk menghindari dependency hell dan memastikan kompabilitas antar-versi dapat dipahami secara konsisten oleh developer maupun pengguna.
Semantic Versioning ditulisa menggunakan format seperti ini:
md
MAJOR.MINOR.PATCH
Terdapat 3 komponen utama pada semantic versioning
Semver Component
MAJOR
Adalah komponen paling awal, dimana angka ini akan naik ketika terdapat perubahan yang tidak backward compatible atau biasa disebut dengan breaking change. Contohnya kenaikan versi dari v1.5.7 ke v2.0.0.
MINOR
Adalah komponen yang berada di pertengahan, dimana angka ini akan naik ketika terdapat penambahan fitur baru yang masih backward compatible. Contohnya kenaikan versi dari v1.5.7 ke v1.6.0.
PATCH
Adalah komponen paling akhir, dimana angka ini akan naik ketika terdapat perbaikan bug atau perubahan kecil yang masih backward compatible seperti fix, hotfix, dan sebagainya. Contohnya adalah kenaikan versi dari v1.5.7 ke v1.5.8.
Selain standarisasi penomoran version, semantic versioning juga erat kaitannya dengan standarisasi commit. Karena berdasarkan commit message inilah yang akan mendeteksi level mana yang harus di bump atau dinaikkan versi nya, apakah itu MAJOR, MINOR, atau PATCH.
Biasanya commit convention ini mengacu pada pengembangan di repository yang terkenal seperti angular, dimana terdapat beberapa jenis aturan commit message seperti:
BREAKING CHANGE
Aturan commit message ini akan menaikan atau bump version untuk level MAJOR, dimana biasanya letak dari commit message ini berada di bagian footer atau di paling bawah commit message. Contoh commit message nya adalah seperti
Catatan: Format penulisan atau aturan dari BREAKING CHANGE ini sifatnya agnostic, sehingga bisa menempel di commit message apa saja, asalkan terdapat keyword BREAKING CHANGE dan biasanya terletak di footer commit.
Conventional Commit MAJOR Changes
# Format penulisanfeat(<feature_name>): <commit_message>BREAKING CHANGE: <breaking_change_message>
Conventional Commit MAJOR Changes
# Contoh penggunaan: 1feat(api-version): change endpoint to api version v2BREAKING CHANGE: THIS FEATURE NOT BACKWARD COMPATIBILITY
Conventional Commit MAJOR Changes
# Contoh penggunaan: 2hotfix(api-response): fix wrong response format on user detail endpointBREAKING CHANGE: response structure has been changed, `fullName` field is removedand replaced with `firstName` and `lastName`. This change breaks existing clients.
feat()
Aturan commit message ini akan menaikkan atau bump version untuk level MINOR, dimana biasanya terdapat kata awalan feat(). Contoh commit message nya adalah seperti
Conventional Commit MINOR Changes
# Format penulisanfeat(<feature_name>): <commit_message>
Aturan commit message ini akan menaikkan atau bump version untuk level PATCH, dimana biasanya terdapat kata awalan fix() atau hotfix(). Contoh commit message nya adalah seperti
Conventional Commit PATCH Changes
# Format penulisan: 1fix(<bug_name>): <commit_message># Format penulisan: 2hotfix(<bug_name>): <commit_message>
Conventional Commit PATCH Changes
# Contoh penggunaan: 1fix(login-validation): resolve incorrect email format validation on login form# Contoh penggunaan: 2hotfix(api-timeout): fix API timeout error on fetch user profile endpoint
Semantic Versioning sering digunakan bersamaan dengan Git Flow, sebuah model branching pada Git yang membantu menjaga konsistensi versi software sesuai dengan tahapan dalam software development life cycle (SDLC).
Dalam Git Flow, terdapat beberapa penamaan branch yang umum digunakan dan biasanya mencerminkan environment pengembangannya, diantaranya adalah sebagai berikut:
dev atau development
Digunakan oleh developer atau QA sebelum tahap User Acceptance Testing (UAT). Pada tahap ini, release version umumnya diberi label beta, misalnya v1.5.7-beta.N (dimana N adalah jumlah iterasi merge request dari feature atau bug fix yang di merge ke branch ini).
staging
Digunakan ketika feature atau bug fix yang sudah loloss di tahap development dianggap stabil, dan siap untuk menjalani User Acceptance Testing (UAT). Pada tahap ini, release version biasanya diberi label rc atau singkatan dari Release Candidate, misalnya v1.5.7-rc.N (dimana N adalah jumlah iterasi merge request dari branch dev atau development yang di merge ke branch ini).
main
Digunakan ketika feature atau bug fix sudah lolos tahap UAT, dijamin stabilitas nya, dan akan di release ke production atau digunakan oleh user. Pada tahap ini, release version tidak memiliki label tambahan apapun sebagai tanda bahwa versi tabil nya, misalnya v1.5.7.
GitHub Container Registry atau disingkat GHCR adalah sebuah tempat penyimpanan untuk container image, dimana jika kalian terbiasa menggunakan docker dan sering menggunakan image dari Docker Hub, nah hal tersebut sebetulnya sama, namun bedanya kita simpan di package repository github. Sehingga nanti container image nya mempunyai penamaan dalam format seperti ini
Environments di GitHub Repository adalah sebuah nama tempat yang me-representasikan sebuah deployments. Environments ini biasanya digunakan untuk pemisahan value untuk secrets dan variables yang digunakan.
Sebagai contoh, apabila deployment memiliki 2 environment seperti staging dan main (production). Kedua deployment tersebut pastinya menggunakan nama key secrets dan variables yang sama, namun yang membedakannya adalah value dari secrets dan variables tersebut.
Sebagai contoh berikut adalah secrets dan variables antara staging dan main:
Secrets di GitHub repository adalah sebuah key value variable yang digunakan untuk menyimpan informasi data sensitive, contohnya adalah seperti informasi seputar token, credentials database, service account, jwt secret, iam, dan sebagainya. Secrets ini nantinya akan di sensor apabila value nya muncul di log pada saat ci/cd pipeline di github actions berjalan. Misalkan terdapat secrets dengan key dan value seperti ini
Berbeda dengan secrets, variables adalah sebuah key value variable yang digunakan untuk menyimpan informasi data yang tidak sensitive, contohnya adalah seperti informasi seputar base url suatu aplikasi, mode environment deployment aplikasi, dan sebagainya. Value dari variables ini nantinya tidak akan di sensor apabila value nya muncul di log pada saat ci/cd pipeline di github actions berjalan. Contoh dari penggunaan key value variables ini adalah seperti berikut ini
bash
NEXT_PUBLIC_APP_NAME=My Super AppNEXT_PUBLIC_APP_ENV=productionNEXT_PUBLIC_API_VERSION=v1NEXT_PUBLIC_ANALYTICS_ENABLED=false
Catatan: Variables ini biasa digunakan untuk menyimpan variables-variables yang sifat nya Client Side atau Build time.
Value dari variables ini bisa kita lihat kembali apabila sudah berhasil dibuat.
Setelah mengetahui penjelasan dari masing-masing pengertian dari pendahuluan di atas, sekarang kita bisa langsung coba membangun CI Pipeline nya. Untuk langkah pertama kita bisa siapkan dulu beberapa kebutuhannya di bawah ini:
Langkah pertama kita perlu membuat github repository nya terlebih dahulu, untuk membuat nya kalian bisa pergi ke GitHub > kemudian click icon + > dan pilih New repository, setelah itu isikan nama repository nya react-ci-pipeline dan biarkan semua nya default (tidak ada README default, .gitignore, license, dan sebagainya).
Catatan: Alternative lebih cepat kalian bisa menggunakan gh (GitHub CLI) dengan menjalankan command seperti berikut ini
Selanjutnya lakukan instalasi dependency package dan coba jalankan aplikasi nya menggunakan perintah berikut ini
bash
cd react-ci-pipelinenpm installnpm run dev
NPM Install
Setelah aplikasi berhasil dijalankan, kita bisa coba akses di web browser dengan alamat url http://localhost:5173, maka akan muncul tampilan nya seperti berikut ini
Setelah project berhasil react vite typescript nya berhasil dijalankan, langkah selanjutnya adalah kita lakukan push ke GitHub Repository yang sebelumnya sudah dibuat, untuk melakukannya kalian bisa jalankan perintah berikut ini
Catatan: Jika kalian menggunakan authentication SSH Key, kalian bisa ubah nama format nama remote origin nya seperti berikut ini
Selanjutnya kita perlu membuat branch staging, dimana branch ini akan dijadikan sebagai versi rc atau Release Candidate nantinya dan branch ini juga yang akan digunakan sebagai base code nantinya (dimana setiap feature, hotfix, dan sebagainya akan diambil dari branch staging), sedangkan branch main dijadikan sebagai versi stable. Untuk membuat nya jalankan perintah berikut ini
bash
# checkout branch staging berdasarkan branch maingit checkout -b staging# push ke remote origingit push -u origin staging
Langkah selanjutnya kita perlu melakukan generate Personal Access Token atau disingkat PAT, token ini nantinya akan digunakan untuk melakukan authentication ke GitHub Container Registry pada proses CI Pipeline. Untuk generate nya kalian bisa pergi ke menu Settings > Developer Settings > Personal access tokens > Tokens (classic) > Generate new token > Generate new token (classic)
GitHub Developer Settings
Generate PATE
Kemudian isikan konfigurasi berikut ini
Catatan: Sesuaikan Note, dan Expiration yang ingin digunakan
GitHub PAT Configuration
Note: GITHUB_ACTIONSExpiration: No expirationScopes: [repo, write:packages, delete:packages]
GitHub New PAT
Setelah itu click button Generate token dan simpan token nya baik-baik karena akan kita gunakan pada saat menambahkan secrets, karena tidak bisa dilihat kembali value nya.
Apabila proses Generate Personal Access Token berhasil dilakukan, langkah selanjutnya kita bisa bbuat environment dan secret untuk repository nya. Untuk melakukannya kalian bisa pergi ke repository react-ci-pipeline yang sebelumnya dibuat > Settings > Environments > New environment
GitHub Create Env
Selanjutnya buat 2 nama environments yaitu staging dan main, sehingga hasil nya akan menjadi seperti berikut ini
Catatan: Penamaan environments staging dan main adalah representasi dari nama branch yang digunakan. Kalian bebas menggunakan nama apapun, namun agar mempermudah memahami deployments berdasarkan nama, kita bisa gunakan nama branch yang digunakan saja.
Selanjutnya kita bisa tambahkan 2 secrets yaitu GH_USERNAME dan GH_TOKEN. Kedua secrets tersebut akan kita gunakan untuk authentication ke ghcr pada saat ci pipeline nya berjalan.
Untuk menambahkannya, kalian bisa click nama environments nya, misalkan untuk yang main terlebih dahulu. Setelah itu click button Add environment secret
GitHub Add Env Secret
Selanjutnya tambahkan 2 secret berikut ini
Catatan: ganti value GH_USERNAME dengan username github account yang digunakan dan GH_TOKEN dengan hasil generate Personal Access Token yang dibuat sebelumnya.
Setelah persiapan pembuatan environments dan secrets berhasil dilakukan, langkah selanjutnya adalah menyiapkan discord server, channel, dan integrasi github repository menggunakan webhook.
Dan click button Copy Webhook URL. Selanjutnya pergi ke repository react-ci-pipeline > Settings > Webhooks > Add webhook
GitHub Add Webhook
Kemudian isikan konfigurasi berikut ini
Catatan: Sesuaikan Payload URL dengan Webhook URL yang sebelumnya di copy dan tambahkan diakhiran url nya /github.
Repository Webhook Configuration
Payload URL: <your_discord_webhook_url>/githubContent type: application/jsonSecret: emptySSL verification: Enable SSL verificationWhich events would you like to trigger this webhook?: Send me everything.Active: true
Setelah semua persiapan di atas berhasil dilakukan, sekarang saatnya kita mengimplementasikan CI Pipeline untuk aplikasi React.js Vite TypeScript yang sudah kita push ke GitHub Repository sebelumnya.
Langkah pertama untuk mengimplementasikannya adalah kita perlu membuat Dockerfile, dimana file tersebut akan berisi perintah-perintah yang dijalankan pada saat proses build container image nya.
Untuk membuatnya kita perlu checkout dulu ke branch baru dengan nama feat/ci dari branch staging. Untuk melakukannya jalankan perintah berikut ini
bash
git checkout -b feat/ci
Setelah itu buat file baru dengan nama Dockerfile dan nginx.conf di root folder project nya dan isikan konfigurasi seperti ini
Dockerfile
# ---- 1. Build Stage ----FROM node:25.2-alpine AS builder# Set wokring directoryWORKDIR /app# Copy package.json & lock file (if exist)COPY package*.json ./# Install dependenciesRUN npm install# Copy all project filesCOPY . .# Build Vite projectRUN npm run build# ---- 2. Production Stage ----FROM nginx:1.29.3-alpine# Delete default nginx static pageRUN rm -rf /usr/share/nginx/html/*# Copy SPA fallback nginx configCOPY nginx.conf /etc/nginx/conf.d/default.conf# Copy from build previous stageCOPY --from=builder /app/dist /usr/share/nginx/html# Expose port 80 for nginxEXPOSE 80# Start nginxCMD ["nginx", "-g", "daemon off;"]
Selanjutnya adalah kita perlu membuat konfigurasi terkait semantic versioning, dimana konfigurasi ini akan berisi nama branch yang digunakan, suffix name release, tag format, commit convention (aturan commit), changelog, dan sebagainya.
Untuk membuatnya kalian buat file baru dengan nama .releaserc.yml di root folder project nya, kemudian isikan konfigurasi berikut ini
Catatan: Ganti <your_username> dengan username github account yang digunakan
.releaserc.yml
branches: - name: main - name: staging prerelease: rctagFormat: v${version}plugins: - - "@semantic-release/commit-analyzer" - preset: conventionalcommits releaseRules: # MAJOR changes - breaking: true release: major # MINOR changes - type: feat release: minor # PATCH changes - type: fix release: patch - type: hotfix release: patch - type: perf release: patch - type: refactor release: patch # NO release - type: docs release: false - type: style release: false - type: test release: false - type: ci release: false parserOpts: noteKeywords: - BREAKING CHANGE - "@semantic-release/release-notes-generator" - - "@semantic-release/changelog" - changelogFile: CHANGELOG.md - - "@semantic-release/git" - assets: - CHANGELOG.md - package.json message: "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" - - "@semantic-release/github" - successComment: | 🎉 This PR is included in version **${nextRelease.version}** 🎉 🔗 **View Release:** [${nextRelease.gitTag}](https://github.com/<your_username>/react-ci-pipeline/releases/tag/${nextRelease.gitTag}) 🤖 *"Kill all humans"* - Your [semantic-release](https://github.com/semantic-release/semantic-release) bot 🚀 failComment: | ❌ **Release Failed** Semantic-release failed to create release for this commit. **Error:** ${error.message} Please check the log CI for more information and fix the problem. labels: - released - - "@semantic-release/exec" - successCmd: 'echo "${nextRelease.version}" > version.txt'
Berikut adalah penjelasan dari masing-masing konfigurasi di atas
.releaserc.yml
branches: - name: main - name: staging prerelease: rc
Adalah konfigurasi terkait branch yang digunakan, dimana list branch tersebut yang akan men-trigger plugin @semantic-release/commit-analyzer, dan untuk branch staging akan di tandai sebagai prerelease dimana terdapat nama akhiran atau suffix rc sebagai tanda bahwa versi di branch tersebut adalah Release Candidate.
.releaserc.yml
tagFormat: v${version}
Adalah konfigurasi terkait formatting nama untuk tag yang digunakan, sebagai contoh hasil di tag nya adalah seperti ini v1.3.9.
Adalah konfigurasi terkait penulisan catatan commit ke release note dan file CHANGELOG.md, dengan custom commit message.
.releaserc.yml
- - "@semantic-release/github" - successComment: | 🎉 This PR is included in version **${nextRelease.version}** 🎉 🔗 **View Release:** [${nextRelease.gitTag}](https://github.com/<your_username>/react-ci-pipeline/releases/tag/${nextRelease.gitTag}) 🤖 *"Kill all humans"* - Your [semantic-release](https://github.com/semantic-release/semantic-release) bot 🚀 failComment: | ❌ **Release Failed** Semantic-release failed to create release for this commit. **Error:** ${error.message} Please check the log CI for more information and fix the problem. labels: - released
Adalah konfigurasi terkait penulisan comment dan labeling pada saat pipeline berhasil atau gagal dijalankan, nantinya terdapat auto comment dan labeling di pull request yang kita buat.
Adalah konfigurasi terkait stdout hasil dari bump version dari semantic version ke file version.txt yang dimana nanti file tersebut akan digunakan sebagai artifacts untuk kebutuhan re-tag container image.
Apabila pembuatan file .releaserc.yml selesai dilakukan, langkah selanjutnya kita add file dan commit
Selanjutnya kita masuk ke tahap membuat konfigurasi workflow yang akan dijalankan oleh github actions. Workflow yang akan kita buat pertama adalah terkait Build dan Test PR Check, dimana jobs-jobs di dalamnya akan ke trigger apabila ada Pull Request ke branch staging dan branch main.
Untuk membuatnya kalian bisa buat file baru dengan nama pr-build.yml di root folder .github/workflows project nya, kemudian isikan konfigurasi berikut ini
Berikut adalah penjelasan dari masing-masing konfigurasi di atas
.github/workflows/pr-build.yml
name: PR Build & Teston: pull_request: branches: - staging - main types: - opened - synchronize - reopened
Adalah nama workflow yang digunakan dan rule atau aturan dari trigger pipeline nya. Dimana rule trigger nya berdasarkan apabila ada open pull request ke branch staging ataupun main.
.github/workflows/pr-build.yml
jobs: build-and-test: name: Build and Test runs-on: ubuntu-latest environment: ${{ github.base_ref }}
Adalah konfigurasi terkait jobs dengan nama build-and-test, dimana environment nya menggunakan nama berdasarkan branch staging atau main, sehingga nantinya jobs ini bisa menggunakan value-value secrets dan variables dari environment yang digunakan.
Adalah konfigurasi terkait penggunaan nodejs versi 25, melakukan instalasi depedency library, melakukan proses pengecekan lint menggunakan eslint, mencoba melakukan build application untuk pengecekan tidak ada error, dan terakhir menjalankan semantic-release dengan mode dry-run atau simulasi.
Catatan: Optional, apabila aplikasi kalian nantinya ada unit testing kalian bisa un-comment steps untuk menjalankan unit testing nya. Selain dari step-step di atas itu apabila terdapat step tambahan kalian bisa tambahkan juga, jadi sesuaikan dengan workflow pipeline yang dibutuhkan.
Setelah berhasil membuat konfigurasi workflow untuk Build dan Test PR Check, langkah selanjutnya adalah membuat konfigurasi untuk Release. Dimana workflow Release ini akan ke trigger apabila Pull Request ke branch staging dan branch main itu di Merge / di-izinkan.
Untuk membuatnya kalian bisa buat file baru dengan nama release.yml di dalam folder .github/workflows, kemudian isikan konfigurasi seperti berikut ini
Adalah nama workflow yang digunakan dan rule atau aturan dari trigger pipeline nya. Dimana rule trigger nya berdasarkan apabila ada event/hook push ke branch staging ataupun main, kemudian ada definisi environment variable dengan key IMAGE_NAME dimana value nya adalah format nama ghcr yang akan digunakan oleh container image.
Jobs di atas adalah konfigurasi terkait jobs dengan nama build-docker-sha, environment nya menggunakan nama berdasarkan branch staging atau main, sehingga nantinya jobs ini bisa menggunakan value-value secrets dan variables dari environment yang digunakan.
Kemudian steps-steps nya adalah:
Mendapatkan 5 digit random string commit SHA
Authentication docker ke ghcr
Build docker image dengan tag berdasarkan 5 unique commit SHA
Kemudian melakukan push container image ke ghcr dan terakhir stdout 5 digit random string ke file sha.txt dan di upload menjadi artifact yang nanti akan digunakan oleh job re-tag.
Jobs di atas adalah konfigurasi terkait jobs dengan nama release-and-tagging-version, environment nya menggunakan nama berdasarkan branch staging atau main, sehingga nantinya jobs ini bisa menggunakan value-value secrets dan variables dari environment yang digunakan.
Kemudian steps-steps nya adalah:
Melakukan setup nodejs versi 25
Melakukan instalasi library-library semantic-release
Menjalankan semantic release yang menggunakan konfigurasi dari file .releaserc.yml, dan terakhir hasil dari stdout bump version ke file version.txt. di upload ke artifact yang nanti akan digunakan oleh jobs ret-tag.
Jobs di atas adalah konfigurasi terkait jobs dengan nama retag-and-push, environment nya menggunakan nama berdasarkan branch staging atau main, sehingga nantinya jobs ini bisa menggunakan value-value secrets dan variables dari environment yang digunakan.
Kemudian steps-steps nya adalah:
Melakukan download artifacts version.txt dan sha.txt
Setelah itu melakukan authentication docker login ke ghcr
Melakukan pull container image dari ghcr berdasarkan tag 5 digit random string SHA dari artifact sha.txt
Terakhir melakukan re-tag container image dengan semantic versioning dari artifact version.txt dan melakukan push ke ghcr berdasarkan tag semantic versioning.
Catatan: Apabila pipeline release.yml di atas berjalan di branch main, maka ada step terakhir tambahan yaitu melakukan tag latest dari container image nya.
Apabila pembuatan file workflow pr-build.yml dan release.yml selesai dilakukan, langkah selanjutnya kita add file, commit, dan push ke remote origin untuk branch feat/ci
Catatan: Jika proses konfigurasi webhook github repository ke discord benar, maka seharusnya ketika melakukan push branch feat/ci ke remote origin, akan mengirim events/actions ke channel discord nya seperti ini.
Untuk mengetest apakah automation CI Pipeline nya bekerja atau tidak, kita bisa coba melakukan Open Pull Request dari source branch feat/ci ke destination branch staging dan setelah itu coba melakukan Open Pull Request dari branch staging ke destination branch main.
Selanjutnya kita akan coba jalankan CI Pipeline nya, untuk mencoba nya kita bisa lakukan Open Pull Request di GitHub Repository dengan pergi ke repository react-ci-pipeline > Pull requests > New pull request
GitHub Open PR
Kemudian pilih
GitHub Open PR
Source / compare branch: feat/ciDestination / base branch: staging
Dan click button Create pull request
GitHub Create PR
Untuk title kalian bisa isikan feat(ci): add ci pipeline configuration dan click button Create pull request
GitHub Create PR 2
Dikarekan kita melakukan pull request ke branch staging, dan terdapat konfigurasi pr-build.yml, maka seharusnya sekarang terdapat pengecekan pipeline sebelum bisa melakukan merge request, sehingga kita bisa memastikan bahwa kode yang di pull request aman dari bug dan bisa di build atau tidak.
GitHub PR Build Check
Tunggu proses pengecekan pipeline tersebut, apabila status nya sudah All checks have passed kita bisa click button Merge pull request > Confirm merge.
GitHub All Checks Have Passed
Setelah merge request di izinkan, maka sekarang seharusnya workflow release.yml akan ke trigger, untuk melihat nya kalian bisa pergi ke menu Actions > pilih workflow run yang berjalan
GitHub All Checks Have Passed
Maka akan terlihat 3 jobs yang sudah di definisikan dari file release.yml sebelumnya seperti ini
GitHub Actions Summary
Untuk mengecek logs dari setiap jobs tersebut, kalian bisa click untuk setiap jobs nya, maka akan terlihat seperti ini
GitHub Actions Jobs Logs
Dikarenakan semua proses pipeline berhasil dijalankan, maka seharusnya sekarang terdapat beberapa update seperti ini
Pull requests auto comment & label
GitHub Auto Comment Label
Tag, Release, dan Changelog
GitHub Tag Release
GitHub Changelog
Discord webhook message
Discord New Message
GitHub Container Registry (GHCR)
Catatan: Untuk GitHub Container Registry (GHCR) kita perlu connect an packages nya ke repository react-ci-pipeline. Untuk melakukannya pergi ke https://github.com/<username>?tab=packages
GitHub Packages
Click button Connect Repository
GitHub Packages Connect Repository
Pilih repository react-ci-pipeline, selantjunya ubah settingan visibility ghcr nya menjadi public dengan cara pergi ke menu Package settings > Change visibility > Public.
GitHub GHCR Visibility
Maka sekarang tampilan di repository nya terdapat reference Packages seperti ini
Lakukan hal yang sama dimana Open Pull Request namun source branch nya dari staging dan destination nya ke branch main, dimana title Open PR nya feat(ci): add ci pipeline configuration.
GitHub Open PR Main
Maka seharusnya trigger pipeline pr-build.yml dan release.yml akan ke trigger kembali, namun nanti hasil tag versioning nya tanpa akhiran rc atau Release Candidate sebagai tanda versi stable.
Setelah container berhasil di build dan di push, kita bisa uji coba jalankan container tersebut di local menggunakan perintah berikut ini
Catatan: Ganti <username> dengan github account yang digunakan. Apabila ghcr yang digunakan adalah private, kita perlu melakukan authentication terlebih dahulu menggunakan perintah berikut ini:
bash
docker login ghcr.io -u "<username>"
Setelah itu isikan password menggunakan Personal Access Token (PAT) yang sebelumnya di generate.
bash
docker run --rm -p 5175:80 ghcr.io/<username>/react-ci-pipeline:1.0.0
Apabila container berhasil dijalankan, kalian bisa buka di url http://localhost:5175, maka akan muncul tampilan seperti gambar berikut ini
Karena sudah berhasil melakukan CI Pipeline, langkah selanjutnya kalian bisa terapkan CD Pipeline untuk full automation deployment nya. Sebagai contoh bisa menambahkan job khusus deploy seperti ini
release.yml
deploy: name: Deploy to Server needs: retag-and-push runs-on: ubuntu-latest permissions: actions: read contents: read environment: ${{ github.ref_name }} steps: - name: Checkout repository uses: actions/checkout@v4 - name: Download version.txt artifact uses: actions/download-artifact@v4 with: name: release-version - name: Set VERSION env run: echo "VERSION=$(cat version.txt)" >> $GITHUB_ENV - name: Setup SSH run: | mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts chmod 644 ~/.ssh/known_hosts - name: Set Docker image name id: vars run: echo "DOCKER_IMAGE_NAME=${{ env.IMAGE_NAME }}:$VERSION" >> $GITHUB_OUTPUT - name: Deploy to Server via SSH run: | echo "Deploying version $VERSION to ${{ github.ref_name }} environment..." ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF set -e if [[ "${{ vars.SERVICE_TYPE }}" == "container" ]]; then cd ${{ secrets.APPS_DIR }} cp docker-compose.template.yml docker-compose.yml sed -i "s|__FRONTEND_IMAGE__|${{ steps.vars.outputs.DOCKER_IMAGE_NAME }}|" docker-compose.yml sed -i "s|__FRONTEND_CONTAINER_NAME__|${{ vars.SERVICE_NAME }}|g" docker-compose.yml sed -i "s|__FRONTEND_CONTAINER_NAME__|${{ vars.SERVICE_NAME }}|" docker-compose.yml sed -i "s|__FRONTEND_PORT__|${{ vars.SERVICE_PORT }}:80'|" docker-compose.yml sed -i "s|__FRONTEND_ENVIRONMENT_FILE__|.env.${{ github.ref_name }}|" docker-compose.yml cp .env.${{ github.ref_name }} .env.tmp echo "# App Environment" > .env.${{ github.ref_name }} echo "NODE_ENV=$([[ "${{ github.ref_name }}" == "main" ]] && echo "production" || echo "development")" >> .env.${{ github.ref_name }} echo "BUILD_VERSION=$VERSION" >> .env.${{ github.ref_name }} echo "" >> .env.${{ github.ref_name }} echo "Pulling and deploying new image version: ${{ steps.vars.outputs.DOCKER_IMAGE_NAME }}" docker compose pull docker compose up -d PREVIOUS_VERSION=\$(grep BUILD_VERSION .env.tmp | cut -d '=' -f2) echo "Removing previous image version: ${{ vars.REGISTRY_SERVER }}/${{ vars.REGISTRY_IMAGE_NAME }}:\$PREVIOUS_VERSION" if docker image inspect ${{ vars.REGISTRY_SERVER }}/${{ vars.REGISTRY_IMAGE_NAME }}:\$PREVIOUS_VERSION > /dev/null 2>&1; then docker image rm ${{ vars.REGISTRY_SERVER }}/${{ vars.REGISTRY_IMAGE_NAME }}:\$PREVIOUS_VERSION else echo "Previous image version not found locally. Skipping removal." fi rm .env.tmp else echo "Service type unknown" exit 1 fi EOF
Apabila deployment nya menggunakan Kubernetes, kalian bisa menerapkan modern GitOps seperti menggunakan ArgoCD atau FluxCD, dimana nanti terdapat repository khusus yang menyimpan manifest-manifest kubernetes dan akan otomatis melakukan sync atau pull oleh ArgoCD ke repository apabila ada perubahan.
Sebagai contoh jika setelah proses CI pipeline bump / naikkan version dan push ke ghcr, setelah itu buat jobs tambahan untuk mengubah versi container image yang digunakan di repository manifest yang sync ke ArgoCD.
Pastikan penggunaan secrets dan variables sesuai dengan kebutuhannya, apabila terdapat Server Side (Runtime) environment, maka bisa simpan di secrets. Namun apabila terdapat Client Side (Build time) environment, maka simpan di variables.
Untuk Client Side (Build time) environment, pastikan melakukan passing argument di Dockerfile dan flag option di docker build nya, sebagai contoh seperti ini
Dockerfile
# Receive VITE_* environment variable when buildARG VITE_BASE_URLENV VITE_BASE_URL=$VITE_BASE_URL
Gunakan multi stage build ketika melakukan build container image, tujuannya adalah untuk memperkecil size dari container image yang di build. Sebetulnya Dockerfile di atas udah mengimplementasikannya, sehingga container image nya hanya berisi nginx dan static file Single Page Application (SPA) dari Client Side Rendering (CSR) React, sehingga tidak ada node_modules yang ikut tersimpan, dimana size nya hanya 20 MB.
Untuk security kalian bisa terapkan terkait secret rotation, sehingga value dari secret bisa berubah-rubah dalam rentang waktu tertentu, dan biasanya menggunakan tool khusus untuk centralized secret seperti Vault dari HashiCorp.
Dengan mengimplementasikan CI Pipeline yang comprehensive seperti yang telah kita bahas, kalian tidak hanya mengotomatiskan proses build dan deployment, tetapi juga menciptakan safety net yang kuat untuk memastikan setiap perubahan kode melalui tahap validasi yang ketat sebelum mencapai production. Kombinasi dari semantic versioning, conventional commit, dan automation GitHub Actions akan membuat tim development kalian lebih produktif dan confident dalam merilis fitur baru. Jangan ragu untuk mengadaptasi konfigurasi ini sesuai dengan kebutuhan spesifik project kalian, dan terus eksplorasi kemungkinan-kemungkinan improvement lainnya seperti CD Pipeline dan GitOps modern untuk menciptakan development workflow yang truly robust dan scalable.
Semoga tulisan ini bisa bermanfaat bagi kalian yang membaca, terima kasih.