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 }