Skip to content

Windows update support: .zip archives, .exe binary, testable pipeline#67

Merged
wesm merged 7 commits intomainfrom
windows-update
Feb 5, 2026
Merged

Windows update support: .zip archives, .exe binary, testable pipeline#67
wesm merged 7 commits intomainfrom
windows-update

Conversation

@wesm
Copy link
Owner

@wesm wesm commented Feb 5, 2026

Summary

  • Add Windows support for the self-update command: use .zip archives and msgvault.exe on Windows instead of .tar.gz and msgvault
  • Extract installFromArchiveTo for a testable update-from-archive pipeline with zip extraction, path-traversal protection, and table-driven tests
  • Avoid double-hashing during update download by plumbing the SHA-256 computed during download through to checksum verification, eliminating a redundant full file re-read

Test plan

  • All existing update tests pass (go test ./internal/update/)
  • Full project test suite passes (make test)
  • New tests cover: zip extraction, zip path traversal, tar.gz path traversal, symlink skipping, installFromArchiveTo happy paths (zip + tar.gz), checksum mismatch, empty checksum, missing binary, overwrite existing binary
  • Manual test on Windows: msgvault update downloads .zip and extracts .exe

🤖 Generated with Claude Code

wesm and others added 7 commits February 4, 2026 21:27
The update command always looked for .tar.gz assets and a binary named
"msgvault", which fails on Windows where releases use .zip archives
and the binary is "msgvault.exe".

Changes:
- Asset name uses .zip extension on Windows, .tar.gz elsewhere
- Add extractZip with zip-slip path traversal protection (reuses
  sanitizeTarPath)
- PerformUpdate dispatches to extractZip or extractTarGz based on
  asset suffix
- installBinary uses msgvault.exe on Windows
- Add TestExtractZip and TestExtractZipPathTraversal
- Add CreateZip test helper

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Split PerformUpdate into download vs extract+install so the checksum
verification, archive extraction, and binary installation can be tested
end-to-end from a local archive without hitting GitHub. Adds hashFile
helper, unexported installFromArchiveTo (checksum → extract → install),
and exported InstallFromArchive wrapper. Includes 6 table-driven subtests
covering zip, tar.gz, checksum mismatch, empty checksum, missing binary,
and overwrite scenarios.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
downloadFile already computes SHA-256 while streaming the archive to
disk, but PerformUpdate was discarding that hash and installFromArchiveTo
would re-read the entire file in hashFile. Plumb the download checksum
through a precomputedChecksum variadic parameter so online updates skip
the redundant I/O pass. The public InstallFromArchive API and all
existing tests continue to work unchanged.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
On Windows, os.MkdirTemp("", ...) can fail with "Access is denied"
when %TEMP% is restricted by antivirus, group policy, or permissions.
Add mkTempDir helper that tries the system temp dir first, then falls
back to creating a tmp/ subdirectory under the msgvault home directory.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Move temp directory creation with fallback logic from update.go into a
shared config.MkTempDir function. The function tries locations in order:
preferred dirs (caller-specified) → system temp → ~/.msgvault/tmp/.

This fixes a regression in build_cache.go where the CSV fallback temp
dir was hardcoded to filepath.Dir(dbPath), which fails for read-only DB
locations. It now passes the DB directory as a preferred dir but falls
back through system temp and ~/.msgvault/tmp/ if that's inaccessible.

Adds 5 test cases covering: system temp, preferred dir, empty preferred
dir, inaccessible preferred dir, and full fallback to msgvault home.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Probe whether chmod 0500 actually restricts writes before asserting
fallback behavior. Skips the test when running as root or with ACLs
that still permit directory creation, avoiding flaky failures in CI.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Explain why the rename-then-copy pattern works on Windows (running
executables can be renamed but not deleted/overwritten) and why the
.old cleanup is expected to fail silently for the current process.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@wesm
Copy link
Owner Author

wesm commented Feb 5, 2026

I manually verified that the artifact from this PR can successfully do msgvault update on windows!

@wesm wesm merged commit 8963e6d into main Feb 5, 2026
2 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.

1 participant