github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/cmd/tk/prefix.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/go-clix/cli"
    11  )
    12  
    13  func prefixCommands(prefix string) (cmds []*cli.Command) {
    14  	externalCommands, err := executablesOnPath(prefix)
    15  	if err != nil {
    16  		// soft fail if no commands found
    17  		return nil
    18  	}
    19  
    20  	for file, path := range externalCommands {
    21  		cmd := &cli.Command{
    22  			Use:   fmt.Sprintf("%s --", strings.TrimPrefix(file, prefix)),
    23  			Short: fmt.Sprintf("external command %s", path),
    24  			Args:  cli.ArgsAny(),
    25  		}
    26  
    27  		extCommand := exec.Command(path)
    28  		if ex, err := os.Executable(); err == nil {
    29  			extCommand.Env = append(os.Environ(), fmt.Sprintf("EXECUTABLE=%s", ex))
    30  		}
    31  		extCommand.Stdout = os.Stdout
    32  		extCommand.Stderr = os.Stderr
    33  
    34  		cmd.Run = func(cmd *cli.Command, args []string) error {
    35  			extCommand.Args = append(extCommand.Args, args...)
    36  			return extCommand.Run()
    37  		}
    38  		cmds = append(cmds, cmd)
    39  	}
    40  	if len(cmds) > 0 {
    41  		return cmds
    42  	}
    43  	return nil
    44  }
    45  
    46  func executablesOnPath(prefix string) (map[string]string, error) {
    47  	path, ok := os.LookupEnv("PATH")
    48  	if !ok {
    49  		// if PATH not set, soft fail
    50  		return nil, fmt.Errorf("PATH not set")
    51  	}
    52  
    53  	executables := make(map[string]string)
    54  	paths := strings.Split(path, ":")
    55  	for _, p := range paths {
    56  		s, err := os.Stat(p)
    57  		if err != nil && os.IsNotExist(err) {
    58  			continue
    59  		}
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		if !s.IsDir() {
    64  			continue
    65  		}
    66  
    67  		files, err := filepath.Glob(fmt.Sprintf("%s/%s*", p, prefix))
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		for _, file := range files {
    72  			base := filepath.Base(file)
    73  			// guarding against a glob character in the prefix or path
    74  			if !strings.HasPrefix(base, prefix) {
    75  				continue
    76  			}
    77  			info, err := os.Stat(file)
    78  			if err != nil {
    79  				return nil, err
    80  			}
    81  			if !info.Mode().IsRegular() {
    82  				continue
    83  			}
    84  			if info.Mode().Perm()&0111 == 0 {
    85  				continue
    86  			}
    87  			executables[base] = file
    88  		}
    89  	}
    90  	return executables, nil
    91  }