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  }