Repository navigation
fix(gitbash): arrow-key selection and confirmation in mintty - #763
Conversation
Git Bash in mintty gives vfox a pty that reaches it as a pipe. IsTTY therefore said no, so use, search and the prompts were skipped as non-interactive. Where the prompts did run, the key reader (atomicgo.dev/keyboard) cannot read a Windows pipe and spins on it. - IsTTY also accepts a Cygwin or MSYS2 pty, recognized by its pipe name the way go-isatty does. - CanReadKeys reports whether stdin is a console on Windows. Off Windows it stays true, as the key reader falls back to /dev/tty. - Without key input, use and search pick a version with printer.LineSelect (a number, a version, or text to filter), and the y/n prompts go through printer.Confirm, which reads a line. Consoles keep the arrow-key selection and single-key prompts. - The FAQ describes how native GitBash behaves now.
…sion-fox#98) A version whose name is a number, such as "2", must choose that version and not the second entry of the list, so keys are matched before positions.
…ms (version-fox#98) Seen on windows-latest under script(1): a native program on an MSYS2 pty gets \msys-<id>-pty0-to-master-nat for stdout and ...-from-master-nat for stdin. The name check already accepts them; these cases pin it.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #763 +/- ##
==========================================
+ Coverage 56.62% 57.45% +0.83%
==========================================
Files 116 123 +7
Lines 8595 8916 +321
==========================================
+ Hits 4867 5123 +256
- Misses 3263 3317 +54
- Partials 465 476 +11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Approval is blocked by an active correctness issue where the Windows named-pipe matcher accepts unsupported suffixes and may incorrectly enable interactive mode, along with a documentation nit requiring rewording.
2 open issues
What changed in this PR
This pull request adds native Git Bash (mintty) support for interactive vfox commands by detecting MSYS2/Cygwin pseudo-terminals and falling back to line-based selection and confirmation when Windows key input is unavailable.
Changes:
- Adds Windows PTY detection and key-reading capability checks.
- Implements line-based version selection, filtering, and confirmation prompts.
- Updates affected commands, tests, and English and Chinese FAQ documentation.
| File | Description |
|---|---|
| internal/shared/util/tty.go | Exposes helpers for detecting terminals and determining whether individual key input is available. |
| internal/shared/util/tty_windows.go | Detects Windows consoles and MSYS2/Cygwin pseudo-terminal named pipes. |
| internal/shared/util/tty_windows_test.go | Tests Windows PTY recognition and key-reading behavior. |
| internal/shared/util/tty_notwindows.go | Provides non-Windows implementations of the terminal capability helpers. |
| internal/shared/printer/select_line.go | Implements line-based version selection, filtering, cancellation, and disabled-option handling. |
| internal/shared/printer/select_line_test.go | Tests line-based selection behavior and edge cases. |
| internal/shared/printer/confirm.go | Adds line-based confirmation handling when key input is unavailable. |
| internal/shared/printer/confirm_test.go | Tests confirmation responses, defaults, and EOF behavior. |
| internal/manager.go | Routes plugin-install confirmations through the shared confirmation helper. |
| docs/zh-hans/guides/faq.md | Documents native Git Bash line-based interaction in Chinese. |
| docs/guides/faq.md | Documents native Git Bash line-based interaction in English. |
| cmd/commands/use.go | Uses line-based version selection when individual key input is unavailable. |
| cmd/commands/use_line_select_test.go | Tests line-based version selection and cancellation for the use command. |
| cmd/commands/select_windows_test.go | Verifies version selection from a Windows pipe. |
| cmd/commands/search.go | Adds line-based version selection to the search command. |
| cmd/commands/remove.go | Routes removal confirmations through the shared confirmation helper. |
| cmd/commands/link.go | Routes link confirmations through the shared confirmation helper. |
| cmd/commands/install.go | Routes installation confirmations through the shared confirmation helper. |
Review effort: Lite
…ersion-fox#98) scripts/gitbash-mintty-test.ps1 starts Git for Windows' mintty.exe running Git for Windows' bash.exe, which runs TestGitBashMinttySelection. The test checks IsTTY and CanReadKeys in that window, then the script types "2" for the selection and "y" for the confirmation with WM_CHAR messages.
…sion-fox#98) MainWindowHandle stayed zero on windows-latest although the session was interactive, so the driver now enumerates the top-level windows and takes the one of class "mintty". On a timeout it prints the windows and the mintty command lines, and it fails at once when mintty has exited.
…x#98) The first run showed that the mintty window's default gives vfox a pseudo console, where the arrow-key selection already works, so the check did not reach the pipe case of the issue. The workflow now runs the window twice: once with MSYS=disable_pcon, the classic pseudo terminal that reaches the program as a pipe, and once with the default. The test checks which one it got, and the driver types the full version, which both selectors accept.
…ote lists (version-fox#98) - search: the line selection of RunSearch moves into installByLine, which takes the installer as an interface, so the cancel, refusal, install and read-error paths run in unit tests. - use, confirm: the choice between keys and a line is a parameter of chooseVersion and confirm, so the line paths run in unit tests on every platform; the wrappers that read util.CanReadKeys stay one line each. - Read errors of LineSelect, lineConfirm and selectVersionByLine, shortAnswer, and the tty helpers off Windows are covered. - On Windows, Confirm reads a line from a pipe. What remains uncovered is interactive only: the confirmation calls in install, link, remove and manager, and the key paths of RunSearch and confirm. Those run only when stdout is a terminal.
Copilot review on version-fox#763: the matcher checked only the first five parts of a pipe name, so \msys-<id>-pty0-to-master-not-a-pty counted as a terminal and could switch a command into interactive mode. The name is now matched as a whole: the documented forms, with -nat as the only suffix, and digits after pty. The table adds names with anything else after the name.
…on-fox#98) The comment read ambiguously ("a pipe, which isCygwinTerminal recognizes").
…rsion-fox#98) In Git Bash in mintty, with the pseudo console off, the program gets its terminal as a pipe, and the keyboard library cannot read it. The arrow keys then arrived only with the line, so the selection fell back to typed lines. Now such a terminal is switched with stty to raw mode (no line buffering, no echo, no signals from Ctrl-C), which stty from Git for Windows does on the pipe. The prompts read the keys themselves: the arrow keys move the highlight in the version list, typed text filters it, Enter chooses, Ctrl-C cancels, and y and n answer a confirmation. The mode is restored afterwards. - printer.InputFor says how the prompts take their answers: the keyboard library where it can read keys, raw keys on a pty pipe where stty works, and a line otherwise. printer.Prompt takes it for choosing and confirming, and the existing call sites of the keyboard library are unchanged. - printer.KeySelect is the key-driven selection; readKey decodes the keys, including the arrow sequences of both cursor modes. - The version selection of use and search and the confirmations use the prompt; the line input stays the fallback. - The mintty check drives the down arrow in both modes and checks that the pipe mode reads the keys raw.
Every job of the workflows now carries if: github.repository == 'version-fox/vfox', so a push to a fork runs no job. The test summary keeps its always() and joins the gate with &&. Pull requests from forks still run the jobs, because a pull_request workflow runs in the base repository.
…version-fox#98) mintty.exe does not always keep its window in the process it starts. The driver treated that process exiting before the window showed as a failure, so a launch that was still starting failed the step: the classic-pty run of a71499f on the fork exited with code 0 at 0.7 seconds, while the same commit passed upstream. In every run that found a window, the window belonged to a different process than the one started. The window is now looked for by its class for 30 seconds, whatever became of the process started. A launch that shows no window is tried once more, after the mintty, bash and test processes of the first are stopped and its leftover files are removed. A bash that starts writes started.txt, which the message for a failed launch reports. The test and its assertions are unchanged.
linghengqian
left a comment
There was a problem hiding this comment.
CC @version-fox/vfox-maintainers @version-fox/vfox-committers . Could you please take a look and provide a review?


Fixes #98.
Problem
vfox useandvfox searchcould not select a version in native Git Bash (mintty):util.IsTTY()reported no terminal. The commands then took their non-interactive branch:usefailed with "Please specify a version to use in non-interactive environments", andsearchonly printed the list. The y/n prompts (remove,link,install,add) took that branch too.atomicgo.dev/keyboard, which pterm's select and confirm printers use as well) cannot read a Windows pipe. Without a console it takes a mock branch and reads from a nil file, soListenspins on empty keys and never reads the input.The pterm issue linked from #98 (pterm/pterm#616) is about
AreaPrinter.Updateappending in Git Bash, not about key input.Change
util.IsTTY()also accepts a Cygwin or MSYS2 pty: a pipe named\msys-<id>-pty<N>-(from|to)-master, with an optional-natfor native programs. Nothing else may follow the name.util.IsPtyInput()tells whether stdin is such a pty.printer.InputFor):stty(which comes with Git for Windows) switches it to raw mode for the prompt: no line buffering, no echo, no signals from Ctrl-C.printer.KeySelectandconfirmKeysread the keys: the up and down arrows move the highlight in the version list, typed text filters it, Enter chooses, Ctrl-C cancels, andyornanswers a confirmation. The mode is restored afterwards.sttycannot be used.printer.Promptcarries the input for the choice and the confirmation. The call sites ofprinter.Confirmare unchanged.Behaviour change, limited to mintty, which was non-interactive before:
use,search,installwithout a version, and the confirmations now wait for input.sdk.go).Testing
CI=true go test ./...passes on this branch, which includesmainat c4f6275.GOOS=windows go vet ./...passes as well.Git Bash in mintty(.github/workflows/gitbash-mintty.yml, driverscripts/gitbash-mintty-test.ps1). Onwindows-latestthe driver starts Git for Windows'mintty.exerunning Git for Windows'bash.exe, which runs the test binary built fromcmd/commands(TestGitBashMinttySelection). It presses the down arrow (postedWM_KEYDOWN/WM_KEYUP) and Enter, theny.MSYS=disable_pcon), which reaches the program as a pipe:IsTTYis true, the input is RawKeys, the down arrow chooses20.19.0in raw mode, andyconfirms. Passes on this head: https://github.com/version-fox/vfox/actions/runs/4359715629220.19.0through pterm. Passes on this head (same run).IsTTY: falseand the selection never returned (timed out waiting for confirm.ready). That is the reported failure: https://github.com/linghengqian/vfox/actions/runs/43594366283 (throwaway branch).ESC [ Bwithout Enter: https://github.com/linghengqian/vfox/actions/runs/43596744947windows-latest) and the other jobs on this head: https://github.com/version-fox/vfox/actions/runs/43597156308 (all pass, including the Windows CLI tests withVFOX_TEST_WINDOWS_REGISTRY=1).-natnames and the rejected suffixes), and the choice of the input.Not covered