fix(scripts/win-installer): install the version reported in Add/Remove Programs - #27927
Draft
david-fraley wants to merge 4 commits into
Draft
fix(scripts/win-installer): install the version reported in Add/Remove Programs#27927david-fraley wants to merge 4 commits into
david-fraley wants to merge 4 commits into
Conversation
SetOverwrite ifnewer compares file timestamps, so the installer skips coder.exe whenever the installed file is newer than the one being installed. Coder publishes patch releases for older minors after the newer minor ships (v2.34.7 on 2026-07-28 versus v2.35.3 on 2026-07-27), so upgrading across those releases updated the Add/Remove Programs entry while leaving the previous coder.exe in place. Downgrades and reinstalls were affected the same way. Fixes #27088 Fixes #27876
…ograms VIProductVersion requires four numeric components, so the installer pads the release version with a trailing ".0" and wrote that padded value to the Add/Remove Programs DisplayVersion. Windows then advertised 2.35.3.0 for a release the CLI reports as v2.35.3. Write the unpadded release version instead and keep the padded one for the version resource.
…emove Programs" This reverts commit 1b9887c.
Drop the sentence restating what SetOverwrite on does and keep only the non-obvious reason the previous ifnewer setting skipped coder.exe.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Installing a newer Coder release over an older one can leave
coder.exeuntouched while Add/Remove Programs is updated to the new version. The reporter installed v2.35.3 over v2.34.7 and got ARP2.35.3.0alongsidecoder versionreportingv2.34.7+b7d2742.Root cause
The install section used
SetOverwrite ifnewer, which compares file mtimes, while the registry writes a few lines below are unconditional. Coder cuts patch releases for older minor branches after newer minors ship, so the older release routinely carries the newer binary. Read straight out of the two published installers with7z l:bin/coder.exemtimeSo upgrading v2.34.7 to v2.35.3 asks NSIS to replace a file that is a day newer than its payload,
ifnewerskips it, and the registry still advertises 2.35.3.SetOverwrite on(the NSIS default) restores the invariant that the installer installs what it contains. Downgrades and repair installs over a locally modified binary fail the same way and are fixed by the same change.Fixes #27088
Fixes #27876
Linear: ENT-137, PLAT-466
Scope
This kills a cause that fires unconditionally: with the shipped artifacts, every v2.34.7 to v2.35.3 upgrade hits it, with no dependence on user action or timing. It does not close the whole symptom class. Two other paths produce the same "ARP disagrees with the CLI" result and are untouched here:
coder.exe. If the binary is running, Windows cannot replace it. Verified under Wine that the silent installer exits 0 and keeps the old binary both before and after this change, so ARP still ends up advertising a version that is not on disk. Closing it needsAllowSkipFiles offplus error handling aroundFile, or rename-and-replace. That makes silent upgrades fail loudly where they currently no-op, which changes the winget and enterprise silent-deploy contract, and it is the one path this Wine harness cannot validate. It belongs in its own PR with a real Windows test.coder versionresolves through PATH, andpath.nshonly ever appends$INSTDIR\bin, so it never wins over an earlier entry. An oldercoder.exefrom a zip or scoop install ahead of it would report the old version even after a completely successful overwrite. The reporter's screenshot shows the output ofcoder version, not the command with a full path, so this cannot be ruled out as a contributing factor in their case. If it turns out to be theirs, it is a separate bug and this change will not help them.Also unchanged: the
.0suffix in ARP.DisplayVersionstays2.35.3.0, the conventional four-part Windows form thatVIProductVersionrequires. Shortening it to2.35.3is cosmetic: winget already treats2.35.3.0and2.35.3as equal,${CODER_VERSION}is2.36.0-devel+<sha>on non-release builds, and existing SCCM/Intune rules matching2.x.y.0would see a split fleet. Not worth coupling to a correctness fix.Verification
Real NSIS installers built by the unmodified
scripts/build_windows_installer.shfrom two trees, run silently under Wine. Payload mtimes are the exact values read out of the released v2.34.7 and v2.35.3 installers above.DisplayVersioncoder.exereportsmain)How the scenario was built and run
Baseline installers come from a clean detached worktree at
origin/main, fixed installers from this branch. Both trees have a byte-identicalbuild_windows_installer.sh, so theinstaller.nsidiff is the only difference:Payloads are stub Windows binaries that print their injected version, with mtimes set to the released installers' embedded values (
2026-07-28 14:58:26for v2.34.7,2026-07-27 09:08:34for v2.35.3). NSIS preserves the source mtime on extraction, so this reproducesifnewer's exact comparison input. Each scenario uses a fresh 64-bit Wine prefix, silent-installs v2.34.7, then silent-installs v2.35.3, then readscoder.exeoutput andHKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\Coder.Secondary scenarios, same method:
maincoder.exeis held openScope of the harness: the mechanism under test is NSIS overwrite logic plus registry writes, which Wine implements faithfully, and the prefix is 64-bit so there is no
WOW6432Nodedivergence. The locked-file row is Wine-only and not verified on Windows; Windows enforces sharing violations more strictly and would show a retry/abort dialog in an interactive install. UAC, ACL-denied targets, and the ARP UI itself are also outside what this harness can show, so a confirmation pass on real Windows before release is still worth doing.The screenshot is hosted on the
dfraley/plat-466-proof-assetsbranch, which exists only to serve that image and can be deleted after merge.This pull request was created by Coder Agents on behalf of @david-fraley.