@@ -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