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 }