Finish bootstrap uv before PM replaces its store entry. Keep failure receipts stdlib-only and align the cryptography requirement and override with the locked version. Let bundle builders declare launch paths and update ownership. Remove payload discovery, Store probing, and the unused develop command. Derive Nix Python from the PM lock and share its provenance stamp. Document setup, activation, optional dependencies, and distribution ownership. Targeted Windows tests, relocated runtime launches, Electron bundling, and bilingual docs builds pass. Native Nix and signed-package acceptance remain CI gates.
4.9 KiB
Shared bundle builds
The build-only Python modules live in scripts/bundles/. They use PM for
package installation and dependency resolution. They do not implement a
second package manager.
| Module | Responsibility | Consumers |
|---|---|---|
payload.py |
Git snapshots, manifest/facts, JS output placement, relocation and launcher generation | Native desktop and Termux |
native.py |
Native tool-store staging and dependency venv assembly | hermes pm bundle, desktop builder |
desktop.py |
Build the JS surfaces, assemble the payload, invoke Electron packaging | Native release workflow, local builds |
stage.py |
Stage a payload and its launchers without creating an Electron package | npm run payload |
mint_launchers.py |
Mint Windows launchers with the payload interpreter's distlib | Shared launcher assembly |
launcher_wrapper.py |
Runtime template for the minted executable | Shared launcher renderer |
Build a desktop bundle from a checkout at its release tag:
uv run --no-project --python 3.14 python scripts/bundles/desktop.py --tag=vX.Y.Z
The builder derives console entrypoints from the archived pyproject.toml.
It checks the interpreter, dependency tree, and generated launchers before
publishing their relative paths in the payload manifest. The desktop build
bakes this launch contract into its stamp. Electron uses the declared paths
without payload probes, adoption, or repair. Non-bundled builds carry no
placeholder payload. The stamp also declares the update mechanism. Store
packages use external, and sideload bundles use app-installer. The runtime
has no separate Store probe or compatibility fallback.
The MSIX hook reads the declared launcher names
rather than carrying a second entrypoint list. POSIX launchers follow
links to the installed payload and call each declared function directly.
Windows launchers are minted by the payload's own Python, preserving native
architecture and relocation behavior.
Termux uses the same snapshot, manifest/fact writer, JS asset placement and
POSIX launcher generator. Its package-manager hooks stay in scripts/termux/.
Its bionic wheel compilation and offline installation remain target-specific:
a glibc host cannot execute the shipped interpreter, and Termux has a fixed
installation prefix. Native desktop staging instead resolves on the target OS.
Neither path compiles dependencies on the user's machine.
Electron-specific work stays with Electron: renderer/main-process bundling, Node native bindings, MSIX metadata, signing, notarization and app packaging. The after-pack hook invokes the shared Python relocation command instead of maintaining its own link rewrite algorithm.
Ordinary bundle staging does not scan user plugin trees. Runtime plugin admission is a separate PM transaction. Build output cleanup is confined to its payload store; it must not prune the machine-wide downloader partials.
Target boundaries
Desktop stages on the target OS and architecture. Its dependency step uses
uv sync --frozen --all-extras --active on the staged interpreter.
The complete desktop builder, not pm bundle alone, adds prebuilt JS surfaces
and invokes Electron packaging.
Termux's host scripts run on Linux ARM64. build_cpython.sh and build_node.sh
stage pinned bionic packages; stage_runtime_libs.py supplies their native
library closure. termux_build.sh exports the tag's core plus acp requirements,
builds the required native wheels in the pinned bionic container, and records
wheelhouse inputs. build_deb.sh assembles the environment offline, creates the
APT package, and validates it in a fresh container without network access.
Those container checks do not substitute for Android device acceptance.
The installed Termux root is $PREFIX/lib/hermes-agent/. It contains app,
tools, runtime-libs, venv, bin, and the PM manifest/facts. Maintainer
hooks manage only the package's CLI symlinks under $PREFIX/bin and refuse
foreign launcher conflicts. They do not compile dependencies on install.
stage_apt_repo.py owns repository metadata and signatures. Stable and canary
suites are hermes-stable and hermes-canary; package files publish before
signed metadata. Runtime and repository pins are distinct from the native
build-toolchain packages used only inside CI.
Docker and Nix consume PM pins but do not run this desktop payload assembly. Docker has its own curated extras and image lifecycle. Nix uses uv2nix and separate derivations. Stable release admission coordinates their acceptance and publication with desktop and Termux packages.
Verification boundary
Tests execute shared snapshot/manifest helpers on real git fixtures, stage real subprocesses and venvs at controlled boundaries, and mint/run Windows launchers after relocation. POSIX launcher and symlink tests run on POSIX. A local helper test is not proof of a signed installer, bionic wheel build, or stable-release upgrade. The release workflows own those native receipts.