Packaging and releases
One repository, two distributions on PyPI, one tag.
Distribution |
Contains |
Built by |
Installed with |
|---|---|---|---|
|
|
the project’s own |
|
|
|
|
|
Tagging vX.Y.Z on main publishes both through PyPI trusted publishing
(OIDC — GitLab namespace plesty/core, project plesty-bench,
.gitlab-ci.yml), one publisher per distribution told apart by the job’s
GitLab environment (pypi-bench, pypi-server), and creates the GitLab
Release.
Building the Windows installer
packaging/windows/build-installer.ps1 has two modes, and the difference is
what the wizard installs:
powershell -ExecutionPolicy Bypass -File .\packaging\windows\build-installer.ps1 -Mode Test
powershell -ExecutionPolicy Bypass -File .\packaging\windows\build-installer.ps1 -Mode Release -Version 0.2.5
The -ExecutionPolicy Bypass is not optional on a default box: an unsigned
.ps1 will not run otherwise. And every .ps1 here is ASCII only —
Windows PowerShell 5.1 reads a file without a BOM as cp1252, where the third
byte of an em-dash decodes to a smart quote, which PowerShell accepts as a
string delimiter. One em-dash in a comment silently opens a string and the
file stops parsing.
Release is what ships: the wizard runs uv tool install "plesty-server[gui]==<version>" against PyPI, so the version must be a real
release — the Inno script refuses a .dev or +local version outright.
install.sh has the same two modes on macOS and Linux — --mode=test builds
both wheels from the checkout and installs those, --mode=release (the
default) installs from PyPI — plus --launcher-only for a build that is
already installed some other way.
Test is how a branch is put through the real wizard: the version is the
working tree’s (0.3.0.dev3+0ca7c6c), both distributions are built from the
tree, and the wheels are carried inside the installer, so what gets
installed is the code in front of you. The output is named …-TEST-… and every
wizard page says so.
That distinction is not cosmetic. The wizard is a process context of its own —
a process descended from setup.exe cannot traverse a directory junction, so
uv behaves differently under it than under a shell — and a defect of that
kind can only be reproduced by running the real installer. Without a test mode
the only way to exercise the wizard was to ship a release first.
The light installers
The installers carry no application code. They install uv if it is
missing and run uv tool install "plesty-server[gui]==<version>", which
downloads the application and its dependencies from PyPI into uv’s tool
environment; then they make the application visible:
Platform |
Installer |
What it adds |
|---|---|---|
Windows |
|
Start-menu entries (PLESTY Bench, console, Update, Uninstall), optional desktop shortcut, uninstaller in “Apps & features” |
macOS |
|
|
Linux |
the same |
a |
plesty-server / plesty-server-gui land on uv’s bin dir (~/.local/bin);
the console remembers the last fleet it used (<home>/last-fleet) so a
double-click reopens it. Updating a bench is uv tool upgrade plesty-server
(Start-menu entry Update PLESTY Bench on Windows).
The pinned version
Both installers install the release they were built for, never latest and
never a development version — a bench is exactly the release it downloaded,
and uv tool upgrade plesty-server is the only thing that moves it forward.
The pin must be a version that exists on PyPI, so it comes from the tag, not
from the working tree: installer:windows derives it from $CI_COMMIT_TAG,
and plesty-server.iss refuses to compile when it carries .dev or a +
local segment (what versioningit yields off a tag — 0.2.3.dev7+abc1234,
0.2.2+modified — none of which are published). release:assets stamps the
same version into install.sh in place of @PLESTY_VERSION@; run from a
checkout the placeholder survives and the install is unpinned.
PLESTY_SERVER_VERSION overrides it, and release:server-wheel fails if the
version it publishes is not the tag.
An installer/<name> branch builds a test installer against the last release
(or INSTALLER_VERSION), so the wizard can be tried end to end before tagging.
Signing
Unsigned installers are refused by name: Windows SmartScreen says “Windows protected your PC — Unknown publisher”, macOS Gatekeeper “unidentified developer”. Both need a paid identity; the build is wired for it but ships unsigned until one exists.
What removes the warning |
Wired |
|
|---|---|---|
Windows |
An Authenticode certificate. |
yes — set the variable |
macOS |
A Developer ID Application certificate (Apple Developer Program) plus notarization with |
no |
install.sh does clear com.apple.quarantine from the launcher it writes and
ad-hoc signs it, so a locally built bundle opens by double-click — the bundle
is created on the machine at the user’s request, and the flag it inherits from
the downloaded script is what Gatekeeper would otherwise object to. That is
not a substitute for Developer ID on anything downloaded as an app.
The CI jobs on a tag
Job |
Does |
|---|---|
|
publishes |
|
|
|
|
|
stamps the tag into |
What the bench still needs
uv (the installers install it) and git for modules installed from a
repository; Python is not needed — uv manages interpreters for the tool
environment and for every device environment.
Re-executing itself — plesty/server/reexec.py
Jobs and the detached launcher are plesty-server processes too: from a
tool environment or a checkout that is python -m plesty.server … /
python -m plesty.server.services.launch with the environment’s interpreter.
reexec is the one module that would also know a frozen bundle, should one
ever be built again.
Headless bench: fleet run
plesty-server fleet run --agent # bring the fleet up, keep it up, serve plesty-bench clients
Bench.run() brings the autostart devices up, then sweeps every 5 s: a
device found exited is started again after a backoff that doubles per
consecutive crash (1, 2, 4 … 60 s) and resets once it answers a probe;
devices stopped on purpose are left alone. SIGINT/SIGTERM/SIGHUP
(Ctrl-C, a service stop, the terminal closing) end the loop and — unless
--leave-running — bring the servers down, so a service manager stopping
fleet run stops the bench cleanly. --agent serves plesty-bench clients
from the same process.