-
Notifications
You must be signed in to change notification settings - Fork 303
/
cmd.go
156 lines (132 loc) · 3.26 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
package model
import (
"fmt"
"runtime"
"strings"
)
type Cmd struct {
Argv []string
Dir string
Env []string
}
func (c Cmd) IsShellStandardForm() bool {
return len(c.Argv) == 3 && c.Argv[0] == "sh" && c.Argv[1] == "-c" && !strings.Contains(c.Argv[2], "\n")
}
func (c Cmd) IsWindowsStandardForm() bool {
return len(c.Argv) == 4 && c.Argv[0] == "cmd" && c.Argv[1] == "/S" && c.Argv[2] == "/C"
}
// Get the script when the shell is in standard form.
// Panics if the command is not in shell standard form.
func (c Cmd) ShellStandardScript() string {
if !c.IsShellStandardForm() {
panic(fmt.Sprintf("Not in shell standard form: %+v", c))
}
return c.Argv[2]
}
func (c Cmd) EntrypointStr() string {
if c.IsShellStandardForm() {
return fmt.Sprintf("ENTRYPOINT %s", c.Argv[2])
}
quoted := make([]string, len(c.Argv))
for i, arg := range c.Argv {
quoted[i] = fmt.Sprintf("%q", arg)
}
return fmt.Sprintf("ENTRYPOINT [%s]", strings.Join(quoted, ", "))
}
func (c Cmd) RunStr() string {
if c.IsShellStandardForm() {
return fmt.Sprintf("RUN %s", c.Argv[2])
}
quoted := make([]string, len(c.Argv))
for i, arg := range c.Argv {
quoted[i] = fmt.Sprintf("%q", arg)
}
return fmt.Sprintf("RUN [%s]", strings.Join(quoted, ", "))
}
func ArgListToString(args []string) string {
return Cmd{Argv: args}.String()
}
func (c Cmd) String() string {
if c.IsShellStandardForm() {
return c.Argv[2]
}
if c.IsWindowsStandardForm() {
return c.Argv[3]
}
quoted := make([]string, len(c.Argv))
for i, arg := range c.Argv {
if strings.Contains(arg, " ") {
quoted[i] = fmt.Sprintf("%q", arg)
} else {
quoted[i] = arg
}
}
return strings.Join(quoted, " ")
}
func (c Cmd) Empty() bool {
return len(c.Argv) == 0
}
// Create a shell command for running on the Host OS
func ToHostCmd(cmd string) Cmd {
if cmd == "" {
return Cmd{}
}
if runtime.GOOS == "windows" {
return ToBatCmd(cmd)
}
return ToUnixCmd(cmd)
}
func ToHostCmdInDir(cmd string, dir string) Cmd {
c := ToHostCmd(cmd)
c.Dir = dir
return c
}
func ToHostCmdInDirWithEnv(cmd string, dir string, env []string) Cmd {
c := ToHostCmdInDir(cmd, dir)
c.Env = env
return c
}
// 🦇🦇🦇
// Named in honor of Bazel
// https://docs.bazel.build/versions/master/be/general.html#genrule.cmd_bat
func ToBatCmd(cmd string) Cmd {
if cmd == "" {
return Cmd{}
}
// from https://docs.docker.com/engine/reference/builder/#run
//
// NOTE(nick): cmd /S /C does not handle multi-line strings correctly.
// It will execute the first line, then exit. Should we warn or error about this?
//
// The TrimSpace ensures we at least execute the first non-empty line.
return Cmd{Argv: []string{"cmd", "/S", "/C", strings.TrimSpace(cmd)}}
}
func ToUnixCmd(cmd string) Cmd {
if cmd == "" {
return Cmd{}
}
// trim spurious spaces and execute them in shell.
return Cmd{Argv: []string{"sh", "-c", strings.TrimSpace(cmd)}}
}
func ToUnixCmdInDir(cmd string, dir string) Cmd {
c := ToUnixCmd(cmd)
c.Dir = dir
return c
}
func ToUnixCmds(cmds []string) []Cmd {
res := make([]Cmd, len(cmds))
for i, cmd := range cmds {
res[i] = ToUnixCmd(cmd)
}
return res
}
func ToRun(cmd Cmd) Run {
return Run{Cmd: cmd}
}
func ToRuns(cmds []Cmd) []Run {
res := make([]Run, len(cmds))
for i, cmd := range cmds {
res[i] = ToRun(cmd)
}
return res
}