зеркало из https://github.com/mislav/hub.git
163 строки
2.9 KiB
Go
163 строки
2.9 KiB
Go
package commands
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/github/hub/cmd"
|
|
"github.com/github/hub/git"
|
|
"github.com/github/hub/ui"
|
|
"github.com/github/hub/utils"
|
|
"github.com/kballard/go-shellquote"
|
|
flag "github.com/ogier/pflag"
|
|
)
|
|
|
|
type ExecError struct {
|
|
Err error
|
|
ExitCode int
|
|
}
|
|
|
|
func (execError *ExecError) Error() string {
|
|
return execError.Err.Error()
|
|
}
|
|
|
|
func newExecError(err error) ExecError {
|
|
exitCode := 0
|
|
if err != nil {
|
|
exitCode = 1
|
|
if exitError, ok := err.(*exec.ExitError); ok {
|
|
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
|
exitCode = status.ExitStatus()
|
|
}
|
|
}
|
|
}
|
|
|
|
return ExecError{Err: err, ExitCode: exitCode}
|
|
}
|
|
|
|
type Runner struct {
|
|
commands map[string]*Command
|
|
execute func(cmds []*cmd.Cmd) error
|
|
}
|
|
|
|
func NewRunner() *Runner {
|
|
return &Runner{
|
|
commands: make(map[string]*Command),
|
|
execute: executeCommands,
|
|
}
|
|
}
|
|
|
|
func (r *Runner) All() map[string]*Command {
|
|
return r.commands
|
|
}
|
|
|
|
func (r *Runner) Use(command *Command, aliases ...string) {
|
|
r.commands[command.Name()] = command
|
|
if len(aliases) > 0 {
|
|
r.commands[aliases[0]] = command
|
|
}
|
|
}
|
|
|
|
func (r *Runner) Lookup(name string) *Command {
|
|
return r.commands[name]
|
|
}
|
|
|
|
func (r *Runner) Execute() ExecError {
|
|
args := NewArgs(os.Args[1:])
|
|
|
|
if args.Command == "" {
|
|
printUsage()
|
|
return newExecError(nil)
|
|
}
|
|
|
|
updater := NewUpdater()
|
|
err := updater.PromptForUpdate()
|
|
utils.Check(err)
|
|
|
|
git.GlobalFlags = args.GlobalFlags // preserve git global flags
|
|
expandAlias(args)
|
|
|
|
cmd := r.Lookup(args.Command)
|
|
if cmd != nil && cmd.Runnable() {
|
|
return r.Call(cmd, args)
|
|
}
|
|
|
|
err = git.Run(args.Command, args.Params...)
|
|
return newExecError(err)
|
|
}
|
|
|
|
func (r *Runner) Call(cmd *Command, args *Args) ExecError {
|
|
err := cmd.Call(args)
|
|
if err != nil {
|
|
if err == flag.ErrHelp {
|
|
err = nil
|
|
}
|
|
return newExecError(err)
|
|
}
|
|
|
|
cmds := args.Commands()
|
|
if args.Noop {
|
|
printCommands(cmds)
|
|
} else {
|
|
err = r.execute(cmds)
|
|
}
|
|
|
|
return newExecError(err)
|
|
}
|
|
|
|
func printCommands(cmds []*cmd.Cmd) {
|
|
for _, c := range cmds {
|
|
ui.Println(c)
|
|
}
|
|
}
|
|
|
|
func executeCommands(cmds []*cmd.Cmd) error {
|
|
for i, c := range cmds {
|
|
var err error
|
|
// Run with `Exec` for the last command in chain
|
|
if i == len(cmds)-1 {
|
|
err = c.Run()
|
|
} else {
|
|
err = c.Spawn()
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func expandAlias(args *Args) {
|
|
cmd := args.Command
|
|
expandedCmd, err := git.Alias(cmd)
|
|
if err == nil && expandedCmd != "" {
|
|
words, e := splitAliasCmd(expandedCmd)
|
|
if e == nil {
|
|
args.Command = words[0]
|
|
args.PrependParams(words[1:]...)
|
|
}
|
|
}
|
|
}
|
|
|
|
func splitAliasCmd(cmd string) ([]string, error) {
|
|
if cmd == "" {
|
|
return nil, fmt.Errorf("alias can't be empty")
|
|
}
|
|
|
|
if strings.HasPrefix(cmd, "!") {
|
|
return nil, fmt.Errorf("alias starting with ! can't be split")
|
|
}
|
|
|
|
words, err := shellquote.Split(cmd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return words, nil
|
|
}
|