зеркало из https://github.com/mislav/hub.git
198 строки
3.5 KiB
Go
198 строки
3.5 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/kballard/go-shellquote"
|
|
flag "github.com/ogier/pflag"
|
|
)
|
|
|
|
type ExecError struct {
|
|
Err error
|
|
Ran bool
|
|
ExitCode int
|
|
}
|
|
|
|
func (execError *ExecError) Error() string {
|
|
return execError.Err.Error()
|
|
}
|
|
|
|
func newExecError(err error) ExecError {
|
|
exitCode := 0
|
|
ran := true
|
|
|
|
if err != nil {
|
|
exitCode = 1
|
|
switch e := err.(type) {
|
|
case *exec.ExitError:
|
|
if status, ok := e.Sys().(syscall.WaitStatus); ok {
|
|
exitCode = status.ExitStatus()
|
|
}
|
|
case *exec.Error:
|
|
ran = false
|
|
}
|
|
}
|
|
|
|
return ExecError{
|
|
Err: err,
|
|
Ran: ran,
|
|
ExitCode: exitCode,
|
|
}
|
|
}
|
|
|
|
type Runner struct {
|
|
commands map[string]*Command
|
|
execute func([]*cmd.Cmd, bool) 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:])
|
|
args.ProgramPath = os.Args[0]
|
|
forceFail := false
|
|
|
|
if args.Command == "" {
|
|
args.Command = "help"
|
|
forceFail = true
|
|
}
|
|
|
|
git.GlobalFlags = args.GlobalFlags // preserve git global flags
|
|
if !isBuiltInHubCommand(args.Command) {
|
|
expandAlias(args)
|
|
}
|
|
|
|
cmd := r.Lookup(args.Command)
|
|
if cmd != nil && cmd.Runnable() {
|
|
execErr := r.Call(cmd, args)
|
|
if execErr.ExitCode == 0 && forceFail {
|
|
execErr = newExecError(fmt.Errorf(""))
|
|
}
|
|
return execErr
|
|
}
|
|
|
|
gitArgs := []string{args.Command}
|
|
gitArgs = append(gitArgs, args.Params...)
|
|
|
|
err := git.Run(gitArgs...)
|
|
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, len(args.Callbacks) == 0)
|
|
}
|
|
|
|
if err == nil {
|
|
for _, fn := range args.Callbacks {
|
|
err = fn()
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return newExecError(err)
|
|
}
|
|
|
|
func printCommands(cmds []*cmd.Cmd) {
|
|
for _, c := range cmds {
|
|
ui.Println(c)
|
|
}
|
|
}
|
|
|
|
func executeCommands(cmds []*cmd.Cmd, execFinal bool) error {
|
|
for i, c := range cmds {
|
|
var err error
|
|
// Run with `Exec` for the last command in chain
|
|
if execFinal && 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 != "" && !git.IsBuiltInGitCommand(cmd) {
|
|
words, e := splitAliasCmd(expandedCmd)
|
|
if e == nil {
|
|
args.Command = words[0]
|
|
args.PrependParams(words[1:]...)
|
|
}
|
|
}
|
|
}
|
|
|
|
func isBuiltInHubCommand(command string) bool {
|
|
for hubCommand, _ := range CmdRunner.All() {
|
|
if hubCommand == command {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
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
|
|
}
|