SavefileArchive
USD/IDR ...
|
BTC ...
|
ETH ...
|
GOLD/gram ...
Terbaru
SavefileArchive — Tutorial coding, tips programming, dan dunia musik untuk developer & pecinta musik Indonesia
Build Gagal di CI/CD tapi Lokal Jalan: Cara Debug dan Fix

Build Gagal di CI/CD tapi Lokal Jalan: Cara Debug dan Fix

Build Gagal di CI/CD tapi Lokal Jalan: Cara Debug dan Fix

Ilustrasi CI/CD Build Gagal

"Works on my machine" adalah lelucon lama di dunia developer — tapi ketika build gagal di CI/CD sementara di lokal berjalan sempurna, itu bukan lelucon lagi. Ini adalah salah satu masalah paling membuang waktu karena kamu tidak bisa langsung debug di environment yang bermasalah.


1. Penyebab Paling Umum

Penyebab 1: Perbedaan Versi Node.js / Python / dll

# Lokal: Node.js 20.x
# CI/CD: Node.js 18.x (default image yang lama)

# Fix: pin versi di konfigurasi CI/CD

# GitHub Actions:
- uses: actions/setup-node@v4
  with:
    node-version: '20.x'  # Pin versi eksplisit, jangan pakai 'latest'

# Atau lebih baik: baca dari .nvmrc atau package.json
- uses: actions/setup-node@v4
  with:
    node-version-file: '.nvmrc'

# Buat file .nvmrc di root project:
echo "20.11.0" > .nvmrc

# package.json engines field:
{
  "engines": {
    "node": ">=20.0.0"
  }
}

Penyebab 2: Environment Variable Tidak Di-set di CI/CD

# Lokal: .env file ada
# CI/CD: .env tidak di-commit (benar), tapi env var juga tidak di-set

# GitHub Actions — set secrets:
# Settings → Secrets and variables → Actions → New repository secret

# Cara pakai di workflow:
jobs:
  build:
    steps:
      - name: Build
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          NODE_ENV: production
        run: npm run build

# Untuk nilai yang tidak sensitif, gunakan variables (bukan secrets):
# Settings → Secrets and variables → Variables
env:
  API_BASE_URL: ${{ vars.API_BASE_URL }}

Penyebab 3: Dependency Tidak Ter-install dengan Benar

# Masalah: npm install di CI menginstall versi berbeda dari lokal
# karena package-lock.json tidak di-commit atau tidak dipakai

# Fix: selalu gunakan npm ci (bukan npm install) di CI/CD
# npm ci: install PERSIS sesuai package-lock.json, lebih cepat, lebih deterministik

# GitHub Actions:
- name: Install dependencies
  run: npm ci  # Bukan npm install!

# Pastikan package-lock.json di-commit ke repository
# .gitignore JANGAN mengecualikan package-lock.json

# Untuk yarn:
- run: yarn install --frozen-lockfile

# Untuk pnpm:
- run: pnpm install --frozen-lockfile

Penyebab 4: File System Case Sensitivity

// macOS: case-insensitive (import './UserProfile' = import './userprofile')
// Linux (CI/CD): case-sensitive!

// ❌ Di macOS jalan, di Linux CI gagal:
import UserProfile from './userprofile';  // File aslinya: UserProfile.tsx
import { helper } from './Utils/Helper';  // File aslinya: utils/helper.ts

// ✅ Fix: selalu gunakan nama file yang PERSIS sama dengan nama file asli
import UserProfile from './UserProfile';
import { helper } from './utils/helper';

// Cara deteksi masalah ini sebelum push:
# Install dan jalankan di lokal:
npx find-imports-case-sensitive

Penyebab 5: Test yang Bergantung pada Waktu atau Urutan

// ❌ Test yang bergantung pada waktu — flaky di CI
test('token expired', () => {
  const token = generateToken({ exp: Date.now() + 1000 }); // 1 detik
  // Di lokal cepat, di CI mungkin sudah expired saat dijalankan
  expect(verifyToken(token)).toBeTruthy();
});

// ✅ Fix: mock waktu
jest.useFakeTimers();
test('token expired', () => {
  const now = new Date('2024-01-01');
  jest.setSystemTime(now);
  
  const token = generateToken({ exp: now.getTime() + 60000 });
  expect(verifyToken(token)).toBeTruthy();
  
  jest.setSystemTime(new Date(now.getTime() + 120000)); // Maju 2 menit
  expect(() => verifyToken(token)).toThrow('TokenExpiredError');
});

// ❌ Test yang bergantung pada urutan eksekusi
// Fix: setiap test harus independen, gunakan beforeEach untuk reset state
beforeEach(async () => {
  await db.truncate(); // Reset database sebelum setiap test
});

2. Cara Reproduce CI Environment di Lokal

# Cara terbaik: jalankan CI di lokal menggunakan act
# https://github.com/nektos/act

# Install act (macOS):
brew install act

# Jalankan workflow GitHub Actions di lokal
act push

# Jalankan job tertentu
act -j build

# Dengan secrets:
act -j build --secret-file .secrets

# Ini menjalankan workflow di Docker container yang sama dengan GitHub Actions
# Sehingga environment-nya identik
# Atau debug langsung di CI dengan SSH (GitHub Actions)
# Tambahkan step ini sementara untuk debug:
- name: Debug SSH
  uses: mxschmitt/action-tmate@v3
  if: failure()  # Hanya aktif jika step sebelumnya gagal
  # Ini akan memberikan link SSH ke runner CI
  # Kamu bisa masuk dan debug secara interaktif

3. Template GitHub Actions yang Robust

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    # Test dengan multiple versi Node.js
    strategy:
      matrix:
        node-version: [18.x, 20.x]
    
    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'  # Cache node_modules
      
      - name: Install dependencies
        run: npm ci  # Bukan npm install!
      
      - name: Type check
        run: npm run type-check
      
      - name: Lint
        run: npm run lint
      
      - name: Test
        run: npm test -- --coverage
        env:
          NODE_ENV: test
          DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
      
      - name: Build
        run: npm run build
        env:
          NODE_ENV: production

4. Checklist Sebelum Push ke CI

# Jalankan ini sebelum push untuk mendeteksi masalah lebih awal:

# 1. Install dengan cara yang sama seperti CI
rm -rf node_modules
npm ci

# 2. Jalankan semua check yang ada di CI
npm run type-check
npm run lint
npm test
npm run build

# 3. Cek case sensitivity import (jika di macOS)
# 4. Pastikan semua env var yang dibutuhkan ada di CI secrets
# 5. Pastikan .nvmrc atau engines field di package.json sudah benar

Kesimpulan

Build yang gagal di CI tapi jalan di lokal hampir selalu disebabkan oleh perbedaan environment: versi runtime, env var yang hilang, dependency yang tidak deterministik, atau case sensitivity. Solusi jangka panjang terbaik adalah membuat environment lokal semirip mungkin dengan CI — gunakan npm ci, pin versi Node.js di .nvmrc, dan sesekali jalankan CI secara lokal menggunakan act.