XC_VM_VERSION / DEV_MODE / DB_ACCESS_ENABLED / DB_ACCESS_PWD are edited on every
release (and by the release automation). Buried as array entries in appConfig()
they were awkward to find and to sed. Move them back to guarded define()s at the
top of ConstantsInitializer.php; appConfig() reads them back, and init() skips
the already-defined ones. The guard keeps a pre-definition (e.g. the PHPStan
stub) from fataling.
Update the release checklist accordingly: the sed commands target the familiar
`define('XC_VM_VERSION', '...')` form in ConstantsInitializer.php again (they were
pointing at the deleted AppConfig.php).
11 KiB
XC_VM Release Preparation Checklist
Step-by-step guide for preparing and publishing an XC_VM release.
1. Changelog
Reset the build output, then generate the commit log (work commits only):
make new # wipe + recreate dist/ ONCE, at the very start
PREV_TAG=$(git describe --tags --abbrev=0)
git log --pretty=format:"- %s (%h)" "$PREV_TAG"..main > dist/changes.md
⚠️ Run
make newhere, at the very start of the release — it wipes and recreatesdist/. Everything below writes intodist/(starting withchanges.md), somake newmust run before this and never again before building — a latermake newwould deletedist/changes.md.
Update changelog.json in the repository root — this file contains only the changes for the upcoming release:
{
"version": "X.Y.Z",
"changes": [
"Description of change 1",
"Description of change 2"
]
}
The panel fetches this file from the release tag automatically via GitHubReleases::getChangelog().
💬 Keep descriptions concise — focus on user-facing improvements and fixes.
2. Pre-Release Validation
Before publishing, verify the build works:
Quality checks (CI runs the same set on the tag — confirm it is green):
make dev-tools && make phpstan && make cs && make gates
php tests/phpunit.phar -c tests/phpunit.xml.dist
make dev-clean # remove the dev tools afterwards, restoring the prod-only vendor/
ℹ️ The Docker test install moved to step 6 — it requires a built
dist/XC_VM.zip.
Security scan: runs automatically on push/PR via .github/workflows/security-scan.yml (Semgrep) — no manual step.
Regenerate translated documentation
Documentation is written in English only (docs/en). The Russian tree
(docs/ru) is a generated, committed artifact refreshed locally before each
release — translation is intentionally not run in CI (it is slow); CI only
builds the committed tree. If docs/en changed since the last release:
make docs-translate # regenerate docs/ru from docs/en (free, no API key)
make docs-build # strict build — fails on any broken link/anchor
make docs-translatere-translates only the English files whose content changed (per-file cache), so this is fast on an incremental release.- Review and commit the regenerated
docs/ru— it is included in the single release commit (step 5). Never hand-editdocs/ru. - Documentation is published per release, not per push:
pages.ymlruns when the version tag is pushed (step 7) and publishes this release's docs as a versioned snapshot (X.Y.Z+ thelatestalias) to thegh-pagesbranch viamike. Edits merged tomainbetween releases go live at the next tagged release (which is also whendocs/ruis regenerated). The Material header's version selector lets readers switch between released versions.
3. Prepare Release Baseline
First, finish all feature/fix/docs work and make sure it is already in main.
Set the version variable once and reuse it in all commands below:
VERSION="X.Y.Z"
Choosing X.Y.Z: MAJOR.MINOR.PATCH — bump PATCH for fixes/small changes (e.g.
2.4.0 → 2.4.1), MINOR for backward-compatible features (2.4.x → 2.5.0), MAJOR for
breaking changes. Hotfixes are a PATCH bump on top of the current release. XC_VM_VERSION
(set in step 5) must match the tag you publish in step 7.
⚠️ Do not create a separate version-bump commit/push at this step. Otherwise
dist/changes.mdwill include extra release commits and force additional edits.
4. Deleted Files
Before building, generate the list of files to delete on update:
make generate_deleted_files
This runs git diff between LAST_TAG and HEAD, extracts deleted files under src/, strips the src/ prefix, and writes the result to src/migrations/deleted_files.txt.
If LAST_TAG cannot be auto-detected (no network / no releases), pass it explicitly:
make generate_deleted_files LAST_TAG=1.2.16
Review the generated file — verify no critical files are listed by mistake:
cat src/migrations/deleted_files.txt
After validation, make lb packs the file into the LB archive via the lb_delete_files_list target; on MAIN the file simply rides along in the full-tree copy (there is no separate delete_files_list target).
During php console.php update post-update, MigrationRunner::runFileCleanup() reads it and deletes the listed files automatically.
⚠️ Lines starting with
#are comments and will be ignored. You can comment out files you want to keep.
5. Update Version and Create a Single Release Commit
Edit the version constant, disable the phpMiniAdmin access flag, and clear its password in:
Why disable
DB_ACCESS_ENABLED/ clearDB_ACCESS_PWD? phpMiniAdmin is a raw database console handy in development, but shipping it enabled would expose the DB to anyone who reaches the panel. This step is a security hardening gate — a release must never go out with it on.
src/Core/Config/ConstantsInitializer.php
These frequently-edited constants are define()s at the top of the file (above the
class); appConfig() reads them back, and init() skips the already-defined ones.
Quick commands:
sed -i "s/define('DB_ACCESS_ENABLED', true);/define('DB_ACCESS_ENABLED', false);/" src/Core/Config/ConstantsInitializer.php
sed -i "s/define('DB_ACCESS_PWD', '[^']*');/define('DB_ACCESS_PWD', '');/" src/Core/Config/ConstantsInitializer.php
sed -i "s/define('XC_VM_VERSION', '[0-9]\+\.[0-9]\+\.[0-9]\+');/define('XC_VM_VERSION', '${VERSION}');/" src/Core/Config/ConstantsInitializer.php
Create one final release commit/push:
git add src/Core/Config/ConstantsInitializer.php changelog.json src/migrations/deleted_files.txt
git add docs/en docs/ru # include any doc edits + the regenerated ru (step 2)
git commit -m "Prepare release ${VERSION}"
git push
⚠️ This removes the need for multiple release commits.
6. Build Archives
🤖 Production builds are handled by GitHub Actions (
.github/workflows/build-release.yml) when a release is published. Assets are attached automatically.
For local builds:
make lb
make main
⚠️ Do not run
make newhere —dist/was already reset in step 1, and re-running it would deletedist/changes.md. The build targets write into the existingdist/.
After building, dist/ should contain:
| File | Description |
|---|---|
XC_VM.zip |
MAIN installer (install script + xc_vm.tar.gz) |
xc_vm.tar.gz |
MAIN archive (install & update) |
loadbalancer.tar.gz |
LB archive (install & update) |
hashes.md5 |
MD5 checksums |
The same archive is used for both clean installation and updates. The update script (
src/update) filters out binary/config directories at runtime using the hardcodedUPDATE_EXCLUDE_DIRSlist inside the Python script itself.
Verify integrity:
cd dist && md5sum -c hashes.md5
Docker test install (see tools/test-install/) — only after building, since it needs dist/XC_VM.zip:
bash tools/test-install/test_release.sh
This builds the image, starts the container with systemd, and runs the installer automatically.
dist/XC_VM.zip is mounted into the container as a read-only volume.
✅ Verify the panel loads at
http://localhost:8880and admin login works.
7. GitHub Release
- Go to GitHub Releases
- Create a new release with the tag from the first step
- Paste the changelog as the release description
- Publish without attaching files — GitHub Actions will build and attach them
After publishing, the workflow will automatically:
- Build all archives + checksums
- Attach them to the release
- Send a Telegram notification via
release-notifier.yml - Publish this version's documentation to GitHub Pages via
pages.yml(mike): aX.Y.Zsnapshot plus thelatestalias, selectable from the docs header
✅ Wait for the Actions workflow to finish, then verify all files are downloadable.
8. Post-Release
MAIN before LB. Update the MAIN node first. Its
post-updatebroadcasts anupdatesignal to every LB whenauto_update_lbsis on, so LBs follow automatically; keep MAIN and LB on the same version — LBs read MAIN's database and a schema/behaviour skew can break streaming. Don't leave LBs a release behind.
- Verify all 4 assets are attached to the release
- Run
md5sum -c hashes.md5on downloaded files - Check Telegram notification was sent
- Close related GitHub issues/milestones
If something goes wrong
- Actions build failed after publishing — the release has no (or partial) assets. Re-run the
failed workflow from the Actions tab; if the tag itself is wrong, delete the release and the
tag (
git push --delete origin vX.Y.Z), fix, and re-tag. Don't leave a published release with missing assets — panels fetchhashes.md5/ archives from it. - A released asset is broken — publish a PATCH hotfix release (new tag) rather than editing a published one; clients pin to a tag.
- A bad release already reached servers — operators can downgrade per-server from the panel (Servers → Rollback Version, see Update Mechanism → Rollback); on MAIN a DB backup is taken automatically first. Migrations are forward-only, so prefer a roll-forward hotfix when the fix is small.
Command Reference
Every make target used during release prep, in one place.
Quality checks — run make dev-tools first, make dev-clean when done:
| Command | Purpose |
|---|---|
make dev-tools |
Install dev tooling (PHPStan, phpcs) via composer install |
make phpstan |
Static analysis (also catches syntax errors) |
make phpstan-baseline |
Regenerate the PHPStan baseline |
make cs |
Code-style check — import/namespace hygiene (phpcs + Slevomat) |
make cs-fix |
Apply code-style fixes in place |
make gates |
PSR-4 regression gates (procedural-use, LB-archive, vendor-prod-only) |
make dev-clean |
Remove the dev tools again, restoring the production-only vendor/ |
php tests/phpunit.phar -c tests/phpunit.xml.dist |
Unit tests |
Release prep & build:
| Command | Purpose |
|---|---|
make generate_deleted_files |
Regenerate src/migrations/deleted_files.txt |
make new |
Wipe + recreate dist/ — run ONCE at the start (step 1), before writing dist/changes.md; never again before building |
make lb |
Build the LoadBalancer archive into dist/ |
make main |
Build the MAIN archive into dist/ |
bash tools/test-install/test_release.sh |
Docker install test of the built release |
Documentation (English source in docs/en; docs/ru is generated + committed):
| Command | Purpose |
|---|---|
make docs-venv |
One-time: local venv (build + translation deps) |
make docs-translate |
Regenerate docs/ru from docs/en (before a release) |
make docs-build |
Strict MkDocs build into ./build/site (what CI runs) |
make docs-serve |
Live docs preview at http://127.0.0.1:8000 |