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