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  }