<commit_before>package plugins

import (
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/gobuffalo/envy"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// List maps a Buffalo command to a slice of Command
type List map[string]Commands

// Available plugins for the `buffalo` command.
// It will look in $PATH and the `./plugins` directory.
//
// Requirements:
// * file/command must be executable
// * file/command must start with `buffalo-`
// * file/command must respond to `available` and return JSON of
//	 plugins.Commands{}
//
// Limit full path scan with direct plugin path
func Available() (List, error) {
	list := List{}
	paths := []string{"plugins"}

	from, err := envy.MustGet("BUFFALO_PLUGIN_PATH")
	if err != nil {
		logrus.Warn(warningMessage)
		from = envy.Get("PATH", "")
	}

	if runtime.GOOS == "windows" {
		paths = append(paths, strings.Split(from, ";")...)
	} else {
		paths = append(paths, strings.Split(from, ":")...)
	}

	for _, p := range paths {
		if ignorePath(p) {
			continue
		}
		if _, err := os.Stat(p); err != nil {
			continue
		}
		err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				// May indicate a permissions problem with the path, skip it
				return nil
			}
			if info.IsDir() {
				return nil
			}
			base := filepath.Base(path)
			if strings.HasPrefix(base, "buffalo-") {
				commands := askBin(path)
				for _, c := range commands {
					bc := c.BuffaloCommand
					if _, ok := list[bc]; !ok {
						list[bc] = Commands{}
					}
					c.Binary = path
					list[bc] = append(list[bc], c)
				}
			}
			return nil
		})
		if err != nil {
			return nil, errors.WithStack(err)
		}
	}
	return list, nil
}

func askBin(path string) Commands {
	commands := Commands{}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctx, path, "available")
	bb := &bytes.Buffer{}
	cmd.Stdout = bb
	cmd.Stderr = bb
	err := cmd.Run()
	if err != nil {
		logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n%s\n", path, err, bb.String())
		return commands
	}
	err = json.NewDecoder(bb).Decode(&commands)
	if err != nil {
		logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n", path, err)
		return commands
	}
	return commands
}

func ignorePath(p string) bool {
	p = strings.ToLower(p)
	for _, x := range []string{`c:\windows`, `c:\program`} {
		if strings.HasPrefix(p, x) {
			return true
		}
	}
	return false
}

const warningMessage = `Could not find BUFFALO_PLUGIN_PATH environment variable, default to PATH instead. Consider setting the BUFFALO_PLUGIN_PATH variable to speed up loading of plugins and/or to set a custom path for locating them.`<commit_msg>removed BUFFALO_PLUGIN_PATH warning message<commit_after>package plugins

import (
	"bytes"
	"context"
	"encoding/json"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/gobuffalo/envy"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// List maps a Buffalo command to a slice of Command
type List map[string]Commands

// Available plugins for the `buffalo` command.
// It will look in $PATH and the `./plugins` directory.
//
// Requirements:
// * file/command must be executable
// * file/command must start with `buffalo-`
// * file/command must respond to `available` and return JSON of
//	 plugins.Commands{}
//
// Limit full path scan with direct plugin path
func Available() (List, error) {
	list := List{}
	paths := []string{"plugins"}

	from, err := envy.MustGet("BUFFALO_PLUGIN_PATH")
	if err != nil {
		from = envy.Get("PATH", "")
	}

	if runtime.GOOS == "windows" {
		paths = append(paths, strings.Split(from, ";")...)
	} else {
		paths = append(paths, strings.Split(from, ":")...)
	}

	for _, p := range paths {
		if ignorePath(p) {
			continue
		}
		if _, err := os.Stat(p); err != nil {
			continue
		}
		err := filepath.Walk(p, func(path string, info os.FileInfo, err error) error {
			if err != nil {
				// May indicate a permissions problem with the path, skip it
				return nil
			}
			if info.IsDir() {
				return nil
			}
			base := filepath.Base(path)
			if strings.HasPrefix(base, "buffalo-") {
				commands := askBin(path)
				for _, c := range commands {
					bc := c.BuffaloCommand
					if _, ok := list[bc]; !ok {
						list[bc] = Commands{}
					}
					c.Binary = path
					list[bc] = append(list[bc], c)
				}
			}
			return nil
		})
		if err != nil {
			return nil, errors.WithStack(err)
		}
	}
	return list, nil
}

func askBin(path string) Commands {
	commands := Commands{}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctx, path, "available")
	bb := &bytes.Buffer{}
	cmd.Stdout = bb
	cmd.Stderr = bb
	err := cmd.Run()
	if err != nil {
		logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n%s\n", path, err, bb.String())
		return commands
	}
	err = json.NewDecoder(bb).Decode(&commands)
	if err != nil {
		logrus.Errorf("[PLUGIN] error loading plugin %s: %s\n", path, err)
		return commands
	}
	return commands
}

func ignorePath(p string) bool {
	p = strings.ToLower(p)
	for _, x := range []string{`c:\windows`, `c:\program`} {
		if strings.HasPrefix(p, x) {
			return true
		}
	}
	return false
}
{'fraction_non_alphanumeric': 0.15071590052750566, 'fraction_numerical': 0.0, 'mean_word_length': 3.2235481304693714, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1955822', 'n_tokens_mistral': 2049, 'n_tokens_neox': 1867, 'n_words': 527}