2013-05-29 22:58:46 +04:00
|
|
|
package cmd
|
2013-04-29 00:41:45 +04:00
|
|
|
|
|
|
|
import (
|
2013-06-18 00:35:02 +04:00
|
|
|
"fmt"
|
2013-04-29 05:47:08 +04:00
|
|
|
"os"
|
2013-04-29 00:41:45 +04:00
|
|
|
"os/exec"
|
2014-11-30 23:14:18 +03:00
|
|
|
"runtime"
|
2013-07-02 22:28:50 +04:00
|
|
|
"strings"
|
2014-11-30 23:14:18 +03:00
|
|
|
"syscall"
|
|
|
|
|
2015-10-31 02:53:41 +03:00
|
|
|
"github.com/github/hub/ui"
|
2014-11-30 23:14:18 +03:00
|
|
|
"github.com/github/hub/utils"
|
2016-01-20 06:37:48 +03:00
|
|
|
"github.com/kballard/go-shellquote"
|
2013-04-29 00:41:45 +04:00
|
|
|
)
|
|
|
|
|
2013-05-29 22:58:46 +04:00
|
|
|
type Cmd struct {
|
2017-06-21 23:55:03 +03:00
|
|
|
Name string
|
|
|
|
Args []string
|
|
|
|
Stdin *os.File
|
|
|
|
Stdout *os.File
|
|
|
|
Stderr *os.File
|
2013-04-29 00:41:45 +04:00
|
|
|
}
|
|
|
|
|
2013-07-02 22:28:50 +04:00
|
|
|
func (cmd Cmd) String() string {
|
|
|
|
return fmt.Sprintf("%s %s", cmd.Name, strings.Join(cmd.Args, " "))
|
|
|
|
}
|
|
|
|
|
2013-05-29 22:58:46 +04:00
|
|
|
func (cmd *Cmd) WithArg(arg string) *Cmd {
|
2014-12-09 19:42:37 +03:00
|
|
|
cmd.Args = append(cmd.Args, arg)
|
2013-04-29 00:41:45 +04:00
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2013-06-29 01:21:48 +04:00
|
|
|
func (cmd *Cmd) WithArgs(args ...string) *Cmd {
|
2013-07-05 22:10:24 +04:00
|
|
|
for _, arg := range args {
|
|
|
|
cmd.WithArg(arg)
|
|
|
|
}
|
2013-06-29 01:21:48 +04:00
|
|
|
|
|
|
|
return cmd
|
|
|
|
}
|
|
|
|
|
2014-11-30 23:16:53 +03:00
|
|
|
func (cmd *Cmd) CombinedOutput() (string, error) {
|
2015-10-31 02:53:41 +03:00
|
|
|
verboseLog(cmd)
|
2013-06-22 05:02:29 +04:00
|
|
|
output, err := exec.Command(cmd.Name, cmd.Args...).CombinedOutput()
|
2013-04-29 00:41:45 +04:00
|
|
|
|
2013-06-22 05:02:29 +04:00
|
|
|
return string(output), err
|
2013-04-29 00:41:45 +04:00
|
|
|
}
|
|
|
|
|
2016-01-22 12:47:51 +03:00
|
|
|
func (cmd *Cmd) Success() bool {
|
|
|
|
verboseLog(cmd)
|
|
|
|
err := exec.Command(cmd.Name, cmd.Args...).Run()
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2014-11-30 23:14:18 +03:00
|
|
|
// Run runs command with `Exec` on platforms except Windows
|
|
|
|
// which only supports `Spawn`
|
|
|
|
func (cmd *Cmd) Run() error {
|
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
return cmd.Spawn()
|
|
|
|
} else {
|
|
|
|
return cmd.Exec()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Spawn runs command with spawn(3)
|
2014-11-30 22:15:13 +03:00
|
|
|
func (cmd *Cmd) Spawn() error {
|
2015-10-31 02:53:41 +03:00
|
|
|
verboseLog(cmd)
|
2014-11-30 22:15:13 +03:00
|
|
|
c := exec.Command(cmd.Name, cmd.Args...)
|
2017-06-21 23:55:03 +03:00
|
|
|
c.Stdin = cmd.Stdin
|
|
|
|
c.Stdout = cmd.Stdout
|
|
|
|
c.Stderr = cmd.Stderr
|
2013-04-29 05:47:08 +04:00
|
|
|
|
|
|
|
return c.Run()
|
|
|
|
}
|
|
|
|
|
2014-11-30 23:14:18 +03:00
|
|
|
// Exec runs command with exec(3)
|
|
|
|
// Note that Windows doesn't support exec(3): http://golang.org/src/pkg/syscall/exec_windows.go#L339
|
|
|
|
func (cmd *Cmd) Exec() error {
|
2016-10-03 19:54:35 +03:00
|
|
|
verboseLog(cmd)
|
|
|
|
|
2014-11-30 23:14:18 +03:00
|
|
|
binary, err := exec.LookPath(cmd.Name)
|
|
|
|
if err != nil {
|
2016-10-03 20:09:41 +03:00
|
|
|
return &exec.Error{
|
|
|
|
Name: cmd.Name,
|
|
|
|
Err: fmt.Errorf("command not found"),
|
|
|
|
}
|
2014-11-30 23:14:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []string{binary}
|
|
|
|
args = append(args, cmd.Args...)
|
|
|
|
|
|
|
|
return syscall.Exec(binary, args, os.Environ())
|
|
|
|
}
|
|
|
|
|
2013-11-26 19:14:47 +04:00
|
|
|
func New(cmd string) *Cmd {
|
|
|
|
cmds, err := shellquote.Split(cmd)
|
|
|
|
utils.Check(err)
|
|
|
|
|
|
|
|
name := cmds[0]
|
2018-04-24 19:39:10 +03:00
|
|
|
args := make([]string, len(cmds)-1)
|
2013-11-26 19:14:47 +04:00
|
|
|
for _, arg := range cmds[1:] {
|
|
|
|
args = append(args, arg)
|
|
|
|
}
|
2017-06-21 23:55:03 +03:00
|
|
|
return &Cmd{Name: name, Args: args, Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}
|
2013-04-29 00:41:45 +04:00
|
|
|
}
|
2013-06-01 03:32:30 +04:00
|
|
|
|
|
|
|
func NewWithArray(cmd []string) *Cmd {
|
2017-08-05 02:16:16 +03:00
|
|
|
return &Cmd{Name: cmd[0], Args: cmd[1:], Stdin: os.Stdin, Stdout: os.Stdout, Stderr: os.Stderr}
|
2013-06-01 03:32:30 +04:00
|
|
|
}
|
2015-10-31 02:53:41 +03:00
|
|
|
|
|
|
|
func verboseLog(cmd *Cmd) {
|
|
|
|
if os.Getenv("HUB_VERBOSE") != "" {
|
|
|
|
msg := fmt.Sprintf("$ %s %s", cmd.Name, strings.Join(cmd.Args, " "))
|
|
|
|
if ui.IsTerminal(os.Stderr) {
|
|
|
|
msg = fmt.Sprintf("\033[35m%s\033[0m", msg)
|
|
|
|
}
|
|
|
|
ui.Errorln(msg)
|
|
|
|
}
|
|
|
|
}
|