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