@@ -6,6 +6,7 @@ package git
6
6
7
7
import (
8
8
"bytes"
9
+ "context"
9
10
"fmt"
10
11
"io"
11
12
"os"
@@ -14,56 +15,62 @@ import (
14
15
"time"
15
16
)
16
17
17
- // Command represents a command with its subcommands or arguments .
18
+ // Command contains the name, arguments and environment variables of a command .
18
19
type Command struct {
19
20
name string
20
21
args []string
21
22
envs []string
22
23
}
23
24
25
+ // String returns the string representation of the command.
24
26
func (c * Command ) String () string {
25
27
if len (c .args ) == 0 {
26
28
return c .name
27
29
}
28
30
return fmt .Sprintf ("%s %s" , c .name , strings .Join (c .args , " " ))
29
31
}
30
32
31
- // NewCommand creates and returns a new Git Command based on given command and arguments .
33
+ // NewCommand creates and returns a new Command with given arguments for "git" .
32
34
func NewCommand (args ... string ) * Command {
33
35
return & Command {
34
36
name : "git" ,
35
37
args : args ,
36
38
}
37
39
}
38
40
39
- // AddArguments adds new argument(s) to the command.
40
- func (c * Command ) AddArguments (args ... string ) * Command {
41
+ // AddArgs appends given arguments to the command.
42
+ func (c * Command ) AddArgs (args ... string ) * Command {
41
43
c .args = append (c .args , args ... )
42
44
return c
43
45
}
44
46
45
- // AddEnvs adds new environment variables to the command.
47
+ // AddEnvs appends given environment variables to the command.
46
48
func (c * Command ) AddEnvs (envs ... string ) * Command {
47
49
c .envs = append (c .envs , envs ... )
48
50
return c
49
51
}
50
52
51
- const DEFAULT_TIMEOUT = 60 * time .Second
53
+ // DefaultTimeout is the default timeout duration for all commands.
54
+ const DefaultTimeout = time .Minute
52
55
53
- // RunInDirTimeoutPipeline executes the command in given directory with given timeout,
54
- // it pipes stdout and stderr to given io.Writer.
55
- func (c * Command ) RunInDirTimeoutPipeline (timeout time.Duration , dir string , stdout , stderr io.Writer ) error {
56
- if timeout == - 1 {
57
- timeout = DEFAULT_TIMEOUT
56
+ // RunInDirPipelineWithTimeout executes the command in given directory and timeout duration.
57
+ // It pipes stdout and stderr to supplied io.Writer. DefaultTimeout will be used if the timeout
58
+ // duration is less than time.Nanosecond (i.e. less than or equal to 0).
59
+ func (c * Command ) RunInDirPipelineWithTimeout (timeout time.Duration , stdout , stderr io.Writer , dir string ) error {
60
+ if timeout < time .Nanosecond {
61
+ timeout = DefaultTimeout
58
62
}
59
63
60
64
if len (dir ) == 0 {
61
- log (c . String () )
65
+ log ("[timeout: %v] %s" , timeout , c )
62
66
} else {
63
- log ("% s: %v" , dir , c )
67
+ log ("[timeout: %v] % s: %s" , timeout , dir , c )
64
68
}
65
69
66
- cmd := exec .Command (c .name , c .args ... )
70
+ ctx , cancel := context .WithTimeout (context .Background (), timeout )
71
+ defer cancel ()
72
+
73
+ cmd := exec .CommandContext (ctx , c .name , c .args ... )
67
74
if c .envs != nil {
68
75
cmd .Env = append (os .Environ (), c .envs ... )
69
76
}
@@ -74,34 +81,38 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std
74
81
return err
75
82
}
76
83
77
- done := make (chan error )
84
+ result := make (chan error )
78
85
go func () {
79
- done <- cmd .Wait ()
86
+ result <- cmd .Wait ()
80
87
}()
81
88
82
- var err error
83
89
select {
84
- case <- time . After ( timeout ):
90
+ case <- ctx . Done ( ):
85
91
if cmd .Process != nil && cmd .ProcessState != nil && ! cmd .ProcessState .Exited () {
86
92
if err := cmd .Process .Kill (); err != nil {
87
- return fmt .Errorf ("fail to kill process: %v" , err )
93
+ return fmt .Errorf ("kill process: %v" , err )
88
94
}
89
95
}
90
96
91
- <- done
97
+ <- result
92
98
return ErrExecTimeout {timeout }
93
- case err = <- done :
99
+ case err := <- result :
100
+ return err
94
101
}
102
+ }
95
103
96
- return err
104
+ // RunInDirPipeline executes the command in given directory and default timeout duration.
105
+ // It pipes stdout and stderr to supplied io.Writer.
106
+ func (c * Command ) RunInDirPipeline (stdout , stderr io.Writer , dir string ) error {
107
+ return c .RunInDirPipelineWithTimeout (DefaultTimeout , stdout , stderr , dir )
97
108
}
98
109
99
- // RunInDirTimeout executes the command in given directory with given timeout,
100
- // and returns stdout in []byte and error (combined with stderr).
101
- func (c * Command ) RunInDirTimeout (timeout time.Duration , dir string ) ([]byte , error ) {
110
+ // RunInDirWithTimeout executes the command in given directory and timeout duration.
111
+ // It returns stdout in []byte and error (combined with stderr).
112
+ func (c * Command ) RunInDirWithTimeout (timeout time.Duration , dir string ) ([]byte , error ) {
102
113
stdout := new (bytes.Buffer )
103
114
stderr := new (bytes.Buffer )
104
- if err := c .RunInDirTimeoutPipeline (timeout , dir , stdout , stderr ); err != nil {
115
+ if err := c .RunInDirPipelineWithTimeout (timeout , stdout , stderr , dir ); err != nil {
105
116
return nil , concatenateError (err , stderr .String ())
106
117
}
107
118
@@ -111,40 +122,24 @@ func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, er
111
122
return stdout .Bytes (), nil
112
123
}
113
124
114
- // RunInDirPipeline executes the command in given directory,
115
- // it pipes stdout and stderr to given io.Writer.
116
- func (c * Command ) RunInDirPipeline (dir string , stdout , stderr io.Writer ) error {
117
- return c .RunInDirTimeoutPipeline (- 1 , dir , stdout , stderr )
118
- }
119
-
120
- // RunInDirBytes executes the command in given directory
121
- // and returns stdout in []byte and error (combined with stderr).
122
- func (c * Command ) RunInDirBytes (dir string ) ([]byte , error ) {
123
- return c .RunInDirTimeout (- 1 , dir )
124
- }
125
-
126
- // RunInDir executes the command in given directory
127
- // and returns stdout in string and error (combined with stderr).
128
- func (c * Command ) RunInDir (dir string ) (string , error ) {
129
- stdout , err := c .RunInDirTimeout (- 1 , dir )
130
- if err != nil {
131
- return "" , err
132
- }
133
- return string (stdout ), nil
125
+ // RunInDir executes the command in given directory and default timeout duration.
126
+ // It returns stdout and error (combined with stderr).
127
+ func (c * Command ) RunInDir (dir string ) ([]byte , error ) {
128
+ return c .RunInDirWithTimeout (DefaultTimeout , dir )
134
129
}
135
130
136
- // RunTimeout executes the command in defualt working directory with given timeout,
137
- // and returns stdout in string and error (combined with stderr).
138
- func (c * Command ) RunTimeout (timeout time.Duration ) (string , error ) {
139
- stdout , err := c .RunInDirTimeout (timeout , "" )
131
+ // RunWithTimeout executes the command in working directory and given timeout duration.
132
+ // It returns stdout in string and error (combined with stderr).
133
+ func (c * Command ) RunWithTimeout (timeout time.Duration ) ([] byte , error ) {
134
+ stdout , err := c .RunInDirWithTimeout (timeout , "" )
140
135
if err != nil {
141
- return "" , err
136
+ return nil , err
142
137
}
143
- return string ( stdout ) , nil
138
+ return stdout , nil
144
139
}
145
140
146
- // Run executes the command in defualt working directory
147
- // and returns stdout in string and error (combined with stderr).
148
- func (c * Command ) Run () (string , error ) {
149
- return c .RunTimeout ( - 1 )
141
+ // Run executes the command in working directory and default timeout duration.
142
+ // It returns stdout in string and error (combined with stderr).
143
+ func (c * Command ) Run () ([] byte , error ) {
144
+ return c .RunWithTimeout ( DefaultTimeout )
150
145
}
0 commit comments