-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd.go
157 lines (128 loc) · 5.25 KB
/
cmd.go
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package shell
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/biptec/go-commons/errors"
)
// Run the specified shell command with the specified arguments. Connect the command's stdin, stdout, and stderr to
// the currently running app.
func RunShellCommand(options *ShellOptions, command string, args ...string) error {
logCommand(options, command, args...)
cmd := exec.Command(command, args...)
// TODO: consider logging this via options.Logger
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
setCommandOptions(options, cmd)
return errors.WithStackTrace(cmd.Run())
}
// Like RunShellCommand, but feed the given content to the stdin of the process.
func RunShellCommandWithInput(options *ShellOptions, inputString string, command string, args ...string) error {
logCommand(options, command, args...)
cmd := exec.Command(command, args...)
// TODO: consider logging this via options.Logger
cmd.Stdin = strings.NewReader(inputString)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
setCommandOptions(options, cmd)
return errors.WithStackTrace(cmd.Run())
}
// Run the specified shell command with the specified arguments. Return its stdout, stderr, and interleaved output as
// separate strings in a struct.
func RunShellCommandAndGetOutputStruct(options *ShellOptions, command string, args ...string) (*Output, error) {
return runShellCommand(options, false, command, args...)
}
// Run the specified shell command with the specified arguments. Return its stdout and stderr as a string
func RunShellCommandAndGetOutput(options *ShellOptions, command string, args ...string) (string, error) {
out, err := runShellCommand(options, false, command, args...)
return out.Combined(), err
}
// Run the specified shell command with the specified arguments. Return its interleaved stdout and stderr as a string
// and also stream stdout and stderr to the OS stdout/stderr
func RunShellCommandAndGetAndStreamOutput(options *ShellOptions, command string, args ...string) (string, error) {
out, err := runShellCommand(options, true, command, args...)
return out.Combined(), err
}
// Run the specified shell command with the specified arguments. Return its stdout as a string
func RunShellCommandAndGetStdout(options *ShellOptions, command string, args ...string) (string, error) {
out, err := runShellCommand(options, false, command, args...)
return out.Stdout(), err
}
// Run the specified shell command with the specified arguments. Return its stdout as a string and also stream stdout
// and stderr to the OS stdout/stderr
func RunShellCommandAndGetStdoutAndStreamOutput(options *ShellOptions, command string, args ...string) (string, error) {
out, err := runShellCommand(options, true, command, args...)
return out.Stdout(), err
}
// Run the specified shell command with the specified arguments. Return its stdout, stderr, and interleaved output as a
// struct and also stream stdout and stderr to the OS stdout/stderr
func RunShellCommandAndGetOutputStructAndStreamOutput(options *ShellOptions, command string, args ...string) (*Output, error) {
return runShellCommand(options, true, command, args...)
}
// Run the specified shell command with the specified arguments. Return its stdout and stderr as a string and also
// stream stdout and stderr to the OS stdout/stderr
func runShellCommand(options *ShellOptions, streamOutput bool, command string, args ...string) (*Output, error) {
logCommand(options, command, args...)
cmd := exec.Command(command, args...)
setCommandOptions(options, cmd)
cmd.Stdin = os.Stdin
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, errors.WithStackTrace(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, errors.WithStackTrace(err)
}
if err := cmd.Start(); err != nil {
return nil, errors.WithStackTrace(err)
}
output, err := readStdoutAndStderr(
options.Logger.Logger,
streamOutput,
stdout,
stderr,
)
if err != nil {
return output, err
}
err = cmd.Wait()
return output, errors.WithStackTrace(err)
}
func logCommand(options *ShellOptions, command string, args ...string) {
if options.SensitiveArgs {
options.Logger.Infof("Running command: %s (args redacted)", command)
} else {
options.Logger.Infof("Running command: %s %s", command, strings.Join(args, " "))
}
}
// Return true if the OS has the given command installed
func CommandInstalled(command string) bool {
_, err := exec.LookPath(command)
return err == nil
}
// CommandInstalledE returns an error if command is not installed
func CommandInstalledE(command string) error {
if commandExists := CommandInstalled(command); !commandExists {
err := fmt.Errorf("Command %s is not installed", command)
return errors.WithStackTrace(err)
}
return nil
}
// setCommandOptions takes the shell options and maps them to the configurations for the exec.Cmd object, applying them
// to the passed in Cmd object.
func setCommandOptions(options *ShellOptions, cmd *exec.Cmd) {
cmd.Dir = options.WorkingDir
cmd.Env = formatEnvVars(options)
}
// formatEnvVars takes environment variables encoded into ShellOptions and converts them to a format understood by
// exec.Command
func formatEnvVars(options *ShellOptions) []string {
env := os.Environ()
for key, value := range options.Env {
env = append(env, fmt.Sprintf("%s=%s", key, value))
}
return env
}