[Bug]: Windows onefile extracts from older versions stay under Local AppData #167
Labels
No labels
Kind/Bug
Kind/Feature
Priority/High
Priority/Medium
Reviewed/Confirmed
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
ai-collab/bulk-image-organizer#167
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
App version
Current packaged Windows builds (Nuitka onefile). Affects
%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizerafter upgrading through several versions / builds.Platform
Windows
Steps to reproduce
.exe(it unpacks into%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizer\<version+build-id>)..exe(different version or different git build id).%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizer.Expected behavior
Opening a newer packaged build removes extract trees from older versions/builds. Only the currently running unpack remains under that product folder. The folder does not keep growing with every upgrade.
This is already the documented intent (DR-016,
prune_stale_onefile_caches()on startup, TECHNICAL_OUTLINE / DESIGN: stale caches pruned on startup).Actual behavior
AppData\Local\ValleyGeek\Bulk Image Organizergrows too large. Artifacts from past versions remain on disk after launching a newer application.Additional context
core/onefile_cache.pyalready tries to prune sibling cache folders at startup, but it is conservative and can leave leftovers:+are treated as cache segments (_is_cache_segment_dir). Older or Nuitka-default{VERSION}folders (no+build-id) are never deleted.rmtreeis logged as a warning and the tree is left in place (locks, AV, in-use files).exeparent ≠ unpack root).%APPDATA%\bulk-image-organizer) and workspace caches (%LOCALAPPDATA%\bulk-image-organizer) are not this folder and must not be deleted.Desired fix
On packaged Windows startup, after the current extract is in use:
%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizerexcept the active unpack (not only+name siblings).BIO_SKIP_ONEFILE_CACHE_PRUNE.%APPDATA%\bulk-image-organizer(settings/logs) or%LOCALAPPDATA%\bulk-image-organizer(network workspace caches).Acceptance criteria
ValleyGeek\Bulk Image Organizer.+in the name (legacy{VERSION}extracts) are also removed.+) and the skip-env / non-onefile no-ops.prune_stale_onefile_caches()only removed sibling unpack dirs whose name contained+(the current<VERSION>+<build-id>convention introduced during beta). Older/legacy<VERSION>-only extract dirs from prior builds never matched that check, so they accumulated indefinitely under%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizer\.Fix: every directory under the product cache folder other than the active unpack is now removed, regardless of naming. Unchanged: the active unpack is still skipped, a sibling that fails to delete (e.g. locked by AV) is logged and skipped rather than aborting startup,
BIO_SKIP_ONEFILE_CACHE_PRUNEis honored, and the check that scopes pruning to theValleyGeek\Bulk Image Organizerfolder underLOCALAPPDATAkeeps this from ever touching the separate workspace-cache (LOCALAPPDATA\bulk-image-organizer) or settings (APPDATA\bulk-image-organizer) paths.Pushed to
0.6.1/issue-167-onefile-cache-prunewith new tests for legacy bare-version folders and a locked-sibling case (tests/test_onefile_cache.py), plus README/USER_GUIDE notes. Full suite (702 passed) and ruff pass. Not yet merged tomain— awaiting PR request.Fresh Windows test of the fix above:
%LOCALAPPDATA%\ValleyGeek\Bulk Image Organizerstill held eleven extract directories spanning 0.4.0 through the current0.6.1/v0.7.0branch, including two consecutive builds of that same branch three hours apart — the newer run never cleaned up the one immediately before it.This is a stronger signal than my original fix accounted for: every listed folder name already contains
+, including builds from before this issue's fix (which only required"+" in path.nameto qualify for deletion). So dropping the+-only naming restriction cannot have been the operative bug — deletion has evidently never happened on a real Windows run, before or after that change.prune_stale_onefile_caches()has several early-return guards ahead of the deletion loop (platform/frozen checks, theexe_parent != bundle_root()onefile-detection check,_is_managed_cache_parent()'s layout check), and packaged builds log at INFO while most of these guards previously logged at DEBUG or not at all — so the existing log file couldn't distinguish "a guard short-circuited" from "ran but every deletion failed" from "never called." Rather than guess again at which of several possible causes it is (issue #167's first fix already shows a plausible-looking guess can ship without being the real cause), I added INFO-level logging to every branch of the prune logic — no behavior change — documented as DR-038. Landed in0.6.1/v0.7.0at commiteb14446(issue references corrected ineab99c6).Next step (needs a Windows run): launch a build with this change, then check
%APPDATA%\bulk-image-organizer\logs\bulk-image-organizer.logfor theOnefile cache prunelines from that session — that will show exactly which guard is short-circuiting, or the sibling list state if it reached the deletion loop, or which specific deletions failed and why. Could you share that log section next time you run a build?(Note: I mistakenly opened a separate issue #171 for this instead of commenting here — closing it as a duplicate of this one.)
Root cause confirmed — thank you for the log. That one line said everything:
is_frozen_runtime()and its duplicate_running_packaged()both fall back totry: import __compiled__ except ImportError: return Falsewheneversys.frozenisn't set — and your real builds evidently never setsys.frozen. The bug: Nuitka does not register a real importable module called__compiled__. It injects__compiled__as a plain global variable into the namespace of every module it compiles — the documented way to check is"__compiled__" in globals().import __compiled__as a statement raisesImportErroreven inside a genuine compiled build, which is exactly why detection always failed. The test suite never caught this because every existing test either setsys.frozen = Truedirectly (skipping the buggy branch) or mocked__compiled__as asys.modulesentry — same wrong assumption baked into the tests as the code.Fixed both functions to use
"__compiled__" in globals(). I didn't just trust the docs on this — I built a real Linux Nuitka--standalonesmoke binary and ran it headlessly: it correctly wrote to the packaged log path (~/.config/.../logs/, INFO-only, no dev fallback) instead of the temp dev log, confirming the fix works against an actual compiled process, not just reasoning about Nuitka's API. New regression tests simulate the exact confirmed failure shape and were verified to fail without the fix and pass with it. Full details in DR-039.Pushed to
0.6.1/v0.7.0ated41f3a. This should now actually delete the oldAppData\Local\ValleyGeek\Bulk Image Organizerextract dirs on the next launch of a build with this fix — appreciate you testing that when you get a chance.Confirmed fixed from your log:
prune_stale_onefile_caches()found only 1 sibling dir under the product folder — the current build itself (0.6.1+adhoc.v0.7.0+f834e80) — meaning every old extract from prior versions was already cleaned up before this run even started.is_frozen_runtime()now correctly detects the onefile runtime (exe_parent=D:\Downloads,unpack_root=...\0.6.1+adhoc.v0.7.0+f834e80) and the deletion loop runs as intended. Thanks for confirming — closing this out.