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 }