The LB strip lists still named files by their pre-PSR-4 www/ paths, so those entries matched nothing and the files kept shipping under Public/: Public/admin/api.php, Public/admin/proxy_api.php, Public/stream/auth.php and Public/stream/probe.php reached every LB. auth.php and probe.php are routed by the LB nginx but fatal there on the stripped Domain/User. LB_DIRS still listed the removed resources/ and www/ trees, and LB_DIRS_TO_REMOVE listed the non-existent Domain/Auth. The gate never noticed, because it only checked the SENSITIVE prefixes. Makefile: - Drop the dead entries (resources, www, Domain/Auth, GeoLite2-City.mmdb, the nine www/* files). - Strip the MAIN-only files that no LB route or LB code needs: the four endpoints above, PlayerV2, AdminAPIWrapper, ResellerAPIWrapper, ActiveCodeApiController, ResellerApiDispatcher, ResellerTableRenderer, Cli/migration_logic.php and DbMigrateCommand (on an LB it would run MAIN's schema migrations). The viewer-API controllers still ship because the LB nginx still routes them; they go later with the routes. - lb_delete_files_list now always writes the LB deleted_files.txt as the LB-scoped git deletions plus every stripped code file (Cli, Core, Domain, Infrastructure, Public, Streaming; never bin/config/content). An update only deletes listed files, so without this the files newly stripped here would stay on LBs that are already installed. The retired resources/ and www/ trees stay in the git-deletion scope (LB_RETIRED_DIRS), so their old entries are still applied. lb_configs/nginx.conf: stop routing /stream/auth, /stream/probe and /probe.php. They now get nginx's 404 instead of a PHP fatal. MAIN nginx is unchanged. verify-lb-archive.sh now also fails when: - an LB_* entry matches no tracked path (STALE); - a script the LB nginx executes is stripped (MISSING): every SCRIPT_FILENAME, and Public/<scope>/<handler>.php for each handler the /stream/ and /admin/ gateway locations accept. SENSITIVE covers the new strips, CacheHandlerCommand and CacheEngineCronJob. The manifest lookups now use here-strings, because under pipefail an early `grep -q` exit could SIGPIPE the writer and hide a leak. LbArchiveGateTest runs the real script with each failure mode injected through MAKEFLAGS. Behaviour: the LB archive loses 26 files that no LB route reaches (or that fatal there today), and the next LB update deletes them. It also deletes any lingering Domain/User or Domain/Device files. Fresh LBs never had those, so an older LB that still carries them loses rtmp and viewer-API paths that only worked because of them, just like a fresh LB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQHeypQpfAXTdCysmi7b6a
6.5 KiB
Development Workflow
How to set up the project locally, run the quality checks, and deploy code to a development server.
Local Setup
Prerequisites: PHP 8.1 (the codebase pins php: 8.1.33; newer majors are not supported) and Composer available locally.
The committed src/vendor/ is production-only, so the dev tools (PHPStan,
phpcs) are not in the tree. Install them once from the committed lock:
make dev-tools # = cd src && composer install
This adds the require-dev packages into src/vendor/. Never commit them —
the committed vendor must stay production-only (composer install --no-dev).
.gitignore keeps the dev packages out of git add, and a CI gate
(check-vendor-prod-only) fails the build if one is ever committed.
Quality Checks
Run these before pushing — CI runs the same set:
| Command | Checks |
|---|---|
make phpstan |
Static analysis against the committed baseline (fails only on NEW issues) |
make cs |
Code style — import/namespace hygiene (phpcs + Slevomat) |
make cs-fix |
Apply the style fixes in place |
make gates |
PSR-4 regression gates (below) |
php tests/phpunit.phar -c tests/phpunit.xml.dist |
Unit tests — see PHPUnit Setup |
make e2e |
Browser tests against a live test panel — see End-to-End Tests |
make rector |
Dry-run automated refactoring — see Automated Refactoring (Rector) |
make phpstan and make cs need the dev tools — run make dev-tools first.
The PHPStan baseline lives at build/phpstan-baseline.neon — it freezes all pre-existing
issues so only new ones fail CI. If you intentionally change the level or accept a batch of
findings, regenerate it with make phpstan-baseline and commit the result. Don't regenerate it
just to silence a real new error — fix the code.
make gates bundles three guards:
- check-procedural-use — procedural / view files import every migrated class they use (PHP imports are positional, so the
usemust precede the usage); - verify-lb-archive — the Load Balancer build excludes privileged code (admin/reseller controllers, user/device domain, install/root commands), every LB list entry still matches a tracked path, and every script the LB nginx routes to still ships — see Build System (MAIN vs LB) for the exclusion boundary;
- check-vendor-prod-only — no
require-devpackage is committed undersrc/vendor/.
End-to-End Tests
tests/e2e is a Playwright suite that drives the admin panel the way an
administrator does: it creates categories, bouquets, packages, lines, devices,
resellers, block-list entries and a live stream, edits them, starts and stops the
stream, and deletes everything again. It needs a test panel (never
production) and an admin account used only by the tests — every admin login
re-hashes the password and signs out that account's other sessions.
Set XC_E2E_BASE_URL (the admin URL including the access code), XC_E2E_USER
and XC_E2E_PASS, then run make e2e-install once and make e2e. The suite's
README.md (in tests/e2e/) lists what each spec covers, how to provision the
test account with tests/e2e/tools/create-admin.php, and what the tests change
on the panel host.
Deploying Code to VDS via SFTP
For daily development, we recommend the SFTP extension for VS Code — edit locally, auto-upload on save.
Setup
Create .vscode/sftp.json:
[
{
"name": "My Dev VDS",
"host": "YOUR_VDS_IP",
"protocol": "sftp",
"port": 22,
"username": "root",
"remotePath": "/home/xc_vm",
"useTempFile": false,
"uploadOnSave": true,
"openSsh": false,
"watcher": {
"files": "**/*",
"autoUpload": false,
"autoDelete": true
},
"ignore": [
".vscode",
".git",
".gitattributes",
".gitignore",
"update",
"*pycache/",
"*.gitkeep",
"bin/",
"config/",
"tmp/"
],
"context": "./src/",
"profiles": {}
},
{
"name": "My Dev VDS Tests",
"host": "YOUR_VDS_IP",
"protocol": "sftp",
"port": 22,
"username": "root",
"remotePath": "/home/xc_vm/tests",
"useTempFile": false,
"uploadOnSave": true,
"openSsh": false,
"watcher": {
"files": "**/*",
"autoUpload": false,
"autoDelete": true
},
"ignore": [
".vscode",
".git",
".gitattributes",
".gitignore",
"tmp/",
".cache/"
],
"context": "./tests/",
"profiles": {}
}
]
Key Settings
context: "./src/"— maps localsrc/to remote/home/xc_vm/context: "./tests/"— maps localtests/to remote/home/xc_vm/tests/uploadOnSave: true— every Ctrl+S pushes the file to VDS instantlyignore— protects server-specific files (bin/,config/,tmp/)
⚠️
watcher.autoDelete: true— deleting a file locally deletes it on the VDS too. Handy for keeping the tree in sync, but a mis-deleted local file (or a bad rename) will remove the remote copy. Keep theignorelist tight, or set it tofalseif you don't want the watcher to propagate deletions.
Security: Use SSH keys instead of password. The
.vscode/directory is in.gitignore, so credentials won't leak to git.
How to sync the tests folder
- Add a second SFTP entry with
context: "./tests/"andremotePath: "/home/xc_vm/tests". - Save files under
tests/locally. - The extension will upload them separately from
src/into/home/xc_vm/tests. - This is required because tests are stored outside
src/and will not be uploaded by the main entry.
Workflow
- Open project in VS Code
- Edit any file under
src/ - If you add a test, edit the file under
tests/ - Save — the matching SFTP entry uploads the file to VDS
- Run the relevant test on VDS
- Commit to git as usual
Related files
| File | Role |
|---|---|
.vscode/sftp.json |
Local → VDS sync config (gitignored) |
Makefile |
make dev-tools, make phpstan, make cs, make gates |
src/composer.json |
Dependencies + PSR-4 autoload |