Skip to content

Commit bf3d6f4

Browse files
Support watch scripts for serve (#478)
1 parent 07b6872 commit bf3d6f4

File tree

5 files changed

+97
-18
lines changed

5 files changed

+97
-18
lines changed

‎cmd/grr/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type Opts struct {
3333
ProxyPort int
3434
CanSave bool
3535
Watch bool
36+
WatchScript string
3637
}
3738

3839
func configPathCmd() *cli.Command {

‎cmd/grr/workflow.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,16 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
376376
}
377377

378378
resourcesPath := ""
379-
if len(args) > 0 {
380-
resourcesPath = args[0]
381-
}
382379
watchPaths := []string{resourcesPath}
383-
if len(args) > 1 {
384-
watchPaths = args[1:]
380+
if opts.WatchScript != "" {
381+
watchPaths = args
382+
} else {
383+
if len(args) > 0 {
384+
resourcesPath = args[0]
385+
}
386+
if len(args) > 1 {
387+
watchPaths = args[1:]
388+
}
385389
}
386390

387391
targets := currentContext.GetTargets(opts.Targets)
@@ -405,6 +409,9 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
405409
server.SetFormatting(onlySpec, format)
406410
if opts.Watch {
407411
server.Watch(watchPaths)
412+
if opts.WatchScript != "" {
413+
server.WatchScript(opts.WatchScript)
414+
}
408415
}
409416
if opts.OpenBrowser {
410417
server.OpenBrowser()
@@ -414,6 +421,7 @@ func serveCmd(registry grizzly.Registry) *cli.Command {
414421
cmd.Flags().BoolVarP(&opts.Watch, "watch", "w", false, "Watch filesystem for changes")
415422
cmd.Flags().BoolVarP(&opts.OpenBrowser, "open-browser", "b", false, "Open Grizzly in default browser")
416423
cmd.Flags().IntVarP(&opts.ProxyPort, "port", "p", 8080, "Port on which the server will listen")
424+
cmd.Flags().StringVarP(&opts.WatchScript, "script", "S", "", "Script to execute on filesystem change")
417425
cmd = initialiseOnlySpec(cmd, &opts)
418426
return initialiseCmd(cmd, &opts)
419427
}

‎docs/content/server.md

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,13 @@ grr serve -w examples/grr.jsonnet examples/*.*sonnet examples/vendor
6464
### Reviewing changes to code in other languages in Grafana
6565
The [Grafana Foundation SDK](https://github.com/grafana/grafana-foundation-sdk) provides libraries in a
6666
range of languages that can be used to render Grafana dashboards. Watching changes to these with Grizzly
67-
is a two stage process, currently requiring an additional tool to watch for changes to source code and
68-
render your dashboard(s) to files. One such tool is [entr](https://github.com/eradman/entr), which can be
69-
used like so (with the Foundation SDK's TypeScript support):
67+
is also possible.
7068

7169
```
7270
git clone https://github.com/grafana/grafana-foundation-sdk
7371
cd grafana-foundation-sdk/examples/typescript/red-method
7472
npm install
75-
find . | entr -s 'npm run -s dev > ts.json'
76-
```
77-
Then, in another window:
78-
```
79-
grr serve -w ts.json
73+
grr serve -w -S 'npm run -s dev' .
8074
```
8175
Finally, open the Grizzly server at [http://localhost:8080](http://localhost:8080) and select the Red
8276
Method dashboard.

‎examples/array-of-resources.jsonnet

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
local dashboard(uid, title) = {
2+
uid: uid,
3+
title: title,
4+
tags: ['templated'],
5+
timezone: 'browser',
6+
schemaVersion: 17,
7+
panels: [],
8+
};
9+
10+
[
11+
dashboard("dashboard-1", "Dashboard 1"),
12+
dashboard("dashboard-2", "Dashboard 2"),
13+
dashboard("dashboard-3", "Dashboard 3"),
14+
]

‎pkg/grizzly/server.go

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package grizzly
22

33
import (
4+
"bytes"
45
"errors"
56
"fmt"
67
"io/fs"
78
"net/http"
89
"net/http/httputil"
910
"os"
11+
"os/exec"
1012

1113
"github.com/go-chi/chi"
1214
"github.com/go-chi/chi/middleware"
@@ -31,6 +33,7 @@ type Server struct {
3133
UserAgent string
3234
ResourcePath string
3335
WatchPaths []string
36+
watchScript string
3437
OnlySpec bool
3538
OutputFormat string
3639
watch bool
@@ -85,6 +88,10 @@ func (s *Server) Watch(watchPaths []string) {
8588
s.WatchPaths = watchPaths
8689
}
8790

91+
func (s *Server) WatchScript(script string) {
92+
s.watchScript = script
93+
}
94+
8895
func (s *Server) SetFormatting(onlySpec bool, outputFormat string) {
8996
s.OnlySpec = onlySpec
9097
s.OutputFormat = outputFormat
@@ -174,10 +181,19 @@ func (s *Server) Start() error {
174181
r.Get("/grizzly/{kind}/{name}", s.IframeHandler)
175182
r.Get("/api/live/ws", livereload.LiveReloadHandlerFunc(upgrader))
176183

177-
if _, err := s.ParseResources(s.ResourcePath); err != nil {
184+
if s.watchScript != "" {
185+
var b []byte
186+
b, err = s.executeWatchScript()
187+
if err != nil {
188+
return err
189+
}
190+
_, err = s.ParseBytes(b)
191+
} else {
192+
_, err = s.ParseResources(s.ResourcePath)
193+
}
194+
if err != nil {
178195
fmt.Print(err)
179196
}
180-
181197
if s.openBrowser {
182198
browser, err := NewBrowserInterface(s.Registry, s.ResourcePath, s.port)
183199
if err != nil {
@@ -217,6 +233,26 @@ func (s *Server) ParseResources(resourcePath string) (Resources, error) {
217233
return resources, err
218234
}
219235

236+
func (s *Server) ParseBytes(b []byte) (Resources, error) {
237+
f, err := os.CreateTemp(".", fmt.Sprintf("*.%s", s.OutputFormat))
238+
if err != nil {
239+
return Resources{}, err
240+
}
241+
defer os.Remove(f.Name())
242+
_, err = f.Write(b)
243+
if err != nil {
244+
return Resources{}, err
245+
}
246+
err = f.Close()
247+
if err != nil {
248+
return Resources{}, err
249+
}
250+
resources, err := s.parser.Parse(f.Name(), s.parserOpts)
251+
s.parserErr = err
252+
s.Resources.Merge(resources)
253+
return resources, err
254+
}
255+
220256
func (s *Server) URL(path string) string {
221257
if len(path) == 0 || path[0] != '/' {
222258
path = "/" + path
@@ -226,10 +262,19 @@ func (s *Server) URL(path string) string {
226262
}
227263

228264
func (s *Server) updateWatchedResource(name string) error {
229-
if !s.parser.Accept(name) {
230-
return nil
265+
var resources Resources
266+
var err error
267+
268+
if s.watchScript != "" {
269+
var b []byte
270+
b, err = s.executeWatchScript()
271+
if err != nil {
272+
return err
273+
}
274+
resources, err = s.ParseBytes(b)
275+
} else {
276+
resources, err = s.ParseResources(s.ResourcePath)
231277
}
232-
resources, err := s.ParseResources(s.ResourcePath)
233278
if errors.As(err, &UnrecognisedFormatError{}) {
234279
uerr := err.(UnrecognisedFormatError)
235280
log.Printf("Skipping %s", uerr.File)
@@ -239,6 +284,7 @@ func (s *Server) updateWatchedResource(name string) error {
239284
log.Error("Error: ", err)
240285
return err
241286
}
287+
242288
for _, resource := range resources.AsList() {
243289
handler, err := s.Registry.GetHandler(resource.Kind())
244290
if err != nil {
@@ -257,6 +303,22 @@ func (s *Server) updateWatchedResource(name string) error {
257303
return nil
258304
}
259305

306+
func (s *Server) executeWatchScript() ([]byte, error) {
307+
var stdout bytes.Buffer
308+
var stderr bytes.Buffer
309+
cmd := exec.Command("sh", "-c", s.watchScript)
310+
cmd.Stdout = &stdout
311+
cmd.Stderr = &stderr
312+
err := cmd.Run()
313+
if err != nil {
314+
return nil, err
315+
}
316+
if stderr.Len() > 0 {
317+
log.Errorf("%s", stderr.String())
318+
}
319+
return stdout.Bytes(), nil
320+
}
321+
260322
func (s *Server) blockHandler(response string) http.HandlerFunc {
261323
return func(w http.ResponseWriter, r *http.Request) {
262324
w.Header().Set("Content-Type", "application/json")

0 commit comments

Comments
 (0)