github.com/travisturner/buffalo@v0.11.1/plugins/plugins.go (about) 1 package plugins 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "time" 13 14 "github.com/gobuffalo/envy" 15 "github.com/pkg/errors" 16 "github.com/sirupsen/logrus" 17 ) 18 19 // List maps a Buffalo command to a slice of Command 20 type List map[string]Commands 21 22 // Available plugins for the `buffalo` command. 23 // It will look in $GOPATH/bin and the `./plugins` directory. 24 // This can be changed by setting the $BUFFALO_PLUGIN_PATH 25 // environment variable. 26 // 27 // Requirements: 28 // * file/command must be executable 29 // * file/command must start with `buffalo-` 30 // * file/command must respond to `available` and return JSON of 31 // plugins.Commands{} 32 // 33 // Limit full path scan with direct plugin path 34 func Available() (List, error) { 35 list := List{} 36 paths := []string{"plugins"} 37 38 from, err := envy.MustGet("BUFFALO_PLUGIN_PATH") 39 if err != nil { 40 from, err = envy.MustGet("GOPATH") 41 if err != nil { 42 return list, errors.WithStack(err) 43 } 44 from = filepath.Join(from, "bin") 45 } 46 47 if runtime.GOOS == "windows" { 48 paths = append(paths, strings.Split(from, ";")...) 49 } else { 50 paths = append(paths, strings.Split(from, ":")...) 51 } 52 53 for _, p := range paths { 54 if ignorePath(p) { 55 continue 56 } 57 if _, err := os.Stat(p); err != nil { 58 continue 59 } 60 err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error { 61 if err != nil { 62 // May indicate a permissions problem with the path, skip it 63 return nil 64 } 65 if info.IsDir() { 66 return nil 67 } 68 base := filepath.Base(path) 69 if strings.HasPrefix(base, "buffalo-") { 70 commands := askBin(path) 71 for _, c := range commands { 72 bc := c.BuffaloCommand 73 if _, ok := list[bc]; !ok { 74 list[bc] = Commands{} 75 } 76 c.Binary = path 77 list[bc] = append(list[bc], c) 78 } 79 } 80 return nil 81 }) 82 if err != nil { 83 return nil, errors.WithStack(err) 84 } 85 } 86 return list, nil 87 } 88 89 func askBin(path string) Commands { 90 commands := Commands{} 91 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 92 defer cancel() 93 cmd := exec.CommandContext(ctx, path, "available") 94 bb := &bytes.Buffer{} 95 cmd.Stdout = bb 96 cmd.Stderr = bb 97 err := cmd.Run() 98 if err != nil { 99 logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n%s\n", path, err, bb.String()) 100 return commands 101 } 102 err = json.NewDecoder(bb).Decode(&commands) 103 if err != nil { 104 logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n", path, err) 105 return commands 106 } 107 return commands 108 } 109 110 func ignorePath(p string) bool { 111 p = strings.ToLower(p) 112 for _, x := range []string{`c:\windows`, `c:\program`} { 113 if strings.HasPrefix(p, x) { 114 return true 115 } 116 } 117 return false 118 }