github.com/jacobsoderblom/buffalo@v0.11.0/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  }