修复 Windows 上 auth login Ctrl+C 后终端无法输入的问题 #246

Open
chluknight wants to merge 1 commits from chluknight/gitlink-cli:fix/powershell-auth into master
2 changed files with 77 additions and 0 deletions

View File

@ -6,6 +6,8 @@ import (
"fmt"
"io"
"os"
"os/signal"
"runtime"
"strings"
"github.com/spf13/cobra"
@ -87,6 +89,30 @@ func readPassword(in io.Reader, reader *bufio.Reader) ([]byte, error) {
if file, ok := in.(*os.File); ok {
fd := int(file.Fd())
if term.IsTerminal(fd) {
// On Windows, ExitProcess kills the process without running deferred
// cleanup, leaving the console in raw mode after Ctrl+C. We save the
// original terminal state and register a SIGINT handler to restore it
// before exit. Linux/macOS terminals recover automatically.
if runtime.GOOS == "windows" {
if oldState, err := term.GetState(fd); err == nil {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
done := make(chan struct{})
go func() {
select {
case <-sigCh:
term.Restore(fd, oldState)
os.Exit(1)
case <-done:
return
}
}()
password, err := term.ReadPassword(fd)
close(done)
signal.Stop(sigCh)
return password, err
}
}
return term.ReadPassword(fd)
}
}

View File

@ -1,9 +1,11 @@
package auth
import (
"bufio"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"github.com/spf13/cobra"
@ -233,6 +235,55 @@ func TestLoginWithPasswordNoTerminal(t *testing.T) {
}
}
func TestReadPasswordFromPipe(t *testing.T) {
r, w, _ := os.Pipe()
go func() {
w.Write([]byte("password123\n"))
w.Close()
}()
password, err := readPassword(r, bufio.NewReader(r))
if err != nil {
t.Fatalf("readPassword from pipe: %v", err)
}
if string(password) != "password123" {
t.Fatalf("password = %q, want password123", string(password))
}
}
func TestReadPasswordFromNonFileReader(t *testing.T) {
reader := bufio.NewReader(strings.NewReader("secret\n"))
password, err := readPassword(strings.NewReader(""), reader)
if err != nil {
t.Fatalf("readPassword from non-file: %v", err)
}
if string(password) != "secret" {
t.Fatalf("password = %q, want secret", string(password))
}
}
func TestReadPasswordCRLF(t *testing.T) {
reader := bufio.NewReader(strings.NewReader("pass\r\n"))
password, err := readPassword(strings.NewReader(""), reader)
if err != nil {
t.Fatalf("readPassword CRLF: %v", err)
}
if string(password) != "pass" {
t.Fatalf("password = %q, want pass", string(password))
}
}
func TestReadPasswordEmpty(t *testing.T) {
reader := bufio.NewReader(strings.NewReader("\n"))
password, err := readPassword(strings.NewReader(""), reader)
if err != nil {
t.Fatalf("readPassword empty: %v", err)
}
if string(password) != "" {
t.Fatalf("password = %q, want empty", string(password))
}
}
func findSub(cmd *cobra.Command, name string) *cobra.Command {
for _, sub := range cmd.Commands() {
if sub.Use == name {