@@ -17,15 +17,22 @@ import (
17
17
18
18
// Command contains the name, arguments and environment variables of a command.
19
19
type Command struct {
20
- name string
21
- args []string
22
- envs []string
20
+ name string
21
+ args []string
22
+ envs []string
23
+ timeout time.Duration
24
+ ctx context.Context
23
25
}
24
26
25
27
// CommandOptions contains options for running a command.
28
+ // If timeout is zero, DefaultTimeout will be used.
29
+ // If timeout is less than zero, no timeout will be set.
30
+ // If context is nil, context.Background() will be used.
26
31
type CommandOptions struct {
27
- Args []string
28
- Envs []string
32
+ Args []string
33
+ Envs []string
34
+ Timeout time.Duration
35
+ Context context.Context
29
36
}
30
37
31
38
// String returns the string representation of the command.
@@ -38,9 +45,16 @@ func (c *Command) String() string {
38
45
39
46
// NewCommand creates and returns a new Command with given arguments for "git".
40
47
func NewCommand (args ... string ) * Command {
48
+ return NewCommandWithContext (context .Background (), args ... )
49
+ }
50
+
51
+ // NewCommandWithContext creates and returns a new Command with given arguments
52
+ // and context for "git".
53
+ func NewCommandWithContext (ctx context.Context , args ... string ) * Command {
41
54
return & Command {
42
55
name : "git" ,
43
56
args : args ,
57
+ ctx : ctx ,
44
58
}
45
59
}
46
60
@@ -56,9 +70,29 @@ func (c *Command) AddEnvs(envs ...string) *Command {
56
70
return c
57
71
}
58
72
73
+ // WithContext returns a new Command with the given context.
74
+ func (c Command ) WithContext (ctx context.Context ) * Command {
75
+ c .ctx = ctx
76
+ return & c
77
+ }
78
+
79
+ // WithTimeout returns a new Command with given timeout.
80
+ func (c Command ) WithTimeout (timeout time.Duration ) * Command {
81
+ c .timeout = timeout
82
+ return & c
83
+ }
84
+
85
+ // SetTimeout sets the timeout for the command.
86
+ func (c * Command ) SetTimeout (timeout time.Duration ) {
87
+ c .timeout = timeout
88
+ }
89
+
59
90
// AddOptions adds options to the command.
91
+ // Note: only the last option will take effect if there are duplicated options.
60
92
func (c * Command ) AddOptions (opts ... CommandOptions ) * Command {
61
93
for _ , opt := range opts {
94
+ c .timeout = opt .Timeout
95
+ c .ctx = opt .Context
62
96
c .AddArgs (opt .Args ... )
63
97
c .AddEnvs (opt .Envs ... )
64
98
}
@@ -111,6 +145,8 @@ type RunInDirOptions struct {
111
145
// Stderr is the error output from the command.
112
146
Stderr io.Writer
113
147
// Timeout is the duration to wait before timing out.
148
+ //
149
+ // Deprecated: Use CommandOptions.Timeout or *Command.WithTimeout instead.
114
150
Timeout time.Duration
115
151
}
116
152
@@ -124,8 +160,15 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
124
160
if len (opts ) > 0 {
125
161
opt = opts [0 ]
126
162
}
127
- if opt .Timeout < time .Nanosecond {
128
- opt .Timeout = DefaultTimeout
163
+
164
+ timeout := c .timeout
165
+ // TODO: remove this in newer version
166
+ if opt .Timeout > 0 {
167
+ timeout = opt .Timeout
168
+ }
169
+
170
+ if timeout == 0 {
171
+ timeout = DefaultTimeout
129
172
}
130
173
131
174
buf := new (bytes.Buffer )
@@ -141,19 +184,27 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
141
184
142
185
defer func () {
143
186
if len (dir ) == 0 {
144
- log ("[timeout: %v] %s\n %s" , opt . Timeout , c , buf .Bytes ())
187
+ log ("[timeout: %v] %s\n %s" , timeout , c , buf .Bytes ())
145
188
} else {
146
- log ("[timeout: %v] %s: %s\n %s" , opt . Timeout , dir , c , buf .Bytes ())
189
+ log ("[timeout: %v] %s: %s\n %s" , timeout , dir , c , buf .Bytes ())
147
190
}
148
191
}()
149
192
150
- ctx , cancel := context .WithTimeout (context .Background (), opt .Timeout )
151
- defer func () {
152
- cancel ()
153
- if err == context .DeadlineExceeded {
154
- err = ErrExecTimeout
155
- }
156
- }()
193
+ ctx := context .Background ()
194
+ if c .ctx != nil {
195
+ ctx = c .ctx
196
+ }
197
+
198
+ if timeout > 0 {
199
+ var cancel context.CancelFunc
200
+ ctx , cancel = context .WithTimeout (ctx , timeout )
201
+ defer func () {
202
+ cancel ()
203
+ if err == context .DeadlineExceeded {
204
+ err = ErrExecTimeout
205
+ }
206
+ }()
207
+ }
157
208
158
209
cmd := exec .CommandContext (ctx , c .name , c .args ... )
159
210
if len (c .envs ) > 0 {
@@ -188,55 +239,72 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
188
239
189
240
}
190
241
242
+ // RunInDirPipeline executes the command in given directory and default timeout
243
+ // duration. It pipes stdout and stderr to supplied io.Writer.
244
+ func (c * Command ) RunInDirPipeline (stdout , stderr io.Writer , dir string ) error {
245
+ return c .RunInDirWithOptions (dir , RunInDirOptions {
246
+ Stdin : nil ,
247
+ Stdout : stdout ,
248
+ Stderr : stderr ,
249
+ })
250
+ }
251
+
191
252
// RunInDirPipelineWithTimeout executes the command in given directory and
192
253
// timeout duration. It pipes stdout and stderr to supplied io.Writer.
193
254
// DefaultTimeout will be used if the timeout duration is less than
194
255
// time.Nanosecond (i.e. less than or equal to 0). It returns an ErrExecTimeout
195
256
// if the execution was timed out.
257
+ //
258
+ // Deprecated: Use RunInDirPipeline and CommandOptions instead.
259
+ // TODO: remove this in the next major version
196
260
func (c * Command ) RunInDirPipelineWithTimeout (timeout time.Duration , stdout , stderr io.Writer , dir string ) (err error ) {
197
- return c .RunInDirWithOptions (dir , RunInDirOptions {
198
- Stdin : nil ,
199
- Stdout : stdout ,
200
- Stderr : stderr ,
201
- Timeout : timeout ,
202
- })
203
- }
204
-
205
- // RunInDirPipeline executes the command in given directory and default timeout
206
- // duration. It pipes stdout and stderr to supplied io.Writer.
207
- func (c * Command ) RunInDirPipeline (stdout , stderr io.Writer , dir string ) error {
208
- return c .RunInDirPipelineWithTimeout (DefaultTimeout , stdout , stderr , dir )
261
+ if timeout != 0 {
262
+ c = c .WithTimeout (timeout )
263
+ }
264
+ return c .RunInDirPipeline (stdout , stderr , dir )
209
265
}
210
266
211
267
// RunInDirWithTimeout executes the command in given directory and timeout
212
268
// duration. It returns stdout in []byte and error (combined with stderr).
269
+ //
270
+ // Deprecated: Use RunInDir and CommandOptions instead.
271
+ // TODO: remove this in the next major version
213
272
func (c * Command ) RunInDirWithTimeout (timeout time.Duration , dir string ) ([]byte , error ) {
214
- stdout := new (bytes.Buffer )
215
- stderr := new (bytes.Buffer )
216
- if err := c .RunInDirPipelineWithTimeout (timeout , stdout , stderr , dir ); err != nil {
217
- return nil , concatenateError (err , stderr .String ())
273
+ if timeout != 0 {
274
+ c = c .WithTimeout (timeout )
218
275
}
219
- return stdout . Bytes (), nil
276
+ return c . RunInDir ( dir )
220
277
}
221
278
222
279
// RunInDir executes the command in given directory and default timeout
223
280
// duration. It returns stdout and error (combined with stderr).
224
281
func (c * Command ) RunInDir (dir string ) ([]byte , error ) {
225
- return c .RunInDirWithTimeout (DefaultTimeout , dir )
282
+ stdout := new (bytes.Buffer )
283
+ stderr := new (bytes.Buffer )
284
+ if err := c .RunInDirPipeline (stdout , stderr , dir ); err != nil {
285
+ return nil , concatenateError (err , stderr .String ())
286
+ }
287
+ return stdout .Bytes (), nil
226
288
}
227
289
228
290
// RunWithTimeout executes the command in working directory and given timeout
229
291
// duration. It returns stdout in string and error (combined with stderr).
292
+ //
293
+ // Deprecated: Use RunInDir and CommandOptions instead.
294
+ // TODO: remove this in the next major version
230
295
func (c * Command ) RunWithTimeout (timeout time.Duration ) ([]byte , error ) {
231
- stdout , err := c .RunInDirWithTimeout (timeout , "" )
232
- if err != nil {
233
- return nil , err
296
+ if timeout != 0 {
297
+ c = c .WithTimeout (timeout )
234
298
}
235
- return stdout , nil
299
+ return c . Run ()
236
300
}
237
301
238
302
// Run executes the command in working directory and default timeout duration.
239
303
// It returns stdout in string and error (combined with stderr).
240
304
func (c * Command ) Run () ([]byte , error ) {
241
- return c .RunWithTimeout (DefaultTimeout )
305
+ stdout , err := c .RunInDir ("" )
306
+ if err != nil {
307
+ return nil , err
308
+ }
309
+ return stdout , nil
242
310
}
0 commit comments