Skip to content

Commit 3924032

Browse files
committed
Add container deps
1 parent 4a4ce8b commit 3924032

File tree

9 files changed

+1461
-68
lines changed

9 files changed

+1461
-68
lines changed

Makefile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
.PHONY: test
1+
.PHONY: test clean
22

3-
all: clean test
3+
all: test
44

55
test: clean
6-
@echo "Running tests..."
7-
@go run gotest.tools/gotestsum@v1.12.0 -- -coverprofile=coverage.txt ./...
6+
go run gotest.tools/gotestsum@v1.12.0 -- -coverprofile=coverage.txt ./...
87

98
clean:
109
git clean -Xdf

dep/cmd/cmd.go

Lines changed: 86 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import (
66
"fmt"
77
"os"
88
"os/exec"
9-
"reflect"
109
"regexp"
11-
"runtime"
12-
"strings"
1310
"time"
1411

1512
"github.com/go-tstr/tstr/strerr"
@@ -23,6 +20,10 @@ const (
2320
ErrOptApply = strerr.Error("failed apply Opt")
2421
ErrNoMatchingLine = strerr.Error("no matching line found")
2522
ErrNilCmdRegexp = strerr.Error("command has to be set before this option can be applied, check the order of options")
23+
ErrPreCmdFailed = strerr.Error("pre command failed")
24+
ErrBadRegexp = strerr.Error("bad regular expression for matching line")
25+
ErrOutputPipe = strerr.Error("failed to aquire output pipe for command")
26+
ErrBuildFailed = strerr.Error("failed to build go binary")
2627
)
2728

2829
type Cmd struct {
@@ -47,7 +48,7 @@ func New(opts ...Opt) *Cmd {
4748
func (c *Cmd) Start() error {
4849
for _, opt := range c.opts {
4950
if err := opt(c); err != nil {
50-
return fmt.Errorf("failed to apply option %s: %w", getFnName(opt), err)
51+
return fmt.Errorf("failed to apply option: %w", err)
5152
}
5253
}
5354

@@ -92,7 +93,8 @@ func WithCommand(name string, args ...string) Opt {
9293
}
9394
}
9495

95-
// WithReadyFn allows user to provide custom ready function.
96+
// WithReadyFn allows user to provide custom readiness function.
97+
// Given fn should block until the command is ready.
9698
func WithReadyFn(fn func(*exec.Cmd) error) Opt {
9799
return func(c *Cmd) error {
98100
c.ready = fn
@@ -108,7 +110,7 @@ func WithStopFn(fn func(*exec.Cmd) error) Opt {
108110
}
109111
}
110112

111-
// WithDir sets environment variables for the command.
113+
// WithEnv sets environment variables for the command.
112114
// By default, the command inherits the environment of the current process and setting this option will override it.
113115
func WithEnv(env ...string) Opt {
114116
return func(c *Cmd) error {
@@ -117,6 +119,15 @@ func WithEnv(env ...string) Opt {
117119
}
118120
}
119121

122+
// WithEnvAppend adds environment variables to commands current env.
123+
// By default, the command inherits the environment of the current process and setting this option will override it.
124+
func WithEnvAppend(env ...string) Opt {
125+
return func(c *Cmd) error {
126+
c.cmd.Env = env
127+
return nil
128+
}
129+
}
130+
120131
// WithDir sets the working directory for the command.
121132
func WithDir(dir string) Opt {
122133
return func(c *Cmd) error {
@@ -125,32 +136,14 @@ func WithDir(dir string) Opt {
125136
}
126137
}
127138

128-
// WithWaitRegexp sets the ready function so that it waits for the command to output a line that matches the given regular expression.
139+
// WithWaitMatchingLine sets the ready function so that it waits for the command to output a line that matches the given regular expression.
129140
func WithWaitMatchingLine(exp string) Opt {
130141
return func(c *Cmd) error {
131-
re, err := regexp.Compile(exp)
132-
if err != nil {
133-
return err
134-
}
135-
136-
if c.cmd == nil {
137-
return ErrNilCmdRegexp
138-
}
139-
140-
stdout, err := c.cmd.StdoutPipe()
142+
fn, err := MatchingLine(exp, c.cmd)
141143
if err != nil {
142144
return err
143145
}
144-
145-
return WithReadyFn(func(cmd *exec.Cmd) error {
146-
scanner := bufio.NewScanner(stdout)
147-
for scanner.Scan() {
148-
if re.Match(scanner.Bytes()) {
149-
return nil
150-
}
151-
}
152-
return errors.Join(ErrNoMatchingLine, scanner.Err())
153-
})(c)
146+
return WithReadyFn(fn)(c)
154147
}
155148
}
156149

@@ -180,6 +173,42 @@ func WithExecCmd(cmd *exec.Cmd) Opt {
180173
}
181174
}
182175

176+
// WithPreCmd runs the given command as part of the setup.
177+
// This can be used to prepare the actual main command.
178+
func WithPreCmd(cmd *exec.Cmd) Opt {
179+
return func(c *Cmd) error {
180+
if err := cmd.Run(); err != nil {
181+
return fmt.Errorf("%w: %w", ErrPreCmdFailed, err)
182+
}
183+
return nil
184+
}
185+
}
186+
187+
// WithGoCode builds the given Go projects and sets the main package as the command.
188+
// By default the command is set to collect coverage data.
189+
func WithGoCode(modulePath, mainPkg string) Opt {
190+
return func(c *Cmd) error {
191+
dir, err := os.MkdirTemp("", "go-tstr")
192+
if err != nil {
193+
return fmt.Errorf("failed to create tmp dir for go binary: %w", err)
194+
}
195+
196+
target := dir + "/" + "go-app"
197+
buildCmd := exec.Command("go", "build", "-race", "-cover", "-covermode", "atomic", "-o", target, mainPkg)
198+
buildCmd.Env = append(os.Environ(), "CGO_ENABLED=1") // Required for -race flag
199+
buildCmd.Stdout = os.Stdout
200+
buildCmd.Stderr = os.Stderr
201+
buildCmd.Dir = modulePath
202+
err = buildCmd.Run()
203+
if err != nil {
204+
return fmt.Errorf("%w: %w", ErrBuildFailed, err)
205+
}
206+
207+
c.cmd = exec.Command(target)
208+
return nil
209+
}
210+
}
211+
183212
// StopWithSignal returns a stop function that sends the given signal to the command and waits for it to exit.
184213
// This can be used with WithStopFn to stop the command with a specific signal.
185214
func StopWithSignal(s os.Signal) func(*exec.Cmd) error {
@@ -195,7 +224,34 @@ func StopWithSignal(s os.Signal) func(*exec.Cmd) error {
195224
}
196225
}
197226

198-
func getFnName(fn any) string {
199-
strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()), ".")
200-
return strs[len(strs)-1]
227+
// MatchLine waits for the command to output a line that matches the given regular expression.
228+
func MatchingLine(exp string, cmd *exec.Cmd) (func(*exec.Cmd) error, error) {
229+
if cmd == nil {
230+
return nil, ErrNilCmdRegexp
231+
}
232+
233+
re, err := regexp.Compile(exp)
234+
if err != nil {
235+
return nil, fmt.Errorf("%w: %w", ErrBadRegexp, err)
236+
}
237+
238+
stdout, err := cmd.StdoutPipe()
239+
if err != nil {
240+
return nil, fmt.Errorf("%w: %w", ErrOutputPipe, err)
241+
}
242+
243+
return func(cmd *exec.Cmd) error {
244+
scanner := bufio.NewScanner(stdout)
245+
for scanner.Scan() {
246+
if re.Match(scanner.Bytes()) {
247+
// drain the rest of the output on background
248+
go func() {
249+
for scanner.Scan() {
250+
}
251+
}()
252+
return nil
253+
}
254+
}
255+
return errors.Join(ErrNoMatchingLine, scanner.Err())
256+
}, nil
201257
}

0 commit comments

Comments
 (0)