66 "io"
77 "os"
88 "os/exec"
9- "regexp"
10- "strings"
119 "syscall"
1210 "time"
1311)
@@ -30,6 +28,7 @@ type Command struct {
3028
3129// NewCommand creates a new command
3230// You can add option with variadic option argument
31+ // Default timeout is set to 30 minutes
3332//
3433// Example:
3534// c := cmd.NewCommand("echo hello", function (c *Command) {
@@ -45,7 +44,7 @@ type Command struct {
4544func NewCommand (cmd string , options ... func (* Command )) * Command {
4645 c := & Command {
4746 Command : cmd ,
48- Timeout : 1 * time .Minute ,
47+ Timeout : 30 * time .Minute ,
4948 executed : false ,
5049 Env : []string {},
5150 }
@@ -65,57 +64,42 @@ func NewCommand(cmd string, options ...func(*Command)) *Command {
6564//
6665// Example:
6766//
68- // c := cmd.NewCommand("echo hello", cmd.WithStandardStreams)
69- // c.Execute()
67+ // c := cmd.NewCommand("echo hello", cmd.WithStandardStreams)
68+ // c.Execute()
7069//
7170func WithStandardStreams (c * Command ) {
7271 c .StdoutWriter = os .Stdout
7372 c .StderrWriter = os .Stderr
7473}
7574
76- // AddEnv adds an environment variable to the command
77- // If a variable gets passed like ${VAR_NAME} the env variable will be read out by the current shell
78- func (c * Command ) AddEnv (key string , value string ) {
79- vars := parseEnvVariableFromShell (value )
80- for _ , v := range vars {
81- value = strings .Replace (value , v , os .Getenv (removeEnvVarSyntax (v )), - 1 )
75+ // WithTimeout sets the timeout of the command
76+ //
77+ // Example:
78+ // cmd.NewCommand("sleep 10;", cmd.WithTimeout(500))
79+ //
80+ func WithTimeout (t time.Duration ) func (c * Command ) {
81+ return func (c * Command ) {
82+ c .Timeout = t
8283 }
83-
84- c .Env = append (c .Env , fmt .Sprintf ("%s=%s" , key , value ))
8584}
8685
87- // Removes the ${...} characters at the beginng and end of the given string
88- func removeEnvVarSyntax ( v string ) string {
89- return v [ 2 :( len ( v ) - 1 )]
86+ // WithoutTimeout disables the timeout for the command
87+ func WithoutTimeout ( c * Command ) {
88+ c . Timeout = 0
9089}
9190
92- //Read all environment variables from the given value
93- //with the syntax ${VAR_NAME}
94- func parseEnvVariableFromShell (val string ) []string {
95- reg := regexp .MustCompile (`\$\{.*?\}` )
96- matches := reg .FindAllString (val , - 1 )
97- return matches
98- }
99-
100- //SetTimeoutMS sets the timeout in milliseconds
101- func (c * Command ) SetTimeoutMS (ms int ) {
102- if ms == 0 {
103- c .Timeout = 1 * time .Minute
104- return
91+ // WithWorkingDir sets the current working directory
92+ func WithWorkingDir (dir string ) func (c * Command ) {
93+ return func (c * Command ) {
94+ c .WorkingDir = dir
10595 }
106- c .Timeout = time .Duration (ms ) * time .Millisecond
10796}
10897
109- // SetTimeout sets the timeout given a time unit
110- // Example: SetTimeout("100s") sets the timeout to 100 seconds
111- func (c * Command ) SetTimeout (timeout string ) error {
112- d , err := time .ParseDuration (timeout )
113- if err != nil {
114- return err
115- }
116-
117- c .Timeout = d
118- return nil
98+ // AddEnv adds an environment variable to the command
99+ // If a variable gets passed like ${VAR_NAME} the env variable will be read out by the current shell
100+ func (c * Command ) AddEnv (key string , value string ) {
101+ value = os .ExpandEnv (value )
102+ c .Env = append (c .Env , fmt .Sprintf ("%s=%s" , key , value ))
119103}
120104
121105//Stdout returns the output to stdout
@@ -157,14 +141,28 @@ func (c *Command) Execute() error {
157141 cmd .Stderr = c .StderrWriter
158142 cmd .Dir = c .WorkingDir
159143
144+ // Create timer only if timeout was set > 0
145+ var timeoutChan = make (<- chan time.Time , 1 )
146+ if c .Timeout != 0 {
147+ timeoutChan = time .After (c .Timeout )
148+ }
149+
160150 err := cmd .Start ()
161151 if err != nil {
162152 return err
163153 }
164154
165- done := make (chan error )
155+ done := make (chan error , 1 )
156+ quit := make (chan bool , 1 )
157+ defer close (quit )
158+
166159 go func () {
167- done <- cmd .Wait ()
160+ select {
161+ case <- quit :
162+ return
163+ case done <- cmd .Wait ():
164+ return
165+ }
168166 }()
169167
170168 select {
@@ -174,14 +172,14 @@ func (c *Command) Execute() error {
174172 break
175173 }
176174 c .exitCode = 0
177- case <- time .After (c .Timeout ):
175+ case <- timeoutChan :
176+ quit <- true
178177 if err := cmd .Process .Kill (); err != nil {
179178 return fmt .Errorf ("Timeout occurred and can not kill process with pid %v" , cmd .Process .Pid )
180179 }
181180 return fmt .Errorf ("Command timed out after %v" , c .Timeout )
182181 }
183182
184- //Remove leading and trailing whitespaces
185183 c .executed = true
186184
187185 return nil
0 commit comments