Menjalankan pipeline CI/CD pada runner Windows sering kali menimbulkan kegagalan build yang tidak terjadi di Linux atau macOS. Sebagian besar masalah ini bukan berasal dari kode aplikasi, melainkan perbedaan arsitektur sistem operasi. Tiga masalah paling umum yang sering menghentikan eksekusi workflow Windows adalah pembatasan MAX_PATH pada Win32 API, inkonsistensi line ending (CRLF vs LF), serta penguncian file (file locking) eksklusif yang memicu error EPERM atau EBUSY.

1. Mengatasi Batasan MAX_PATH (260 Karakter)

Windows API secara default memberlakukan batas panjang path sebesar 260 karakter (didefinisikan sebagai konstanta MAX_PATH). Struktur proyek modern dengan direktori bertingkat—seperti node_modules, cache Cargo, atau intermediate output C++—mudah melampaui limit ini dan memunculkan error seperti Filename too long atau PathTooLongException.

Aktivasi Long Paths di Level Sistem dan Git

Untuk mengaktifkan dukungan jalur panjang, Windows Registry dan Git perlu dikonfigurasi secara eksplisit di awal job runner:

# Aktifkan LongPaths di Registry Windows via PowerShell
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1

# Aktifkan pembacaan long path pada Git client
git config --system core.longpaths true

Solusi Compiler dan Tooling

Meskipun registry telah aktif, beberapa compiler atau runtime lawas tetap membaca path berbasis Win32 non-Unicode API. Gunakan pendekatan berikut jika terjadi kegagalan build compiler:

  • MSVC (cl.exe): Gunakan prefix \\?\ untuk path absolut pada script generator, atau pastikan executable compiler memiliki manifest XML yang menyertakan tag <longPathAware>true</longPathAware>.
  • Clang/LLVM: Clang mendukung path panjang secara native pada versi modern, tetapi argumen include path yang diteruskan via shell Windows (PowerShell/CMD) dapat terpotong jika total buffer command line melebihi 32.767 karakter. Solusinya, gunakan response files (contoh: clang++ @args.rsp).
  • Mount Virtual Drive (Subst): Jika executable pihak ketiga menolak path panjang, pendekkan direktori kerja runner menggunakan virtual drive mapping:
subst X: D:\a\my-repo\nested\very\deep\directory
cd X:\
# Jalankan perintah build di root drive X:
subst X: /D

2. Standardisasi Line Ending: CRLF vs LF

Secara default, Git for Windows sering mengubah LF menjadi CRLF saat checkout (jika core.autocrlf aktif). Perubahan ini dapat merusak verifikasi hash file (checksum verification), eksekusi bash script (/bin/sh^M: bad interpreter), serta parser compiler yang sensitif terhadap byte carriage return.

Solusi Tepat: Konfigurasi .gitattributes

Hindari mengandalkan konfigurasi lokal mesin individual. Tambahkan file .gitattributes di root repositori untuk memaksa LF secara seragam di seluruh runner:

# Force LF untuk semua file teks
* text=auto eol=lf

# Ekstensi spesifik yang wajib LF murni
*.sh text eol=lf
*.bash text eol=lf
*.py text eol=lf

# File biner tidak boleh dimodifikasi
*.png binary
*.jar binary
*.exe binary

Pada pipeline CI, matikan konversi otomatis Git sebelum tahap checkout dilakukan:

git config --global core.autocrlf false

3. Mengatasi File Locking (EPERM dan EBUSY)

Berbeda dengan kernel Linux yang mengizinkan penghapusan file yang sedang terbuka oleh proses lain (unlink on unclosed file descriptors), Windows kernel menerapkan mandatory file locking. Jika file sedang dibaca oleh Windows Defender, proses telemetry, build daemon (seperti Gradle atau MSBuild), upaya penimpaan atau pembersihan folder build/ atau dist/ akan gagal dengan status EPERM: operation not permitted.

Menonaktifkan Defender / Realtime Antivirus

Pada pipeline CI publik, runner dibuat sementara (ephemeral), sehingga scanning antivirus realtime sering kali hanya memperlambat proses dan mengunci file binary output. Nonaktifkan scanning pada direktori workspace runner:

# PowerShell: Exclude folder workspace dari scanning Defender
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE

Mendeteksi dan Membunuh Proses yang Mengunci File

Jika proses background memblokir penghapusan direktori, gunakan utility handle.exe dari Sysinternals untuk melacak proses tersebut, atau gunakan PowerShell untuk menghentikan daemon yang tersisa:

# Mencari process ID yang memegang handle ke direktori tertentu via Sysinternals handle64
handle64.exe -nobanner $env:GITHUB_WORKSPACE\target\debug

# Menghentikan paksa daemon MSBuild atau Node yang tertinggal
Get-Process -Name "msbuild", "node", "vctip" -ErrorAction SilentlyContinue | Stop-Process -Force

4. Contoh Workflow GitHub Actions yang Robust

Workflow berikut menggabungkan penanganan batasan path, standardisasi checkout tanpa konversi CRLF, eliminasi intervensi Defender, serta penanganan cleanup yang aman pada runner windows-latest.

name: Windows Robust CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  build-windows:
    runs-on: windows-latest
    defaults:
      run:
        shell: pwsh
    steps:
      - name: Configure System-Level Prerequisites
        run: |
          # 1. Enable Long Paths
          Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1
          git config --system core.longpaths true
          
          # 2. Disable CRLF translation
          git config --global core.autocrlf false
          
          # 3. Add workspace to Defender exclusion list to prevent file locking
          Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -ErrorAction SilentlyContinue

      - name: Checkout Code
        uses: actions/checkout@v4
        with:
          # Memastikan file diambil sesuai line-ending di tree repositori
          set-safe-directory: true

      - name: Build Application
        run: |
          # Contoh simulasi build
          Write-Host "Compiling artifacts in: $env:GITHUB_WORKSPACE"
          # Pasang flag compiler jika menggunakan build C++
          $env:CFLAGS = "/utf-8"
          $env:CXXFLAGS = "/utf-8"
          
          # Simpan dummy build file di path dalam
          $deepPath = New-Item -ItemType Directory -Path "target\build\intermediate\cache\x64" -Force
          New-Item -ItemType File -Path "$deepPath\output.bin" -Value "artifact data" -Force

      - name: Post-Build Cleanup / Unlock Verification
        if: always()
        run: |
          # Hentikan background daemon yang mungkin memegang file lock
          Get-Process -Name "msbuild", "link", "node" -ErrorAction SilentlyContinue | Stop-Process -Force
          
          # Cleanup aman
          if (Test-Path "target") {
            Remove-Item -Path "target" -Recurse -Force -ErrorAction SilentlyContinue
          }

Ringkasan Tindakan Preventif

Kegagalan build di Windows hampir selalu dapat diprediksi dengan standarisasi lingkungan berikut:

  • Gunakan .gitattributes untuk menegakkan LF dan matikan core.autocrlf.
  • Setel LongPathsEnabled dan core.longpaths true sebagai langkah pertama pipeline.
  • Kecualikan workspace build dari pemindaian antivirus dan pastikan background daemon dihentikan sebelum fase cleanup.