github.com/alloyci/alloy-runner@v1.0.1-0.20180222164613-925503ccafd6/shells/bash.go (about) 1 package shells 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "gitlab.com/gitlab-org/gitlab-runner/common" 8 "gitlab.com/gitlab-org/gitlab-runner/helpers" 9 "io" 10 "path" 11 "runtime" 12 "strconv" 13 "strings" 14 ) 15 16 const bashDetectShell = `if [ -x /usr/local/bin/bash ]; then 17 exec /usr/local/bin/bash $@ 18 elif [ -x /usr/bin/bash ]; then 19 exec /usr/bin/bash $@ 20 elif [ -x /bin/bash ]; then 21 exec /bin/bash $@ 22 elif [ -x /usr/local/bin/sh ]; then 23 exec /usr/local/bin/sh $@ 24 elif [ -x /usr/bin/sh ]; then 25 exec /usr/bin/sh $@ 26 elif [ -x /bin/sh ]; then 27 exec /bin/sh $@ 28 else 29 echo shell not found 30 exit 1 31 fi 32 33 ` 34 35 type BashShell struct { 36 AbstractShell 37 Shell string 38 } 39 40 type BashWriter struct { 41 bytes.Buffer 42 TemporaryPath string 43 indent int 44 } 45 46 func (b *BashWriter) GetTemporaryPath() string { 47 return b.TemporaryPath 48 } 49 50 func (b *BashWriter) Line(text string) { 51 b.WriteString(strings.Repeat(" ", b.indent) + text + "\n") 52 } 53 54 func (b *BashWriter) CheckForErrors() { 55 } 56 57 func (b *BashWriter) Indent() { 58 b.indent++ 59 } 60 61 func (b *BashWriter) Unindent() { 62 b.indent-- 63 } 64 65 func (b *BashWriter) Command(command string, arguments ...string) { 66 b.Line(b.buildCommand(command, arguments...)) 67 } 68 69 func (b *BashWriter) buildCommand(command string, arguments ...string) string { 70 list := []string{ 71 helpers.ShellEscape(command), 72 } 73 74 for _, argument := range arguments { 75 list = append(list, strconv.Quote(argument)) 76 } 77 78 return strings.Join(list, " ") 79 } 80 81 func (b *BashWriter) TmpFile(name string) string { 82 return b.Absolute(path.Join(b.TemporaryPath, name)) 83 } 84 85 func (b *BashWriter) Variable(variable common.JobVariable) { 86 if variable.File { 87 variableFile := b.TmpFile(variable.Key) 88 b.Line(fmt.Sprintf("mkdir -p %q", helpers.ToSlash(b.TemporaryPath))) 89 b.Line(fmt.Sprintf("echo -n %s > %q", helpers.ShellEscape(variable.Value), variableFile)) 90 b.Line(fmt.Sprintf("export %s=%q", helpers.ShellEscape(variable.Key), variableFile)) 91 } else { 92 b.Line(fmt.Sprintf("export %s=%s", helpers.ShellEscape(variable.Key), helpers.ShellEscape(variable.Value))) 93 } 94 } 95 96 func (b *BashWriter) IfDirectory(path string) { 97 b.Line(fmt.Sprintf("if [[ -d %q ]]; then", path)) 98 b.Indent() 99 } 100 101 func (b *BashWriter) IfFile(path string) { 102 b.Line(fmt.Sprintf("if [[ -e %q ]]; then", path)) 103 b.Indent() 104 } 105 106 func (b *BashWriter) IfCmd(cmd string, arguments ...string) { 107 cmdline := b.buildCommand(cmd, arguments...) 108 b.Line(fmt.Sprintf("if %s >/dev/null 2>/dev/null; then", cmdline)) 109 b.Indent() 110 } 111 112 func (b *BashWriter) IfCmdWithOutput(cmd string, arguments ...string) { 113 cmdline := b.buildCommand(cmd, arguments...) 114 b.Line(fmt.Sprintf("if %s; then", cmdline)) 115 b.Indent() 116 } 117 118 func (b *BashWriter) Else() { 119 b.Unindent() 120 b.Line("else") 121 b.Indent() 122 } 123 124 func (b *BashWriter) EndIf() { 125 b.Unindent() 126 b.Line("fi") 127 } 128 129 func (b *BashWriter) Cd(path string) { 130 b.Command("cd", path) 131 } 132 133 func (b *BashWriter) MkDir(path string) { 134 b.Command("mkdir", "-p", path) 135 } 136 137 func (b *BashWriter) MkTmpDir(name string) string { 138 path := path.Join(b.TemporaryPath, name) 139 b.MkDir(path) 140 141 return path 142 } 143 144 func (b *BashWriter) RmDir(path string) { 145 b.Command("rm", "-r", "-f", path) 146 } 147 148 func (b *BashWriter) RmFile(path string) { 149 b.Command("rm", "-f", path) 150 } 151 152 func (b *BashWriter) Absolute(dir string) string { 153 if path.IsAbs(dir) { 154 return dir 155 } 156 return path.Join("$PWD", dir) 157 } 158 159 func (b *BashWriter) Print(format string, arguments ...interface{}) { 160 coloredText := helpers.ANSI_RESET + fmt.Sprintf(format, arguments...) 161 b.Line("echo " + helpers.ShellEscape(coloredText)) 162 } 163 164 func (b *BashWriter) Notice(format string, arguments ...interface{}) { 165 coloredText := helpers.ANSI_BOLD_GREEN + fmt.Sprintf(format, arguments...) + helpers.ANSI_RESET 166 b.Line("echo " + helpers.ShellEscape(coloredText)) 167 } 168 169 func (b *BashWriter) Warning(format string, arguments ...interface{}) { 170 coloredText := helpers.ANSI_YELLOW + fmt.Sprintf(format, arguments...) + helpers.ANSI_RESET 171 b.Line("echo " + helpers.ShellEscape(coloredText)) 172 } 173 174 func (b *BashWriter) Error(format string, arguments ...interface{}) { 175 coloredText := helpers.ANSI_BOLD_RED + fmt.Sprintf(format, arguments...) + helpers.ANSI_RESET 176 b.Line("echo " + helpers.ShellEscape(coloredText)) 177 } 178 179 func (b *BashWriter) EmptyLine() { 180 b.Line("echo") 181 } 182 183 func (b *BashWriter) Finish(trace bool) string { 184 var buffer bytes.Buffer 185 w := bufio.NewWriter(&buffer) 186 187 if trace { 188 io.WriteString(w, "set -o xtrace\n") 189 } 190 191 io.WriteString(w, "set -eo pipefail\n") 192 io.WriteString(w, "set +o noclobber\n") 193 io.WriteString(w, ": | eval "+helpers.ShellEscape(b.String())+"\n") 194 io.WriteString(w, "exit 0\n") 195 w.Flush() 196 return buffer.String() 197 } 198 199 func (b *BashShell) GetName() string { 200 return b.Shell 201 } 202 203 func (b *BashShell) GetConfiguration(info common.ShellScriptInfo) (script *common.ShellConfiguration, err error) { 204 var detectScript string 205 var shellCommand string 206 if info.Type == common.LoginShell { 207 detectScript = strings.Replace(bashDetectShell, "$@", "--login", -1) 208 shellCommand = b.Shell + " --login" 209 } else { 210 detectScript = strings.Replace(bashDetectShell, "$@", "", -1) 211 shellCommand = b.Shell 212 } 213 214 script = &common.ShellConfiguration{} 215 script.DockerCommand = []string{"sh", "-c", detectScript} 216 217 // su 218 if info.User != "" { 219 script.Command = "su" 220 if runtime.GOOS == "linux" { 221 script.Arguments = append(script.Arguments, "-s", "/bin/"+b.Shell) 222 } 223 script.Arguments = append(script.Arguments, info.User) 224 script.Arguments = append(script.Arguments, "-c", shellCommand) 225 } else { 226 script.Command = b.Shell 227 if info.Type == common.LoginShell { 228 script.Arguments = append(script.Arguments, "--login") 229 } 230 } 231 232 return 233 } 234 235 func (b *BashShell) GenerateScript(buildStage common.BuildStage, info common.ShellScriptInfo) (script string, err error) { 236 w := &BashWriter{ 237 TemporaryPath: info.Build.FullProjectDir() + ".tmp", 238 } 239 240 if buildStage == common.BuildStagePrepare { 241 if len(info.Build.Hostname) != 0 { 242 w.Line("echo " + strconv.Quote("Running on $(hostname) via "+info.Build.Hostname+"...")) 243 } else { 244 w.Line("echo " + strconv.Quote("Running on $(hostname)...")) 245 } 246 } 247 248 err = b.writeScript(w, buildStage, info) 249 script = w.Finish(info.Build.IsDebugTraceEnabled()) 250 return 251 } 252 253 func (b *BashShell) IsDefault() bool { 254 return runtime.GOOS != "windows" && b.Shell == "bash" 255 } 256 257 func init() { 258 common.RegisterShell(&BashShell{Shell: "sh"}) 259 common.RegisterShell(&BashShell{Shell: "bash"}) 260 }