0
0
mirror of https://github.com/thegeeklab/wp-plugin-go.git synced 2024-11-21 04:00:40 +00:00

refactor: rework plugin cmd (#82)

BREAKING CHANGE: `types.Cmd` was moved to `exec.Cmd` and the `Private` field from the struct was removed. The filed `Trace` is now a bool field instead of a bool pointer, and the helper method `SetTrace` was also removed.

BREAKING CHANGE: The method `trace.Cmd` was removed, please use the `Trace` field of `exec.Cmd`.
This commit is contained in:
Robert Kaussow 2024-05-17 09:39:33 +02:00 committed by GitHub
parent e2bc944f1f
commit 6be7f2b898
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 75 additions and 131 deletions

58
exec/command.go Normal file
View File

@ -0,0 +1,58 @@
package exec
import (
"fmt"
"io"
"os"
"os/exec"
"strings"
"golang.org/x/sys/execabs"
)
// Cmd represents a command to be executed, with options to control its behavior.
// The Cmd struct embeds the standard library's exec.Cmd, adding additional fields
// to control the command's output and tracing.
type Cmd struct {
*exec.Cmd
Trace bool // Print composed command before execution.
TraceWriter io.Writer // Where to write the trace output.
}
// Run runs the command and waits for it to complete.
// If there is an error starting the command, it is returned.
// Otherwise, the command is waited for and its exit status is returned.
func (c *Cmd) Run() error {
if c.Trace {
fmt.Fprintf(c.TraceWriter, "+ %s\n", strings.Join(c.Args, " "))
}
if err := c.Start(); err != nil {
return err
}
return c.Wait()
}
// Command creates a new Cmd struct with the given name and arguments. It looks up the
// absolute path of the executable using execabs.LookPath, and sets up the Cmd with
// the necessary environment and output streams. The Cmd is configured to trace
// the command execution by setting Trace to true and TraceWriter to os.Stdout.
func Command(name string, arg ...string) (*Cmd, error) {
abs, err := execabs.LookPath(name)
if err != nil {
return nil, fmt.Errorf("could not find executable %q: %w", name, err)
}
cmd := &Cmd{
Cmd: execabs.Command(abs, arg...),
Trace: true,
TraceWriter: os.Stdout,
}
cmd.Env = os.Environ()
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd, nil
}

View File

@ -1,4 +1,4 @@
package types package exec
import ( import (
"bytes" "bytes"
@ -20,7 +20,7 @@ func TestCmdRun(t *testing.T) {
{ {
name: "trace enabled", name: "trace enabled",
cmd: &Cmd{ cmd: &Cmd{
Trace: boolPtr(true), Trace: true,
Cmd: &exec.Cmd{ Cmd: &exec.Cmd{
Path: "/usr/bin/echo", Path: "/usr/bin/echo",
Args: []string{"echo", "hello"}, Args: []string{"echo", "hello"},
@ -30,19 +30,20 @@ func TestCmdRun(t *testing.T) {
wantStdout: "hello\n", wantStdout: "hello\n",
}, },
{ {
name: "private output", name: "trace disabled",
cmd: &Cmd{ cmd: &Cmd{
Private: true, Trace: false,
Cmd: &exec.Cmd{ Cmd: &exec.Cmd{
Path: "/usr/bin/echo", Path: "/usr/bin/echo",
Args: []string{"echo", "hello"}, Args: []string{"echo", "hello"},
}, },
}, },
wantTrace: "+ echo hello\n", wantStdout: "hello\n",
}, },
{ {
name: "custom env", name: "custom env",
cmd: &Cmd{ cmd: &Cmd{
Trace: true,
Cmd: &exec.Cmd{ Cmd: &exec.Cmd{
Path: "/bin/sh", Path: "/bin/sh",
Args: []string{"sh", "-c", "echo $TEST"}, Args: []string{"sh", "-c", "echo $TEST"},
@ -52,21 +53,10 @@ func TestCmdRun(t *testing.T) {
wantTrace: "+ sh -c echo $TEST\n", wantTrace: "+ sh -c echo $TEST\n",
wantStdout: "1\n", wantStdout: "1\n",
}, },
{
name: "custom stdout",
cmd: &Cmd{
Cmd: &exec.Cmd{
Path: "/bin/sh",
Args: []string{"sh", "-c", "echo hello"},
Stdout: new(bytes.Buffer),
},
},
wantTrace: "+ sh -c echo hello\n",
wantStdout: "hello\n",
},
{ {
name: "custom stderr", name: "custom stderr",
cmd: &Cmd{ cmd: &Cmd{
Trace: true,
Cmd: &exec.Cmd{ Cmd: &exec.Cmd{
Path: "/bin/sh", Path: "/bin/sh",
Args: []string{"sh", "-c", "echo error >&2"}, Args: []string{"sh", "-c", "echo error >&2"},
@ -76,6 +66,16 @@ func TestCmdRun(t *testing.T) {
wantTrace: "+ sh -c echo error >&2\n", wantTrace: "+ sh -c echo error >&2\n",
wantStderr: "error\n", wantStderr: "error\n",
}, },
{
name: "error",
cmd: &Cmd{
Trace: true,
Cmd: &exec.Cmd{
Path: "/invalid/path",
},
},
wantErr: true,
},
} }
for _, tt := range tests { for _, tt := range tests {
@ -101,42 +101,3 @@ func TestCmdRun(t *testing.T) {
}) })
} }
} }
func TestCmdSetTrace(t *testing.T) {
tests := []struct {
name string
cmd *Cmd
trace bool
expected *bool
}{
{
name: "set trace to true",
cmd: &Cmd{},
trace: true,
expected: boolPtr(true),
},
{
name: "set trace to false",
cmd: &Cmd{},
trace: false,
expected: boolPtr(false),
},
{
name: "overwrite existing trace value",
cmd: &Cmd{Trace: boolPtr(true)},
trace: false,
expected: boolPtr(false),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.cmd.SetTrace(tt.trace)
assert.Equal(t, tt.expected, tt.cmd.Trace)
})
}
}
func boolPtr(b bool) *bool {
return &b
}

View File

@ -12,9 +12,6 @@ import (
"fmt" "fmt"
"net/http/httptrace" "net/http/httptrace"
"net/textproto" "net/textproto"
"os"
"os/exec"
"strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
@ -120,8 +117,3 @@ func HTTP(ctx context.Context) context.Context {
}, },
}) })
} }
// Cmd prints the executed command to stdout.
func Cmd(cmd *exec.Cmd) {
fmt.Fprintf(os.Stdout, "+ %s\n", strings.Join(cmd.Args, " "))
}

View File

@ -1,67 +0,0 @@
package types
import (
"fmt"
"io"
"os"
"strings"
"golang.org/x/sys/execabs"
)
// Cmd represents a command to be executed. It extends the execabs.Cmd struct
// and adds fields for controlling the command's execution, such as whether
// it should be executed in private mode (with output discarded) and whether
// its execution should be traced.
type Cmd struct {
*execabs.Cmd
Private bool
Trace *bool
TraceWriter io.Writer
}
// Run executes the command and waits for it to complete.
// If the Trace field is nil, it is set to true.
// The Env, Stdout, and Stderr fields are set to their default values if they are nil.
// If the Private field is true, the Stdout field is set to io.Discard.
// If the Trace field is true, the command line is printed to Stdout.
func (c *Cmd) Run() error {
if c.Trace == nil {
c.SetTrace(true)
}
if c.Env == nil {
c.Env = os.Environ()
}
if c.Stdout == nil {
c.Stdout = os.Stdout
}
if c.Stderr == nil {
c.Stderr = os.Stderr
}
if c.Private {
c.Stdout = io.Discard
}
if c.TraceWriter == nil {
c.TraceWriter = os.Stdout
}
if *c.Trace {
fmt.Fprintf(c.TraceWriter, "+ %s\n", strings.Join(c.Args, " "))
}
if err := c.Start(); err != nil {
return err
}
return c.Wait()
}
// SetTrace sets the Trace field of the Cmd to the provided boolean value.
func (c *Cmd) SetTrace(trace bool) {
c.Trace = &trace
}