Skip to content

Commit 8238ac2

Browse files
committed
feat: run command with context
Pass a context down to *exec.Command and make timing out optional when o.Timeout < 0. Using `context.Background()` _all the time_ is not practical as sometimes we need to stop the execution when _a_ parent context stops. Signed-off-by: Ayman Bagabas <[email protected]>
1 parent 2085e8a commit 8238ac2

File tree

1 file changed

+46
-10
lines changed

1 file changed

+46
-10
lines changed

command.go

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ type Command struct {
2020
name string
2121
args []string
2222
envs []string
23+
ctx context.Context
2324
}
2425

2526
// CommandOptions contains options for running a command.
27+
// If timeout is zero, DefaultTimeout will be used.
28+
// If timeout is less than zero, no timeout will be set.
29+
// If context is nil, context.Background() will be used.
2630
type CommandOptions struct {
27-
Args []string
28-
Envs []string
31+
Args []string
32+
Envs []string
33+
Timeout time.Duration
34+
Context context.Context
2935
}
3036

3137
// String returns the string representation of the command.
@@ -38,9 +44,16 @@ func (c *Command) String() string {
3844

3945
// NewCommand creates and returns a new Command with given arguments for "git".
4046
func NewCommand(args ...string) *Command {
47+
return NewCommandWithContext(context.Background(), args...)
48+
}
49+
50+
// NewCommandWithContext creates and returns a new Command with given arguments
51+
// and context for "git".
52+
func NewCommandWithContext(ctx context.Context, args ...string) *Command {
4153
return &Command{
4254
name: "git",
4355
args: args,
56+
ctx: ctx,
4457
}
4558
}
4659

@@ -56,8 +69,21 @@ func (c *Command) AddEnvs(envs ...string) *Command {
5669
return c
5770
}
5871

72+
// WithContext sets the context for the command.
73+
func (c *Command) WithContext(ctx context.Context) *Command {
74+
c.ctx = ctx
75+
return c
76+
}
77+
5978
// AddOptions adds options to the command.
6079
func (c *Command) AddOptions(opts ...CommandOptions) *Command {
80+
if len(opts) > 0 {
81+
opt := opts[0]
82+
if opt.Context != nil {
83+
c.ctx = opt.Context
84+
}
85+
}
86+
6187
for _, opt := range opts {
6288
c.AddArgs(opt.Args...)
6389
c.AddEnvs(opt.Envs...)
@@ -124,7 +150,9 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
124150
if len(opts) > 0 {
125151
opt = opts[0]
126152
}
127-
if opt.Timeout < time.Nanosecond {
153+
if opt.Timeout < 0 {
154+
opt.Timeout = -1
155+
} else if opt.Timeout == 0 {
128156
opt.Timeout = DefaultTimeout
129157
}
130158

@@ -147,13 +175,21 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
147175
}
148176
}()
149177

150-
ctx, cancel := context.WithTimeout(context.Background(), opt.Timeout)
151-
defer func() {
152-
cancel()
153-
if err == context.DeadlineExceeded {
154-
err = ErrExecTimeout
155-
}
156-
}()
178+
ctx := context.Background()
179+
if c.ctx != nil {
180+
ctx = c.ctx
181+
}
182+
183+
if opt.Timeout > 0 {
184+
var cancel context.CancelFunc
185+
ctx, cancel = context.WithTimeout(ctx, opt.Timeout)
186+
defer func() {
187+
cancel()
188+
if err == context.DeadlineExceeded {
189+
err = ErrExecTimeout
190+
}
191+
}()
192+
}
157193

158194
cmd := exec.CommandContext(ctx, c.name, c.args...)
159195
if len(c.envs) > 0 {

0 commit comments

Comments
 (0)