Skip to content

Commit

Permalink
Windows Execute (#62)
Browse files Browse the repository at this point in the history
* fix: windows --execute

* fix: print more information if we have it
  • Loading branch information
maaslalani authored Mar 29, 2024
1 parent dc29ba2 commit 05a366c
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 31 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
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.15
golang.org/x/sys v0.18.0
)

require (
Expand All @@ -25,6 +26,7 @@ require (
github.com/catppuccin/go v0.2.0 // indirect
github.com/charmbracelet/bubbles v0.18.0 // indirect
github.com/charmbracelet/bubbletea v0.25.0 // indirect
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
Expand All @@ -40,7 +42,6 @@ require (
github.com/tetratelabs/wazero v1.7.0 // indirect
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/term v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ github.com/charmbracelet/lipgloss v0.10.1-0.20240325130315-f16ea2bdcb88 h1:mgadl
github.com/charmbracelet/lipgloss v0.10.1-0.20240325130315-f16ea2bdcb88/go.mod h1:7V+J+a41Lz/77Nsya9Srb7WFRj02LvdMcAXZtc4zroQ=
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4 h1:E3qdydPjQJIGnT6t6lY0P70sou+Vmh+VcQHAYR4qTtA=
github.com/charmbracelet/x/exp/strings v0.0.0-20240327191656-1e1cd98f30d4/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ=
github.com/charmbracelet/x/exp/term v0.0.0-20240327191656-1e1cd98f30d4 h1:WckoQq/mT+RkBq1JaCWNp65snXleZzQEEu/c3AL0TTU=
Expand Down
37 changes: 7 additions & 30 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package main

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"

Expand All @@ -17,7 +14,6 @@ import (
"github.com/alecthomas/chroma/v2/styles"
"github.com/alecthomas/kong"
"github.com/beevik/etree"
"github.com/caarlos0/go-shellwords"
in "github.com/charmbracelet/freeze/input"
"github.com/charmbracelet/freeze/svg"
"github.com/charmbracelet/lipgloss"
Expand Down Expand Up @@ -51,7 +47,13 @@ func main() {

// Copy the pty output to buffer
if config.Execute != "" {
input = executeCommand(config)
input, err = executeCommand(config)
if err != nil {
printErrorFatal("Something went wrong", err)
}
if input == "" {
printErrorFatal("Something went wrong", errors.New("no command output"))
}
}

isDefaultConfig := config.Config == "default"
Expand Down Expand Up @@ -405,31 +407,6 @@ func main() {
}
}

func executeCommand(config Config) string {
args, err := shellwords.Parse(config.Execute)
if err != nil {
printErrorFatal("Something went wrong", err)
}
ctx, cancel := context.WithTimeout(context.Background(), config.ExecuteTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, args[0], args[1:]...)
pty, err := config.runInPty(cmd)
if err != nil {
printErrorFatal("Something went wrong", err)
}
defer pty.Close()
var out bytes.Buffer
go func() {
_, _ = io.Copy(&out, pty)
}()
err = cmd.Wait()
if err != nil {
printError("Command failed", err)
}
return out.String()
}

var outputHeader = lipgloss.NewStyle().Foreground(lipgloss.Color("#F1F1F1")).Background(lipgloss.Color("#6C50FF")).Bold(true).Padding(0, 1).MarginRight(1).SetString("WROTE")

func printFilenameOutput(filename string) {
Expand Down
33 changes: 33 additions & 0 deletions pty.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
//go:build !windows
// +build !windows

package main

import (
"bytes"
"context"
"io"
"os"
"os/exec"
"syscall"

"github.com/caarlos0/go-shellwords"
"github.com/creack/pty"
)

Expand All @@ -18,3 +25,29 @@ func (cfg Config) runInPty(c *exec.Cmd) (*os.File, error) {
X: uint16(cfg.Width),
}, &syscall.SysProcAttr{})
}

func executeCommand(config Config) (string, error) {
args, err := shellwords.Parse(config.Execute)
if err != nil {
return "", err
}
ctx, cancel := context.WithTimeout(context.Background(), config.ExecuteTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, args[0], args[1:]...)
pty, err := config.runInPty(cmd)
if err != nil {
return "", err
}
defer pty.Close()
var out bytes.Buffer
go func() {
_, _ = io.Copy(&out, pty)
}()

err = cmd.Wait()
if err != nil {
return "", err
}
return out.String(), nil
}
78 changes: 78 additions & 0 deletions pty_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//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
}

0 comments on commit 05a366c

Please sign in to comment.