github.com/echohead/hub@v2.2.1+incompatible/commands/runner.go (about) 1 package commands 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "strings" 8 "syscall" 9 10 "github.com/github/hub/Godeps/_workspace/src/github.com/kballard/go-shellquote" 11 flag "github.com/github/hub/Godeps/_workspace/src/github.com/ogier/pflag" 12 "github.com/github/hub/cmd" 13 "github.com/github/hub/git" 14 "github.com/github/hub/ui" 15 "github.com/github/hub/utils" 16 ) 17 18 type ExecError struct { 19 Err error 20 ExitCode int 21 } 22 23 func (execError *ExecError) Error() string { 24 return execError.Err.Error() 25 } 26 27 func newExecError(err error) ExecError { 28 exitCode := 0 29 if err != nil { 30 exitCode = 1 31 if exitError, ok := err.(*exec.ExitError); ok { 32 if status, ok := exitError.Sys().(syscall.WaitStatus); ok { 33 exitCode = status.ExitStatus() 34 } 35 } 36 } 37 38 return ExecError{Err: err, ExitCode: exitCode} 39 } 40 41 type Runner struct { 42 commands map[string]*Command 43 execute func(cmds []*cmd.Cmd) error 44 } 45 46 func NewRunner() *Runner { 47 return &Runner{ 48 commands: make(map[string]*Command), 49 execute: executeCommands, 50 } 51 } 52 53 func (r *Runner) All() map[string]*Command { 54 return r.commands 55 } 56 57 func (r *Runner) Use(command *Command) { 58 r.commands[command.Name()] = command 59 } 60 61 func (r *Runner) Lookup(name string) *Command { 62 return r.commands[name] 63 } 64 65 func (r *Runner) Execute() ExecError { 66 args := NewArgs(os.Args[1:]) 67 68 if args.Command == "" { 69 printUsage() 70 return newExecError(nil) 71 } 72 73 updater := NewUpdater() 74 err := updater.PromptForUpdate() 75 utils.Check(err) 76 77 git.GlobalFlags = args.GlobalFlags // preserve git global flags 78 expandAlias(args) 79 80 cmd := r.Lookup(args.Command) 81 if cmd != nil && cmd.Runnable() { 82 return r.Call(cmd, args) 83 } 84 85 err = git.Run(args.Command, args.Params...) 86 return newExecError(err) 87 } 88 89 func (r *Runner) Call(cmd *Command, args *Args) ExecError { 90 err := cmd.Call(args) 91 if err != nil { 92 if err == flag.ErrHelp { 93 err = nil 94 } 95 return newExecError(err) 96 } 97 98 cmds := args.Commands() 99 if args.Noop { 100 printCommands(cmds) 101 } else { 102 err = r.execute(cmds) 103 } 104 105 return newExecError(err) 106 } 107 108 func printCommands(cmds []*cmd.Cmd) { 109 for _, c := range cmds { 110 ui.Println(c) 111 } 112 } 113 114 func executeCommands(cmds []*cmd.Cmd) error { 115 for i, c := range cmds { 116 var err error 117 // Run with `Exec` for the last command in chain 118 if i == len(cmds)-1 { 119 err = c.Run() 120 } else { 121 err = c.Spawn() 122 } 123 124 if err != nil { 125 return err 126 } 127 } 128 129 return nil 130 } 131 132 func expandAlias(args *Args) { 133 cmd := args.Command 134 expandedCmd, err := git.Alias(cmd) 135 if err == nil && expandedCmd != "" { 136 words, e := splitAliasCmd(expandedCmd) 137 if e == nil { 138 args.Command = words[0] 139 args.PrependParams(words[1:]...) 140 } 141 } 142 } 143 144 func splitAliasCmd(cmd string) ([]string, error) { 145 if cmd == "" { 146 return nil, fmt.Errorf("alias can't be empty") 147 } 148 149 if strings.HasPrefix(cmd, "!") { 150 return nil, fmt.Errorf("alias starting with ! can't be split") 151 } 152 153 words, err := shellquote.Split(cmd) 154 if err != nil { 155 return nil, err 156 } 157 158 return words, nil 159 }