fix: clean up failed NUP extraction members #1412
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!1412
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/clean-failed-nup-member"
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?
When NUP extraction fails while writing a member or replacing its destination, the
.partfile remains on disk. This can consume space after a disk-full error or leave a complete staging file after a denied replacement.Remove the staging file after a failed write or replacement. Preserve the previous destination and the extraction error. Completed members remain available for retry.
The game-free regression injects both a partial write followed by
ENOSPCand a denied replacement. It verifies that the staging file is gone, the previous file is intact, and a retry succeeds. Both cases fail on the old code. The NUP path, missing NUP and Builder cache suites passed locally on macOS. The full game-free workflow passed on Windows and Ubuntu in the fork.AI assistance from OpenAI Codex is recorded in the commit trailers.
This landed in the v2.0.12 branch (merged there for testing; it reaches
mainwith that release), so closing. Thanks!Reopened: closing this was a mistake on my side. It was tested in the v2.0.12 branch, and it will be merged into main here, so it shows as merged. Sorry for the noise.
Looks good, nothing blocking.
dst + ".part", whichextraction_path()has already lstat-checked (no link/reparse point, no extra hard links) and kept under<out>/<part>.os.unlinkdoesn't follow links, so cleanup can't reach outside the unpack folder.with g:block exits (handle closed) before theexceptruns, so we aren't unlinking our own open handle. The.partcomes from a plainopen(..., "wb"), so no inherited read-only bit (unlike #1395's copy2 case). If AV/indexer still holds it,suppress(OSError)leaves it and the next run truncates it withwb: no regression.BaseExceptionalso covers theSystemExitfromcopy_toand Ctrl-C; the original error is re-raised unchanged. The previous destination is untouched sinceos.replaceis the only writer.open()sits outside thetry, correctly.nx2/test_nup_paths.pyis picked up by thenx2/test_*.pyglob intests/run_all.py, so no registration needed. CI: Windows source checks green, Ubuntu pending when I looked.Overlap:
make_game.pyand removes the whole.gamestaging dir when extraction/validation fails; #1412 is innup.pyand removes only the failing member's.part. Different files/hunks, they stack. For the Builder, #1393 is what actually gives the space back (a failed unpack leaves all completed members, which #1412 keeps on purpose for retry). #1412 still matters for directnup.py --extractruns, where resume is a feature. I'd land both, #1393 being the one that fixes the user-visible problem.main()innup.pyand a different hunk of the same test file: no conflict.make_game.py, not NUP member extraction. #1395 is a sibling (same stage-then-replace idea). #1393 and #1395 both touchmake_game.pyandtests/test_make_game_cache.pyin different regions, so the second to land may need a trivial rebase.Nit:
g = open(...)thenwith g:works but reads oddly;with open(temporary, "wb") as g:inside thetryis equivalent.