diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 466f60bdc..196c9227e 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 ff8263105..81e1557cb 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 74a24c17e..5fcf94c5d 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 4a5836f35..4631ef036 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 a2ef1896a..481cc9fd1 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 07ba22eef..c37eea66d 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 000000000..a9a1f692b --- /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) +}