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  }