Dalam proses pembangunan sistem yang mengikuti kitaran SDLC, seperti KRISA, pengujian bukan hanya dilakukan pada penghujung pembangunan. Pengujian perlu dilaksanakan secara berterusan bagi memastikan sistem yang dibangunkan kekal serasi dengan perubahan pada persekitaran teknikalnya.
Salah satu perkara yang boleh diuji ialah keserasian antara interpreter atau runtime yang digunakan dengan software framework yang dibangunkan. Dalam konteks aplikasi Laravel, contohnya, kita boleh menguji aplikasi yang sama menggunakan versi PHP yang lebih baharu untuk melihat sama ada terdapat perubahan pada dependency, extension, API atau tingkah laku runtime yang memberi kesan kepada sistem.
Pendekatan ini membolehkan kita mengesan isu keserasian lebih awal dalam kitaran pembangunan, bukannya hanya selepas sesuatu versi PHP mula digunakan di production.
Dalam nota ini, Incus digunakan untuk menyediakan environment PHP yang berbeza supaya aplikasi Laravel yang sama boleh diuji secara berulang terhadap beberapa versi PHP.
WSL
└── Incus
├── laravel13-php85
│ └── PHP 8.5
│
└── laravel13-php86
└── PHP 8.6
Tujuannya bukan untuk menjalankan dua project yang berbeza, tetapi untuk menguji codebase dan dependency yang sama pada runtime PHP yang berbeza.
Prinsip penting: gunakan composer.lock yang sama
Untuk compatibility testing, kita mahu kedua-dua environment menggunakan dependency yang sama.
Jadi jangan terus buat:
composer update
dalam setiap container.
Sebaliknya gunakan:
composer install
composer install akan menggunakan composer.lock yang sedia ada.
Contohnya:
composer.lock
│
┌───────┴───────┐
▼ ▼
PHP 8.5 PHP 8.6
│ │
test #1 test #2
Dengan cara ini, perbezaan yang kita lihat lebih tertumpu kepada PHP runtime, bukan kerana Composer memilih versi dependency yang berbeza.
Flow 1 — Bind Mount
Ini ialah flow yang paling mudah untuk development dan testing harian.
Source code kekal di WSL:
/home/hardy/projects/laravel13
Kemudian directory tersebut di-mount ke kedua-dua container.
Contoh:
incus config device add laravel13-php85 app disk \
source=/home/hardy/projects/laravel13 \
path=/var/www/html
Dan:
incus config device add laravel13-php86 app disk \
source=/home/hardy/projects/laravel13 \
path=/var/www/html
Sekarang kedua-dua container melihat source code yang sama.
WSL
└── projects/
└── laravel13/
├── app/
├── config/
├── routes/
├── composer.json
└── composer.lock
│
├──────────────► PHP 8.5 container
│
└──────────────► PHP 8.6 container
Install dependency
Masuk ke container PHP 8.5:
incus exec laravel13-php85 -- bash
Kemudian:
cd /var/www/html
php -v
composer install
composer check-platform-reqs
Run test:
php artisan test
Kemudian ulangi pada PHP 8.6:
incus exec laravel13-php86 -- bash
cd /var/www/html
php -v
composer install
composer check-platform-reqs
php artisan test
Perkara yang perlu diperhatikan dengan bind mount
Bind mount sangat convenient, tetapi filesystem host dan container menjadi agak coupled.
Antara masalah yang mungkin muncul ialah UID/GID dan permission.
Dalam sesetengah setup Incus, kita mungkin menggunakan:
shift=true
pada disk device.
Contohnya:
incus config device set laravel13-php85 app shift=true
Tetapi ini bukan sesuatu yang perlu dijadikan default untuk flow kita.
shift=true boleh mempengaruhi bagaimana ownership file dipersembahkan antara host dan container. Untuk development workflow, kita lebih baik kekalkan setup mount yang simple dan hanya tackle UID/GID apabila memang diperlukan.
Jika selesai testing dan sebelum kembali kepada workflow biasa, kita boleh set:
incus config device set laravel13-php85 app shift=false
incus config device set laravel13-php86 app shift=false
Flow 2 — Rsync atau Git
Untuk environment yang lebih clean, kita boleh elakkan bind mount.
Sebaliknya, source code disalin ke dalam filesystem container.
Contohnya menggunakan rsync:
WSL source
│
│ rsync
▼
Incus container
│
└── /var/www/html
Contoh:
rsync -a --delete \
--exclude vendor \
--exclude node_modules \
./ /path/to/container/
Atau kita boleh gunakan Git:
git clone <repository> /var/www/html
Kemudian:
cd /var/www/html
composer install
Penting: composer.lock masih digunakan
Rsync atau Git tidak bermaksud Composer akan ignore composer.lock.
Selagi file ini ada:
composer.json
composer.lock
dan kita menjalankan:
composer install
Composer akan menggunakan composer.lock.
Apa yang fresh ialah directory:
vendor/
Contohnya:
Source repository
│
├── composer.json
├── composer.lock
├── app/
├── routes/
└── ...
│
│ rsync / git
▼
Container
│
├── composer.json
├── composer.lock
├── app/
├── routes/
└── ...
│
│ composer install
▼
vendor/
Jadi kita masih mendapat dependency versions yang sama.
Kenapa Flow 2 lebih clean?
Dengan Git atau rsync, container mempunyai filesystem sendiri.
Contohnya:
laravel13-php85
└── /var/www/html
├── app
├── config
├── composer.json
├── composer.lock
└── vendor
laravel13-php86
└── /var/www/html
├── app
├── config
├── composer.json
├── composer.lock
└── vendor
Setiap environment mempunyai vendor/ sendiri.
Ini lebih dekat dengan situasi:
- CI/CD
- staging
- deployment
- production build
- compatibility testing yang isolated
Cuma workflow menjadi sedikit lebih lambat kerana setiap perubahan source perlu di-sync atau checkout semula.
Mana satu digunakan?
Untuk kerja harian, saya akan gunakan Flow 1 — bind mount.
Sebabnya mudah:
Edit code
↓
WSL
↓
Container nampak perubahan terus
↓
Run test
Ia sangat sesuai untuk exploratory testing.
Manakala Flow 2 lebih sesuai apabila kita mahu memastikan environment betul-betul isolated:
Git/Rsync
↓
Container
↓
composer install
↓
Test
Checklist compatibility test
Untuk setiap versi PHP:
php -v
Kemudian:
composer install
Semak platform requirements:
composer check-platform-reqs
Run Laravel test:
php artisan test
Jika mahu periksa dependency yang menghalang PHP tertentu:
composer why-not php 8.6
Untuk melihat apa yang akan berubah tanpa benar-benar update:
composer update --dry-run
Tetapi command ini digunakan untuk mengkaji dependency resolution, bukan baseline compatibility test.
Contoh workflow
Katakan kita sedang berada pada PHP 8.5.
incus exec laravel13-php85 -- bash
cd /var/www/html
php -v
composer install
composer check-platform-reqs
php artisan test
Kemudian test PHP 8.6:
incus exec laravel13-php86 -- bash
cd /var/www/html
php -v
composer install
composer check-platform-reqs
php artisan test
Hasilnya boleh dibandingkan:
Laravel 13
│
├── PHP 8.5
│ ├── composer install ✓
│ ├── platform reqs ✓
│ └── tests ✓
│
└── PHP 8.6
├── composer install ✓
├── platform reqs ✓
└── tests ✓
Jika PHP 8.6 gagal, kita boleh isolate puncanya:
PHP runtime?
│
├── Extension missing?
│
├── Platform requirement?
│
├── Composer dependency?
│
└── Application/test failure?
Contohnya, error:
Class "Normalizer" not found
boleh menunjukkan PHP intl extension belum tersedia.
Check:
php -m | grep -i intl
Dan:
php -r 'var_dump(class_exists("Normalizer"));'
Kesimpulan
Incus sesuai digunakan untuk membuat compatibility matrix yang kecil tanpa perlu Docker atau VM penuh.
Untuk development/testing cepat:
Bind mount
↓
PHP 8.5 / PHP 8.6
↓
composer install
↓
composer check-platform-reqs
↓
php artisan test
Untuk environment yang lebih isolated:
Git / rsync
↓
Container filesystem
↓
composer install
↓
php artisan test
Perkara paling penting ialah jangan ubah dependency set ketika membandingkan PHP versions.
Gunakan:
composer install
dengan composer.lock yang sama.
Dengan itu kita boleh membezakan dengan lebih jelas sama ada sesuatu masalah datang daripada PHP version, PHP extension, dependency, atau memang daripada application code itu sendiri.
Top comments (0)