github.com/abdfnx/gh-api@v0.0.0-20210414084727-f5432eec23b8/pkg/cmd/alias/expand/expand.go (about) 1 package expand 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "path/filepath" 8 "regexp" 9 "runtime" 10 "strings" 11 12 "github.com/abdfnx/gh-api/internal/config" 13 "github.com/cli/safeexec" 14 "github.com/google/shlex" 15 ) 16 17 // ExpandAlias processes argv to see if it should be rewritten according to a user's aliases. The 18 // second return value indicates whether the alias should be executed in a new shell process instead 19 // of running gh itself. 20 func ExpandAlias(cfg config.Config, args []string, findShFunc func() (string, error)) (expanded []string, isShell bool, err error) { 21 if len(args) < 2 { 22 // the command is lacking a subcommand 23 return 24 } 25 expanded = args[1:] 26 27 aliases, err := cfg.Aliases() 28 if err != nil { 29 return 30 } 31 32 expansion, ok := aliases.Get(args[1]) 33 if !ok { 34 return 35 } 36 37 if strings.HasPrefix(expansion, "!") { 38 isShell = true 39 if findShFunc == nil { 40 findShFunc = findSh 41 } 42 shPath, shErr := findShFunc() 43 if shErr != nil { 44 err = shErr 45 return 46 } 47 48 expanded = []string{shPath, "-c", expansion[1:]} 49 50 if len(args[2:]) > 0 { 51 expanded = append(expanded, "--") 52 expanded = append(expanded, args[2:]...) 53 } 54 55 return 56 } 57 58 extraArgs := []string{} 59 for i, a := range args[2:] { 60 if !strings.Contains(expansion, "$") { 61 extraArgs = append(extraArgs, a) 62 } else { 63 expansion = strings.ReplaceAll(expansion, fmt.Sprintf("$%d", i+1), a) 64 } 65 } 66 lingeringRE := regexp.MustCompile(`\$\d`) 67 if lingeringRE.MatchString(expansion) { 68 err = fmt.Errorf("not enough arguments for alias: %s", expansion) 69 return 70 } 71 72 var newArgs []string 73 newArgs, err = shlex.Split(expansion) 74 if err != nil { 75 return 76 } 77 78 expanded = append(newArgs, extraArgs...) 79 return 80 } 81 82 func findSh() (string, error) { 83 shPath, err := safeexec.LookPath("sh") 84 if err == nil { 85 return shPath, nil 86 } 87 88 if runtime.GOOS == "windows" { 89 winNotFoundErr := errors.New("unable to locate sh to execute the shell alias with. The sh.exe interpreter is typically distributed with Git for Windows.") 90 // We can try and find a sh executable in a Git for Windows install 91 gitPath, err := safeexec.LookPath("git") 92 if err != nil { 93 return "", winNotFoundErr 94 } 95 96 shPath = filepath.Join(filepath.Dir(gitPath), "..", "bin", "sh.exe") 97 _, err = os.Stat(shPath) 98 if err != nil { 99 return "", winNotFoundErr 100 } 101 102 return shPath, nil 103 } 104 105 return "", errors.New("unable to locate sh to execute shell alias with") 106 }