diff --git a/.changes/unreleased/Added-20240907-161636.yaml b/.changes/unreleased/Added-20240907-161636.yaml new file mode 100644 index 00000000..30ea7e0d --- /dev/null +++ b/.changes/unreleased/Added-20240907-161636.yaml @@ -0,0 +1,3 @@ +kind: Added +body: Windows support. +time: 2024-09-07T16:16:36.18073-07:00 diff --git a/.gitattributes b/.gitattributes index 24a8e879..c8548d8b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,10 @@ +# Don't magically change line endings by default, +# but always use LF in test scripts. +# +# Ref: +# - https://github.com/golang/go/blob/807e01db4840e25e4d98911b28a8fa54244b8dfa/.gitattributes +# - https://github.com/rogpeppe/go-internal/pull/106 +* -text +*.txt text eol=lf + *.png filter=lfs diff=lfs merge=lfs -text diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12d7f25f..bcf61883 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: name: Test (${{ matrix.os}}) strategy: matrix: - os: ["ubuntu-latest"] # TODO: ["windows-latest"] + os: ["ubuntu-latest", "windows-latest"] steps: - uses: actions/checkout@v4 - name: Set up Go diff --git a/.goreleaser.yml b/.goreleaser.yml index 28a43313..6e7ce649 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -6,13 +6,15 @@ builds: - CGO_ENABLED=0 main: . binary: gs - goos: [darwin, linux] + goos: [darwin, linux, windows] goarch: [amd64, arm64, arm] goarm: [5, 6, 7] ldflags: '-s -w -X main._version={{.Version}}' ignore: - goos: darwin goarch: arm + - goos: windows + goarch: arm flags: - -trimpath diff --git a/go.mod b/go.mod index b40ad2e9..766980de 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/charmbracelet/bubbletea v1.1.0 github.com/charmbracelet/lipgloss v0.13.0 github.com/charmbracelet/log v0.4.0 - github.com/creack/pty v1.1.23 + github.com/creack/pty/v2 v2.0.0-20231028221118-e9c1f000f465 github.com/dustin/go-humanize v1.0.1 github.com/mattn/go-isatty v0.0.20 github.com/rogpeppe/go-internal v1.12.0 diff --git a/go.sum b/go.sum index 556ac67d..e9caaa40 100644 --- a/go.sum +++ b/go.sum @@ -24,8 +24,8 @@ github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqo github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= -github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= -github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/creack/pty/v2 v2.0.0-20231028221118-e9c1f000f465 h1:KQ+iWxxc2qYyfK3GGrjNJ73GvDAd9Ecz8pK4FpdtI6Y= +github.com/creack/pty/v2 v2.0.0-20231028221118-e9c1f000f465/go.mod h1:zTYJ0iXAFhiKeguJdMR/LEecltLOG5Wz3lo1lIi0CLU= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/git/gittest/config.go b/internal/git/gittest/config.go index 97a720c2..3a01270b 100644 --- a/internal/git/gittest/config.go +++ b/internal/git/gittest/config.go @@ -11,6 +11,7 @@ func DefaultConfig() Config { return Config{ "init.defaultBranch": "main", "alias.graph": "log --graph --decorate --oneline", + "core.autocrlf": "false", } } diff --git a/internal/termtest/with_term.go b/internal/termtest/with_term.go index 8ebaafc1..fc202a86 100644 --- a/internal/termtest/with_term.go +++ b/internal/termtest/with_term.go @@ -17,7 +17,7 @@ import ( "sync" "time" - "github.com/creack/pty" + "github.com/creack/pty/v2" "github.com/vito/midterm" ) @@ -254,14 +254,14 @@ func WithTerm() (exitCode int) { type terminalEmulator struct { mu sync.Mutex cmd *exec.Cmd - pty *os.File + pty pty.Pty logf func(string, ...any) term *midterm.Terminal } func newVT100Emulator( - f *os.File, + f pty.Pty, cmd *exec.Cmd, rows, cols int, autoResize bool, @@ -307,29 +307,36 @@ loop: func (m *terminalEmulator) Close() error { _, writeErr := m.pty.Write([]byte{4}) // EOT + if writeErr != nil { + writeErr = fmt.Errorf("send EOT: %w", writeErr) + } waitErrc := make(chan error, 1) go func() { - waitErrc <- m.cmd.Wait() + err := m.cmd.Wait() + if err != nil { + err = fmt.Errorf("command %v: %w", m.cmd, err) + } + waitErrc <- err }() var waitErr error select { case waitErr = <-waitErrc: // ok - case <-time.After(3 * time.Second): + case <-time.After(10 * time.Second): waitErr = fmt.Errorf("timeout waiting for %v", m.cmd) _ = m.cmd.Process.Kill() } - closeErr := m.pty.Close() - - return errors.Join(waitErr, closeErr, writeErr) + // On Windows, pty.Close may fail with invalid handle or access denied. + // It's not really a problem, so we ignore the error. + _ = m.pty.Close() + return errors.Join(waitErr, writeErr) } func (m *terminalEmulator) FeedKeys(s string) error { _, err := io.WriteString(m.pty, s) - _ = m.pty.Sync() return err } diff --git a/testdata/script/auth_insecure_storage.txt b/testdata/script/auth_insecure_storage.txt index 006daaa5..809212dc 100644 --- a/testdata/script/auth_insecure_storage.txt +++ b/testdata/script/auth_insecure_storage.txt @@ -41,7 +41,7 @@ gs auth login --forge=shamhub cmp stderr $WORK/golden/login-secure.stderr -- golden/login.stderr -- -WRN Storing secrets in plain text at $WORK/home/.config/git-spice/secrets.json. Be careful! +WRN Storing secrets in plain text at $WORK${/}home${/}.config${/}git-spice${/}secrets.json. Be careful! INF shamhub: successfully logged in -- golden/login-secure.stderr -- INF shamhub: successfully logged in diff --git a/testdata/script/branch_submit_remote_prompt.txt b/testdata/script/branch_submit_remote_prompt.txt index a09ae2b3..2a0fa5cf 100644 --- a/testdata/script/branch_submit_remote_prompt.txt +++ b/testdata/script/branch_submit_remote_prompt.txt @@ -36,6 +36,7 @@ Contents of feature1 await Please select a remote snapshot dialog feed \r +await -- golden/prompt.txt -- ### dialog ###