修复 Windows 上 auth login Ctrl+C 后终端无法输入的问题 #246
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue