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 }