Skip to content

fix: require magic string in portable marker to prevent false portable mode on scoop installs#1126

Merged
cjpais merged 3 commits intocjpais:mainfrom
back1ply:fix/portable-magic-string
Mar 26, 2026
Merged

fix: require magic string in portable marker to prevent false portable mode on scoop installs#1126
cjpais merged 3 commits intocjpais:mainfrom
back1ply:fix/portable-magic-string

Conversation

@back1ply
Copy link
Copy Markdown
Contributor

@back1ply back1ply commented Mar 23, 2026

Summary

Fixes #1124

Scoop's extras bucket extracts the NSIS installer via #/dl.7z, which leaves a stale empty portable file next to the exe. This caused portable::init() to falsely trigger portable mode on non-portable scoop installs — data ends up in Data/ next to the exe, which is wiped on every scoop update.

Root cause

The original portable marker check only tested file existence (exe_dir.join("portable").exists()), so any file named portable — including the empty one left by scoop's extraction — would activate portable mode.

Fix

  • portable.rs: read the marker file and only enable portable mode if it contains the magic string "Handy Portable Mode" (whitespace-trimmed). Extracted to is_valid_portable_marker() with unit tests.
  • installer.nsi (marker creation): write "Handy Portable Mode" into the file instead of creating an empty file.
  • installer.nsi (update auto-detect): validate the magic string before setting $PortableMode = 1, so updates also respect the new format.

v0.8.0 migration

v0.8.0 shipped with an empty marker file. Users who manually update (drop new exe in place) would silently lose portable mode since the new check rejects empty files. Migration logic in portable::init(): if the marker exists but has invalid/empty content and a Data/ directory is present alongside it, treat it as a real portable install and rewrite the marker with the magic string in place. This safely distinguishes a real v0.8.0 portable install (has Data/) from a scoop extraction artifact (no Data/).

Additional fixes included

  • Desktop shortcut (finish page): removed the early return that silently ignored the "Create Desktop Shortcut" checkbox in portable mode — users can now opt in.
  • Portable update popup: added is_portable Tauri command; UpdateChecker now shows a manual-update dialog (with GitHub Releases link) instead of attempting an auto-install that would download the MSI and break portable layout.

Test plan

  • Fresh scoop install (scoop install extras/handy) — app should start in normal mode, data in %APPDATA%
  • Portable NSIS install — app should start in portable mode, data in Data/ next to exe
  • Scoop update — should not lose settings/data
  • v0.8.0 portable upgrade (empty marker + existing Data/) — app stays in portable mode, marker upgraded to magic string
  • Portable: "Check for updates" → manual update popup appears with GitHub Releases link
  • Portable: finish page "Create Desktop Shortcut" checkbox → shortcut created on desktop
  • Unit tests: cargo test portable::tests — 6 tests pass

🤖 Generated with Claude Code

back1ply and others added 3 commits March 23, 2026 17:23
…e mode

Scoop's extras bucket extracts the NSIS installer via #/dl.7z, which can
leave a stale empty portable file next to the exe. This caused
portable::init() to falsely trigger portable mode on non-portable scoop
installs, storing data in Data/ next to the exe (wiped on each update).

Changes:
- portable.rs: only enable portable mode when marker contains
  "Handy Portable Mode"; extracted is_valid_portable_marker() with tests
- installer.nsi: write magic string when creating marker file
- installer.nsi: validate magic string in update auto-detect block
- installer.nsi: allow desktop shortcut creation from finish page
  checkbox in portable mode (was silently doing nothing)
- commands/mod.rs + lib.rs: expose is_portable() as a Tauri command
- UpdateChecker.tsx: show manual-update popup for portable installs
  instead of attempting auto-install (which would download MSI)
- en/translation.json: add i18n strings for portable update popup

Fixes cjpais#1124

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…matting

Add portableUpdateTitle, portableUpdateMessage, portableUpdateButton to
all 17 non-English locales (English fallback text) so check:translations
passes. Also run prettier on the PR's modified files to fix format:check.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
v0.8.0 created an empty `portable` marker file. Users who manually
update (drop new exe in place) would silently lose portable mode since
the new magic-string check rejects empty files.

Migration: if the marker exists but has invalid/empty content AND a
`Data/` directory is present, treat it as a real portable install and
rewrite the marker with the magic string. This handles the v0.8.0 →
new upgrade path without regressing the scoop false-positive fix
(scoop does not create a Data/ directory).

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@cjpais cjpais merged commit 214e22d into cjpais:main Mar 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Setting are lost with every update using scoop

3 participants