Pengenalan: Menjawab kebutuhan linting yang terukur
Pipeline linting multi-stage di CI untuk monorepo TypeScript memastikan bahwa kode seluruh paket tervalidasi tanpa membuat CI berjalan terlalu lama. Dalam beberapa tahap, kita bisa melakukan pre-commit ringan di lokal, mengidentifikasi path lint yang berubah, serta menjalankan linting komprehensif di cloud dengan caching dan pencatatan hasil.
Pada artikel ini kita langsung menguraikan pendekatan konkret: bagaimana pipeline lint di GitHub Actions/GitLab CI bisa memisah tugas, menghemat waktu, dan tetap menjadi gating PR yang efektif.
Perbedaan pre-commit dan lint path spesifik
Pre-commit hook idealnya menjalankan quick lint terhadap file yang akan di-commit agar developer mendapatkan feedback instan. Misalnya, menjalankan ESLint hanya pada staged files dan memblokir commit jika ada error fatal. Namun, lint di CI harus menyertakan seluruh paket yang terpengaruh agar tidak ada konflik antar dependensi.
Untuk menghindari menjalankan linting di seluruh monorepo, gunakan pendekatan lint path spesifik: deteksi paket yang berubah dengan perintah seperti git diff --name-only origin/main...HEAD | cut -d/ -f1 | uniq dan jalankan lint hanya pada paket itu. Ini juga memungkinkan job lint CI cepat dan relevan.
Membangun multi-stage pipeline linting di CI
Langkah utama pada pipeline linting multi-stage adalah:
- Stage persiapan: checkout, caching node_modules, dan deteksi paket berubah.
- Stage lint cepat: lint path yang berubah untuk gating awal.
- Stage lint menyeluruh: lint semua paket yang penting (misalnya
library-core) secara paralel. - Stage agregasi hasil untuk laporan dan gating PR.
Contoh GitHub Actions
Snippet berikut menunjukkan multi-stage linting dengan caching dan paralelisasi.
name: Lint Monorepo TypeScript
on:
pull_request:
paths:
- 'packages/**'
- 'apps/**'
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
changed-packages: ${{ steps.detect.outputs.packages }}
steps:
- uses: actions/checkout@v4
- name: Restore node cache
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-cache-${{ hashFiles('**/package-lock.json') }}
- name: Detect changed packages
id: detect
run: |
echo "packages=$(git diff --name-only origin/main...HEAD | grep -o '^[^/]*' | sort -u | tr '\n' ' ')" >> $GITHUB_OUTPUT
lint-changed:
needs: prepare
runs-on: ubuntu-latest
if: steps.detect.outputs.packages != ''
strategy:
matrix:
package: ${{ fromJson('[' + needs.prepare.outputs.changed-packages + ']') }}
steps:
- uses: actions/checkout@v4
- name: Restore dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-cache-${{ matrix.package }}-${{ hashFiles(matrix.package + '/package-lock.json') }}
- name: Lint ${matrix.package}
run: npx eslint ${matrix.package}/src --max-warnings=0
lint-core:
needs: prepare
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore core cache
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-cache-core-${{ hashFiles('packages/library-core/package-lock.json') }}
- name: Full lint core
run: npx eslint packages/library-core/src --format json -o reports/core-lint.json
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: lint-report-core
path: reports/core-lint.json
Strategi ini membagi linting menjadi job kecil (per paket) sehingga berjalan paralel. Cache npm mengurangi waktu install, sementara job lint-changed diatur hanya jika ada paket yang berubah.
Stage lint di GitLab CI
Pada GitLab CI, gunakan stages dan rules untuk meniru behavior serupa.
stages:
- prepare
- lint
prepare:
stage: prepare
script:
- npm ci
- echo "changed=$(git diff --name-only $CI_MERGE_REQUEST_TARGET_BRANCH_NAME...$CI_COMMIT_SHA | cut -d/ -f1 | sort -u)" > changed.txt
artifacts:
files:
- changed.txt
lint_changed:
stage: lint
needs: [prepare]
rules:
- exists: changed.txt
script:
- packages=$(cat changed.txt)
- for pkg in $packages; do npx eslint "$pkg/src" --max-warnings=0; done
parallel: 4
cache:
paths:
- .npm
Pakai parallel untuk menjalankan lint lintas paket sekaligus; GitLab membagi script di atas menjadi 4 worker.
Teknik tambahan: caching, paralelisasi, skip folder tak berubah, agregasi laporan
Caching: Simpan direktori ~/.npm atau node_modules antara job lint. Pastikan key cache menyertakan digest package-lock.json agar cache valid saat dependensi berubah.
Paralelisasi: Jalankan lint per paket sebagai matrix job di GitHub atau gunakan parallel di GitLab. Batas grepping lint path dan sabar dengan concurrency level sesuai resources runner.
Skip folder tak berubah: Gunakan deteksi git diff atau tool graph dependency (misalnya nx affected:lint) untuk menjalankan lint hanya pada paket yang memodifikasi file. Ini mengurangi waktu CI saat PR cuma mengubah satu layanan.
Agregasi laporan: Output lint ke format JSON/Checkstyle, kemudian unggah sebagai artifact. Di GitHub Actions, job lint bisa menulis reports/lint.json dan job final menggabungkan artifact menjadi laporan yang bisa diunduh reviewer.
Strategi gating PR tanpa memperlambat developer
- Lint path spesifik di pre-merge: Jalankan lint hanya pada paket yang berubah agar blocking singkat yet meaningful.
- Lint menyeluruh di branch utama: Saat merge ke main, jalankan lint penuh untuk seluruh monorepo sebagai sanity check.
- Fail fast dengan threshold: Gunakan
eslint --max-warnings=0agar sekali ada error langsung jatuh. Tambahkan juga lint per-package di pre-commit untuk catch error lebih awal. - Gunakan gate report: Ekspor lint report, kemudian menggunakan
pull_requestreview untuk melihat detail error; PR hanya bisa merge setelah job lint sukses.
Tips troubleshooting umum
- Lint job timeout: Pastikan paralelisasi tidak melebihi kuota runner. Kurangi jumlah matrix jika lint per-package memakan memori besar.
- Error cache stale: Perbarui key cache dan tambahkan
restore-keyspartial. Jika lint menolak karena modul tidak ditemukan, bersihkan cache dan jalankannpm cimanual di runner. - Lint jalan tapi laporan kosong: Pastikan flag
--format jsondan path output valid, kemudian confirm job lint upload artifact dengan action yang sesuai. - Skip folder tapi lint tetap jalan: Periksa log job
lint-changeduntuk output paket; pastikan script deteksi tidak mengembalikan empty string dengan whitespace.
Dengan pipeline linting multi-stage yang tertata, monorepo TypeScript dapat mempertahankan kualitas tanpa membebani CI atau developer. Fokus pada lint path spesifik, cache efisien, paralelisasi, dan strategi gating PR untuk proses yang cepat sekaligus aman.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!