github.com/tilt-dev/wat@v0.0.2-0.20180626175338-9349b638e250/cli/wat/plugins.go (about)

     1  package wat
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"strings"
     9  
    10  	"os"
    11  
    12  	"github.com/windmilleng/wat/errors"
    13  )
    14  
    15  // GetUserPlugins gets user-specified WatList Plugins
    16  // (stored as a newline-separated list in .wat/plugins)
    17  func getUserPlugins(ws WatWorkspace) ([]plugin, error) {
    18  	exists, err := ws.Exists(fnameUserPlugins)
    19  	if !exists || err != nil {
    20  		return nil, err
    21  	}
    22  
    23  	contents, err := ws.Read(fnameUserPlugins)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	res := []plugin{}
    29  	for _, cmd := range strings.Split(string(contents), "\n") {
    30  		res = append(res, userPlugin{cmd: cmd})
    31  	}
    32  
    33  	return res, nil
    34  }
    35  
    36  type plugin interface {
    37  	name() string
    38  	run(ctx context.Context, root string) ([]WatCommand, error)
    39  }
    40  
    41  type userPlugin struct{ cmd string }
    42  
    43  func (p userPlugin) name() string { return p.cmd }
    44  func (p userPlugin) run(ctx context.Context, root string) ([]WatCommand, error) {
    45  	result := []WatCommand{}
    46  
    47  	rawRes, err := runAndCaptureStdout(ctx, root, p.cmd)
    48  	if err != nil {
    49  		return result, err
    50  	}
    51  
    52  	jErr := json.Unmarshal(rawRes, &result)
    53  	if jErr != nil {
    54  		return result, fmt.Errorf("[json.Unmarshal: %q] %v", rawRes, jErr)
    55  	}
    56  
    57  	return result, nil
    58  }
    59  
    60  var _ plugin = userPlugin{}
    61  
    62  // NOTE: to register your builtin plugin with wat, add it to this array!
    63  var builtins = []plugin{
    64  	PluginGo{},
    65  	PluginNodeJS{},
    66  	PluginPytest{},
    67  }
    68  
    69  func RunBuiltinPlugins(ctx context.Context, ws WatWorkspace) (result []WatCommand) {
    70  	return runPlugins(ctx, ws, builtins)
    71  }
    72  
    73  func RunUserPlugins(ctx context.Context, ws WatWorkspace) (result []WatCommand, err error) {
    74  	plugins, err := getUserPlugins(ws)
    75  	if err != nil {
    76  		return []WatCommand{}, errors.Propagatef(err, "getUserPlugins")
    77  	}
    78  
    79  	return runPlugins(ctx, ws, plugins), nil
    80  }
    81  
    82  func runPlugins(ctx context.Context, ws WatWorkspace, plugins []plugin) (result []WatCommand) {
    83  	for _, p := range plugins {
    84  		res, err := p.run(ctx, ws.root)
    85  		if err != nil {
    86  			// TODO: what kind of err is serious enough to return up?
    87  			fmt.Fprintf(os.Stderr, "ERROR (p: '%s'): %v\n", p.name(), err)
    88  			continue
    89  		}
    90  		result = append(result, res...)
    91  	}
    92  
    93  	return result
    94  }
    95  
    96  func runAndCaptureStdout(ctx context.Context, root, invocation string) ([]byte, error) {
    97  	outBuf := bytes.Buffer{}
    98  	errBuf := bytes.Buffer{}
    99  
   100  	err := runCmd(ctx, root, invocation, &outBuf, &errBuf)
   101  
   102  	if ctx.Err() != nil {
   103  		// Propagate cancel/timeout errors
   104  		return []byte{}, ctx.Err()
   105  	}
   106  	if err != nil {
   107  		return []byte{}, fmt.Errorf("ERROR %v (%s)", err, errBuf.String())
   108  	}
   109  
   110  	// The result of this plugin is just whatever we got into stdOut
   111  	return outBuf.Bytes(), nil
   112  }