Files
penguin-harness/changelog/0.2.0/2026-07-31-unified-installer-artifact.md
T
2026-08-03 21:22:05 +08:00

5.0 KiB

Tooling: one canonical install bundle per target, for online and offline alike

Each Release now attaches exactly one artifact per target: penguin-{linux,darwin}-{x64,arm64}.tar.gz, penguin-universal.tar.gz and penguin-win32-x64.zip are flat installer bundles, each sealing the native installer, the program payload (payload.tar.gz / payload.zip) and the payload's SHA256 checksum. Raw program archives and the five *-offline wrappers introduced in 0.1.5 are no longer published; SHA256SUMS covers exactly the six bundles.

Both installation modes consume the same file. Online, install.sh / install.ps1 download the bundle, verify it against its published .sha256 — checksums are now always mandatory; the online warn-and-skip fallback is gone — then open the flat outer layer and verify the payload checksum sealed inside before staging. Offline, the user transfers that one file, extracts it once and runs the bundled ./install.sh (or double-clicks install.cmd); the installer finds the sibling payload by itself and verifies the same sealed checksum with no network access, so no separate checksum file needs to be carried.

Archive shape is probed by content, not filename: a top-level payload.* means bundle, anything else is treated as a program archive with a top-level penguin/. That single probe replaces the previous online/offline split and keeps pinned pre-0.1.6 versions and legacy local archives installable (see the batch's backward-compatibility notes). On Windows, the flat outer layer means the initial extraction never creates deep paths — PowerShell 5.1's 260-character limit stops being reachable from the download directory — and the zip shape probe reads the entry list without extracting; only the installer expands the payload, straight into its short staging directory.

The canonical installers travel byte-identical inside the bundles — the generated POSIX wrapper entry (penguin-installer.sh + stub install.sh) is gone, replaced by sibling-payload detection in the one real installer. That detection is hardened to keep 0.1.5's "never trust archives beside a temporary script" property: it engages only for a real file actually named install.sh, and the penguin.ooo forwarder now stages its download in a private mktemp -d directory rather than a bare file in shared /tmp. The Windows forwarder already runs the installer as an in-memory script block and keeps its sibling detection offline-only.

In-place upgrades stop failing on filesystems that pin in-use directories. penguin update re-runs the installer while the CLI process is still executing out of lib/; on overlayfs under Docker the plain mv lib .old.<pid>/lib failed with Device or resource busy and rolled back. Directory moves now degrade through three strategies — whole-directory rename, per-entry renames into a fresh or existing destination, then per-entry copy with the source cleared (POSIX keeps an unlinked-but-open file valid for the process using it) — with a pinned, empty directory husk reused by the incoming move, rollback following the same rules, and a failure message that says to stop running penguin processes. A hermetic test stubs mv/rmdir to refuse directory renames touching the installed lib/ and verifies the upgrade completes cleanly.

Two Windows papercuts are fixed at the root. The payload no longer ships a penguin.ps1 launcher: PowerShell prefers .ps1 over .cmd on PATH and client Windows defaults to the Restricted execution policy, so a fresh install's plain penguin command failed with "running scripts is disabled"; batch files are policy-exempt, both PowerShell and cmd.exe resolve penguin.cmd, and since upgrades swap bin\ wholesale, re-running the installer also removes the old shim (see the batch's backward-compatibility notes). And after appending to the user Path registry value, the installer now broadcasts WM_SETTINGCHANGE — a raw registry write alone left Explorer, and every terminal launched from it, on the stale Path until the next logon, which is why a brand-new PowerShell window still could not find penguin; the completion note now says to open a new terminal window rather than a new tab.

scripts/package-release-bundles.sh replaces package-offline-bundles.sh, and the release workflow now builds payloads as intermediates, packages the six bundles, and validates the real outputs before upload: outer checksum, exact flat member set, byte-identical installer and payload, and a passing sealed checksum. scripts/test-installer.sh and scripts/test-installer.ps1 replace the offline-only test scripts and hermetically cover bundle layout, offline installs with network access stubbed to fail, corrupted-payload rejection, upgrade rollback, both checksum layers, no-fallback download failures and pinned legacy versions, wired into the existing Linux and Windows CI jobs. README, the docs Installation page and the landing install copy describe the single-artifact flow.