From e3cfc0c4e9b9d585a48e2dffad17325602950a36 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Wed, 20 Nov 2024 12:39:40 -0300 Subject: [PATCH 1/4] fix: using xpty Signed-off-by: Carlos Alexandro Becker --- go.mod | 7 +++-- go.sum | 6 ++++ pty.go | 40 ++++++++++++-------------- pty_windows.go | 78 -------------------------------------------------- 4 files changed, 29 insertions(+), 102 deletions(-) delete mode 100644 pty_windows.go diff --git a/go.mod b/go.mod index 71d061a..6de5608 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,8 @@ require ( github.com/charmbracelet/log v0.4.0 github.com/charmbracelet/x/ansi v0.4.0 github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 - github.com/creack/pty v1.1.23 + github.com/charmbracelet/x/term v0.2.0 + github.com/charmbracelet/x/xpty v0.1.0 github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.16 @@ -30,9 +31,11 @@ require ( github.com/catppuccin/go v0.2.0 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect github.com/charmbracelet/bubbletea v1.1.2 // indirect + github.com/charmbracelet/x/conpty v0.1.0 // indirect github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240906161213-162f3037fef5 // indirect - github.com/charmbracelet/x/term v0.2.0 // indirect + github.com/charmbracelet/x/termios v0.1.0 // indirect + github.com/creack/pty v1.1.23 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect diff --git a/go.sum b/go.sum index f900377..8f0b9b3 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8 github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= github.com/charmbracelet/x/ansi v0.4.0 h1:NqwHA4B23VwsDn4H3VcNX1W1tOmgnvY1NDx5tOXdnOU= github.com/charmbracelet/x/ansi v0.4.0/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= +github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5 h1:rIt3LGU1yOC7U48eZjaAtjdzuSjH6Y0GA1KsRN7wqn8= github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= github.com/charmbracelet/x/exp/strings v0.0.0-20240906161213-162f3037fef5 h1:73C9VsX8PMlXxVMKjg7ix67cZWg+zySdyzWRaXS239A= @@ -42,6 +44,10 @@ github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 h1:LewLBF github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= 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/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k= +github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8ouDK0AZEbG7r+/U= +github.com/charmbracelet/x/xpty v0.1.0 h1:762t0rQshWYRCzJe7MrUSM/8wBu6rg39HBc6PiMBFcs= +github.com/charmbracelet/x/xpty v0.1.0/go.mod h1:sDG9QjERjtelfbSf2iyZsa5d8Uvi4X9fK3iUqdb9XRw= github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/pty.go b/pty.go index 27e7f7c..9cf7616 100644 --- a/pty.go +++ b/pty.go @@ -1,6 +1,3 @@ -//go:build !windows -// +build !windows - package main import ( @@ -9,38 +6,38 @@ import ( "io" "os" "os/exec" - "syscall" "github.com/caarlos0/go-shellwords" - "github.com/creack/pty" + "github.com/charmbracelet/x/term" + "github.com/charmbracelet/x/xpty" ) -// runInPty opens a new pty and runs the given command in it. -// The returned file is the pty's file descriptor and must be closed by the -// caller. -func (cfg Config) runInPty(c *exec.Cmd) (*os.File, error) { - //nolint: wrapcheck - return pty.StartWithAttrs(c, &pty.Winsize{ - Cols: 80, - Rows: 10, - X: uint16(cfg.Width), - }, &syscall.SysProcAttr{}) -} - func executeCommand(config Config) (string, error) { args, err := shellwords.Parse(config.Execute) if err != nil { return "", err //nolint: wrapcheck } + ctx, cancel := context.WithTimeout(context.Background(), config.ExecuteTimeout) defer cancel() - cmd := exec.CommandContext(ctx, args[0], args[1:]...) //nolint: gosec - pty, err := config.runInPty(cmd) + width, height, err := term.GetSize(os.Stdout.Fd()) + if err != nil { + width = 80 + height = 24 + } + + pty, err := xpty.NewPty(width, height) if err != nil { return "", err } - defer pty.Close() //nolint: errcheck + defer func() { _ = pty.Close() }() + + cmd := exec.CommandContext(ctx, args[0], args[1:]...) //nolint: gosec + if err := pty.Start(cmd); err != nil { + return "", err + } + var out bytes.Buffer var errorOut bytes.Buffer go func() { @@ -48,8 +45,7 @@ func executeCommand(config Config) (string, error) { errorOut.Write(out.Bytes()) }() - err = cmd.Wait() - if err != nil { + if err := xpty.WaitProcess(ctx, cmd); err != nil { return errorOut.String(), err //nolint: wrapcheck } return out.String(), nil diff --git a/pty_windows.go b/pty_windows.go deleted file mode 100644 index 1a7a0c0..0000000 --- a/pty_windows.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build windows -// +build windows - -package main - -import ( - "bytes" - "context" - "io" - "os" - "syscall" - - "github.com/caarlos0/go-shellwords" - "github.com/charmbracelet/log" - "github.com/charmbracelet/x/exp/term/conpty" - "golang.org/x/sys/windows" -) - -func executeCommand(config Config) (string, error) { - args, err := shellwords.Parse(config.Execute) - if err != nil { - log.Error(err) - printErrorFatal("Something went wrong", err) - } - ctx, cancel := context.WithTimeout(context.Background(), config.ExecuteTimeout) - defer cancel() - - cpty, err := conpty.New(80, 10, 0) - if err != nil { - return "", err - } - defer cpty.Close() - - pid, proc, err := cpty.Spawn(args[0], args, &syscall.ProcAttr{Env: os.Environ()}) - if err != nil { - return "", err - } - - process, err := os.FindProcess(pid) - if err != nil { - // If we can't find the process via os.FindProcess, terminate the - // process as that's what we rely on for all further operations on the - // object. - if tErr := windows.TerminateProcess(windows.Handle(proc), 1); tErr != nil { - return "", tErr - } - return "", err - } - - type result struct { - *os.ProcessState - error - } - donec := make(chan result, 1) - go func() { - state, err := process.Wait() - donec <- result{state, err} - }() - - ctx, cancelFunc := context.WithTimeout(context.Background(), config.ExecuteTimeout) - defer cancelFunc() - var out bytes.Buffer - go func() { - _, _ = io.Copy(&out, cpty) - }() - - select { - case <-ctx.Done(): - err = windows.TerminateProcess(windows.Handle(proc), 1) - case r := <-donec: - err = r.error - } - - if err != nil { - return "", err - } - return out.String(), nil -} From ae1471984c68eafa0942cf09cc2361304df37198 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Wed, 18 Dec 2024 16:19:22 -0300 Subject: [PATCH 2/4] chore: update dep Signed-off-by: Carlos Alexandro Becker --- go.mod | 9 ++++----- go.sum | 18 ++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 6de5608..33646f1 100644 --- a/go.mod +++ b/go.mod @@ -15,14 +15,12 @@ require ( github.com/charmbracelet/lipgloss v0.13.1 github.com/charmbracelet/log v0.4.0 github.com/charmbracelet/x/ansi v0.4.0 - github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 - github.com/charmbracelet/x/term v0.2.0 - github.com/charmbracelet/x/xpty v0.1.0 + github.com/charmbracelet/x/term v0.2.1 + github.com/charmbracelet/x/xpty v0.1.1 github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.16 github.com/muesli/reflow v0.3.0 - golang.org/x/sys v0.26.0 ) require ( @@ -35,7 +33,7 @@ require ( github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240906161213-162f3037fef5 // indirect github.com/charmbracelet/x/termios v0.1.0 // indirect - github.com/creack/pty v1.1.23 // indirect + github.com/creack/pty v1.1.24 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect @@ -50,5 +48,6 @@ require ( github.com/tetratelabs/wazero v1.8.0 // indirect golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.18.0 // indirect ) diff --git a/go.sum b/go.sum index 8f0b9b3..1b8ef9c 100644 --- a/go.sum +++ b/go.sum @@ -40,16 +40,14 @@ github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5 h1:rIt3LGU1 github.com/charmbracelet/x/errors v0.0.0-20240906161213-162f3037fef5/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= github.com/charmbracelet/x/exp/strings v0.0.0-20240906161213-162f3037fef5 h1:73C9VsX8PMlXxVMKjg7ix67cZWg+zySdyzWRaXS239A= github.com/charmbracelet/x/exp/strings v0.0.0-20240906161213-162f3037fef5/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= -github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4 h1:LewLBFkff+bCxgMZn1m8xNYQbUksWaY71d1QARHA11s= -github.com/charmbracelet/x/exp/term v0.0.0-20240403043919-dea9035a27d4/go.mod h1:6GZ13FjIP6eOCqWU4lqgveGnYxQo9c3qBzHPeFu4HBE= -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/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/charmbracelet/x/termios v0.1.0 h1:y4rjAHeFksBAfGbkRDmVinMg7x7DELIGAFbdNvxg97k= github.com/charmbracelet/x/termios v0.1.0/go.mod h1:H/EVv/KRnrYjz+fCYa9bsKdqF3S8ouDK0AZEbG7r+/U= -github.com/charmbracelet/x/xpty v0.1.0 h1:762t0rQshWYRCzJe7MrUSM/8wBu6rg39HBc6PiMBFcs= -github.com/charmbracelet/x/xpty v0.1.0/go.mod h1:sDG9QjERjtelfbSf2iyZsa5d8Uvi4X9fK3iUqdb9XRw= -github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= -github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/charmbracelet/x/xpty v0.1.1 h1:A3DxWhvNjSkBR8/thRTcVnJ5bdv7OXRmChdMKtsi/5M= +github.com/charmbracelet/x/xpty v0.1.1/go.mod h1:jnu0EnSUYO5l+BExMLFiKVgpGPPV/RT2+6LYyCyf8zs= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= @@ -99,8 +97,8 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 88cbc96fd9d09987796656e4cdfbb1ca9f779248 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Wed, 18 Dec 2024 16:21:04 -0300 Subject: [PATCH 3/4] chore: go mod tidy --- go.mod | 3 +-- go.sum | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index ca2e74e..f3c10cc 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.16 github.com/muesli/reflow v0.3.0 - golang.org/x/sys v0.28.0 ) require ( @@ -49,6 +48,6 @@ require ( github.com/tetratelabs/wazero v1.8.0 // indirect golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.18.0 // indirect ) diff --git a/go.sum b/go.sum index 63261ef..fb09b7b 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,6 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= From 16c942ccde81b4599db753c4b6c4248f09798a46 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Wed, 18 Dec 2024 16:22:31 -0300 Subject: [PATCH 4/4] fix: lint issues --- pty.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pty.go b/pty.go index 9cf7616..d7e8271 100644 --- a/pty.go +++ b/pty.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "fmt" "io" "os" "os/exec" @@ -15,7 +16,7 @@ import ( func executeCommand(config Config) (string, error) { args, err := shellwords.Parse(config.Execute) if err != nil { - return "", err //nolint: wrapcheck + return "", fmt.Errorf("could not execute: %w", err) } ctx, cancel := context.WithTimeout(context.Background(), config.ExecuteTimeout) @@ -29,13 +30,13 @@ func executeCommand(config Config) (string, error) { pty, err := xpty.NewPty(width, height) if err != nil { - return "", err + return "", fmt.Errorf("could not execute: %w", err) } defer func() { _ = pty.Close() }() cmd := exec.CommandContext(ctx, args[0], args[1:]...) //nolint: gosec if err := pty.Start(cmd); err != nil { - return "", err + return "", fmt.Errorf("could not execute: %w", err) } var out bytes.Buffer @@ -46,7 +47,7 @@ func executeCommand(config Config) (string, error) { }() if err := xpty.WaitProcess(ctx, cmd); err != nil { - return errorOut.String(), err //nolint: wrapcheck + return errorOut.String(), fmt.Errorf("could not execute: %w", err) } return out.String(), nil }