Commit d4b8c08
authored
console: replace terminal UI with Spectre.Console (#2377)
## Summary
Replace GCM's line-oriented `ITerminal` / `TerminalMenu` stack with a
Spectre.Console-backed console layer. Interactive prompts now use the
controlling terminal, while diagnostics and other output-only messages
continue to use standard error. Git's credential-protocol stdin and
stdout remain untouched.
The branch adds the cross-platform TTY plumbing needed to make that
split work on macOS, Linux, and Windows, then migrates every existing
terminal prompt and removes the old abstraction. This is deliberately
the console foundation for richer account-picker flows; it does not add
the follow-on account-selection behaviour itself.
## Why
The existing terminal surface supports basic line output, text/secret
input, and numbered one-shot menus. That is enough for today's prompts,
but not for the multi-choice, filter-as-you-type, confirmation, and
progress experiences needed by the account-picker work.
Using Spectre.Console directly is not safe for a credential helper
because its default console uses process stdin/stdout, which Git
reserves for the credential protocol. This change introduces an explicit
routing layer:
- Output-only messages render to stderr, where they remain visible and
capturable even without a terminal.
- Prompts use the controlling TTY through `/dev/tty` on POSIX and
`CONIN$` / `CONOUT$` on Windows.
## What changes
**Console architecture**
- Add Spectre.Console 0.57.0 and expose a single `IConsoleService
Console` on `ICommandContext`.
- Back the service with two `IAnsiConsole` instances: an output-only
stderr console and an interactive controlling-TTY console.
- Preserve plain redirected output by disabling ANSI colour when stderr
is redirected.
- Fall back to a non-interactive, null-input console when no controlling
TTY can be opened rather than ever borrowing Git's standard streams.
- Add a Spectre-backed test console so prompt tests feed real text,
secret, arrow-key, and Enter input through the same prompt
implementations used in production.
**Native TTY support**
- Add POSIX output and input adapters over `/dev/tty`. The input side
uses `termios` raw mode plus a platform-neutral ANSI escape parser for
navigation, function, modifier, control, and UTF-8 key sequences.
- Use `select(2)` for key availability and the short standalone-Escape
disambiguation window. Unlike `poll(2)`, this works for TTY character
devices on both macOS and Linux.
- Add Windows output and input adapters over `CONOUT$` and `CONIN$`.
Output enables virtual-terminal processing where available; input
translates `ReadConsoleInput` key events into `ConsoleKeyInfo`.
- Scope raw mode to an active read or prompt instead of the process
lifetime. A prompt-level hold prevents fast typeahead from being echoed
between reads during masked input, while returning to cooked mode
between prompts keeps normal Ctrl+C handling working during network,
broker, and GUI operations.
- Restore terminal state on normal completion, exception unwinding, and
relevant POSIX signals. Ctrl+C read during a prompt becomes an
`InterruptedException`, which exits quietly with the conventional status
code 130 after the terminal has been restored.
**Prompt and diagnostic migration**
- Move username, password/token, two-factor, authentication-mode,
Windows integrated-authentication, OAuth, Microsoft-authentication, and
existing account-selection prompts to Spectre.Console.
- Replace numbered text menus with interactive selection prompts while
preserving the available choices, ordering, and default selection.
- Route warnings, errors, informational messages, device-code text, and
fatal exception output through `IConsoleService`, giving them one
consistent stderr-backed surface.
- Delete `ITerminal`, the POSIX/macOS/Linux/Windows terminal
implementations, `TerminalMenu`, and the old test double once their
final consumers move.
- Include the process ID in the wait-for-debugger message so the correct
GCM process is unambiguous when several helpers are running.
## User-visible behaviour and compatibility
- The Git credential wire protocol is unchanged: stdin and stdout are
still used only for communication with Git.
- Terminal selection prompts now use arrow-key navigation instead of
asking the user to enter a menu number. Authentication choices and
ordering are unchanged.
- Human-facing prefixes are coloured when stderr is attached to a
terminal and remain plain text when redirected.
- Ctrl+C during an interactive prompt restores the terminal and exits
with status 130 without printing a misleading `fatal:` message.
- Existing GUI prompt paths and authentication decisions are unchanged.
- In headless environments, interactive input degrades to the explicit
non-interactive console rather than reading from redirected protocol
input.
## Testing
- `dotnet test git-credential-manager.slnx --configuration Debug`
New coverage includes the ANSI escape parser, POSIX `select(2)`
semantics, Windows key-event translation, raw-mode prompt lifetime and
cleanup, headless console contracts, stderr routing, interrupt
detection, and the migrated provider prompt flows.
## Notes for reviewers
- The eight commits are arranged as a progression: console facade, TTY
output, POSIX input, Windows input, contract tests, the debugger-message
tweak, diagnostic routing, and finally the atomic prompt
migration/removal of `ITerminal`. Reviewing commit-by-commit is
recommended.
- The highest-risk code is isolated in the native input adapters. The
key invariant is that raw mode is reference-counted and held only while
a read or prompt owns it; every completion and interruption path must
restore the original terminal mode.
- The account-picker work can build on this branch without reopening the
credential-protocol or platform-TTY layers.65 files changed
Lines changed: 2795 additions & 946 deletions
File tree
- src
- Atlassian.Bitbucket.Tests
- Atlassian.Bitbucket
- Core.Tests
- Authentication
- Interop
- Posix
- Windows
- Tty
- Core
- Authentication
- Commands
- Interop
- Linux
- MacOS
- Posix
- Native
- Windows
- Native
- Tty
- GitHub.Tests
- GitHub
- GitLab.Tests
- GitLab
- Microsoft.AzureRepos
- TestInfrastructure
- Objects
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
34 | 36 | | |
| |||
Lines changed: 7 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
20 | | - | |
| 19 | + | |
21 | 20 | | |
22 | 21 | | |
23 | 22 | | |
| |||
52 | 51 | | |
53 | 52 | | |
54 | 53 | | |
55 | | - | |
| 54 | + | |
56 | 55 | | |
57 | 56 | | |
58 | 57 | | |
| |||
73 | 72 | | |
74 | 73 | | |
75 | 74 | | |
76 | | - | |
77 | | - | |
78 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
79 | 78 | | |
80 | 79 | | |
81 | 80 | | |
| |||
96 | 95 | | |
97 | 96 | | |
98 | 97 | | |
99 | | - | |
100 | | - | |
| 98 | + | |
| 99 | + | |
101 | 100 | | |
102 | 101 | | |
103 | 102 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
| 14 | + | |
13 | 15 | | |
14 | 16 | | |
15 | 17 | | |
| |||
102 | 104 | | |
103 | 105 | | |
104 | 106 | | |
105 | | - | |
| 107 | + | |
106 | 108 | | |
107 | 109 | | |
108 | 110 | | |
| |||
145 | 147 | | |
146 | 148 | | |
147 | 149 | | |
148 | | - | |
| 150 | + | |
149 | 151 | | |
150 | 152 | | |
151 | 153 | | |
152 | 154 | | |
153 | 155 | | |
154 | 156 | | |
155 | | - | |
| 157 | + | |
156 | 158 | | |
157 | 159 | | |
158 | 160 | | |
159 | 161 | | |
160 | | - | |
| 162 | + | |
161 | 163 | | |
162 | 164 | | |
163 | 165 | | |
164 | 166 | | |
165 | | - | |
| 167 | + | |
166 | 168 | | |
167 | 169 | | |
168 | 170 | | |
169 | | - | |
| 171 | + | |
170 | 172 | | |
171 | 173 | | |
172 | 174 | | |
| |||
180 | 182 | | |
181 | 183 | | |
182 | 184 | | |
183 | | - | |
184 | | - | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
185 | 188 | | |
186 | | - | |
187 | | - | |
| 189 | + | |
| 190 | + | |
188 | 191 | | |
189 | | - | |
190 | | - | |
| 192 | + | |
191 | 193 | | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | | - | |
| 194 | + | |
| 195 | + | |
197 | 196 | | |
198 | 197 | | |
199 | 198 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
318 | 319 | | |
319 | 320 | | |
320 | 321 | | |
321 | | - | |
| 322 | + | |
322 | 323 | | |
323 | 324 | | |
324 | 325 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
348 | 348 | | |
349 | 349 | | |
350 | 350 | | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
351 | 399 | | |
352 | 400 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
| 31 | + | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
50 | | - | |
| 49 | + | |
| 50 | + | |
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| |||
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
72 | 86 | | |
73 | 87 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
0 commit comments