Files
XC_VM/docs/en/guides/dev-workflow.md
T
Claude 488c923972 fix(build): strip stale LB manifest paths and gate stale/routed entries
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
2026-09-25 01:14:19 +00:00

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 use must 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-dev package is committed under src/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 local src/ to remote /home/xc_vm/
  • context: "./tests/" — maps local tests/ to remote /home/xc_vm/tests/
  • uploadOnSave: true — every Ctrl+S pushes the file to VDS instantly
  • ignore — 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 the ignore list tight, or set it to false if 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

  1. Add a second SFTP entry with context: "./tests/" and remotePath: "/home/xc_vm/tests".
  2. Save files under tests/ locally.
  3. The extension will upload them separately from src/ into /home/xc_vm/tests.
  4. This is required because tests are stored outside src/ and will not be uploaded by the main entry.

Workflow

  1. Open project in VS Code
  2. Edit any file under src/
  3. If you add a test, edit the file under tests/
  4. Save — the matching SFTP entry uploads the file to VDS
  5. Run the relevant test on VDS
  6. Commit to git as usual
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