16 KiB
GovOPlaN Release Dependencies
Release installs must not depend on sibling checkout paths. Local development can keep editable installs and file: WebUI links, but release packaging should resolve modules from tagged git refs or from a package registry.
Backend
Local development:
cd /mnt/DATA/git/govoplan-core
./.venv/bin/python -m pip install -r requirements-dev.txt
Release install from a core checkout plus tagged module repositories:
cd /mnt/DATA/git/govoplan-core
./.venv/bin/python -m pip install -r requirements-release.txt
.[server] is resolved relative to the current working directory. If you create the virtualenv elsewhere, still run the install command from the core checkout:
cd /mnt/DATA/git/govoplan-core
/tmp/govoplan-release-test/bin/python -m pip install -r requirements-release.txt
requirements-release.txt pins the module repositories to the release tag. Update those refs when cutting a release:
govoplan-access git@git.add-ideas.de:add-ideas/govoplan-access.git v0.1.4
govoplan-admin git@git.add-ideas.de:add-ideas/govoplan-admin.git v0.1.4
govoplan-tenancy git@git.add-ideas.de:add-ideas/govoplan-tenancy.git v0.1.4
govoplan-policy git@git.add-ideas.de:add-ideas/govoplan-policy.git v0.1.4
govoplan-audit git@git.add-ideas.de:add-ideas/govoplan-audit.git v0.1.4
govoplan-files git@git.add-ideas.de:add-ideas/govoplan-files.git v0.1.4
govoplan-mail git@git.add-ideas.de:add-ideas/govoplan-mail.git v0.1.4
govoplan-campaign git@git.add-ideas.de:add-ideas/govoplan-campaign.git v0.1.4
govoplan-calendar git@git.add-ideas.de:add-ideas/govoplan-calendar.git v0.1.4
Runtime Module Package Changes
The admin module manager can hot-enable and hot-disable packages that are already installed. It does not install or uninstall Python/npm packages from inside the running server.
For runtime package changes, create an operator install plan in Admin > System > Modules. The module manager shows the trusted installer preflight status and blocks unsafe uninstalls before the operator touches packages.
Preflight from the server shell:
govoplan-module-installer --format shell
Apply from a controlled operator shell while maintenance mode is active:
govoplan-module-installer --apply --build-webui
For real install/uninstall work, prefer supervised mode with the deployment's restart command and health endpoint:
govoplan-module-installer \
--supervise \
--migrate \
--build-webui \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
To let the admin UI trigger package work without executing pip/npm inside a FastAPI request, run the installer daemon in a separate operator shell. This is the preferred development/early-production mode for now because the operator can watch output, stop before queueing risky changes, and keep restart commands deployment-specific:
govoplan-module-installer \
--daemon \
--migrate \
--build-webui \
--database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \
--database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
Admin > System > Modules can then queue the saved install plan as a supervised
request. Install rows can be planned directly from the approved package catalog;
uninstall rows are generated from installed, disabled modules so the Python
distribution and WebUI package names do not need to be typed by hand. The
daemon claims one queued request at a time and writes request/run records below
runtime/module-installer. For process-manager one-shot usage or tests, use
--daemon-once. The daemon also writes
runtime/module-installer/daemon.status.json; check it with:
govoplan-module-installer --daemon-status --format json
The installer uses a runtime lock, snapshots pip freeze plus WebUI
package.json/package-lock.json, writes a run record below
runtime/module-installer/runs, and marks planned rows as applied only after
all commands succeed. When --migrate is used with a sqlite:/// database URL,
the installer also snapshots the SQLite database with SQLite's backup API before
running migrations. For other database engines, pass external backup/restore
hooks:
govoplan-module-installer \
--supervise \
--migrate \
--database-backup-command 'pg_dump --format=custom "$GOVOPLAN_DATABASE_URL" > "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--database-restore-check-command 'pg_restore --list "$GOVOPLAN_DATABASE_BACKUP_PATH" >/dev/null' \
--database-restore-command 'pg_restore --clean --if-exists --dbname "$GOVOPLAN_DATABASE_URL" "$GOVOPLAN_DATABASE_BACKUP_PATH"' \
--health-url http://127.0.0.1:8000/health \
--restart-command '<restart govoplan server>'
The backup command runs before migrations. The restore-check command validates
the produced backup artifact before migrations proceed, without restoring over
the live database. The restore command is stored in the run record and runs
during rollback unless an override is passed to --rollback.
Supervised mode treats package command failure, migration failure, restart failure, and health timeout as rollback triggers. It restores the Python/WebUI package snapshots, re-runs the restart command when supplied, and restores the saved install plan state so the operator can correct it. The supervisor must run outside the FastAPI server process; the admin UI saves and validates plans but does not mutate packages from an HTTP request.
After a successful install plan, the installer adds installed modules to saved
startup state by default so the restarted server can discover and enable them.
After a successful uninstall plan, the installer removes uninstalled modules
from saved startup state by default. Use
--no-activate-installed-modules or
--keep-uninstalled-modules-in-desired only for staged rollout workflows that
will update module state separately.
Uninstall is non-destructive by default. A planned uninstall row can set
destroy_data: true to request destructive module retirement. The module must
provide an automated retirement provider, and the installer snapshots the
database before dropping module-owned tables. For SQLite this uses the built-in
snapshot path; for PostgreSQL or another non-SQLite database, provide
--database-backup-command, --database-restore-check-command, and
--database-restore-command. If a destructive run fails during package removal,
the installer restores the database snapshot before returning the failed run
result; supervised restart/health failures also roll back through the normal
supervisor path.
Package rollback is automatic. SQLite database rollback is automatic for
installer runs that used --migrate and captured a database snapshot.
Non-SQLite rollback is automatic when the run used
--database-backup-command and --database-restore-command; otherwise migrated
non-SQLite runs are blocked before package changes are applied.
Rollback uses the saved run snapshot:
govoplan-module-installer --rollback <run-id>
govoplan-module-installer --rollback <run-id> --database-restore-command '<override restore command>'
Database hook commands run with these environment variables:
GOVOPLAN_INSTALLER_RUN_DIR: the run snapshot directoryGOVOPLAN_DATABASE_URL: the configured database URLGOVOPLAN_DATABASE_BACKUP_PATH: a suggested backup artifact path inside the run directoryGOVOPLAN_DATABASE_BACKUP_METADATA: optional JSON metadata path that backup commands may write for operator diagnostics
Avoid embedding secrets directly in commands; prefer environment variables, service credentials, or deployment-local secret injection.
Inspect installer history and lock state from the operator shell:
govoplan-module-installer --list-runs --format json
govoplan-module-installer --show-run <run-id> --format json
govoplan-module-installer --lock-status --format json
govoplan-module-installer --list-requests --format json
govoplan-module-installer --show-request <request-id> --format json
govoplan-module-installer --cancel-request <request-id> --format json
govoplan-module-installer --retry-request <request-id> --format json
Package catalogs can be local files or remote static resources, for example
served by govoplan-web. Set GOVOPLAN_MODULE_PACKAGE_CATALOG for a local file
or GOVOPLAN_MODULE_PACKAGE_CATALOG_URL for a remote catalog matching
docs/module-package-catalog.example.json; the admin UI will show those entries
and can save them into the install plan. This keeps the release approval
decision outside the running server while avoiding hand-typed package refs.
Remote catalogs can be cached for offline inspection:
GOVOPLAN_MODULE_PACKAGE_CATALOG_URL=https://govoplan.example/catalogs/v1/channels/stable.json
GOVOPLAN_MODULE_PACKAGE_CATALOG_CACHE=/srv/govoplan/runtime/catalog-cache/stable.json
Validate the catalog before handing it to operators:
govoplan-module-installer --validate-package-catalog docs/module-package-catalog.example.json --format json
Release catalogs should be signed, channel-gated, expiring, and sequence
tracked. The supported signing format is an Ed25519 signature over the
canonical catalog JSON object with the signature and signatures fields
removed. Core accepts the legacy single signature field and the newer
signatures array used during key rotation. Sign a catalog with:
govoplan-module-installer \
--sign-package-catalog docs/module-package-catalog.example.json \
--catalog-signing-key-id release-key-1 \
--catalog-signing-private-key /path/to/ed25519-private.pem
Validate an approved release catalog with:
govoplan-module-installer \
--validate-package-catalog docs/module-package-catalog.example.json \
--require-signed-catalog \
--approved-catalog-channel stable \
--catalog-trusted-key release-key-1=<base64-ed25519-public-key> \
--format json
For the admin UI/daemon path, configure the same policy through environment variables:
GOVOPLAN_MODULE_PACKAGE_CATALOG=/path/to/catalog.json
GOVOPLAN_MODULE_PACKAGE_CATALOG_REQUIRE_SIGNATURE=true
GOVOPLAN_MODULE_PACKAGE_CATALOG_APPROVED_CHANNELS=stable
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS='{"release-key-1":"<base64-ed25519-public-key>"}'
GOVOPLAN_MODULE_PACKAGE_CATALOG_SEQUENCE_STATE=/srv/govoplan/runtime/catalog-sequences.json
GOVOPLAN_MODULE_PACKAGE_CATALOG_ENFORCE_SEQUENCE=true
Trusted keys can also be loaded from
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_FILE. A URL-backed keyring is
supported for development and tightly controlled deployments through
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_URL plus
GOVOPLAN_MODULE_PACKAGE_CATALOG_TRUSTED_KEYS_CACHE, but production systems
should pin trusted keys locally.
Catalog entries can declare license_features. If
GOVOPLAN_LICENSE_ENFORCEMENT=true, core blocks planning catalog installs
whose required features are not present in the configured offline license:
GOVOPLAN_LICENSE_FILE=/srv/govoplan/license.json
GOVOPLAN_LICENSE_TRUSTED_KEYS_FILE=/srv/govoplan/trust/license-keyring.json
GOVOPLAN_LICENSE_ENFORCEMENT=true
See docs/CATALOG_TRUST_AND_LICENSING.md for key rotation, replay protection,
and licensing details. See docs/RELEASE_CATALOG_WORKFLOW.md for the concrete
release-machine workflow that generates signing keys and publishes signed
catalogs through govoplan-web. Unsigned catalogs remain usable for local
development when signature enforcement is off, but the admin UI labels them as
unsigned.
Install rows must use tagged package/git refs or registry packages, not local
file: or workspace links. The installer daemon can run npm install and
npm run build for WebUI package changes; that is the supported path. Browser
remote bundles are still experimental and should be treated as a controlled
deployment option, not the normal install/uninstall mechanism.
Module manifests can declare core compatibility bounds and uninstall guard
providers. Preflight blocks incompatible manifest contracts/core versions,
active modules, desired startup state, protected modules, and active dependents.
Default uninstall is non-destructive: module data and schema remain dormant if
the package is removed. Persistent-data guards therefore warn by default instead
of requiring export/delete. A module that supports explicit data/schema
retirement should register a retirement provider. When destroy_data is set on
an uninstall plan row, that provider is allowed to destroy module-owned data
after the installer has captured a database snapshot; otherwise it is used only
for preflight reporting.
WebUI
Local development uses webui/package.json, which may point at sibling module checkouts while active development is happening.
Release WebUI installs should use webui/package.release.json. It points module dependencies at the same tagged git repositories. After the module tags referenced there exist, generate the committed release lockfile without touching the development package files:
cd /mnt/DATA/git/govoplan-core
scripts/generate-release-lock.sh
cd webui
PATH=/home/zemion/.nvm/versions/node/v22.22.3/bin:$PATH /home/zemion/.nvm/versions/node/v22.22.3/bin/npm run build
The module repositories include root-level npm package manifests so git installs can resolve @govoplan/access-webui, @govoplan/admin-webui, @govoplan/files-webui, @govoplan/mail-webui, @govoplan/campaign-webui, and @govoplan/calendar-webui from repository roots even though their source lives below webui/src.
The normal release path is automated by scripts/push-release-tag.sh: it bumps or accepts the target version, updates Python/WebUI/module manifest versions, commits/tags/pushes the module repositories first, regenerates webui/package-lock.release.json, and then commits/tags/pushes core. If the working tree has already been bumped, pass the current version explicitly:
cd /mnt/DATA/git/govoplan-core
scripts/push-release-tag.sh --version 0.1.2
The script also includes GovOPlaN roadmap/scaffold module repositories that do not yet have package metadata. Those repositories are committed, tagged, and pushed with the same release tag, but they are tag-only until they contain pyproject.toml, module manifests, or WebUI packages. Tag-only repositories are not listed in requirements-release.txt or webui/package.release.json.
Current tag-only module repositories:
govoplan-addressesgovoplan-appointmentsgovoplan-casesgovoplan-connectorsgovoplan-dmsgovoplan-erpgovoplan-fit-connectgovoplan-formsgovoplan-identity-trustgovoplan-idmgovoplan-ledgergovoplan-notificationsgovoplan-opsgovoplan-paymentsgovoplan-portalgovoplan-reportinggovoplan-schedulinggovoplan-searchgovoplan-tasksgovoplan-templatesgovoplan-workflowgovoplan-xoevgovoplan-xrechnunggovoplan-xta-osci
Release lockfile strategy
The supported release composition currently is the full Multi Seal Mail product: core plus access, admin, tenancy, policy, audit, files, mail, campaign, and calendar. Keep one committed full-product release lockfile at webui/package-lock.release.json, generated from webui/package.release.json in a clean release workspace. Development package-lock.json may continue to point at local file: dependencies.
Frontend module permutations are regression-tested through GOVOPLAN_WEBUI_MODULE_PACKAGES and temporary build output, not through committed lockfiles for every possible combination. If a smaller composition becomes a separately shipped product, add an explicit release manifest and lockfile pair for that product, for example package.release.files-mail.json and package-lock.release.files-mail.json, generated in a clean release workspace from tagged git dependencies.
Release Checklist
- Keep Python package versions, WebUI package versions, and git tags aligned.
- Tag core, access, admin, tenancy, policy, audit, files, mail, campaign, calendar, and scaffold module repositories together.
- Update
requirements-release.txtandwebui/package.release.jsonwhen the release tag changes. - Generate the committed full-product release lockfile from
package.release.jsonwithscripts/generate-release-lock.sh. - Add separate release manifest/lockfile pairs only for module compositions that are shipped as their own products.
- Do not commit local sibling paths into release manifests.