Matrix CI
.github/workflows/tests.yml menjalankan lima job pada setiap push ke main dan setiap pull request yang menargetkannya. Matrix diperlukan karena package ini mendukung dua major Laravel, tiga minor PHP, dan dua cara resolution dependency. Selain itu, separuh lain package ini adalah Vue dan TypeScript yang tidak dapat divalidasi oleh job PHP. Halaman ini menjelaskan apa yang dibuktikan setiap job dan mengapa kombinasi tersebut dipilih — berguna ketika sebuah job gagal, sekaligus dapat dijadikan pola untuk package Anda sendiri.
Contoh minimal yang berfungsi
Semua yang dijalankan CI dapat dijalankan secara lokal, masing-masing melalui satu command:
composer ci # pint --test, then phpstan, then pest
npm run ci # prettier --check, then eslint, then vue-tsc, then vite build2
Dua command tersebut mencakup seluruh pemeriksaan selain matrix. Jika sebuah perubahan lulus keduanya secara lokal tetapi gagal di CI, penyebabnya seharusnya spesifik terhadap versi — dan memang itulah fungsi matrix.
Job yang dijalankan
| Job | Menjalankan | Matrix | Blocking |
|---|---|---|---|
test | vendor/bin/pest | PHP × Laravel × stability, dikurangi satu exclusion — 10 job | ya |
static-analysis | vendor/bin/phpstan analyse | dua ujung rentang versi yang didukung | ya |
code-style | vendor/bin/pint --test | satu job, PHP 8.4 | ya |
frontend | format:check, lint, typecheck, build | Node 20, 22, 24 | ya |
frontend-latest | typecheck, build terhadap versi teratas setiap range | satu job, Node 22 | tidak — continue-on-error |
fail-fast: false digunakan pada setiap matrix sehingga satu kombinasi yang gagal tidak membatalkan kombinasi lainnya. Ketika tiga dari sepuluh job gagal, mengetahui tiga kombinasi mana yang gagal sering kali sudah menjadi sebagian besar proses diagnosis.
Matrix test
strategy:
fail-fast: false
matrix:
php: ['8.2', '8.3', '8.4']
laravel: ['12.*', '13.*']
stability: [prefer-lowest, prefer-stable]
include:
- laravel: '12.*'
testbench: '10.*'
- laravel: '13.*'
testbench: '11.*'
exclude:
- php: '8.2'
laravel: '13.*'2
3
4
5
6
7
8
9
10
11
12
13
14
Terdapat tiga axis dan satu exclusion:
- PHP 8.2, 8.3, 8.4 — batas bawahnya adalah
"php": "^8.2"dicomposer.json. - Laravel 12 dan 13 —
"laravel/framework": "^12.0|^13.0", masing-masing dipasangkan dengan major Testbench yang dapat melakukan boot terhadap versi tersebut: Testbench 10 untuk Laravel 12 dan Testbench 11 untuk Laravel 13. prefer-lowestdanprefer-stable— dua ujung dari setiap dependency range.prefer-lowestadalah kombinasi yang menangkap penggunaan method yang baru ditambahkan di minor release tetapi tidak pernah Anda nyatakan sebagai minimum dependency.- Exclusion: Laravel 13 membutuhkan PHP 8.3. Kombinasi PHP 8.2 + Laravel 13 memang tidak tersedia; job yang tidak mungkin melakukan resolve hanya menghasilkan noise, bukan coverage. Dukungan PHP 8.2 dibuktikan melalui Laravel 12.
Instalasi dilakukan dengan require --no-update untuk setiap pin, lalu satu kali composer update sesuai stability yang dipilih:
- name: Install dependencies
run: |
composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
composer require "orchestra/testbench:${{ matrix.testbench }}" --dev --no-interaction --no-update
composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress
- name: Run tests
run: vendor/bin/pest2
3
4
5
6
7
8
Tidak ada composer install di sisi PHP. composer.lock yang di-commit adalah kemudahan untuk development, sedangkan CI melakukan resolve ulang terhadap range yang dideklarasikan pada setiap run. Sebuah library harus bekerja terhadap rentang dependency yang dijanjikannya, bukan hanya satu hasil resolution tertentu. Extension yang dibutuhkan suite adalah mbstring, pdo_sqlite, dan zip — sqlite karena test harness menggunakan :memory:, zip karena writer xlsx benar-benar dijalankan.
Static analysis, dua kali
strategy:
matrix:
include:
- laravel: '12.*'
testbench: '10.*'
php: '8.2'
- laravel: '13.*'
testbench: '11.*'
php: '8.4'2
3
4
5
6
7
8
9
Static analysis dijalankan dua kali karena kedua ujung rentang dapat memiliki API berbeda. toPasswordRulesString() ada pada Laravel 13 tetapi tidak pada 12. Menganalisis hanya versi tertinggi akan melewatkan pemanggilan yang merusak versi terendah, sedangkan menganalisis hanya versi terendah dapat melaporkan guard yang tidak dibutuhkan pada versi tertinggi.
phpstan.neon:
includes:
- vendor/larastan/larastan/extension.neon
parameters:
level: 4
paths:
- src
- database
tmpDir: build/phpstan2
3
4
5
6
7
8
9
Level 4 dipilih alih-alih level yang lebih tinggi karena satu alasan yang jelas: level 5 menambahkan pemeriksaan "view-string", sedangkan analyzer tidak dapat me-resolve namespaced package view (panda-panel::*) karena service provider tidak di-boot selama analysis. Path yang dianalisis hanya src dan database; tests/ serta examples/ tidak termasuk.
Secara lokal jalankan composer analyse, yang setara dengan vendor/bin/phpstan analyse --memory-limit=1G.
Code style
Satu job menjalankan vendor/bin/pint --test. Isi pint.json:
{
"preset": "laravel",
"rules": {
"declare_strict_types": true,
"ordered_imports": { "sort_algorithm": "alpha" },
"no_unused_imports": true
},
"exclude": ["integration", "vendor"]
}2
3
4
5
6
7
8
9
declare_strict_types dijadikan rule, bukan sekadar convention, sehingga setiap file repository harus memilikinya. Secara lokal gunakan composer format untuk memperbaiki dan composer format-check untuk memeriksa.
Frontend
Empat pemeriksaan dijalankan dalam urutan yang memberi error paling berguna terlebih dahulu — formatting, lint, type, lalu real build. Type error biasanya memberikan pesan yang lebih jelas daripada versi error yang sama dari bundler.
strategy:
fail-fast: false
matrix:
node: ['20', '22', '24']
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm run build2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| Script | Command |
|---|---|
format:check | prettier --check resources/js frontend resources/css |
lint | eslint resources/js frontend --max-warnings=0 |
typecheck | vue-tsc --noEmit -p tsconfig.json |
build | vite build |
ci | keempatnya, dalam urutan tersebut |
Frontend menggunakan npm ci, bukan npm install, terhadap lockfile yang di-commit. Ini kebalikan dari sisi PHP dan memang disengaja. Dependency range di package.json menentukan apa yang akan dipasang aplikasi, sedangkan toolchain milik repository ini sendiri harus reproducible terlepas dari bagaimana range tersebut kebetulan ter-resolve hari ini. engines mendeklarasikan node >= 20.19, sehingga matrix dimulai dari batas tersebut.
Tidak ada toolchain frontend yang ikut dikirim ke pengguna package. package.json, konfigurasi Vite, tsconfig, dan konfigurasi lint semuanya di-export-ignore, sehingga composer require tidak membawa file tersebut dan aplikasi tidak pernah melihat lockfile development package ini.
Frontend dengan dependency terbaru
frontend-latest:
name: frontend (latest dependencies)
continue-on-error: true
steps:
- run: npm install --no-package-lock
- run: npm run typecheck
- run: npm run build2
3
4
5
6
7
8
Job ini memberikan pendapat kedua terhadap dependency range dan menangkap kegagalan yang benar-benar dapat dialami aplikasi: misalnya package.json menyatakan ^4.1.0 sedangkan lockfile masih berada di 4.3.3, sehingga npm ci tidak pernah mencoba versi terbaru di dalam range. npm install --no-package-lock melakukannya.
Job ini diizinkan gagal. Minor release upstream yang merusak compatibility adalah informasi penting, tetapi bukan alasan otomatis untuk memblokir pull request yang tidak terkait. Job merah yang non-blocking tetap harus diperhatikan.
Membaca kegagalan
| Gejala | Tempat yang perlu diperiksa |
|---|---|
satu job prefer-lowest merah, prefer-stable hijau | ada pemanggilan API yang baru tersedia di minor version tetapi minimum constraint tidak mensyaratkannya |
| semua job Laravel 13 merah | kemungkinan ada perubahan API antar-major; bandingkan dengan job static-analysis pada ujung rentang lainnya |
static-analysis merah hanya pada satu Laravel | version guard hilang atau arah guard terbalik |
frontend merah hanya pada Node 24 | dependency menggunakan sesuatu yang dihapus pada Node tersebut; build step biasanya menyebutkannya |
frontend-latest merah, frontend hijau | ada upstream release baru; reproduksi dengan npm install --no-package-lock |
code-style merah | jalankan composer format lalu commit perubahan |
Menjalankan matrix secara lokal
Job test dapat direproduksi melalui dua command. Lakukan di branch dan pulihkan file setelahnya karena composer require mengubah composer.json:
composer require "laravel/framework:12.*" --no-interaction --no-update
composer require "orchestra/testbench:10.*" --dev --no-interaction --no-update
composer update --prefer-lowest --prefer-dist --no-interaction
vendor/bin/pest
git checkout composer.json && composer update2
3
4
5
6
7
Untuk frontend:
npm ci && npm run ci # what the blocking job runs
npm install --no-package-lock && npm run build # what the non-blocking one runs2
Untuk CI aplikasi
Aplikasi yang menguji panel membutuhkan matrix jauh lebih sederhana karena biasanya hanya menggunakan satu versi Laravel yang dikunci di lockfile. Bagian yang paling berguna untuk disalin adalah:
fail-fast: falsepada matrix apa pun.- Job frontend. Component Vue panel berada di
resources/jsaplikasi setelah publish, dannpm run buildadalah satu-satunya pemeriksaan yang membuktikan component tersebut masih dapat dikompilasi terhadap starter kit aplikasi. Seam ini paling sering rusak saat upgrade — lihat Frontend contract test. php artisan panel:assetsdi pipeline, yang melaporkan published file mana yang tertinggal dan mana yang sudah diedit aplikasi.- Extension sqlite dan zip, jika test mencakup export.
Hal yang perlu diperhatikan
composer requiredi CI mengubahcomposer.json. Flag--no-updatehanya menunda proses resolution sampai satu kalicomposer update; reproduksi lokal tetap meninggalkan file berubah. Pulihkan setelah selesai.prefer-lowestbiasanya menjadi yang pertama gagal ketika dependency baru ditambahkan. Range dengan batas bawah yang tidak pernah benar-benar diuji dapat ter-resolve ke versi yang terlalu tua.- Job PHP tidak menggunakan cache dependency. Resolve ulang terhadap range adalah tujuan utama matrix; cache yang hanya dikunci pada
composer.lockakan mengalahkan tujuan tersebut. npm cigagal jika lockfile tidak sesuai denganpackage.json, bukan melakukan resolve baru. Ini perilaku yang diinginkan: perubahan range tanpa regenerasi lockfile berarti perubahan yang belum benar-benar dijalankan.continue-on-errortetap menampilkan merah. Kegagalanfrontend-latesttidak memblokir merge, tetapi tetap perlu dibaca.- Coverage tidak ada di workflow.
composer test-coveragetersedia secara lokal; step setup-php menggunakancoverage: nonepada seluruh job.
Lihat juga
- Setup pengujian — harness yang dijalankan job
test - Frontend contract test
- Matrix kompatibilitas dan requirements
- Negative security test
panel:assets