Repository navigation
Add MSI installers for Windows and offer them through winget - #4948
Conversation
winget currently only has a `portable` installer to offer for fzf, which
installs via a symlink into the WinGet Links folder. That link is created
by whoever runs winget, normally a non-elevated terminal, and Windows will
not let an elevated process follow a link that a less-privileged process
created ("The path cannot be traversed because it contains an untrusted
mount point"). An administrator's SSH session is elevated, so fzf, and the
shell integration that runs it at startup, fails there.
Build a real per-machine .msi with the WiX dotnet-tool CLI (this is a Go
project, so cargo-wix doesn't apply) from the goreleaser-built fzf.exe, for
amd64 and arm64 (WiX's -arch has no armv7 target). It installs to Program
Files with its own machine PATH entry, so there is no link. WiX only runs
on Windows, so the release job hands the Windows binaries to a new msi job
on windows-latest. The MSIs are built in both workflow modes, so a
workflow_dispatch validation run checks them, and uploaded alongside the
.zip on a tag push; the winget-releaser installers-regex also matches .msi.
WiX is pinned to 5.0.2, the last release that does not require accepting
its Open Source Maintenance Fee EULA.
- goreleaser creates a draft release - msi job publishes it after uploading the MSIs - Publishing triggers winget.yml, which previously ran before the MSIs existed and left them out of the manifest - If the msi job fails, the release stays a draft
- MSI versions cannot have a suffix such as -rc1, so wix build fails - Release is still published
- msi job used RELEASE_PAT only so that publishing would trigger winget.yml, which required approving the release environment again - Publish with GITHUB_TOKEN instead and call winget.yml directly - Skip winget for prerelease versions, as the released event did
There was a problem hiding this comment.
🔵 Needs a closer look
Approval should be deferred because the release path’s draft publication, Windows MSI and checksum uploads, and post-publish WinGet workflow remain unverified in an end-to-end real-tag run.
No open issues 🎉
Review effort: Lite
- winget-releaser passes github.token to cargo-bins/cargo-binstall@main - Token had contents: write from release.yml, and repository default (write) when run by hand
- goreleaser looks up the release by tag, which does not return drafts - A retry created a second draft for the same tag, and msi job could publish either one
meop
left a comment
There was a problem hiding this comment.
I reviewed the diff and it looks good to me. Nothing blocking.
I also ran the real flow from tag pushes on my fork, since the PR lists it as untested, with Claude Code doing the runs: v0.99.11, v0.99.12, v0.99.13-rc1.
- Draft and publish: goreleaser's draft ends up published and becomes latest over the older releases.
- Checksums: both MSIs are in the file and the downloads match. Re-running the msi job doesn't duplicate the lines.
- winget job: called through
workflow_call, it finds both zips and both MSIs. Its token is read-only (a write attempt got a 403). -rc1: published as a prerelease with no MSIs, winget skipped, latest unchanged.- MSIs, x64 and arm64: installing over the previous version leaves one Installed apps entry and one PATH entry.
fzf.exeis the right architecture and runs from PATH. Uninstall removes the entry and folder, and restores PATH exactly.
How that differs from your release:
- goreleaser ran on Ubuntu with fewer targets and no notarization, using
GITHUB_TOKENinstead ofRELEASE_PAT. - A stub replaced winget-releaser. It does the same release lookup and regex, but nothing was submitted, and Komac wasn't run.
- I didn't test retrying the release job over an existing draft (
replace_existing_draft). - The installs ran elevated on the runner, so no UAC. My earlier test on #4943 covered UAC on x64 only.
Small things, your call:
wix/main.wxs: the usage comment sayswix build main.wxs, butSource="LICENSE"looks like it only resolves from the repo root, which is where CI runs it.wix build wix/main.wxswould match.winget.yml: the regex still listsarmv7, which has no Windows build now.CHANGELOG.md: existing portable installs stay portable onwinget upgrade, so those users need to uninstall and reinstall once to get the fix. That may be worth a line.- Re-running the msi job also re-runs the winget job, since it depends on msi. After a successful release, that would try to submit to winget again.
- Source="LICENSE" resolves from repository root, where CI runs it
- No Windows armv7 build since 0.73.1
|
Thanks, @meop. I've addressed your comments in the following commits. |
- Release workflow replaced it - With draft releases, it would push master while the release was still a draft, so install scripts would point at assets not yet public
- Re-run would replace published MSIs with new builds, whose hashes differ from a winget manifest that may already be merged - Also stops winget job from submitting again
meop
left a comment
There was a problem hiding this comment.
Thanks, the changes look good to me.
|
Thanks for the review. I'll release a new version this weekend and see how it goes. |
|
Thank you for working on this so quickly. |
|
Please take a look: https://github.com/junegunn/fzf/releases/tag/v0.75.0 |

Based on @meop's branch from #4943, with fixes on top.
Problem
winget installs fzf as a portable package, so it runs
fzf.exethrough a symlink in%LOCALAPPDATA%\Microsoft\WinGet\Links. A non-admin user created that link, and an administrator's SSH session refuses to follow it (error 448, "The path cannot be traversed because it contains an untrusted mount point").Changes
.msiinstallers for amd64 and arm64 with WiX 5 on a Windows runner (@meop)fzf.exeunder Program Files and adds it to the system PATHwinget.ymlcould start before the MSIs existed and leave them out of the manifestreplace_existing_draftkeeps a retried release job from creating a second draftGITHUB_TOKEN, so it does not need thereleaseenvironmentGITHUB_TOKENdoes not trigger other workflows, sorelease.ymlcallswinget.ymldirectly throughworkflow_callwinget.ymlruns with a read-only tokenfzf_<ver>_checksums.txt-rc1, which MSI does not supportmake release. It would pushmasterwhile the release was still a draft.RELEASE.mdfor the draft flowTesting
-rc1prerelease, and install, upgrade, and uninstall of both MSIsNot tested:
Close #4943
🤖 Generated with Claude Code