tools.go
 1  package tools
 2  
 3  import (
 4  	"errors"
 5  	"fmt"
 6  	"sort"
 7  
 8  	pkgerrors "github.com/pkg/errors"
 9  	"golang.org/x/exp/maps"
10  
11  	"github.com/symflower/eval-dev-quality/log"
12  )
13  
14  // Tools holds a register of all tools.
15  var Tools = map[string]Tool{}
16  
17  // Register adds a tool to the common tool list.
18  func Register(tool Tool) {
19  	id := tool.ID()
20  	if _, ok := Tools[id]; ok {
21  		panic(pkgerrors.WithMessage(pkgerrors.New("tool was already registered"), id))
22  	}
23  
24  	Tools[id] = tool
25  }
26  
27  // Tool defines an external tool.
28  type Tool interface {
29  	// ID returns the unique ID of this tool.
30  	ID() (id string)
31  	// BinaryName returns the name of the tool's binary.
32  	BinaryName() string
33  	// BinaryPath returns the file path of the tool's binary or the command name that should be executed.
34  	// The binary path might also be just the binary name in case the tool is expected to be on the system path.
35  	BinaryPath() string
36  
37  	// CheckVersion checks if the tool's version is compatible with the required version.
38  	CheckVersion(logger *log.Logger, binaryPath string) error
39  	// RequiredVersion returns the required version of the tool.
40  	RequiredVersion() string
41  
42  	// Install installs the tool's binary to the given install path.
43  	Install(logger *log.Logger, installPath string) error
44  }
45  
46  // InstallAll installs all tools.
47  func InstallAll(logger *log.Logger, installPath string) (err error) {
48  	var installErrors []error
49  	toolKeys := maps.Keys(Tools)
50  	sort.Strings(toolKeys)
51  	for _, toolID := range toolKeys {
52  		tool := Tools[toolID]
53  
54  		if err := InstallTool(logger, tool, installPath); err != nil {
55  			// Log if a tool is not supported by the operating system, but do not fail as it is not a necessary tool.
56  			if pkgerrors.Is(err, ErrUnsupportedOperatingSystem) {
57  				logger.Printf("WARNING: tool %s is not supported by the operating system", tool.ID())
58  
59  				continue
60  			}
61  
62  			err = pkgerrors.WithStack(pkgerrors.WithMessage(err, fmt.Sprintf("cannot install %q", tool.ID())))
63  			installErrors = append(installErrors, err)
64  		}
65  	}
66  
67  	if len(installErrors) > 0 {
68  		return errors.Join(installErrors...)
69  	}
70  
71  	return nil
72  }
73  
74  // InstallEvaluation installs all basic evaluation tools.
75  func InstallEvaluation(logger *log.Logger, installPath string) (err error) {
76  	if err := InstallTool(logger, NewSymflower(), installPath); err != nil {
77  		return pkgerrors.WithStack(pkgerrors.WithMessage(err, "cannot install Symflower"))
78  	}
79  
80  	return nil
81  }