feat(builder): add relocatable Windows portable installations #2367
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2367
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/windows-portable-builder"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Windows Builder could not stay portable on an external SSD: setup, app storage and the game updater selected different locations, while saved absolute paths broke after a move. This adds opt-in
--portablesetup with one marked, relocatable Builder home. Standard installs retain their existing location.Fixes #2258. Claim.
Technical changes
Validation
Current tested head
f75c156f, rebased onto main355453de. A successful standard rebuild now removes the former portable association before publishing standard Builder metadata. The transition regression rejects the previous production source; it also checks repeated standard builds and denied removal preserving old metadata. All eight focused Windows fixtures pass, including native setup/transactions, archive/relocation and actual published self-contained WPF CLI startup. ZIP fallback repair limits and portable setup discovery are documented. Physical removable-drive relocation and a complete public-release/game repair remain untested. Fresh hosted checks are separate exact-head evidence.Earlier validation evidence and limits
Earlier tested head
6affe74b, rebased onto main7759a6f3:env_onfixtures now fixed on main by #2380; the old WPF failure was a short/long path assertion, not selection of a different home.At
64856415onb744b1ec: Windows 359 passed / 5 failed / 31 skipped; Linux 362 passed / 2 failed / 31 skipped. Both hit the two missing-env_onfixtures; Windows also retained the documented recorder negative control, NUP link-privilege and compute-guard fixture failures. These are earlier-head results, not current-head green checks.At
9e3aa287: complete native setup/host-update compilation and self-contained WPF publish passed; ten broken variants were rejected. Full Linux was 351/0/31; Windows349/3/30 had the same failure identities as its exact main baseline.Tests use authored packages/files, with no game data, downloaded SDKs or built game. Actual removable-media/drive-letter changes, live release download/repair and gameplay remain untested. #2373's new launcher shares setup/storage paths and needs the portable contract preserved whichever lands second.
Integration note for #2373: the launcher and this opt-in portable-home package have distinct behavior but share setup/storage paths. A merge-tree check at
64856415+ launcher6adbe93breports conflicts in MainWindow.xaml.cs, Storage.cs and setup.c. Whichever lands second needs to preserve the marked home, relative game association and process-only child context, including the launcher's new--engine --tagroute. I'll handle the portable rebase if the launcher lands first. No launcher UI is included here.This is a well-contained feature, and I couldn't find a regression for standard installs, which is what most players run. I focused on the native setup (
portable_home.h,setup.c), the game-side association (make_game.py,host/hos_update.c) andBuilderHome/PortableArchive. For a standard install:init_homeresolves to%LOCALAPPDATA%\HookshotBuilderas before;publish_installis the oldMoveFileW,reuse_partstill moves, andrmtree(final_dir)is unchanged;home_pathonly fails on truncation for portable homes;make_game.py's--workdefault,HOOKSHOT_SETUP_BASEor elseLOCALAPPDATA, resolves to the same folder.On the game side,
local_builderruns<game>\hookshot.exe, which is the setup launcher and not the game, becausehos_game_path_wmapsbin\hookshot.exeback to the game folder. The relative association is accepted only when it resolves to a marked home, so it can't point anywhere a plainbuilder=line couldn't already. All checks pass on this head, including Hookshot Builder package and setup.should fix
nx2/make_game.py:1469-1472withbuilder/setup/portable_home.h:110-133: the association is written when the build is portable, but never removed when it isn't. If a game folder built by a portable Builder is later built again (or updated) by a standard one,hookshot_version.txtgetsbuilder=and nobuilder_local=1, while the old.hookshot_builder_homestays.hb_home_gamereads that file first, without looking atbuilder_local, so the game's launcher keeps opening the portable Builder's Play/Update window whilehos_update_checkhands updates to the standard one. If the portable home has since been moved or deleted,hb_home_gamereturns -1,g_home_okis 0, and the launcher silently skips the Builder altogether. The simplest fix is forwrite_game_home(out, None)to delete an existing.hookshot_builder_home. Alternatively (or as well),hb_home_gamecould honour the file only whenhookshot_version.txthasbuilder_local=1. A case for this intest_portable_game_home.pywould pin it down.question
builder/HookshotBuilder/PortableArchive.cs:23: an existingversions\<tag>folder is reused whenever it hasHookshotBuilder.exeand a matchingtools\version.txt. That means the ZIP fallback can never repair a version that antivirus has partly removed: it validates and starts the same broken folder. I understand not overwriting a possibly running installation, and the ZIP route is only the fallback for releases without the setup's list, so its reach is small. Is the setup route (MainWindow.xaml.cs's repair throughHookshotBuilder-Setup.exe) the intended way to repair in portable mode? If so, a line about it indocs/windows-portable-builder.mdwould help.minor:
--portableis a player-facing option, but it's only documented indocs/windows-portable-builder.md, which is linked from CONTRIBUTING.md. A sentence and a link in the readme's Windows install section would let players who want an external-SSD install find it.Not covered by me: the WPF startup fixture and the
.NETtests beyond reading them (they need Windows; CI ran them), and real removable-drive moves, which the PR says are untested. The heads-up about #2373 overlapping insetup.c,Storage.csandMainWindow.xaml.csis useful. Whichever lands second needs to keepBuilderHome.PassToon the launcher's new child processes.Thanks for the review. Fixed in
f75c156fon main355453de: a successful standard rebuild removes the old portable association before writing standard Builder metadata. The new transition case fails against the previous source and passes now, including a second standard build and denied removal preserving the old metadata. All eight focused Windows fixtures pass again.I also linked portable setup from the readme and clarified repair: the packaged setup is the repair route; the legacy ZIP fallback reuses a matching installed version and does not audit all its files. Fresh CI is running.
write_game_home(out, None)removes a stale.hookshot_builder_homeon a standard rebuild, and it's tested. Standard installs resolveHOOKSHOT_SETUP_BASEto the same%LOCALAPPDATA%\HookshotBuilderas before. The new deletions are portable-only and scoped:rmtree(to)touches only this attempt's staging folder, thepublish_installbackup is restored on failure, and the tag is checked against an allow-list before it's used in a path.Merge order: this before #2373. They share
setup.c,Storage.cs,MainWindow.xaml.cs,Updater.csandApp.xaml.cs, and #2373 then needsBuilderHome.PassToon its--enginesetup and the portable home inFreeAfterBuild.github-sync referenced this pull request2026-11-12 06:22:54 +00:00