github.com/secure-build/gitlab-runner@v12.5.0+incompatible/executors/shell/executor_shell.go (about)

     1  package shell
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"runtime"
    12  	"time"
    13  
    14  	"github.com/kardianos/osext"
    15  	"github.com/sirupsen/logrus"
    16  
    17  	"gitlab.com/gitlab-org/gitlab-runner/common"
    18  	"gitlab.com/gitlab-org/gitlab-runner/executors"
    19  	"gitlab.com/gitlab-org/gitlab-runner/helpers"
    20  )
    21  
    22  type executor struct {
    23  	executors.AbstractExecutor
    24  }
    25  
    26  func (s *executor) Prepare(options common.ExecutorPrepareOptions) error {
    27  	if options.User != "" {
    28  		s.Shell().User = options.User
    29  	}
    30  
    31  	// expand environment variables to have current directory
    32  	wd, err := os.Getwd()
    33  	if err != nil {
    34  		return fmt.Errorf("Getwd: %v", err)
    35  	}
    36  
    37  	mapping := func(key string) string {
    38  		switch key {
    39  		case "PWD":
    40  			return wd
    41  		default:
    42  			return ""
    43  		}
    44  	}
    45  
    46  	s.DefaultBuildsDir = os.Expand(s.DefaultBuildsDir, mapping)
    47  	s.DefaultCacheDir = os.Expand(s.DefaultCacheDir, mapping)
    48  
    49  	// Pass control to executor
    50  	err = s.AbstractExecutor.Prepare(options)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	s.Println("Using Shell executor...")
    56  	return nil
    57  }
    58  
    59  func (s *executor) killAndWait(cmd *exec.Cmd, waitCh chan error) error {
    60  	for {
    61  		s.Debugln("Aborting command...")
    62  		helpers.KillProcessGroup(cmd)
    63  		select {
    64  		case <-time.After(time.Second):
    65  		case err := <-waitCh:
    66  			return err
    67  		}
    68  	}
    69  }
    70  
    71  func (s *executor) Run(cmd common.ExecutorCommand) error {
    72  	// Create execution command
    73  	c := exec.Command(s.BuildShell.Command, s.BuildShell.Arguments...)
    74  	if c == nil {
    75  		return errors.New("Failed to generate execution command")
    76  	}
    77  
    78  	helpers.SetProcessGroup(c)
    79  	defer helpers.KillProcessGroup(c)
    80  
    81  	// Fill process environment variables
    82  	c.Env = append(os.Environ(), s.BuildShell.Environment...)
    83  	c.Stdout = s.Trace
    84  	c.Stderr = s.Trace
    85  
    86  	if s.BuildShell.PassFile {
    87  		scriptDir, err := ioutil.TempDir("", "build_script")
    88  		if err != nil {
    89  			return err
    90  		}
    91  		defer os.RemoveAll(scriptDir)
    92  
    93  		scriptFile := filepath.Join(scriptDir, "script."+s.BuildShell.Extension)
    94  		err = ioutil.WriteFile(scriptFile, []byte(cmd.Script), 0700)
    95  		if err != nil {
    96  			return err
    97  		}
    98  
    99  		c.Args = append(c.Args, scriptFile)
   100  	} else {
   101  		c.Stdin = bytes.NewBufferString(cmd.Script)
   102  	}
   103  
   104  	// Start a process
   105  	err := c.Start()
   106  	if err != nil {
   107  		return fmt.Errorf("Failed to start process: %s", err)
   108  	}
   109  
   110  	// Wait for process to finish
   111  	waitCh := make(chan error)
   112  	go func() {
   113  		err := c.Wait()
   114  		if _, ok := err.(*exec.ExitError); ok {
   115  			err = &common.BuildError{Inner: err}
   116  		}
   117  		waitCh <- err
   118  	}()
   119  
   120  	// Support process abort
   121  	select {
   122  	case err = <-waitCh:
   123  		return err
   124  
   125  	case <-cmd.Context.Done():
   126  		return s.killAndWait(c, waitCh)
   127  	}
   128  }
   129  
   130  func init() {
   131  	// Look for self
   132  	runnerCommand, err := osext.Executable()
   133  	if err != nil {
   134  		logrus.Warningln(err)
   135  	}
   136  
   137  	options := executors.ExecutorOptions{
   138  		DefaultCustomBuildsDirEnabled: false,
   139  		DefaultBuildsDir:              "$PWD/builds",
   140  		DefaultCacheDir:               "$PWD/cache",
   141  		SharedBuildsDir:               true,
   142  		Shell: common.ShellScriptInfo{
   143  			Shell:         common.GetDefaultShell(),
   144  			Type:          common.LoginShell,
   145  			RunnerCommand: runnerCommand,
   146  		},
   147  		ShowHostname: false,
   148  	}
   149  
   150  	creator := func() common.Executor {
   151  		return &executor{
   152  			AbstractExecutor: executors.AbstractExecutor{
   153  				ExecutorOptions: options,
   154  			},
   155  		}
   156  	}
   157  
   158  	featuresUpdater := func(features *common.FeaturesInfo) {
   159  		features.Variables = true
   160  		features.Shared = true
   161  
   162  		if runtime.GOOS != "windows" {
   163  			features.Session = true
   164  			features.Terminal = true
   165  		}
   166  	}
   167  
   168  	common.RegisterExecutor("shell", executors.DefaultExecutorProvider{
   169  		Creator:          creator,
   170  		FeaturesUpdater:  featuresUpdater,
   171  		DefaultShellName: options.Shell.Shell,
   172  	})
   173  }