Conversation
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Add NSIS packaging, signing and timestamp verification, source and artifact inventories, and explicit unsigned builds for development and CI. Protect installation changes with application lifetime locks, inventoried file ownership, staged replacement, and recoverable rollback. Preserve user state and launch from Finish with the desktop user's token and environment. Setuptools silently pruned the tracked build/windows directory from source archives because it shared the generated build directory. Move generated files to .python-build and check the Windows builder's presence in CI. Validated on Windows: 727 tests passed, 5 skipped; Ruff, unsigned NSIS and portable builds, frozen smoke checks, wheel/sdist builds, isolated wheel installation, and final inventories/checksums passed. Hardware signing and clean-machine interactive installation remain unqualified.
owenpkent
left a comment
There was a problem hiding this comment.
Reviewed the installation transaction, ownership/reparse checks, installed-instance lock, upgrade recovery, uninstall boundaries, and build/signing integration against this PR's base. A fresh-install rollback defect remains, detailed inline.
A controlled reproduction used only a synthetic payload in workspace scratch and injected a failure at the installation-manifest write. No actual application installation, signing, elevation, or clean-machine qualification was performed. The observed retry failure is independent of those remaining release gates.
| destination = _path_under(target, relative) | ||
| if _path_exists_or_link(destination): | ||
| _assert_file_matches(destination, new.files[relative], root=target) | ||
| destination.unlink() |
There was a problem hiding this comment.
[P2] Remove transaction-created empty directories during fresh-install rollback.
This removes promoted files, but leaves the destination parent directories created during promotion. After a failure writing .offloader-install.json, a synthetic first install rolled back to ['.offloader-install.lock', '_internal']. An immediate retry then failed with InstallationError: refusing to install into a nonempty unowned directory, because _assert_fresh_target() sees the leftover _internal directory without an ownership manifest.
A transient first-install failure therefore cannot be retried through the normal installer, even though recovery reports no outstanding transaction. Track the directories created by this transaction and prune only those that are still empty, stopping at the installation root and preserving unrelated content. Add a regression that injects a failure after a nested file is promoted, then retries the same fresh install successfully.
Rollback removed the files it had promoted and left the directories it had made to promote them into. After a failure writing the installation manifest a synthetic first install rolled back to ['.offloader-install.lock', '_internal'], and the immediate retry failed with "refusing to install into a nonempty unowned directory", because _assert_fresh_target sees that leftover directory and no ownership manifest. So a transient first-install failure could not be retried through the installer at all, even though recovery reported no outstanding transaction. The transaction now records each directory it creates on the way to a promotion, before creating it - a crash in between leaves the marker naming a directory that is not there, which pruning tolerates, rather than a directory nothing knows about, which it cannot fix. Rollback prunes exactly those, deepest first, and only while they are still empty. A directory that already existed was never recorded and is not touched; one holding a file that arrived from somewhere else is left with it. Pruning runs last, after an upgrade has restored its old files, because those refill the same directories. Writing the regression turned up a second way the same failure wedged the install, in the same handler and with the same cause, so it is fixed here too. The manifest write is what commits an update and it is atomic: either the new inventory landed or the previous one is still there. Recovery treated the second as "manifest does not match a committing update" and raised - and every later install and uninstall recovers first, so a failed upgrade left an installation that no maintenance operation could get past. Both outcomes now mean it did not commit, and roll back. Three regressions: a rolled-back first install retried successfully, a created directory holding somebody else's file kept along with it, and a failed upgrade keeping the directories it did not create and still being upgradable and uninstallable afterwards. The first and third fail without the fix.
What this changes
Offloader can now be built as a Windows x64 desktop/CLI bundle and an NSIS installer for machines without Python. A single version source supplies package metadata, runtime/report versions, and executable/installer versions. Runtime and packaging dependencies are pinned.
The build signs by default with the existing OK Studio certificate, verifies trusted publishers and timestamp coverage, signs the generated uninstaller during NSIS compilation, and signs the setup executable afterward.
--no-signproduces development and CI artifacts without accessing the key. Source/file inventories reject stale--skip-buildbundles;--verify-onlychecks existing outputs without the private key, and final checksums cover the installer, portable ZIP, and inventory.Installed GUI and CLI processes hold lifetime locks that block maintenance while any instance is open. Maintenance blocks new launches, stages and checks replacements, preserves old owned files for rollback, recovers interrupted operations, and removes only inventoried files. Settings, history, and unrelated files are preserved. The wizard includes shortcut choices, silent installation, and an optional Finish launch using the desktop user's token and environment, with no elevated fallback.
CI builds unsigned installers and portable bundles, runs frozen smoke checks, and checks that the source archive includes the Windows builder. Setuptools output moves to
.python-buildbecause its default cleanup otherwise omitted the trackedbuild/windowsdirectory. The earlier file-control test fixes remove disk-speed races without changing production copy or verification behavior.Stacked on #6; targets
timeline-offload. Timeline import remains source-only in this bundle. No release is published and no hardware-key signing was performed.What you verified
Hardware-key signing, the actual NSIS install lifecycle under UAC, alternate administrator credentials and desktop launch, independent clean-machine/real-storage qualification, and the complete third-party license inventory/SBOM remain pending. Hosted CI for this update must be evaluated separately from the local results above.
Checklist