Upgrading and rollback
An upgrade is a tag bump plus a deploy. Images are built only from tags, so
VIDRA_CORE_TAG, VIDRA_USER_TAG and VIDRA_SEARCH_TAG in the env file name
exactly what runs.
Upgrade
# On a git checkout: compose files and Caddyfile only. CHECKOUT TREES ONLY.
cd /opt/vidra && git pull --ff-only
$EDITOR env/production.env # VIDRA_CORE_TAG=v0.5.0 …
./deploy/deploy.sh # dump → pull → gated migrate → up → probe
On a bundle tree there is no git pull. The upgrade is the tag bump plus
vidra deploy. To take a release's new compose files and deploy scripts as
well, unpack its bundle over the tree — it contains no env/ secrets and no
Caddyfile.local, so neither is touched:
cd /opt/vidra
curl -fsSLO https://github.com/yegamble/vidra-core/releases/download/v0.5.0/vidra-bundle_v0.5.0.tar.gz
tar -xzf vidra-bundle_v0.5.0.tar.gz # overwrites tracked files, keeps yours
$EDITOR env/production.env # VIDRA_CORE_TAG=v0.5.0 …
./deploy/deploy.sh
Verify its checksum against the release's SHA256SUMS first if you did not get
it through install.sh.
Release the three repos at the same version. Nothing enforces it, but
rollback.sh v0.5.0 sets all three tags from one argument, promotion copies
three identical lines, and "which build is running?" during an incident has one
answer instead of three. Release a component that did not change anyway —
skipping it means its tag does not exist.
Rollback
# App only, no schema change:
./deploy/rollback.sh v0.4.0 # or: vidra rollback v0.4.0
# Across an incompatible schema change:
./deploy/compose.sh stop api frontend
./deploy/restore.sh backups/pre-deploy-<ts>.dump.gz
./deploy/rollback.sh v0.4.0
rollback.sh rewrites the three tag values, pulls, restarts and re-probes. It
does not touch the database, and that is only safe because of the
one-release schema-compatibility policy: release N−1's code must run against
release N's schema. That policy is mechanically enforced by migrate-lint.sh
and the schema-compat workflow — see
Production deployment.
A true schema rollback is "restore the pre-deploy dump", not "migrate down".
That is what the second form above does, and it is why deploy.sh refuses to
proceed when its pre-deploy dump fails.
Failed health probes leave the broken release running. deploy.sh reports it
and stops; deciding to roll back is yours.
The embedded-migrator tag floor
Both migration one-shots are the service image itself, running its compiled-in
migrate up. deploy.sh and rollback.sh carry a MIN_EMBEDDED_MIGRATE_TAG
constant and refuse a VIDRA_CORE_TAG or VIDRA_SEARCH_TAG below it.
The reason is a hang, not an error: an older image's main() ignores the
migrate up argv and starts an API server, so the one-shot never exits.
deploy.sh would hang mid-pipeline and rollback.sh inside up -d. A hang with
no message is worse than a refusal.
To run a component release older than the floor you must also check out the
meta-repo revision that shipped with it — the pre-embedded compose files drove a
separate migrator container with a bind-mounted migrations/ directory.
VIDRA_USER_TAG is not gated: the frontend has no migrator.
Raise the floor, never lower it.
Cutting a release
make release VERSION=v0.5.0 # all three repos; prompts first
make release VERSION=v0.5.0 CONFIRM=1 # same, unattended
make release VERSION=v0.5.0 REPOS="vidra-core" # one repo only
./deploy/release.sh --yes v0.5.0 # the script directly
vidra release --yes v0.5.0 # the CLI, which execs that script
Each component repo's publish-container.yml runs on release: published and
pushes ghcr.io/<owner>/<repo>:<tag> — so cutting the release is building
the image. release.sh creates the release in each repo, watches the resulting
workflow run to its conclusion, then verifies the image is really in GHCR. It
exits non-zero with a per-image summary if any repo fails, and it does not
deploy anything.
The meta-repo is tagged too — first, and without a release — because
vidra-core's release-assets.yml builds the deployment bundle by checking this
repo out at the same tag. Run release.sh from a clean main; it refuses to tag
a HEAD that is not already on origin/main.
Guards, all of which fire before the first release is created:
- the tag must match
^v[0-9]+\.[0-9]+\.[0-9]+$— the leadingvis what every image reference assumes; ghmust be authenticated;- the tag must not already exist in any target repo;
- HEAD must be an ancestor of
origin/main, and an existing meta tag of that name must point at that very commit; vidra-userneeds theNEXT_PUBLIC_API_BASE_URLrepository variable set, or its workflow refuses to build and there is no published image for any tag:gh variable set NEXT_PUBLIC_API_BASE_URL -R yegamble/vidra-user -b https://your.origin
Re-publishing an existing tag — a build that failed on a transient registry
error — needs no new release. Each publish-container.yml takes a
workflow_dispatch with a required tag input and refuses a tag that does not
exist, so a typo can never publish the default branch:
gh workflow run publish-container.yml -R yegamble/vidra-search -f tag=v0.5.0
Merge order, when the compose revision changes
The chain is bootstrap.sh → component checkouts → docker-compose.yml
include:s vidra-core's compose file. A host on a new compose revision runs
against whatever images the tags name, so component releases land before the
compose revision that requires them:
vidra-core— release the change.vidra-search— same.- the meta-repo revision, with any floors raised to the tags from 1 and 2, then
VIDRA_*_TAGbumped in the env file.