diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 466f60bdcf..196c9227e2 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,5 +1,7 @@ #### Features ๐Ÿš€ +- Configure timeout value with D2_TIMEOUT env var. [#1392](https://github.com/terrastruct/d2/pull/1392) + #### Improvements ๐Ÿงน - Display version on CLI help invocation [#1400](https://github.com/terrastruct/d2/pull/1400) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index ff8263105b..81e1557cb5 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -103,30 +103,41 @@ Set the diagram layout engine to the passed string. For a list of available opti .Ar layout .Ns . .It Fl b , -bundle Ar true -Bundle all assets and layers into the output svg. +Bundle all assets and layers into the output svg +.Ns . .It Fl -force-appendix Ar false An appendix for tooltips and links is added to PNG exports since they are not interactive. Setting this to true adds an appendix to SVG exports as well .Ns . .It Fl d , -debug -Print debug logs. +Print debug logs +.Ns . .It Fl -img-cache Ar true In watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change .Ns . +.It Fl -timeout Ar 120 +The maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value +.Ns . .It Fl h , -help -Print usage information and exit. +Print usage information and exit +.Ns . .It Fl v , -version -Print version information and exit. +Print version information and exit +.Ns . .El .Sh SUBCOMMANDS .Bl -tag -width Fl .It Ar layout -Lists available layout engine options with short help. +Lists available layout engine options with short help +.Ns . .It Ar layout Op Ar name -Display long help for a particular layout engine, including its configuration options. +Display long help for a particular layout engine, including its configuration options +.Ns . .It Ar themes -Lists available themes. +Lists available themes +.Ns . .It Ar fmt Ar file.d2 ... -Format all passed files. +Format all passed files +.Ns . .El .Sh SEE ALSO .Xr d2plugin-tala 1 diff --git a/d2cli/main.go b/d2cli/main.go index 74a24c17e5..5fcf94c5dc 100644 --- a/d2cli/main.go +++ b/d2cli/main.go @@ -37,6 +37,7 @@ import ( "oss.terrastruct.com/d2/lib/png" "oss.terrastruct.com/d2/lib/pptx" "oss.terrastruct.com/d2/lib/textmeasure" + timelib "oss.terrastruct.com/d2/lib/time" "oss.terrastruct.com/d2/lib/version" "oss.terrastruct.com/d2/lib/xgif" @@ -88,6 +89,11 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } + timeoutFlag, err := ms.Opts.Int64("D2_TIMEOUT", "timeout", "", 120, "the maximum number of seconds that D2 runs for before timing out and exiting. When rendering a large diagram, it is recommended to increase this value") + if err != nil { + return err + } + versionFlag, err := ms.Opts.Bool("", "version", "v", false, "get the version") if err != nil { return err @@ -160,6 +166,9 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { if *browserFlag != "" { ms.Env.Setenv("BROWSER", *browserFlag) } + if timeoutFlag != nil { + os.Setenv("D2_TIMEOUT", fmt.Sprintf("%d", *timeoutFlag)) + } var inputPath string var outputPath string @@ -298,7 +307,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) { return w.run() } - ctx, cancel := context.WithTimeout(ctx, time.Minute*2) + ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2) defer cancel() _, written, err := compile(ctx, ms, plugin, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page) diff --git a/d2plugin/exec.go b/d2plugin/exec.go index 4a5836f352..4631ef0366 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -15,6 +15,7 @@ import ( "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" + timelib "oss.terrastruct.com/d2/lib/time" ) // execPlugin uses the binary at pathname with the plugin protocol to implement @@ -147,7 +148,7 @@ func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { } func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - ctx, cancel := context.WithTimeout(ctx, time.Minute) + ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2) defer cancel() graphBytes, err := d2graph.SerializeGraph(g) diff --git a/e2etests/report/main.go b/e2etests/report/main.go index a2ef1896a5..481cc9fd14 100644 --- a/e2etests/report/main.go +++ b/e2etests/report/main.go @@ -16,6 +16,7 @@ import ( "time" "oss.terrastruct.com/d2/lib/log" + timelib "oss.terrastruct.com/d2/lib/time" ) //go:embed template.html @@ -69,8 +70,10 @@ func main() { if !*skipTests { ctx := log.Stderr(context.Background()) - ctx, cancel := context.WithTimeout(ctx, 2*time.Minute) + + ctx, cancel := timelib.WithTimeout(ctx, 2*time.Minute) defer cancel() + // don't want to pass empty args to CommandContext args := []string{"test", testDir, testMatchString} if cpuProfileStr != "" { diff --git a/lib/env/env.go b/lib/env/env.go index 07ba22eef8..c37eea66df 100644 --- a/lib/env/env.go +++ b/lib/env/env.go @@ -2,6 +2,7 @@ package env import ( "os" + "strconv" ) func Test() bool { @@ -25,3 +26,13 @@ func DevOnly() bool { func SkipGraphDiffTests() bool { return os.Getenv("SKIP_GRAPH_DIFF_TESTS") != "" } + +func Timeout() (int, bool) { + if s := os.Getenv("D2_TIMEOUT"); s != "" { + i, err := strconv.ParseInt(s, 10, 64) + if err == nil { + return int(i), true + } + } + return -1, false +} diff --git a/lib/time/time.go b/lib/time/time.go new file mode 100644 index 0000000000..a9a1f692b0 --- /dev/null +++ b/lib/time/time.go @@ -0,0 +1,26 @@ +package time + +import ( + "context" + "time" + + "oss.terrastruct.com/d2/lib/env" +) + +func HumanDate(t time.Time) string { + local := t.Local() + return local.Format(time.RFC822) +} + +// WithTimeout returns context.WithTimeout(ctx, timeout) but timeout is overridden with D2_TIMEOUT if set +func WithTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + t := timeout + if seconds, has := env.Timeout(); has { + t = time.Duration(seconds) * time.Second + } + if t <= 0 { + return ctx, func() {} + } + + return context.WithTimeout(ctx, t) +}