1
1
package exec
2
2
3
3
import (
4
+ "fmt"
4
5
"io"
5
6
"os"
6
7
"os/exec"
@@ -12,75 +13,41 @@ import (
12
13
13
14
var log = logutil .New ("module" , "util/exec" )
14
15
15
- // Command is the template which is rendered before it's executed
16
- type Command string
17
-
18
- // Output runs the command until completion and returns the results
19
- func (c Command ) Output (args ... string ) ([]byte , error ) {
20
- return c .builder ().Output (args ... )
21
- }
22
-
23
- // Start runs the command without blocking
24
- func (c Command ) Start (args ... string ) error {
25
- return c .builder ().Start (args ... )
26
- }
27
-
28
- // Run does a Cmd.Run on the command
29
- func (c Command ) Run (args ... string ) error {
30
- return c .builder ().Run (args ... )
31
- }
32
-
33
- // String returns the interpolated version of the command
34
- func (c Command ) String (args ... string ) (string , error ) {
35
- p , err := c .builder ().generate (args ... )
36
- if err == nil {
37
- return strings .Join (p , " " ), nil
16
+ // Command returns a fluent builder for running a command where the command string
17
+ // can have template functions and arguments
18
+ func Command (s string ) * Builder {
19
+ return & Builder {
20
+ command : s ,
21
+ funcs : map [string ]interface {}{},
22
+ args : map [interface {}]interface {}{},
38
23
}
39
- return string (c ), err
40
- }
41
-
42
- // WithOptions adds the template options
43
- func (c Command ) WithOptions (options template.Options ) * Builder {
44
- return c .builder ().WithOptions (options )
45
- }
46
-
47
- // WithFunc adds a function that can be used in the template
48
- func (c Command ) WithFunc (name string , function interface {}) * Builder {
49
- return c .builder ().WithFunc (name , function )
50
- }
51
-
52
- // WithContext sets the context for the template
53
- func (c Command ) WithContext (context interface {}) * Builder {
54
- return c .builder ().WithContext (context )
55
- }
56
-
57
- // InheritEnvs determines whether the process should inherit the envs of the parent
58
- func (c Command ) InheritEnvs (v bool ) * Builder {
59
- return c .builder ().InheritEnvs (v )
60
- }
61
-
62
- // NewCommand creates an instance of the command builder to allow detailed configuration
63
- func NewCommand (s string ) * Builder {
64
- return Command (s ).builder ()
65
24
}
66
25
67
26
// Builder collects options until it's run
68
27
type Builder struct {
69
- command Command
28
+ command string
70
29
options template.Options
71
30
inheritEnvs bool
72
31
envs []string
73
32
funcs map [string ]interface {}
33
+ args map [interface {}]interface {}
74
34
context interface {}
75
35
rendered string // rendered command string
76
36
cmd * exec.Cmd
77
37
}
78
38
79
- func (c Command ) builder () * Builder {
80
- return & Builder {
81
- command : c ,
82
- funcs : map [string ]interface {}{},
39
+ // WithArg sets the arg key, value pair that can be accessed via the 'arg' function
40
+ func (b * Builder ) WithArg (key string , value interface {}) * Builder {
41
+ b .args [key ] = value
42
+ return b
43
+ }
44
+
45
+ // WithArgs adds the command line args array
46
+ func (b * Builder ) WithArgs (args ... interface {}) * Builder {
47
+ for i , arg := range args {
48
+ b .args [i + 1 ] = arg
83
49
}
50
+ return b
84
51
}
85
52
86
53
// InheritEnvs determines whether the process should inherit the envs of the parent
@@ -113,24 +80,6 @@ func (b *Builder) WithContext(context interface{}) *Builder {
113
80
return b
114
81
}
115
82
116
- // Output runs the command until completion and returns the results
117
- func (b * Builder ) Output (args ... string ) ([]byte , error ) {
118
- run , err := b .exec (args ... )
119
- if err != nil {
120
- return nil , err
121
- }
122
- return run .Output ()
123
- }
124
-
125
- // Start runs the command without blocking
126
- func (b * Builder ) Start (args ... string ) error {
127
- run , err := b .exec (args ... )
128
- if err != nil {
129
- return err
130
- }
131
- return run .Start ()
132
- }
133
-
134
83
// Step is something you do with the processes streams
135
84
type Step func (stdin io.WriteCloser , stdout io.ReadCloser , stderr io.ReadCloser ) error
136
85
@@ -154,10 +103,10 @@ func (t *Thenable) Then(then Step) *Thenable {
154
103
155
104
// Done returns the final function
156
105
func (t * Thenable ) Done () Step {
157
- steps := t .steps
106
+ all := t .steps
158
107
return func (stdin io.WriteCloser , stdout , stderr io.ReadCloser ) error {
159
- for _ , step := range steps {
160
- if err := step (stdin , stdout , stderr ); err != nil {
108
+ for _ , next := range all {
109
+ if err := next (stdin , stdout , stderr ); err != nil {
161
110
return err
162
111
}
163
112
}
@@ -200,57 +149,89 @@ func MergeOutput(out io.Writer) Step {
200
149
201
150
// StartWithStreams starts the the process and then calls the function which allows
202
151
// the streams to be wired. Calling the provided function blocks.
203
- func (b * Builder ) StartWithStreams (f Step ,
204
- args ... string ) error {
152
+ func (b * Builder ) StartWithStreams (f Step , args ... interface {}) error {
205
153
206
- _ , err := b .exec (args ... )
207
- if err != nil {
154
+ if err := b .Prepare (args ... ); err != nil {
208
155
return err
209
156
}
210
157
211
- pOut , err := b .cmd .StdoutPipe ()
212
- if err != nil {
213
- return err
214
- }
215
- pErr , err := b .cmd .StderrPipe ()
216
- if err != nil {
217
- return err
158
+ run := func () error { return nil }
159
+ if f != nil {
160
+ pIn , err := b .cmd .StdinPipe ()
161
+ if err != nil {
162
+ return err
163
+ }
164
+
165
+ pOut , err := b .cmd .StdoutPipe ()
166
+ if err != nil {
167
+ return err
168
+ }
169
+
170
+ pErr , err := b .cmd .StderrPipe ()
171
+ if err != nil {
172
+ return err
173
+ }
174
+
175
+ run = func () error {
176
+ return f (pIn , pOut , pErr )
177
+ }
218
178
}
219
- pIn , err := b . cmd . StdinPipe ()
220
- if err != nil {
179
+
180
+ if err := b . cmd . Start (); err != nil {
221
181
return err
222
182
}
223
183
224
- err = b .cmd .Start ()
225
- if err != nil {
184
+ return run ()
185
+ }
186
+
187
+ // Start does a Cmd.Start on the command
188
+ func (b * Builder ) Start (args ... interface {}) error {
189
+ if err := b .Prepare (args ... ); err != nil {
226
190
return err
227
191
}
192
+ return b .StartWithStreams (nil , args ... )
193
+ }
228
194
229
- return f (pIn , pOut , pErr )
195
+ // Wait waits for the command to complete
196
+ func (b * Builder ) Wait () error {
197
+ return b .cmd .Wait ()
230
198
}
231
199
232
- // Run does a Cmd.Run on the command
233
- func (b * Builder ) Run (args ... string ) error {
234
- run , err := b .exec (args ... )
235
- if err != nil {
236
- return err
200
+ // Output runs the command until completion and returns the results
201
+ func (b * Builder ) Output (args ... interface {}) ([]byte , error ) {
202
+ if err := b .Prepare (args ... ); err != nil {
203
+ return nil , err
237
204
}
238
- return run . Run ()
205
+ return b . cmd . Output ()
239
206
}
240
207
241
- func (b * Builder ) generate (args ... string ) ([]string , error ) {
208
+ func (b * Builder ) generate (args ... interface {}) ([]string , error ) {
209
+ // also index the args by index
210
+ for i , v := range args {
211
+ b .args [i + 1 ] = v
212
+ }
213
+
242
214
ct , err := template .NewTemplate ("str://" + string (b .command ), template.Options {})
243
215
if err != nil {
244
216
return nil , err
245
217
}
246
218
for k , v := range b .funcs {
247
219
ct .AddFunc (k , v )
248
220
}
249
- ct .AddFunc ("arg" , func (i int ) interface {} {
250
- return args [i - 1 ] // starts at 1
221
+ ct .AddFunc ("arg" , func (i interface {}) interface {} {
222
+ if i , is := i .(int ); is {
223
+ if len (args ) > i {
224
+ return args [i - 1 ] // starts at 1
225
+ }
226
+ }
227
+ return b .args [i ]
251
228
})
252
229
ct .AddFunc ("argv" , func () interface {} {
253
- return args
230
+ argv := []string {}
231
+ for _ , arg := range args {
232
+ argv = append (argv , fmt .Sprintf ("%v" , arg ))
233
+ }
234
+ return argv
254
235
})
255
236
cmd , err := ct .Render (b .context )
256
237
if err != nil {
@@ -267,16 +248,39 @@ func (b *Builder) generate(args ...string) ([]string, error) {
267
248
}
268
249
return command , nil
269
250
}
270
- func (b * Builder ) exec (args ... string ) (* exec.Cmd , error ) {
251
+
252
+ // Prepare generates the command based on the input args. This is the step before actual Start or Run
253
+ func (b * Builder ) Prepare (args ... interface {}) error {
271
254
command , err := b .generate (args ... )
272
255
if err != nil {
273
- return nil , err
256
+ return err
274
257
}
275
258
log .Debug ("exec" , "command" , command )
276
259
b .cmd = exec .Command (command [0 ], command [1 :]... )
277
260
if b .inheritEnvs {
278
261
b .cmd .Env = append (os .Environ (), b .envs ... )
279
262
}
263
+ return nil
264
+ }
265
+
266
+ // Stdin takes the input from the writer
267
+ func (b * Builder ) Stdin (f func (w io.Writer ) error ) error {
268
+ input , err := b .cmd .StdinPipe ()
269
+ if err != nil {
270
+ return err
271
+ }
272
+ defer input .Close ()
273
+ return f (input )
274
+ }
275
+
276
+ // StdoutTo connects the stdout of this to the next stage
277
+ func (b * Builder ) StdoutTo (next * Builder ) {
278
+ r , w := io .Pipe ()
279
+ b .cmd .Stdout = w
280
+ next .cmd .Stdin = r
281
+ }
280
282
281
- return b .cmd , nil
283
+ // Stdout sets the stdout
284
+ func (b * Builder ) Stdout (w io.Writer ) {
285
+ b .cmd .Stdout = w
282
286
}
0 commit comments