Compare commits

...
17 Commits
Author SHA1 Message Date
Junegunn Choi ab9a552f5b Re-order CHANGELOG 2026-11-06 10:39:01 +09:00
Junegunn Choi a38beb544f Merge remote-tracking branch 'origin/master' into winget-msi 2026-11-06 10:37:26 +09:00
Junegunn Choi b5c054ee18 Document draft release flow in RELEASE.md 2026-11-05 22:55:33 +09:00
Junegunn Choi 3dd8e03ddc Fail msi job when release is already published
- 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
2026-11-05 22:55:16 +09:00
Junegunn Choi 02d8843869 Remove make release
- 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
2026-11-05 22:55:01 +09:00
Junegunn Choi c076805825 Note how to switch existing winget installs to MSI 2026-11-04 16:02:58 +09:00
Junegunn Choi 398b66342d Drop armv7 from winget installer regex
- No Windows armv7 build since 0.73.1
2026-11-04 16:02:58 +09:00
Junegunn Choi fa30c5a278 Fix wix build path in usage comment
- Source="LICENSE" resolves from repository root, where CI runs it
2026-11-04 16:02:58 +09:00
Junegunn Choi e279faac50 Replace existing draft when retrying release job
- 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
2026-11-03 13:51:47 +09:00
Junegunn Choi 386c3e9ea9 Limit winget workflow token to read access
- 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
2026-11-03 13:51:37 +09:00
Junegunn Choi 14aacc115d Avoid second environment approval for msi job
- 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
2026-10-29 21:41:27 +09:00
Junegunn Choi 5853d57518 Add CHANGELOG entry for #4943 2026-10-26 20:29:14 +09:00
Junegunn Choi e19c2c4c6e Shorten MSI comments 2026-10-26 20:28:46 +09:00
Junegunn Choi 68ef9cf0d0 Skip MSI build for prerelease versions
- MSI versions cannot have a suffix such as -rc1, so wix build fails
- Release is still published
2026-10-26 20:27:47 +09:00
Junegunn Choi 243ccc325b Add MSIs to release checksum file 2026-10-26 20:27:22 +09:00
Junegunn Choi d7138593f2 Publish release only after MSI upload
- 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
2026-10-26 20:26:50 +09:00
Marshall Porter 5b2936db82 Add WiX .msi installer alongside portable zip for Windows
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.
2026-10-26 20:26:33 +09:00
8 changed files with 188 additions and 54 deletions

No files matched your search

+94
View File
@@ -18,6 +18,8 @@ jobs:
release:
runs-on: macos-latest
environment: release
outputs:
version: ${{ steps.ver.outputs.version }}
steps:
- uses: actions/checkout@v7
with:
@@ -74,3 +76,95 @@ jobs:
MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
MACOS_NOTARY_KEY: ${{ secrets.MACOS_NOTARY_KEY }}
# For the msi job
- uses: actions/upload-artifact@v7
with:
name: windows-binaries
path: |
dist/*windows_amd64*/fzf.exe
dist/*windows_arm64*/fzf.exe
if-no-files-found: error
# winget's portable install runs fzf through a symlink that admin SSH
# sessions refuse to follow (#4943). The MSI installs a real file instead.
# WiX only runs on Windows.
msi:
needs: release
runs-on: windows-latest
env:
# MSI versions cannot have a suffix such as -rc1
BUILD_MSI: ${{ !contains(needs.release.outputs.version, '-') }}
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
if: env.BUILD_MSI == 'true'
with:
name: windows-binaries
path: dist
- uses: actions/setup-dotnet@v6
if: env.BUILD_MSI == 'true'
with:
dotnet-version: '8.0.x'
# WiX 6 and later fall under the Open Source Maintenance Fee
- name: Install WiX
if: env.BUILD_MSI == 'true'
run: dotnet tool install --global wix --version 5.0.2
- name: Build Windows MSI installers
if: env.BUILD_MSI == 'true'
shell: bash
run: |
set -e
V='${{ needs.release.outputs.version }}'
for pair in "amd64:x64" "arm64:arm64"; do
goarch="${pair%%:*}"
wixarch="${pair##*:}"
bindir=$(dirname "$(find dist -type f -iname fzf.exe -path "*windows_${goarch}*" | head -n1)")
wix build wix/main.wxs -arch "$wixarch" -d Version="$V" -d BinDir="$bindir" -o "dist/fzf-${V}-windows_${goarch}.msi"
done
ls -la dist/*.msi
- uses: actions/upload-artifact@v7
if: env.BUILD_MSI == 'true'
with:
name: windows-msi
path: dist/*.msi
- name: Upload MSI installers and publish release
if: github.event_name == 'push'
shell: bash
run: |
set -e
V='${{ needs.release.outputs.version }}'
# A re-run would replace MSIs that a winget manifest may already
# reference, and submit to winget again
if [[ $(gh release view "v$V" --json isDraft --jq .isDraft) != true ]]; then
echo "::error::v$V is already published"
exit 1
fi
if [[ $BUILD_MSI == true ]]; then
sums="fzf_${V}_checksums.txt"
# Drop MSI lines from a previous run of this job
gh release download "v$V" -p "$sums" -O - | grep -v '\.msi$' > "dist/$sums"
# sha256sum on Windows may print '<hash> *<name>'
(cd dist && sha256sum fzf-*.msi) | sed 's/ \*/ /' >> "dist/$sums"
gh release upload "v$V" dist/*.msi "dist/$sums" --clobber
fi
gh release edit "v$V" --draft=false
env:
GITHUB_TOKEN: ${{ github.token }}
# Called directly because a release published with GITHUB_TOKEN does not
# trigger other workflows
winget:
needs: [release, msi]
if: github.event_name == 'push' && !contains(needs.release.outputs.version, '-')
uses: ./.github/workflows/winget.yml
with:
release-tag: v${{ needs.release.outputs.version }}
secrets:
WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }}
+15 -4
View File
@@ -1,7 +1,13 @@
name: Publish to Winget
on:
release:
types: [released]
workflow_call:
inputs:
release-tag:
type: string
required: true
secrets:
WINGET_TOKEN:
required: true
workflow_dispatch:
inputs:
release-tag:
@@ -9,6 +15,11 @@ on:
required: true
type: string
# winget-releaser passes this token to unpinned actions. Submission uses
# WINGET_TOKEN.
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
@@ -16,6 +27,6 @@ jobs:
- uses: vedantmgoyal2009/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e # v2
with:
identifier: junegunn.fzf
release-tag: ${{ inputs.release-tag || github.event.release.tag_name }}
installers-regex: '-windows_(armv7|arm64|amd64)\.zip$'
release-tag: ${{ inputs.release-tag }}
installers-regex: '-windows_(arm64|amd64)\.(zip|msi)$'
token: ${{ secrets.WINGET_TOKEN }}
+4
View File
@@ -120,6 +120,10 @@ release:
owner: junegunn
name: fzf
prerelease: auto
# Published by the msi job in release.yml after it uploads the MSIs
draft: true
# A retried release job would otherwise add a second draft for the tag
replace_existing_draft: true
name_template: '{{ .Version }}'
snapshot:
-3
View File
@@ -19,9 +19,6 @@ make install
# Build fzf binaries and archives for all platforms using goreleaser
make build
# Publish GitHub release
make release
```
> [!WARNING]
+11 -5
View File
@@ -9,8 +9,6 @@ CHANGELOG
--bind 'ctrl-a:change-info-command:echo "mode: A | $FZF_INFO"' \
--bind 'ctrl-b:change-info-command:echo "mode: B | $FZF_INFO"'
```
- Fixed `--with-shell` on Windows not detecting cmd, PowerShell, and pwsh, which made fzf quote commands and entries for a POSIX shell (#4936) (@vimkim)
- e.g. With `--with-shell "cmd.exe /d /c"`, `--preview '"C:\Program Files\nu\bin\nu.exe" --version'` failed
- `transform-nth` and `transform-with-nth` (and their `bg-` variants) no longer split the output at `|`
- Only `change-nth` and `change-with-nth` rotate through `|`-separated values
- Fixed the bound command being replaced with the output. e.g. With `transform-nth(echo '1|2')`, the next key press ran `2|1` as a shell command.
@@ -30,12 +28,20 @@ CHANGELOG
# Now you can write:
ls -al | fzf --with-nth .. --bind 'space:change-with-nth:..|1|2'
```
- nushell
- The completion integration now requires nushell 0.116.1 or above, where an interactive completer can take the terminal through the `@interactive` attribute (#4934) (@sim590)
- Fixed `--no-sort` being ignored in filter mode with `--sync`, `--tac`, or `--bench` (#4940) (@NavpreetSSidhu)
- Vim plugin: Fixed garbage text on screen in Neovim with `set title` when the fzf command contains ANSI escape sequences (#4941)
- Fixed `--walker=follow` skipping a symlink in one `--walker-root` when its target is an ancestor of another root (#4944) (@mabunemeh)
- Fixed O(n^2) action parsing in `--listen`. A single ~1 MB request could block the server for ~30 s (@zachariah-mithani, AploSec)
- Windows
- Fixed `--with-shell` on Windows not detecting cmd, PowerShell, and pwsh, which made fzf quote commands and entries for a POSIX shell (#4936) (@vimkim)
- e.g. With `--with-shell "cmd.exe /d /c"`, `--preview '"C:\Program Files\nu\bin\nu.exe" --version'` failed
- Added `.msi` installers for amd64 and arm64, also offered through winget (#4943) (@meop)
- The portable package runs fzf through a symlink in the WinGet Links folder, which an administrator's SSH session refuses to follow
- The MSI installs `fzf.exe` under Program Files and adds it to the system PATH
- `winget upgrade` keeps an existing portable install. To switch to the MSI, run `winget uninstall junegunn.fzf` and `winget install junegunn.fzf`.
- Vim plugin
- Fixed garbage text on screen in Neovim with `set title` when the fzf command contains ANSI escape sequences (#4941)
- nushell
- The completion integration now requires nushell 0.116.1 or above, where an interactive completer can take the terminal through the `@interactive` attribute (#4934) (@sim590)
0.74.5
------
+1 -41
View File
@@ -144,46 +144,6 @@ tag: prerelease
git tag -s v$(VERSION) -m v$(VERSION)
git push origin v$(VERSION)
release:
# Make sure that the tests pass and the build works
TAGS=tcell make test
make test build clean
ifndef GITHUB_TOKEN
$(error GITHUB_TOKEN is not defined)
endif
# Check if we are on master branch
ifneq ($(shell git symbolic-ref --short HEAD),master)
$(error Not on master branch)
endif
# Check if version numbers are properly updated
grep -q ^$(VERSION_REGEX)$$ CHANGELOG.md
grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf.1
grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf-tmux.1
grep -qF $(VERSION) install
grep -qF $(VERSION) install.ps1
# Make release note out of CHANGELOG.md
mkdir -p tmp
sed -n '/^$(VERSION_REGEX)$$/,/^[0-9]/p' CHANGELOG.md | tail -r | \
sed '1,/^ *$$/d' | tail -r | sed 1,2d | tee tmp/release-note
# Push to temp branch first so that install scripts always works on master branch
git checkout -B temp master
git push origin temp --follow-tags --force
# Make a GitHub release
goreleaser --clean --release-notes tmp/release-note
# Push to master
git checkout master
git push origin master
# Delete temp branch
git push origin --delete temp
clean:
$(RM) -r dist target
@@ -233,4 +193,4 @@ update:
$(GO) get -u
$(GO) mod tidy
.PHONY: all generate build prerelease tag release test itest bench lint install clean docker docker-test update fmt
.PHONY: all generate build prerelease tag test itest bench lint install clean docker docker-test update fmt
+20 -1
View File
@@ -35,13 +35,29 @@ triggered by a tag push.
4. The workflow fires on the tag push and pauses on the `release`
environment gate. Approve it in the Actions tab to release.
- goreleaser creates a draft release
- The `msi` job builds the Windows MSIs, uploads them, and publishes
the release
- The `winget` job submits the release to winget
5. After the GitHub release is published, fast-forward `master`:
5. After the `msi` job publishes the GitHub release, fast-forward `master`:
```sh
git push origin master
```
### If the `msi` job fails
The release stays a draft. Do not push `master` until it is published.
- Fix the problem and re-run the failed jobs, or
- Publish without MSIs and submit to winget by hand:
```sh
gh release edit v0.74.5 --draft=false
gh workflow run winget.yml -f release-tag=v0.74.5
```
## Testing the workflow
To exercise the workflow without firing a real release:
@@ -53,6 +69,9 @@ To exercise the workflow without firing a real release:
3. Approve the `release` environment gate when prompted.
4. Goreleaser runs with `--snapshot --skip=publish`. Signing and
notarization run; only the GitHub release upload is skipped.
5. The `msi` job builds the MSIs and uploads them as the `windows-msi`
artifact of the run. The release upload and the `winget` job are
skipped.
Use this to validate the workflow YAML, version-extraction logic,
the macOS runner setup, and the signing/notarization credentials.
+43
View File
@@ -0,0 +1,43 @@
<?xml version='1.0' encoding='utf-8'?>
<!--
Per-machine installer for fzf. See the msi job in .github/workflows/release.yml.
wix build wix/main.wxs -arch <x64|arm64> -d Version=<ver> -d BinDir=<dir of fzf.exe>
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package
Name="fzf"
Manufacturer="junegunn"
Version="$(var.Version)"
UpgradeCode="C34C8C24-3E05-4D14-BD9A-439C3F6E9942"
Language="1033"
Scope="perMachine">
<SummaryInformation Description="fzf - a command-line fuzzy finder" Manufacturer="junegunn" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed. Setup will now exit." />
<MediaTemplate EmbedCab="yes" />
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="APPLICATIONFOLDER" Name="fzf">
<Directory Id="BinFolder" Name="bin" />
</Directory>
</StandardDirectory>
<Feature Id="Main" Title="fzf" Level="1" AllowAbsent="no">
<ComponentGroupRef Id="MainComponents" />
</Feature>
<ComponentGroup Id="MainComponents" Directory="BinFolder">
<Component Id="FzfBinary">
<File Id="FzfExe" Name="fzf.exe" Source="$(var.BinDir)\fzf.exe" KeyPath="yes" />
</Component>
<Component Id="LicenseFile" Directory="APPLICATIONFOLDER">
<File Id="License" Name="LICENSE" Source="LICENSE" KeyPath="yes" />
</Component>
<Component Id="PathEnv" Guid="8B7C2C6A-F4A0-4E3E-9E37-2E8F3E4E5B6A">
<Environment Id="PATH" Name="PATH" Value="[BinFolder]" Permanent="no" Part="last" Action="set" System="yes" />
</Component>
</ComponentGroup>
</Package>
</Wix>