06_UPDATE_PACKAGING.md 4.5 KB

Building Release Packages

Overview

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, deploying
  • in this package: scripts/create-release-zip.sh – ready-made build script

Package layout

The 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.

What belongs in it

  • all application files
  • includes/version.php (or the configured version file) with the new version
  • manage-client/ without config.php — this is how the client itself gets updated too
  • migrations/, if the release brings migrations along

What must stay out

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.

Version number

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.

Building with the bundled script

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

  1. writes the version into the version file,
  2. packs every file tracked by Git, minus the exclusion list,
  3. prints SHA-256 and size.

It uses git ls-files, so only checked-in files end up in the package — local experiments and ignored files stay out automatically.

Building by hand

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

Publishing

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.

What the client checks when deploying

  1. Size and SHA-256 must match the manifest, otherwise the file is deleted.
  2. Every entry in the ZIP is checked against path traversal (.., absolute paths, drive letters, null bytes).
  3. The package must contain at least one of the paths from MANAGE_UPDATE_SANITY_PATHS.
  4. While copying, every existing target file is first backed up to MANAGE_UPDATE_BACKUP_DIR.

Limits of this approach

  • Deleted files are not removed. Deployment is an overlay. A file no longer present in the new release stays behind in the installation. To make it truly disappear, that belongs in a migration.
  • No maintenance mode. The application stays reachable while files are being copied. For larger changes, update during a quiet period.
  • No rollback. The backup copies in MANAGE_UPDATE_BACKUP_DIR are meant for manual restoration; there's no command for it. Only the most recent run is kept.

Next