diff --git a/common/config.go b/common/config.go index af9012057f7ecb667d1b983570f9883b36e91bb8..7d6c457d74845708813928f6e0d8bb9644c97f92 100644 --- a/common/config.go +++ b/common/config.go @@ -48,6 +48,7 @@ type RunnerConfig struct { Shell string `toml:"shell" json:"shell"` DisableVerbose bool `toml:"disable_verbose" json:"disable_verbose"` + DisablePTY *bool `toml:"disable_pty" json:"disable_pty"` SSH *ssh.Config `toml:"ssh" json:"ssh"` Docker *DockerConfig `toml:"docker" json:"docker"` @@ -72,6 +73,10 @@ func (c *RunnerConfig) UniqueID() string { return c.URL + c.Token } +func (c *RunnerConfig) IsPTYEnabled() bool { + return c.DisablePTY == nil || !*c.DisablePTY +} + func (c *Config) LoadConfig(configFile string) error { info, err := os.Stat(configFile) if err != nil { diff --git a/executors/shell/executor_shell.go b/executors/shell/executor_shell.go index 421c5d339aa019450b155ea0077b1385ba984931..47ef17a8f0621f9fe54de0d3d43b4e0e1b887465 100644 --- a/executors/shell/executor_shell.go +++ b/executors/shell/executor_shell.go @@ -8,15 +8,19 @@ import ( "os/exec" "path/filepath" + krpty "github.com/kr/pty" + "github.com/ayufan/gitlab-ci-multi-runner/common" "github.com/ayufan/gitlab-ci-multi-runner/executors" "github.com/ayufan/gitlab-ci-multi-runner/helpers" + "io" ) type ShellExecutor struct { executors.AbstractExecutor cmd *exec.Cmd scriptDir string + pty *os.File } func (s *ShellExecutor) Prepare(config *common.RunnerConfig, build *common.Build) error { @@ -51,6 +55,21 @@ func (s *ShellExecutor) Start() error { s.cmd.Stdout = s.BuildLog s.cmd.Stderr = s.BuildLog + // Open PTY if available + if s.Config.IsPTYEnabled() { + pty, tty, err := krpty.Open() + if err == nil { + go io.Copy(s.BuildLog, pty) + defer tty.Close() + s.cmd.Stdout = tty + s.cmd.Stderr = tty + s.pty = pty + } else if err != krpty.ErrUnsupported { + s.Errorln("Failed to open pty", err) + } + } + + // Save shell script as file if s.ShellScript.PassFile { scriptDir, err := ioutil.TempDir("", "build_script") if err != nil { @@ -89,6 +108,10 @@ func (s *ShellExecutor) Cleanup() { os.RemoveAll(s.scriptDir) } + if s.pty != nil { + s.pty.Close() + } + s.AbstractExecutor.Cleanup() }