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  }