Skip to content

Commit c4647b4

Browse files
committed
Improve cmd
1 parent c56c5ed commit c4647b4

File tree

2 files changed

+27
-43
lines changed

2 files changed

+27
-43
lines changed

dep/cmd/cmd.go

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cmd
22

33
import (
44
"bufio"
5+
"context"
56
"errors"
67
"fmt"
78
"net/http"
@@ -31,7 +32,7 @@ const (
3132

3233
type Cmd struct {
3334
opts []Opt
34-
ready func(*exec.Cmd) error
35+
ready func(context.Context, *exec.Cmd) error
3536
stop func(*exec.Cmd) error
3637
cmd *exec.Cmd
3738
readyTimeout time.Duration
@@ -42,7 +43,7 @@ type Opt func(*Cmd) error
4243
func New(opts ...Opt) *Cmd {
4344
return &Cmd{
4445
opts: opts,
45-
ready: func(*exec.Cmd) error { return nil },
46+
ready: func(context.Context, *exec.Cmd) error { return nil },
4647
stop: StopWithSignal(os.Interrupt),
4748
readyTimeout: 30 * time.Second,
4849
}
@@ -63,10 +64,13 @@ func (c *Cmd) Start() error {
6364
}
6465

6566
func (c *Cmd) Ready() error {
67+
ctx, cancel := context.WithTimeout(context.Background(), c.readyTimeout)
68+
defer cancel()
69+
6670
errCh := make(chan error, 1)
6771
go func() {
6872
defer close(errCh)
69-
errCh <- c.ready(c.cmd)
73+
errCh <- c.ready(ctx, c.cmd)
7074
}()
7175

7276
select {
@@ -92,6 +96,8 @@ func (c *Cmd) wrapErr(wErr, err error) error {
9296
func WithCommand(name string, args ...string) Opt {
9397
return func(c *Cmd) error {
9498
c.cmd = exec.Command(name, args...)
99+
c.cmd.Stdout = os.Stdout
100+
c.cmd.Stderr = os.Stderr
95101
return nil
96102
}
97103
}
@@ -111,7 +117,7 @@ func WithCommandFn(fn func() (*exec.Cmd, error)) Opt {
111117

112118
// WithReadyFn allows user to provide custom readiness function.
113119
// Given fn should block until the command is ready.
114-
func WithReadyFn(fn func(*exec.Cmd) error) Opt {
120+
func WithReadyFn(fn func(context.Context, *exec.Cmd) error) Opt {
115121
return func(c *Cmd) error {
116122
c.ready = fn
117123
return nil
@@ -121,19 +127,20 @@ func WithReadyFn(fn func(*exec.Cmd) error) Opt {
121127
// WithReadyHTTP sets the ready function to wait for url to return 200 OK.
122128
func WithReadyHTTP(url string) Opt {
123129
return func(c *Cmd) error {
124-
c.ready = func(cmd *exec.Cmd) error {
130+
c.ready = func(ctx context.Context, cmd *exec.Cmd) error {
125131
client := &http.Client{
126-
Timeout: 10 * time.Second,
132+
Timeout: 1 * time.Second,
127133
}
128134
for {
135+
if ctx.Err() != nil {
136+
return ctx.Err()
137+
}
129138
resp, err := client.Get(url)
130-
if err != nil {
139+
if err != nil || resp.StatusCode != http.StatusOK {
140+
time.Sleep(time.Millisecond * 100)
131141
continue
132142
}
133-
if resp.StatusCode == http.StatusOK {
134-
return nil
135-
}
136-
time.Sleep(100 * time.Millisecond)
143+
return nil
137144
}
138145
}
139146
return nil
@@ -213,7 +220,7 @@ func WithReadyTimeout(d time.Duration) Opt {
213220
// This is useful for commands that exit on their own and don't need to be stopped manually.
214221
func WithWaitExit() Opt {
215222
return func(c *Cmd) error {
216-
c.ready = func(cmd *exec.Cmd) error { return cmd.Wait() }
223+
c.ready = func(_ context.Context, cmd *exec.Cmd) error { return cmd.Wait() }
217224
c.stop = func(*exec.Cmd) error { return nil }
218225
return nil
219226
}
@@ -227,17 +234,6 @@ func WithExecCmd(cmd *exec.Cmd) Opt {
227234
}
228235
}
229236

230-
// WithPreCmd runs the given command as part of the setup.
231-
// This can be used to prepare the actual main command.
232-
func WithPreCmd(cmd *exec.Cmd) Opt {
233-
return func(c *Cmd) error {
234-
if err := cmd.Run(); err != nil {
235-
return fmt.Errorf("%w: %w", ErrPreCmdFailed, err)
236-
}
237-
return nil
238-
}
239-
}
240-
241237
// WithGoCode builds the given Go projects and sets the main package as the command.
242238
// By default the the output binary is instrumented to collect coverage data
243239
// Working directory for build command is set to modulePath which means that the mainPkg should be relative to it.
@@ -316,7 +312,7 @@ func StopWithSignal(s os.Signal) func(*exec.Cmd) error {
316312
}
317313

318314
// MatchLine waits for the command to output a line that matches the given regular expression.
319-
func MatchingLine(exp string, cmd *exec.Cmd) (func(*exec.Cmd) error, error) {
315+
func MatchingLine(exp string, cmd *exec.Cmd) (func(context.Context, *exec.Cmd) error, error) {
320316
if cmd == nil {
321317
return nil, ErrNilCmdRegexp
322318
}
@@ -332,7 +328,7 @@ func MatchingLine(exp string, cmd *exec.Cmd) (func(*exec.Cmd) error, error) {
332328
return nil, fmt.Errorf("%w: %w", ErrOutputPipe, err)
333329
}
334330

335-
return func(cmd *exec.Cmd) error {
331+
return func(ctx context.Context, cmd *exec.Cmd) error {
336332
scanner := bufio.NewScanner(stdout)
337333
for scanner.Scan() {
338334
if re.Match(scanner.Bytes()) {
@@ -343,6 +339,9 @@ func MatchingLine(exp string, cmd *exec.Cmd) (func(*exec.Cmd) error, error) {
343339
}()
344340
return nil
345341
}
342+
if ctx.Err() != nil {
343+
return ctx.Err()
344+
}
346345
}
347346
return errors.Join(ErrNoMatchingLine, scanner.Err())
348347
}, nil

dep/cmd/cmd_test.go

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cmd_test
22

33
import (
4+
"context"
45
"net/http"
56
"net/http/httptest"
67
"os"
@@ -83,14 +84,6 @@ func TestCmd(t *testing.T) {
8384
),
8485
err: cmd.ErrNilCmdRegexp,
8586
},
86-
{
87-
name: "WithBuildCmd_error",
88-
cmd: cmd.New(
89-
cmd.WithPreCmd(exec.Command("go", "build", "non/existing/path/main.go")),
90-
cmd.WithCommand("non/existing/path/main"),
91-
),
92-
err: cmd.ErrPreCmdFailed,
93-
},
9487
{
9588
name: "WithEnv",
9689
cmd: cmd.New(
@@ -112,18 +105,10 @@ func TestCmd(t *testing.T) {
112105
{
113106
name: "WithExecCmd",
114107
cmd: cmd.New(
115-
cmd.WithExecCmd(exec.Command("go", "version")),
108+
cmd.WithExecCmd(exec.Command("go", "build", "-o", waitBin, waitPkg+"/main.go")),
116109
cmd.WithWaitExit(),
117110
),
118111
},
119-
{
120-
name: "WithBuildCmd",
121-
cmd: cmd.New(
122-
cmd.WithPreCmd(exec.Command("go", "build", "-o", waitBin, waitPkg+"/main.go")),
123-
cmd.WithCommand(waitBin),
124-
cmd.WithWaitMatchingLine("Waiting for signal"),
125-
),
126-
},
127112
{
128113
name: "DefaultReady",
129114
cmd: cmd.New(
@@ -202,7 +187,7 @@ func TestCmd_WithGoCode_Coverage(t *testing.T) {
202187
require.Len(t, files, 2)
203188
}
204189

205-
func blockForever(*exec.Cmd) error {
190+
func blockForever(context.Context, *exec.Cmd) error {
206191
select {}
207192
}
208193

0 commit comments

Comments
 (0)