@@ -17,15 +17,22 @@ import (
1717
1818// Command contains the name, arguments and environment variables of a command. 
1919type  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 
2325}
2426
2527// 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. 
2631type  CommandOptions  struct  {
27- 	Args  []string 
28- 	Envs  []string 
32+ 	Args     []string 
33+ 	Envs     []string 
34+ 	Timeout  time.Duration 
35+ 	Context  context.Context 
2936}
3037
3138// String returns the string representation of the command. 
@@ -38,9 +45,16 @@ func (c *Command) String() string {
3845
3946// NewCommand creates and returns a new Command with given arguments for "git". 
4047func  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  {
4154	return  & Command {
4255		name : "git" ,
4356		args : args ,
57+ 		ctx :  ctx ,
4458	}
4559}
4660
@@ -56,9 +70,29 @@ func (c *Command) AddEnvs(envs ...string) *Command {
5670	return  c 
5771}
5872
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+ 
5990// AddOptions adds options to the command. 
91+ // Note: only the last option will take effect if there are duplicated options. 
6092func  (c  * Command ) AddOptions (opts  ... CommandOptions ) * Command  {
6193	for  _ , opt  :=  range  opts  {
94+ 		c .timeout  =  opt .Timeout 
95+ 		c .ctx  =  opt .Context 
6296		c .AddArgs (opt .Args ... )
6397		c .AddEnvs (opt .Envs ... )
6498	}
@@ -111,6 +145,8 @@ type RunInDirOptions struct {
111145	// Stderr is the error output from the command. 
112146	Stderr  io.Writer 
113147	// Timeout is the duration to wait before timing out. 
148+ 	// 
149+ 	// Deprecated: Use CommandOptions.Timeout or *Command.WithTimeout instead. 
114150	Timeout  time.Duration 
115151}
116152
@@ -124,8 +160,15 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
124160	if  len (opts ) >  0  {
125161		opt  =  opts [0 ]
126162	}
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 
129172	}
130173
131174	buf  :=  new (bytes.Buffer )
@@ -141,19 +184,27 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
141184
142185	defer  func () {
143186		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 ())
145188		} 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 ())
147190		}
148191	}()
149192
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+ 	}
157208
158209	cmd  :=  exec .CommandContext (ctx , c .name , c .args ... )
159210	if  len (c .envs ) >  0  {
@@ -188,55 +239,72 @@ func (c *Command) RunInDirWithOptions(dir string, opts ...RunInDirOptions) (err
188239
189240}
190241
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+ 
191252// RunInDirPipelineWithTimeout executes the command in given directory and 
192253// timeout duration. It pipes stdout and stderr to supplied io.Writer. 
193254// DefaultTimeout will be used if the timeout duration is less than 
194255// time.Nanosecond (i.e. less than or equal to 0). It returns an ErrExecTimeout 
195256// if the execution was timed out. 
257+ // 
258+ // Deprecated: Use RunInDirPipeline and CommandOptions instead. 
259+ // TODO: remove this in the next major version 
196260func  (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 )
209265}
210266
211267// RunInDirWithTimeout executes the command in given directory and timeout 
212268// 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 
213272func  (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 )
218275	}
219- 	return  stdout . Bytes (),  nil 
276+ 	return  c . RunInDir ( dir ) 
220277}
221278
222279// RunInDir executes the command in given directory and default timeout 
223280// duration. It returns stdout and error (combined with stderr). 
224281func  (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 
226288}
227289
228290// RunWithTimeout executes the command in working directory and given timeout 
229291// 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 
230295func  (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 )
234298	}
235- 	return  stdout ,  nil 
299+ 	return  c . Run () 
236300}
237301
238302// Run executes the command in working directory and default timeout duration. 
239303// It returns stdout in string and error (combined with stderr). 
240304func  (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 
242310}
0 commit comments