From 46bf849e2bf9cf6912c98ffffd31200e801d2b5a Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Tue, 31 Oct 2023 15:52:48 -0700 Subject: [PATCH] cli: Handle invalid board paths --- ci/release/changelogs/next.md | 1 + d2cli/fmt.go | 2 +- d2cli/main.go | 5 ++- d2plugin/plugin_dagre.go | 2 +- d2plugin/plugin_elk.go | 2 +- e2etests-cli/main_test.go | 76 +++++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 6 +++ 8 files changed, 91 insertions(+), 5 deletions(-) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 89a8916b7..461c9cf76 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -20,3 +20,4 @@ - Correctly reports errors from invalid values set by globs. [#1691](https://github.com/terrastruct/d2/pull/1691) - Fixes panic when spread substitution referenced a nonexistant var. [#1695](https://github.com/terrastruct/d2/pull/1695) - Fixes incorrect appendix icon numbering. [#1704](https://github.com/terrastruct/d2/pull/1704) +- Fixes crash when using `--watch` and navigating to an invalid board path [#1693](https://github.com/terrastruct/d2/pull/1693) diff --git a/d2cli/fmt.go b/d2cli/fmt.go index 39f29bfd6..a2b837158 100644 --- a/d2cli/fmt.go +++ b/d2cli/fmt.go @@ -17,7 +17,7 @@ import ( func fmtCmd(ctx context.Context, ms *xmain.State) (err error) { defer xdefer.Errorf(&err, "failed to fmt") - ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:]) + ms.Opts = xmain.NewOpts(ms.Env, ms.Opts.Flags.Args()[1:]) if len(ms.Opts.Args) == 0 { return xmain.UsageErrorf("fmt must be passed at least one file to be formatted") } diff --git a/d2cli/main.go b/d2cli/main.go index 52f3a2ed0..239d60d4f 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -2,6 +2,7 @@ package d2cli import ( "context" + "encoding/json" "errors" "fmt" "io" @@ -490,6 +491,8 @@ func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, la default: compileDur := time.Since(start) if animateInterval <= 0 { + b, _ := json.MarshalIndent(diagram, "", " ") + println("\033[1;31m--- DEBUG:", string(b), "\033[m") // Rename all the "root.layers.x" to the paths that the boards get output to linkToOutput, err := resolveLinks("root", outputPath, diagram) if err != nil { @@ -503,7 +506,7 @@ func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, la board := diagram.GetBoard(boardPath) if board == nil { - return nil, false, fmt.Errorf("Diagram with path %s not found", boardPath) + return nil, false, fmt.Errorf(`Diagram with path "%s" not found. Did you mean to specify a board like "layers.%s"?`, boardPath, boardPath) } boards, err := render(ctx, ms, compileDur, plugin, renderOpts, inputPath, outputPath, bundle, forceAppendix, page, ruler, board) diff --git a/d2plugin/plugin_dagre.go b/d2plugin/plugin_dagre.go index 3c8fd3dcf..f3d095ebd 100644 --- a/d2plugin/plugin_dagre.go +++ b/d2plugin/plugin_dagre.go @@ -61,7 +61,7 @@ func (p *dagrePlugin) HydrateOpts(opts []byte) error { func (p *dagrePlugin) Info(ctx context.Context) (*PluginInfo, error) { p.mu.Lock() defer p.mu.Unlock() - opts := xmain.NewOpts(nil, nil, nil) + opts := xmain.NewOpts(nil, nil) flags, err := p.Flags(ctx) if err != nil { return nil, err diff --git a/d2plugin/plugin_elk.go b/d2plugin/plugin_elk.go index d28fba3cb..4726a666d 100644 --- a/d2plugin/plugin_elk.go +++ b/d2plugin/plugin_elk.go @@ -76,7 +76,7 @@ func (p *elkPlugin) HydrateOpts(opts []byte) error { } func (p elkPlugin) Info(ctx context.Context) (*PluginInfo, error) { - opts := xmain.NewOpts(nil, nil, nil) + opts := xmain.NewOpts(nil, nil) flags, err := p.Flags(ctx) if err != nil { return nil, err diff --git a/e2etests-cli/main_test.go b/e2etests-cli/main_test.go index 29c92ec5c..7f55ad668 100644 --- a/e2etests-cli/main_test.go +++ b/e2etests-cli/main_test.go @@ -3,12 +3,17 @@ package e2etests_cli import ( "bytes" "context" + "fmt" + "net/http" "os" "path/filepath" + "regexp" "strings" "testing" "time" + "github.com/davecgh/go-spew/spew" + "nhooyr.io/websocket" "oss.terrastruct.com/util-go/assert" "oss.terrastruct.com/util-go/diff" "oss.terrastruct.com/util-go/xmain" @@ -544,6 +549,77 @@ i used to read assert.Equal(t, "x -> y\n", string(gotBar)) }, }, + { + name: "watch", + run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) { + writeFile(t, dir, "index.d2", ` +a -> b +b.link: cream + +layers: { + cream: { + c -> b + } +}`) + stderr := &bytes.Buffer{} + tms := testMain(dir, env, "--watch", "--browser=0", "index.d2") + tms.Stderr = stderr + + doneChan := make(chan struct{}, 1) + + tms.Start(t, ctx) + defer tms.Cleanup(t) + + go tms.Wait(ctx) + + ticker := time.NewTicker(100 * time.Millisecond) + urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`) + compiled := false + go func() { + var url string + for i := 0; i < 10 && url == ""; i++ { + select { + case <-ticker.C: + out := string(stderr.Bytes()) + url = urlRE.FindString(out) + compiled, _ = regexp.MatchString(`failed to recompile`, out) + println("\033[1;31m--- DEBUG:", compiled, "\033[m") + case <-ctx.Done(): + ticker.Stop() + return + } + } + + if url != "" { + c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", url), nil) + assert.Success(t, err) + + req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("http://%s/cream", url), nil) + assert.Success(t, err) + var httpClient = &http.Client{} + resp, err := httpClient.Do(req) + assert.Success(t, err) + defer resp.Body.Close() + assert.Equal(t, 200, resp.StatusCode) + + time.Sleep(1000) + spew.Dump(string(stderr.Bytes())) + + _, _, err = c.Read(ctx) + spew.Dump(err) + + defer c.Close(websocket.StatusNormalClosure, "") + } + + doneChan <- struct{}{} + }() + + <-doneChan + + err := tms.Signal(ctx, os.Interrupt) + assert.Error(t, err) + }, + }, } ctx := context.Background() diff --git a/go.mod b/go.mod index 92b1ac752..18a31b9a4 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 gonum.org/v1/plot v0.12.0 nhooyr.io/websocket v1.8.7 - oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14 + oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2 ) require ( diff --git a/go.sum b/go.sum index 545957784..bd2909b60 100644 --- a/go.sum +++ b/go.sum @@ -331,4 +331,10 @@ nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14 h1:oy5vtt6O2qYxeSpqWhyevrdUenFfuhphixozUlpL6qY= oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4= +oss.terrastruct.com/util-go v0.0.0-20231101215508-f36525fd4280 h1:AoxRLGSofA+l7zYwwxzdQSqyEYPOq8GznZU1CXByxsQ= +oss.terrastruct.com/util-go v0.0.0-20231101215508-f36525fd4280/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4= +oss.terrastruct.com/util-go v0.0.0-20231101220527-f06cd5cb4db4 h1:trpfw07GR3LYlM/7MLMRM7y7o7H8e8PEF9XtVl5J6FE= +oss.terrastruct.com/util-go v0.0.0-20231101220527-f06cd5cb4db4/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4= +oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2 h1:n6y6RoZCgZDchN4gLGlzNRO1Jdf9xOGGqohDBph5BG8= +oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4= rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=