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  }