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