github.com/ssube/gitlab-ci-multi-runner@v1.2.1-0.20160607142738-b8d1285632e6/executors/shell/executor_shell.go (about)

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