fix(builder): Cancel stops a Linux update while it downloads #2160
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!2160
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/linux-update-cancel"
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?
In the Linux builder, Cancel does nothing while an update is still downloading. The download carries on, the new builder starts and the update runs. Cancel only knew how to stop make_game.py, and during the download there is none yet. The Windows builder cancels its download.
fetch_bytesandinstallinlinux_updatenow take an optional stop event. With one, a part is read a megabyte at a time and the download ends withCancelledat the next one, or in the wait between two tries, and a cancelled download gets none of the retries a failed one does.installlooks at the event between parts and once more before the new folder takes the old one's place, and removes what it staged, as for any error. Without an event they read and wait as before. The builder's Cancel sets the event when no make_game.py is running, and the page then says "The update was cancelled. Your game folder is unchanged." with Back, the way a failed update does.Once the new version is completely in place the new builder still starts; from there Cancel stops make_game.py as it always did.
Testing (Linux, Tk 8.6)
--update, with a 30 MB part served slowly by a patched opener. Before: Cancel changes nothing, the version is installed and the new builder is started. After: the page shows the cancelled message at once, the staging folder is gone and the installed version is untouched.tests/test_linux_update_cancel.py(new, 13 tests) covers the read in pieces (same bytes, same size limit), no retry after a cancel, Cancel during the wait between tries,installcancelled in the first or the last part, an update nobody cancels, and the builder's owncancelandbegin_updateon a stand-in window. 12 fail on main.tests/test_linux_update.pyandtests/test_update_tracks.pypass.Nice fix, and well contained. Without a stop event,
fetch_bytes,_read_at_mostandinstallbehave exactly as before. With one, a cancel is seen between 1 MB pieces, in the wait between tries, between parts, and once more right before the new folder replaces the old one, and theexcept Exceptionininstallremoves the staged folder.Cancelledis re-raised ahead of the generic handlers in bothfetch_bytespaths, so a cancel never gets retried or falls through to the API download. On the builder side, Cancel only exists on the progress page, and a normal build setsself.procin the same callback that shows the page, so the newelif self.proc is Nonebranch can only run while an update's thread is working. I rantests/test_linux_update_cancel.py(13 tests),tests/test_linux_update.pyandtests/test_update_tracks.pyat the PR's head, and all pass. CI is green, and it merges cleanly with main.Two small things:
builder/linux/hookshot_builder.py:979-981with:1140-1141: the thread checksstop.is_set()and then queuesdo-build, butpumponly drains the queue every 100 ms. If Cancel lands after the check and beforepumphandlesdo-build(most likely on the already-fetched path, where the thread does no download and finishes at once),start_make_updatestill runs.make_game.pythen starts, and "Stopping the update" is overwritten by "Compiling what changed". The fix is a guard in the handler:builder/linux/linux_update.py:256-263: a cancel is noticed between reads, so on a connection that has stalled mid-read it only takes effect when the socket times out (60 s for a part, 120 s for the API path). The page says "Stopping the update" during that time, so it isn't wrong, just possibly slow. No change needed if you're fine with that; it might be worth a word in the PR description's "Not tested".It needs a rebase: it conflicts with #2406 in
builder/linux/hookshot_builder.pyaroundstartup_manifest. Keep main'smanifest.tag != targetcheck alongside the newget=...stopargument.The earlier should-fix is still open: a Cancel between the thread's
stop.is_set()check andpump()handlingdo-buildstill startsmake_game.py. A guard in thedo-buildhandler (~line 1140) closes it. Everything else checks out.Thanks. Rebased on main (
6f4f28ac) and done inf62186e6:manifest.tag != targetcheck stays, right after thestartup_manifest(..., get=...stop)call.do-buildhandler checks the stop event, so a Cancel that comes after the thread's last check and before the window takes the line ends as cancelled ("The update was cancelled. Your game folder is unchanged.") and does not startmake_game.py. A new test covers that order.test_linux_downgrade.py(from main) builds its window by hand, so it now gives it the stop event too; and the cancel tests' stub gained what main'sbegin_updatenow asks for (game_version,older_version,older_ok).Tested on Linux:
test_linux_update_cancel.py(14 tests),test_linux_downgrade.py,test_linux_update.py,test_linux_update_skip.pyandtest_linux_saves_move.pypass.Looks good to me. Checked at
f62186e6.The open point from Joey-Magnus's review is fixed: the
do-buildhandler inpumpnow looks at the stop event, so a Cancel that lands after the thread's last check ends as cancelled and does not start make_game.py. The test for that order is there too. The rebase keeps main'smanifest.tag != targetcheck right after thestartup_manifest(..., get=...)call, and thegetoverride is only used whenHOOKSHOT_LINUX_LISTis unset, so it does not change the override path.I also checked that
elif self.proc is Nonecan only be reached during an update's download:self.procis reset after a build ends, and the Cancel button only exists on the progress page.Cancelledis re-raised ahead of the generic handlers in bothfetch_bytespaths, so no retries and no fall-through to the API download, andinstallremoves the staged folder through its existingexcept Exception. Without an event, the reads and waits are as before.Still open, and not worth blocking on: Joey-Magnus's minor note that a stalled socket only ends at its timeout (60 s / 120 s), so "Stopping the update" can stay up for that long. A word in the description would cover it.
CI is green (Linux and Windows Python checks, AppImage, Windows Vulkan build); the Linux-only test is skipped on Windows through
RUN_ALL_SKIP.github-sync referenced this pull request2026-11-12 06:22:54 +00:00