-
Notifications
You must be signed in to change notification settings - Fork 209
Expand file tree
/
Copy pathexec.go
More file actions
136 lines (112 loc) · 2.98 KB
/
Copy pathexec.go
File metadata and controls
136 lines (112 loc) · 2.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package exec
import (
"context"
"fmt"
"io"
"os"
osexec "os/exec"
)
type ExecutableType string
const BashExecutable ExecutableType = `bash`
const ShExecutable ExecutableType = `sh`
const CmdExecutable ExecutableType = `cmd`
var finders map[ExecutableType](func() (shell, error)) = map[ExecutableType](func() (shell, error)){
BashExecutable: newBashShell,
ShExecutable: newShShell,
CmdExecutable: newCmdShell,
}
type Command interface {
// Wait for command to terminate. It must have been previously started.
Wait() error
// StdinPipe returns a pipe that will be connected to the command's standard input when the command starts.
Stdout() io.ReadCloser
// StderrPipe returns a pipe that will be connected to the command's standard error when the command starts.
Stderr() io.ReadCloser
}
type command struct {
cmd *osexec.Cmd
execContext *execContext
stdout io.ReadCloser
stderr io.ReadCloser
}
func (c *command) Wait() error {
// After the command has finished (cmd.Wait call), remove the temporary script file
defer os.Remove(c.execContext.scriptFile)
err := c.cmd.Wait()
if err != nil {
return err
}
return nil
}
func (c *command) Stdout() io.ReadCloser {
return c.stdout
}
func (c *command) Stderr() io.ReadCloser {
return c.stderr
}
type Executor struct {
shell shell
dir string
}
func NewCommandExecutor(dir string) (*Executor, error) {
shell, err := findShell()
if err != nil {
return nil, err
}
return &Executor{
shell: shell,
dir: dir,
}, nil
}
func NewCommandExecutorWithExecutable(dir string, execType ExecutableType) (*Executor, error) {
f, ok := finders[execType]
if !ok {
return nil, fmt.Errorf("%s is not supported as an artifact executable, options are: %s, %s or %s", execType, BashExecutable, ShExecutable, CmdExecutable)
}
shell, err := f()
if err != nil {
return nil, err
}
return &Executor{
shell: shell,
dir: dir,
}, nil
}
func (e *Executor) prepareCommand(ctx context.Context, command string) (*osexec.Cmd, *execContext, error) {
ec, err := e.shell.prepare(command)
if err != nil {
return nil, nil, err
}
cmd := osexec.CommandContext(ctx, ec.executable, ec.args...)
cmd.Dir = e.dir
return cmd, ec, nil
}
func (e *Executor) StartCommand(ctx context.Context, command string) (Command, error) {
cmd, ec, err := e.prepareCommand(ctx, command)
if err != nil {
return nil, err
}
return e.start(ctx, cmd, ec)
}
func (e *Executor) start(ctx context.Context, cmd *osexec.Cmd, ec *execContext) (Command, error) {
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
return &command{cmd, ec, stdout, stderr}, cmd.Start()
}
func (e *Executor) Exec(ctx context.Context, command string) ([]byte, error) {
cmd, ec, err := e.prepareCommand(ctx, command)
if err != nil {
return nil, err
}
defer os.Remove(ec.scriptFile)
return cmd.CombinedOutput()
}
func (e *Executor) ShellType() ExecutableType {
return e.shell.getType()
}