Skip to content

Windows: make in-app update work without Get-FileHash - #198

Merged
plombeer31 merged 1 commit into
mainfrom
fix/win-update-hash-fallback
Aug 21, 2026
Merged

Windows: make in-app update work without Get-FileHash#198
plombeer31 merged 1 commit into
mainfrom
fix/win-update-hash-fallback

Conversation

@sosidudku1

Copy link
Copy Markdown
Collaborator

Fixes #174.

What was broken

In-app update failed on Windows with install script exited with code 1:

error: The term 'Get-FileHash' is not recognized as the name of a cmdlet...

The same update run from cmd worked, which is the detail that points at the second cause below.

Cause 1: the installer assumed cmdlets that are not always there

Get-FileHash and Expand-Archive are not language built-ins. They ship in Microsoft.PowerShell.Utility and Microsoft.PowerShell.Archive, added in PowerShell 4.0 and 5.0 respectively, so they are missing under a 2.0 engine and absent whenever a trimmed image or an overridden PSModulePath stops those modules loading.

install.ps1 now hashes and unzips through .NET, which requires no module and is available wherever PowerShell runs at all, keeping the cmdlets and certutil as fallbacks.

Expand-Archive is fixed alongside deliberately: it fails on exactly the same machines, so fixing only the hash would have moved the error one step later rather than removing it.

Cause 2: a bare powershell.exe resolved against the user's PATH

The updater spawned powershell.exe by name, so which shell actually ran was decided by the user's PATH — a trimmed, relocated or 2.0-engine copy there loses the modules above. That is why the same script behaved differently when launched from cmd, where the name resolves to the system copy.

It now names the system copy under %SystemRoot%, falling back to the bare name when that variable is unset.

On the open question in the issue

The issue asked whether a failed checksum step should abort or warn and continue. This aborts. The checksum is the only thing standing between a tampered or truncated download and an executable the user then runs, so a checksum that cannot be computed is a failure, never a skip. The fix is to be able to compute it three ways, not to make verifying optional.

Verification

install.ps1 was executed against PowerShell 7.7, not just read:

  • hashes match shasum -a 256 byte for byte, on a 3 MB binary and a text file
  • with Get-FileHash removed (the issue Windows: in-app update fails under PowerShell (Get-FileHash not recognized), works in cmd #174 machine), hashing still returns the correct digest
  • with Get-FileHash and Expand-Archive both removed, a full verify-and-extract run succeeds
  • when no implementation can hash, it fails with our own message rather than leaking certutil.exe is not recognized
  • extraction handles nested paths and re-running over a populated directory
  • the file parses cleanly

On the TypeScript side: 14/14 in src/update/, and typecheck clean. The three new tests were confirmed to fail against the old bare-name code before passing against the new code.

Two suite failures (fs-glob-real, send-message-concurrency) reproduce on unmodified main and are unrelated to this change.

What is not covered

The end-to-end run happened on PowerShell 7 on macOS. The .NET APIs used are the ones present on Windows PowerShell 5.1, and the ExtractToDirectory behaviour that differs there is precisely why extraction walks entries — but a real 5.1 box, ideally one of the two reporters', is still worth a confirmation before release.

🤖 Generated with Claude Code

The Windows installer aborted with "'Get-FileHash' is not recognized"
during in-app self-update, while the same update run from cmd succeeded
(issue #174). Two users hit it independently on v0.2.1 -> v0.2.2.

Two independent causes, both fixed here.

Get-FileHash and Expand-Archive are not language built-ins: they live in
Microsoft.PowerShell.Utility and Microsoft.PowerShell.Archive, added in
PowerShell 4.0 and 5.0. They are absent under a 2.0 engine and whenever a
trimmed image or an overridden PSModulePath keeps those modules from
loading. install.ps1 now hashes and unzips through .NET, which needs no
module, and keeps the cmdlets plus certutil only as fallbacks.

Checksum verification stays mandatory: a checksum that cannot be computed
is a failure, never a skip, since it is the only thing standing between a
tampered download and an executable the user then runs.

The updater also spawned a bare "powershell.exe", resolved against the
user's PATH rather than ours -- which is why the same script behaved
differently from cmd. It now names the system copy under %SystemRoot%,
falling back to the bare name when that variable is unset.

Extraction walks zip entries rather than calling ExtractToDirectory,
which throws on .NET Framework when the destination already exists, as
the staging dir always does.

Verified against PowerShell 7.7: hashes match shasum byte for byte on the
normal path and with Get-FileHash removed, and a full verify-and-extract
run succeeds with both cmdlets absent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@plombeer31
plombeer31 merged commit edf3444 into main Aug 21, 2026
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.

Windows: in-app update fails under PowerShell (Get-FileHash not recognized), works in cmd

2 participants