The updater rolls a ZIP out over the application root: every file in the package is copied to the same relative position in the project. For that to work, the package has to be cut correctly.
Relevant files:
manage-client/lib/updater.php – checking, extracting, deployingscripts/create-release-zip.sh – ready-made build scriptThe root of the ZIP is the application root. No extra top-level directory:
correct wrong
------- -----
index.php myproject-v1.3.0/index.php
admin/orders.php myproject-v1.3.0/admin/orders.php
includes/version.php myproject-v1.3.0/includes/version.php
manage-client/lib/client.php
migrations/2026-08-20-01-x.php
A package with a top-level directory wouldn't update the project — it would create a new subfolder.
includes/version.php (or the configured version file) with the new
versionmanage-client/ without config.php — this is how the client itself
gets updated toomigrations/, if the release brings migrations along| Exclusion | Reason |
|---|---|
config.php |
holds the target installation's credentials |
manage-client/config.php |
holds the target installation's instance token |
data/ |
the target installation's operational data |
.git/ |
doesn't belong on a production server |
build/, storage/ |
build artifacts |
config.php, data/ and .git/ are additionally protected via
MANAGE_UPDATE_PROTECTED_PATHS: even if they end up in the package by
accident, they're skipped during deployment. Excluding them at build time
is still necessary, because otherwise the package contains someone else's
credentials and can be downloaded from the Manage server.
The format is vX.Y.Z — no suffix, no prefix. Both client and server
reject anything else.
The version lives in exactly one place: the version file inside the package. The client never writes it itself; it changes as a side effect of copying files. If it's forgotten at build time, the instance keeps reporting the old version after the update and keeps offering the same update again.
This package contains scripts/create-release-zip.sh. The script gets
copied to scripts/ of the project, adjusted once per project at the top
(product name, version file, exclusions), and then run from the project
directory:
./scripts/create-release-zip.sh v1.3.0
The script
It uses git ls-files, so only checked-in files end up in the package —
local experiments and ignored files stay out automatically.
cd /path/to/project
zip -r ../myproject-v1.3.0.zip . \
-x 'config.php' \
'manage-client/config.php' \
'data/*' \
'.git/*' \
'build/*'
Check what's actually in it — this step always pays off:
unzip -l ../myproject-v1.3.0.zip | head -30
In the Manage server under Releases: enter the version, upload the ZIP. Checksum and size are computed by the server itself; they are never taken from the uploader. An upload automatically sets the release as current.
.., absolute
paths, drive letters, null bytes).MANAGE_UPDATE_SANITY_PATHS.MANAGE_UPDATE_BACKUP_DIR.MANAGE_UPDATE_BACKUP_DIR are meant
for manual restoration; there's no command for it. Only the most recent
run is kept.manageUpdateApply()