From 4d4a00743d71904ad8a65276b3711d4550cf57b5 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 14 Nov 2022 16:35:22 -0800 Subject: [PATCH 01/28] 2022-11-14 04:35:22PM --- cmd/d2/help.go | 14 ++--- cmd/d2/main.go | 52 ++++++++---------- cmd/d2/watch.go | 6 ++- d2plugin/serve.go | 6 +-- lib/xmain/opts.go | 132 +++++++++++++++++++++++++++++++++++++++++++++ lib/xmain/xmain.go | 24 ++------- 6 files changed, 174 insertions(+), 60 deletions(-) create mode 100644 lib/xmain/opts.go diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 961c9c275..3e18a56cf 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -28,13 +28,13 @@ Subcommands: %[1]s layout [layout name] - Display long help for a particular layout engine See more docs and the source code at https://oss.terrastruct.com/d2 -`, ms.Name, ms.FlagHelp()) +`, ms.Name, ms.Opts.Help()) } func layoutHelp(ctx context.Context, ms *xmain.State) error { - if len(ms.FlagSet.Args()) == 1 { + if len(ms.Opts.Args()) == 1 { return shortLayoutHelp(ctx, ms) - } else if len(ms.FlagSet.Args()) == 2 { + } else if len(ms.Opts.Args()) == 2 { return longLayoutHelp(ctx, ms) } else { return pluginSubcommand(ctx, ms) @@ -61,7 +61,7 @@ func shortLayoutHelp(ctx context.Context, ms *xmain.State) error { %s Usage: - To use a particular layout engine, set the environment variable D2_LAYOUT=[layout name]. + To use a particular layout engine, set the environment variable D2_LAYOUT=[name] or flag --layout=[name]. Example: D2_LAYOUT=dagre d2 in.d2 out.svg @@ -75,7 +75,7 @@ See more docs at https://oss.terrastruct.com/d2 } func longLayoutHelp(ctx context.Context, ms *xmain.State) error { - layout := ms.FlagSet.Arg(1) + layout := ms.Opts.Arg(1) plugin, path, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) @@ -119,13 +119,13 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`, } func pluginSubcommand(ctx context.Context, ms *xmain.State) error { - layout := ms.FlagSet.Arg(1) + layout := ms.Opts.Arg(1) plugin, _, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) } - ms.Args = ms.FlagSet.Args()[2:] + ms.Opts.SetArgs(ms.Opts.Args()[2:]) return d2plugin.Serve(plugin)(ctx, ms) } diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 6ce4d5459..d2f1ecdfd 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -6,7 +6,6 @@ import ( "fmt" "os/exec" "path/filepath" - "strconv" "strings" "time" @@ -32,19 +31,20 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) - watchFlag := ms.FlagSet.BoolP("watch", "w", false, "watch for changes to input and live reload. Use $PORT and $HOST to specify the listening address.\n$D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0") - themeFlag := ms.FlagSet.Int64P("theme", "t", 0, "set the diagram theme. For a list of available options, see https://oss.terrastruct.com/d2") - bundleFlag := ms.FlagSet.BoolP("bundle", "b", true, "bundle all assets and layers into the output svg") - versionFlag := ms.FlagSet.BoolP("version", "v", false, "get the version and check for updates") - debugFlag := ms.FlagSet.BoolP("debug", "d", false, "print debug logs") - err = ms.FlagSet.Parse(ms.Args) + watchFlag := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + bundleFlag := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") + debugFlag := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + layoutFlag := ms.Opts.String("D2_LAYOUT", "layout", "l", "dagre", `the layout engine used.`) + themeFlag := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") + versionFlag := ms.Opts.Bool("", "version", "v", false, "get the version and check for updates") + err = ms.Opts.Parse() if !errors.Is(err, pflag.ErrHelp) && err != nil { return xmain.UsageErrorf("failed to parse flags: %v", err) } - if len(ms.FlagSet.Args()) > 0 { - switch ms.FlagSet.Arg(0) { + if len(ms.Opts.Args()) > 0 { + switch ms.Opts.Arg(0) { case "layout": return layoutHelp(ctx, ms) } @@ -62,25 +62,25 @@ func run(ctx context.Context, ms *xmain.State) (err error) { var inputPath string var outputPath string - if len(ms.FlagSet.Args()) == 0 { + if len(ms.Opts.Args()) == 0 { if versionFlag != nil && *versionFlag { version.CheckVersion(ctx, ms.Log) return nil } help(ms) return nil - } else if len(ms.FlagSet.Args()) >= 3 { + } else if len(ms.Opts.Args()) >= 3 { return xmain.UsageErrorf("too many arguments passed") } - if len(ms.FlagSet.Args()) >= 1 { - if ms.FlagSet.Arg(0) == "version" { + if len(ms.Opts.Args()) >= 1 { + if ms.Opts.Arg(0) == "version" { version.CheckVersion(ctx, ms.Log) return nil } - inputPath = ms.FlagSet.Arg(0) + inputPath = ms.Opts.Arg(0) } - if len(ms.FlagSet.Args()) >= 2 { - outputPath = ms.FlagSet.Arg(1) + if len(ms.Opts.Args()) >= 2 { + outputPath = ms.Opts.Arg(1) } else { if inputPath == "-" { outputPath = "-" @@ -93,16 +93,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) { if match == (d2themes.Theme{}) { return xmain.UsageErrorf("-t[heme] could not be found. The available options are:\n%s\nYou provided: %d", d2themescatalog.CLIString(), *themeFlag) } - ms.Env.Setenv("D2_THEME", fmt.Sprintf("%d", *themeFlag)) + ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag) - envD2Layout := ms.Env.Getenv("D2_LAYOUT") - if envD2Layout == "" { - envD2Layout = "dagre" - } - - plugin, path, err := d2plugin.FindPlugin(ctx, envD2Layout) + plugin, path, err := d2plugin.FindPlugin(ctx, *layoutFlag) if errors.Is(err, exec.ErrNotFound) { - return layoutNotFound(ctx, envD2Layout) + return layoutNotFound(ctx, *layoutFlag) } else if err != nil { return err } @@ -111,14 +106,14 @@ func run(ctx context.Context, ms *xmain.State) (err error) { if path != "" { pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path)) } - ms.Log.Debug.Printf("using layout plugin %s (%s)", envD2Layout, pluginLocation) + ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, pluginLocation) if *watchFlag { if inputPath == "-" { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, inputPath, outputPath) + w, err := newWatcher(ctx, ms, plugin, *themeFlag, inputPath, outputPath) if err != nil { return err } @@ -132,7 +127,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { _ = 343 } - _, err = compile(ctx, ms, plugin, inputPath, outputPath) + _, err = compile(ctx, ms, plugin, *themeFlag, inputPath, outputPath) if err != nil { return err } @@ -140,7 +135,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return nil } -func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, inputPath, outputPath string) ([]byte, error) { +func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) ([]byte, error) { input, err := ms.ReadPath(inputPath) if err != nil { return nil, err @@ -151,7 +146,6 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, input return nil, err } - themeID, _ := strconv.ParseInt(ms.Env.Getenv("D2_THEME"), 10, 64) d, err := d2.Compile(ctx, string(input), &d2.CompileOptions{ Layout: plugin.Layout, Ruler: ruler, diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index f9570da66..18dd9ea3c 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -42,6 +42,7 @@ type watcher struct { ms *xmain.State layoutPlugin d2plugin.Plugin + themeID int64 inputPath string outputPath string @@ -68,7 +69,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -78,6 +79,7 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug ms: ms, layoutPlugin: layoutPlugin, + themeID: themeID, inputPath: inputPath, outputPath: outputPath, @@ -325,7 +327,7 @@ func (w *watcher) compileLoop(ctx context.Context) error { recompiledPrefix = "re" } - b, err := compile(ctx, w.ms, w.layoutPlugin, w.inputPath, w.outputPath) + b, err := compile(ctx, w.ms, w.layoutPlugin, w.themeID, w.inputPath, w.outputPath) if err != nil { err = fmt.Errorf("failed to %scompile: %w", recompiledPrefix, err) w.ms.Log.Error.Print(err) diff --git a/d2plugin/serve.go b/d2plugin/serve.go index 919eab48b..a51c48614 100644 --- a/d2plugin/serve.go +++ b/d2plugin/serve.go @@ -19,12 +19,12 @@ import ( // Also see execPlugin in exec.go for the d2 binary plugin protocol. func Serve(p Plugin) func(context.Context, *xmain.State) error { return func(ctx context.Context, ms *xmain.State) (err error) { - if len(ms.Args) < 1 { + if len(ms.Opts.Args()) < 1 { return errors.New("expected first argument to plugin binary to be function name") } - reqFunc := ms.Args[0] + reqFunc := ms.Opts.Arg(0) - switch ms.Args[0] { + switch ms.Opts.Arg(0) { case "info": return info(ctx, p, ms) case "layout": diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go new file mode 100644 index 000000000..c095d58cb --- /dev/null +++ b/lib/xmain/opts.go @@ -0,0 +1,132 @@ +package xmain + +import ( + "fmt" + "io" + "strconv" + "strings" + + "github.com/spf13/pflag" + "oss.terrastruct.com/cmdlog" + "oss.terrastruct.com/xos" +) + +type Opts struct { + args []string + flags *pflag.FlagSet + env *xos.Env + log *cmdlog.Logger + + registeredEnvs []string +} + +func NewOpts(env *xos.Env, args []string, log *cmdlog.Logger) *Opts { + flags := pflag.NewFlagSet("", pflag.ContinueOnError) + flags.SortFlags = false + flags.Usage = func() {} + flags.SetOutput(io.Discard) + return &Opts{ + args: args, + flags: flags, + env: env, + log: log, + } +} + +func (o *Opts) Help() string { + b := &strings.Builder{} + o.flags.SetOutput(b) + o.flags.PrintDefaults() + + if len(o.registeredEnvs) > 0 { + b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") + for i, e := range o.registeredEnvs { + s := fmt.Sprintf("- $%s", e) + if i != len(o.registeredEnvs)-1 { + s += "\n" + } + b.WriteString(s) + } + } + + return b.String() +} + +func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) *int64 { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) + if err != nil { + o.log.Error.Printf(`ignoring invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) + } else if envVal != defaultVal { + defaultVal = envVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.Int64P(flag, shortFlag, defaultVal, usage) +} + +func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal := o.env.Getenv(envKey) + if envVal != defaultVal { + defaultVal = envVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.StringP(flag, shortFlag, defaultVal, usage) +} + +func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) *bool { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal := o.env.Getenv(envKey) + if !boolyEnv(envVal) { + o.log.Error.Printf(`ignoring invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) + } else if (defaultVal && falseyEnv(envVal)) || + (!defaultVal && truthyEnv(envVal)) { + defaultVal = !defaultVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.BoolP(flag, shortFlag, defaultVal, usage) +} + +func boolyEnv(s string) bool { + return falseyEnv(s) || truthyEnv(s) +} + +func falseyEnv(s string) bool { + return s == "0" || s == "false" || s == "f" +} + +func truthyEnv(s string) bool { + return s == "1" || s == "true" || s == "t" +} + +func (o *Opts) Parse() error { + err := o.flags.Parse(o.args) + if err != nil { + return err + } + return nil +} + +func (o *Opts) SetArgs(args []string) { + o.args = args +} + +func (o *Opts) Args() []string { + return o.flags.Args() +} + +func (o *Opts) Arg(i int) string { + return o.flags.Arg(i) +} diff --git a/lib/xmain/xmain.go b/lib/xmain/xmain.go index d71147776..3ef9b89aa 100644 --- a/lib/xmain/xmain.go +++ b/lib/xmain/xmain.go @@ -9,13 +9,11 @@ import ( "io" "os" "os/signal" - "strings" "syscall" "time" "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" - "github.com/spf13/pflag" "oss.terrastruct.com/xos" @@ -41,14 +39,10 @@ func Main(run RunFunc) { Stdout: os.Stdout, Stderr: os.Stderr, - Env: xos.NewEnv(os.Environ()), - FlagSet: pflag.NewFlagSet("", pflag.ContinueOnError), - Args: args, + Env: xos.NewEnv(os.Environ()), } ms.Log = cmdlog.Log(ms.Env, os.Stderr) - ms.FlagSet.SortFlags = false - ms.FlagSet.Usage = func() {} - ms.FlagSet.SetOutput(io.Discard) + ms.Opts = NewOpts(ms.Env, args, ms.Log) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) @@ -88,10 +82,9 @@ type State struct { Stdout io.WriteCloser Stderr io.WriteCloser - Log *cmdlog.Logger - Env *xos.Env - Args []string - FlagSet *pflag.FlagSet + Log *cmdlog.Logger + Env *xos.Env + Opts *Opts } func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(context.Context, *State) error) error { @@ -129,13 +122,6 @@ func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(conte } } -func (ms *State) FlagHelp() string { - b := &strings.Builder{} - ms.FlagSet.SetOutput(b) - ms.FlagSet.PrintDefaults() - return b.String() -} - type ExitError struct { Code int `json:"code"` Message string `json:"message"` From 06fa7094617e024b7a04faca358c56376c7ea879 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:42:39 -0800 Subject: [PATCH 02/28] 2022-11-16 04:42:39PM --- cmd/d2/main.go | 25 ++++++++++++++++++++----- lib/xmain/opts.go | 39 +++++++++++++++++---------------------- lib/xmain/xmain.go | 2 +- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/cmd/d2/main.go b/cmd/d2/main.go index d5976671a..e111d72f0 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -31,12 +31,27 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) - watchFlag := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") - bundleFlag := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") - debugFlag := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + bundleFlag, err := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + debugFlag, err := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } layoutFlag := ms.Opts.String("D2_LAYOUT", "layout", "l", "dagre", `the layout engine used.`) - themeFlag := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") - versionFlag := ms.Opts.Bool("", "version", "v", false, "get the version") + themeFlag, err := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + versionFlag, err := ms.Opts.Bool("", "version", "v", false, "get the version") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } err = ms.Opts.Parse() if !errors.Is(err, pflag.ErrHelp) && err != nil { diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index c095d58cb..15a0c71a1 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -20,7 +20,7 @@ type Opts struct { registeredEnvs []string } -func NewOpts(env *xos.Env, args []string, log *cmdlog.Logger) *Opts { +func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SortFlags = false flags.Usage = func() {} @@ -52,29 +52,26 @@ func (o *Opts) Help() string { return b.String() } -func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) *int64 { +func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) if err != nil { - o.log.Error.Printf(`ignoring invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) - } else if envVal != defaultVal { - defaultVal = envVal + return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) } + defaultVal = envVal } o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.Int64P(flag, shortFlag, defaultVal, usage) + return o.flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal := o.env.Getenv(envKey) - if envVal != defaultVal { - defaultVal = envVal - } + defaultVal = envVal } o.registeredEnvs = append(o.registeredEnvs, envKey) } @@ -82,21 +79,23 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) return o.flags.StringP(flag, shortFlag, defaultVal, usage) } -func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) *bool { +func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal := o.env.Getenv(envKey) if !boolyEnv(envVal) { - o.log.Error.Printf(`ignoring invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) - } else if (defaultVal && falseyEnv(envVal)) || - (!defaultVal && truthyEnv(envVal)) { - defaultVal = !defaultVal + return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) + } + if truthyEnv(envVal) { + defaultVal = true + } else { + defaultVal = false } } o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.BoolP(flag, shortFlag, defaultVal, usage) + return o.flags.BoolP(flag, shortFlag, defaultVal, usage), nil } func boolyEnv(s string) bool { @@ -104,19 +103,15 @@ func boolyEnv(s string) bool { } func falseyEnv(s string) bool { - return s == "0" || s == "false" || s == "f" + return s == "0" || s == "false" } func truthyEnv(s string) bool { - return s == "1" || s == "true" || s == "t" + return s == "1" || s == "true" } func (o *Opts) Parse() error { - err := o.flags.Parse(o.args) - if err != nil { - return err - } - return nil + return o.flags.Parse(o.args) } func (o *Opts) SetArgs(args []string) { diff --git a/lib/xmain/xmain.go b/lib/xmain/xmain.go index 3ef9b89aa..18eba1367 100644 --- a/lib/xmain/xmain.go +++ b/lib/xmain/xmain.go @@ -42,7 +42,7 @@ func Main(run RunFunc) { Env: xos.NewEnv(os.Environ()), } ms.Log = cmdlog.Log(ms.Env, os.Stderr) - ms.Opts = NewOpts(ms.Env, args, ms.Log) + ms.Opts = NewOpts(ms.Env, ms.Log, args) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) From e1846b0e4a88cc24d3d5d0cc4a6eedb5ffcae92d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:48:19 -0800 Subject: [PATCH 03/28] 2022-11-16 04:48:19PM --- cmd/d2/help.go | 10 +++++----- cmd/d2/main.go | 20 ++++++++++---------- lib/xmain/opts.go | 34 +++++++++------------------------- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 3e18a56cf..9b304ee34 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -32,9 +32,9 @@ See more docs and the source code at https://oss.terrastruct.com/d2 } func layoutHelp(ctx context.Context, ms *xmain.State) error { - if len(ms.Opts.Args()) == 1 { + if len(ms.Opts.Flags.Args()) == 1 { return shortLayoutHelp(ctx, ms) - } else if len(ms.Opts.Args()) == 2 { + } else if len(ms.Opts.Flags.Args()) == 2 { return longLayoutHelp(ctx, ms) } else { return pluginSubcommand(ctx, ms) @@ -75,7 +75,7 @@ See more docs at https://oss.terrastruct.com/d2 } func longLayoutHelp(ctx context.Context, ms *xmain.State) error { - layout := ms.Opts.Arg(1) + layout := ms.Opts.Flags.Arg(1) plugin, path, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) @@ -119,13 +119,13 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`, } func pluginSubcommand(ctx context.Context, ms *xmain.State) error { - layout := ms.Opts.Arg(1) + layout := ms.Opts.Flags.Arg(1) plugin, _, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) } - ms.Opts.SetArgs(ms.Opts.Args()[2:]) + ms.Opts.Args = ms.Opts.Flags.Args()[2:] return d2plugin.Serve(plugin)(ctx, ms) } diff --git a/cmd/d2/main.go b/cmd/d2/main.go index e111d72f0..914ca295e 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -53,13 +53,13 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf(err.Error()) } - err = ms.Opts.Parse() + err = ms.Opts.Flags.Parse(ms.Opts.Args) if !errors.Is(err, pflag.ErrHelp) && err != nil { return xmain.UsageErrorf("failed to parse flags: %v", err) } - if len(ms.Opts.Args()) > 0 { - switch ms.Opts.Arg(0) { + if len(ms.Opts.Flags.Args()) > 0 { + switch ms.Opts.Flags.Arg(0) { case "layout": return layoutHelp(ctx, ms) } @@ -77,26 +77,26 @@ func run(ctx context.Context, ms *xmain.State) (err error) { var inputPath string var outputPath string - if len(ms.Opts.Args()) == 0 { + if len(ms.Opts.Flags.Args()) == 0 { if versionFlag != nil && *versionFlag { fmt.Println(version.Version) return nil } help(ms) return nil - } else if len(ms.Opts.Args()) >= 3 { + } else if len(ms.Opts.Flags.Args()) >= 3 { return xmain.UsageErrorf("too many arguments passed") } - if len(ms.Opts.Args()) >= 1 { - if ms.Opts.Arg(0) == "version" { + if len(ms.Opts.Flags.Args()) >= 1 { + if ms.Opts.Flags.Arg(0) == "version" { fmt.Println(version.Version) return nil } - inputPath = ms.Opts.Arg(0) + inputPath = ms.Opts.Flags.Arg(0) } - if len(ms.Opts.Args()) >= 2 { - outputPath = ms.Opts.Arg(1) + if len(ms.Opts.Flags.Args()) >= 2 { + outputPath = ms.Opts.Flags.Arg(1) } else { if inputPath == "-" { outputPath = "-" diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index 15a0c71a1..498f0db7e 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -12,8 +12,8 @@ import ( ) type Opts struct { - args []string - flags *pflag.FlagSet + Args []string + Flags *pflag.FlagSet env *xos.Env log *cmdlog.Logger @@ -26,8 +26,8 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags.Usage = func() {} flags.SetOutput(io.Discard) return &Opts{ - args: args, - flags: flags, + Args: args, + Flags: flags, env: env, log: log, } @@ -35,8 +35,8 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { func (o *Opts) Help() string { b := &strings.Builder{} - o.flags.SetOutput(b) - o.flags.PrintDefaults() + o.Flags.SetOutput(b) + o.Flags.PrintDefaults() if len(o.registeredEnvs) > 0 { b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") @@ -64,7 +64,7 @@ func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage str o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.Int64P(flag, shortFlag, defaultVal, usage), nil + return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { @@ -76,7 +76,7 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.StringP(flag, shortFlag, defaultVal, usage) + return o.Flags.StringP(flag, shortFlag, defaultVal, usage) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { @@ -95,7 +95,7 @@ func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage strin o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.BoolP(flag, shortFlag, defaultVal, usage), nil + return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil } func boolyEnv(s string) bool { @@ -109,19 +109,3 @@ func falseyEnv(s string) bool { func truthyEnv(s string) bool { return s == "1" || s == "true" } - -func (o *Opts) Parse() error { - return o.flags.Parse(o.args) -} - -func (o *Opts) SetArgs(args []string) { - o.args = args -} - -func (o *Opts) Args() []string { - return o.flags.Args() -} - -func (o *Opts) Arg(i int) string { - return o.flags.Arg(i) -} From 359a12b5f891239fef50540a6805e82446ed3509 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:48:46 -0800 Subject: [PATCH 04/28] 2022-11-16 04:48:46PM --- d2plugin/serve.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2plugin/serve.go b/d2plugin/serve.go index a51c48614..2db42d215 100644 --- a/d2plugin/serve.go +++ b/d2plugin/serve.go @@ -19,12 +19,12 @@ import ( // Also see execPlugin in exec.go for the d2 binary plugin protocol. func Serve(p Plugin) func(context.Context, *xmain.State) error { return func(ctx context.Context, ms *xmain.State) (err error) { - if len(ms.Opts.Args()) < 1 { + if len(ms.Opts.Flags.Args()) < 1 { return errors.New("expected first argument to plugin binary to be function name") } - reqFunc := ms.Opts.Arg(0) + reqFunc := ms.Opts.Flags.Arg(0) - switch ms.Opts.Arg(0) { + switch ms.Opts.Flags.Arg(0) { case "info": return info(ctx, p, ms) case "layout": From d90625dc0a91844c0f09861ec88a2fa30bae63e3 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:55:12 -0800 Subject: [PATCH 05/28] 2022-11-16 04:55:12PM --- ci/release/changelogs/next.md | 3 ++- lib/xmain/opts.go | 49 ++++++++++++++++------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index ffd49ca9c..abd4b28f7 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -6,7 +6,8 @@ For v0.0.99 we focused on X, Y and Z. Enjoy! #### Improvements 🔧 -- Add table columns indices in edges between SQL Tables so that layout engines can route exactly between them +- Equivalency between flags and environment variables. You can set either one for all + options (flags take precedence). #### Bugfixes 🔴 diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index 498f0db7e..ed260537f 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -52,47 +52,44 @@ func (o *Opts) Help() string { return b.String() } +func (o *Opts) getEnv(k string) string { + if k != "" { + o.registeredEnvs = append(o.registeredEnvs, k) + return o.env.Getenv(k) + } + return "" +} + func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) - if err != nil { - return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) - } - defaultVal = envVal + if env := o.getEnv(envKey); env != "" { + envVal, err := strconv.ParseInt(env, 10, 64) + if err != nil { + return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) } - o.registeredEnvs = append(o.registeredEnvs, envKey) + defaultVal = envVal } return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal := o.env.Getenv(envKey) - defaultVal = envVal - } - o.registeredEnvs = append(o.registeredEnvs, envKey) + if env := o.getEnv(envKey); env != "" { + defaultVal = env } return o.Flags.StringP(flag, shortFlag, defaultVal, usage) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal := o.env.Getenv(envKey) - if !boolyEnv(envVal) { - return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) - } - if truthyEnv(envVal) { - defaultVal = true - } else { - defaultVal = false - } + if env := o.getEnv(envKey); env != "" { + if !boolyEnv(env) { + return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env) + } + if truthyEnv(env) { + defaultVal = true + } else { + defaultVal = false } - o.registeredEnvs = append(o.registeredEnvs, envKey) } return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil From 474d5f1655732f2506a19cab42ada2cc1e738819 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 17:13:38 -0800 Subject: [PATCH 06/28] 2022-11-16 05:13:38PM --- ci/release/template/man/d2.1 | 4 +++- cmd/d2/main.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index 6d187ea83..b9c680414 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -39,7 +39,9 @@ Watch for changes to input and live reload. Use .Ev $D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0 .It Fl t , -theme Ar 0 Set the diagram theme to the passed integer. For a list of available options, see -.Lk https://oss.terrastruct.com/d2 +.It Fl l , -layout Ar dagre +Set the diagram layout engine to the passed string. For a list of available options, run +.Ar layout .It Fl b , -bundle Ar true Bundle all assets and layers into the output svg. .It Fl d , -debug diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 914ca295e..6392541f4 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -31,6 +31,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) + // These should be kept up-to-date with the d2 man page watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") if err != nil { return xmain.UsageErrorf(err.Error()) From 052b461b031c28128cd598e73554961b2ac868e4 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 22:33:57 -0800 Subject: [PATCH 07/28] 2022-11-16 10:33:57PM --- cmd/d2/help.go | 2 +- lib/xmain/flag_helpers.go | 45 ++++++++++++++++ lib/xmain/opts.go | 111 ++++++++++++++++++++++++++++++-------- 3 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 lib/xmain/flag_helpers.go diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 9b304ee34..ccc85332f 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -28,7 +28,7 @@ Subcommands: %[1]s layout [layout name] - Display long help for a particular layout engine See more docs and the source code at https://oss.terrastruct.com/d2 -`, ms.Name, ms.Opts.Help()) +`, ms.Name, ms.Opts.Defaults()) } func layoutHelp(ctx context.Context, ms *xmain.State) error { diff --git a/lib/xmain/flag_helpers.go b/lib/xmain/flag_helpers.go new file mode 100644 index 000000000..4d17066dc --- /dev/null +++ b/lib/xmain/flag_helpers.go @@ -0,0 +1,45 @@ +// flag_helpers.go are private functions from pflag/flag.go +package xmain + +import "strings" + +func wrap(i, w int, s string) string { + if w == 0 { + return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) + } + wrap := w - i + var r, l string + if wrap < 24 { + i = 16 + wrap = w - i + r += "\n" + strings.Repeat(" ", i) + } + if wrap < 24 { + return strings.Replace(s, "\n", r, -1) + } + slop := 5 + wrap = wrap - slop + l, s = wrapN(wrap, slop, s) + r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) + for s != "" { + var t string + t, s = wrapN(wrap, slop, s) + r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) + } + return r +} + +func wrapN(i, slop int, s string) (string, string) { + if i+slop > len(s) { + return s, "" + } + w := strings.LastIndexAny(s[:i], " \t\n") + if w <= 0 { + return s, "" + } + nlPos := strings.LastIndex(s[:i], "\n") + if nlPos > 0 && nlPos < w { + return s[:nlPos], s[nlPos+1:] + } + return s[:w], s[w+1:] +} diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index ed260537f..98d9387ef 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -1,6 +1,7 @@ package xmain import ( + "bytes" "fmt" "io" "strconv" @@ -17,7 +18,7 @@ type Opts struct { env *xos.Env log *cmdlog.Logger - registeredEnvs []string + flagEnv map[string]string } func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { @@ -26,42 +27,106 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags.Usage = func() {} flags.SetOutput(io.Discard) return &Opts{ - Args: args, - Flags: flags, - env: env, - log: log, + Args: args, + Flags: flags, + env: env, + log: log, + flagEnv: make(map[string]string), } } -func (o *Opts) Help() string { - b := &strings.Builder{} - o.Flags.SetOutput(b) - o.Flags.PrintDefaults() +// Mostly copy pasted pasted from pflag.FlagUsagesWrapped +// with modifications for env var +func (o *Opts) Defaults() string { + buf := new(bytes.Buffer) - if len(o.registeredEnvs) > 0 { - b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") - for i, e := range o.registeredEnvs { - s := fmt.Sprintf("- $%s", e) - if i != len(o.registeredEnvs)-1 { - s += "\n" - } - b.WriteString(s) + var lines []string + + maxlen := 0 + maxEnvLen := 0 + o.Flags.VisitAll(func(flag *pflag.Flag) { + if flag.Hidden { + return } + + line := "" + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) + } else { + line = fmt.Sprintf(" --%s", flag.Name) + } + + varname, usage := pflag.UnquoteUsage(flag) + if varname != "" { + line += " " + varname + } + if flag.NoOptDefVal != "" { + switch flag.Value.Type() { + case "string": + line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) + case "bool": + if flag.NoOptDefVal != "true" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + default: + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + } + + line += "\x00" + + if len(line) > maxlen { + maxlen = len(line) + } + + if e, ok := o.flagEnv[flag.Name]; ok { + line += fmt.Sprintf("$%s", e) + } + + line += "\x01" + + if len(line) > maxEnvLen { + maxEnvLen = len(line) + } + + line += usage + if flag.Value.Type() == "string" { + line += fmt.Sprintf(" (default %q)", flag.DefValue) + } else { + line += fmt.Sprintf(" (default %s)", flag.DefValue) + } + if len(flag.Deprecated) != 0 { + line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) + } + + lines = append(lines, line) + }) + + for _, line := range lines { + sidx1 := strings.Index(line, "\x00") + sidx2 := strings.Index(line, "\x01") + spacing1 := strings.Repeat(" ", maxlen-sidx1) + spacing2 := strings.Repeat(" ", (maxEnvLen-maxlen)-sidx2+sidx1) + fmt.Fprintln(buf, line[:sidx1], spacing1, line[sidx1+1:sidx2], spacing2, wrap(maxEnvLen+3, 0, line[sidx2+1:])) } - return b.String() + return buf.String() } -func (o *Opts) getEnv(k string) string { +func (o *Opts) getEnv(flag, k string) string { if k != "" { - o.registeredEnvs = append(o.registeredEnvs, k) + o.flagEnv[flag] = k return o.env.Getenv(k) } return "" } func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { envVal, err := strconv.ParseInt(env, 10, 64) if err != nil { return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) @@ -73,7 +138,7 @@ func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage str } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { defaultVal = env } @@ -81,7 +146,7 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { if !boolyEnv(env) { return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env) } From e08a0fb8dce03061e04b568355acab741cc2544d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 22:45:08 -0800 Subject: [PATCH 08/28] 2022-11-16 10:45:08PM --- ci/release/template/man/d2.1 | 12 +++++++++++- cmd/d2/main.go | 6 ++++-- cmd/d2/watch.go | 19 ++++++------------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index b9c680414..0583e90c3 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -36,12 +36,22 @@ See more docs, the source code and license at .It Fl w , -watch Ar false Watch for changes to input and live reload. Use .Ev $PORT and Ev $HOST to specify the listening address. -.Ev $D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0 +.It Fl h , -host Ar localhost +Host listening address when used with +.Ar watch +.Ns . +.It Fl p , -port Ar 0 +Port listening address when used with +.Ar watch +.Ns . .It Fl t , -theme Ar 0 Set the diagram theme to the passed integer. For a list of available options, see +.Lk https://oss.terrastruct.com/d2 +.Ns . .It Fl l , -layout Ar dagre Set the diagram layout engine to the passed string. For a list of available options, run .Ar layout +.Ns . .It Fl b , -bundle Ar true Bundle all assets and layers into the output svg. .It Fl d , -debug diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 6392541f4..cfca96ddf 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -32,10 +32,12 @@ func run(ctx context.Context, ms *xmain.State) (err error) { ctx = xmain.DiscardSlog(ctx) // These should be kept up-to-date with the d2 man page - watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n(default localhost:0, which is will open on a randomly available local port).") if err != nil { return xmain.UsageErrorf(err.Error()) } + hostFlag := ms.Opts.String("HOST", "host", "h", "localhost", "host listening address when used with watch") + portFlag := ms.Opts.String("PORT", "port", "p", "0", "port listening address when used with watch") bundleFlag, err := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") if err != nil { return xmain.UsageErrorf(err.Error()) @@ -130,7 +132,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, *themeFlag, inputPath, outputPath) + w, err := newWatcher(ctx, ms, plugin, *themeFlag, *hostFlag, *portFlag, inputPath, outputPath) if err != nil { return err } diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index 18dd9ea3c..a5d22c42c 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -43,6 +43,8 @@ type watcher struct { ms *xmain.State layoutPlugin d2plugin.Plugin themeID int64 + host string + port string inputPath string outputPath string @@ -69,7 +71,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, host, port, inputPath, outputPath string) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -80,6 +82,8 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug ms: ms, layoutPlugin: layoutPlugin, themeID: themeID, + host: host, + port: port, inputPath: inputPath, outputPath: outputPath, @@ -353,18 +357,7 @@ func (w *watcher) compileLoop(ctx context.Context) error { } func (w *watcher) listen() error { - host := "localhost" - port := "0" - hostEnv := w.ms.Env.Getenv("HOST") - if hostEnv != "" { - host = hostEnv - } - portEnv := w.ms.Env.Getenv("PORT") - if portEnv != "" { - port = portEnv - } - - l, err := net.Listen("tcp", net.JoinHostPort(host, port)) + l, err := net.Listen("tcp", net.JoinHostPort(w.host, w.port)) if err != nil { return err } From c8739d8147bbb31a0e705f553483d1b70e8ac77f Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 23:49:45 -0800 Subject: [PATCH 09/28] 2022-11-16 11:49:45PM --- cmd/d2/main.go | 10 +++++++++- cmd/d2/watch.go | 29 ++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cmd/d2/main.go b/cmd/d2/main.go index cfca96ddf..61107d6ea 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -132,7 +132,15 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, *themeFlag, *hostFlag, *portFlag, inputPath, outputPath) + + w, err := newWatcher(ctx, ms, watcherOpts{ + layoutPlugin: plugin, + themeID: *themeFlag, + host: *hostFlag, + port: *portFlag, + inputPath: inputPath, + outputPath: outputPath, + }) if err != nil { return err } diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index a5d22c42c..f4fef9a6d 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -34,19 +34,23 @@ var devMode = false //go:embed static var staticFS embed.FS -type watcher struct { - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup - devMode bool - - ms *xmain.State +type watcherOpts struct { layoutPlugin d2plugin.Plugin themeID int64 host string port string inputPath string outputPath string +} + +type watcher struct { + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + devMode bool + + ms *xmain.State + watcherOpts compileCh chan struct{} @@ -71,7 +75,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, host, port, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, opts watcherOpts) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -79,13 +83,8 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug cancel: cancel, devMode: devMode, - ms: ms, - layoutPlugin: layoutPlugin, - themeID: themeID, - host: host, - port: port, - inputPath: inputPath, - outputPath: outputPath, + ms: ms, + watcherOpts: opts, compileCh: make(chan struct{}, 1), wsclients: make(map[*wsclient]struct{}), From 84141efe37b7ba4724f06419474b02119bc0e18a Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Wed, 16 Nov 2022 10:48:39 -0800 Subject: [PATCH 10/28] ci: Many updates --- .github/workflows/ci.yml | 12 +- .github/workflows/daily.yml | 2 +- ci/release/_install.sh | 10 +- ci/release/build.sh | 9 +- ci/release/builders/aws_copy_keys.sh | 8 +- ci/release/builders/aws_ensure.sh | 10 +- ci/release/builders/ssh.sh | 8 +- ci/release/gen_template_lib.sh | 29 +++++ ci/release/template/scripts/lib.sh | 171 +++++++++++++++++++++++---- ci/sub | 2 +- go.sum | 2 + install.sh | 69 +++++------ 12 files changed, 232 insertions(+), 100 deletions(-) create mode 100755 ci/release/gen_template_lib.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73f3d930c..1727e3638 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive - - run: TERM=xterm-256color ./make.sh assert-linear + - run: COLOR=1 ./make.sh assert-linear env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} @@ -25,7 +25,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: TERM=xterm-256color ./make.sh fmt + - run: COLOR=1 ./make.sh fmt env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} @@ -39,7 +39,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: TERM=xterm-256color ./make.sh lint + - run: COLOR=1 ./make.sh lint env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} @@ -53,7 +53,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: TERM=xterm-256color ./make.sh build + - run: COLOR=1 ./make.sh build env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} @@ -67,7 +67,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: TERM=xterm-256color ./make.sh test + - run: COLOR=1 ./make.sh test env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} @@ -86,7 +86,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: TERM=xterm-256color ./make.sh race + - run: COLOR=1 ./make.sh race env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index ed2e89be5..6334923c1 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -20,7 +20,7 @@ jobs: with: go-version-file: ./go.mod cache: true - - run: CI_ALL=1 TERM=xterm-256color ./make.sh + - run: CI_ALL=1 COLOR=1 ./make.sh env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} diff --git a/ci/release/_install.sh b/ci/release/_install.sh index 2917323ad..833f965f8 100755 --- a/ci/release/_install.sh +++ b/ci/release/_install.sh @@ -86,8 +86,7 @@ EOF main() { METHOD=standalone - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -129,15 +128,12 @@ main() { flag_noarg && shift "$FLAGSHIFT" UNINSTALL=1 ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done + shift "$FLAGSHIFT" if [ $# -gt 0 ]; then flag_errusage "no arguments are accepted" @@ -176,7 +172,7 @@ install() { TALA_VERSION="$( install_tala && echo "$VERSION" )" fi - COLOR=2 header success + FGCOLOR=2 header success log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX" if [ -n "${TALA-}" ]; then log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX" diff --git a/ci/release/build.sh b/ci/release/build.sh index 135160f82..04cdc7217 100755 --- a/ci/release/build.sh +++ b/ci/release/build.sh @@ -42,8 +42,7 @@ EOF } main() { - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -78,16 +77,12 @@ main() { flag_noarg && shift "$FLAGSHIFT" LOCKFILE_FORCE=1 ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done - + shift "$FLAGSHIFT" if [ $# -gt 0 ]; then flag_errusage "no arguments are accepted" fi diff --git a/ci/release/builders/aws_copy_keys.sh b/ci/release/builders/aws_copy_keys.sh index df5c46408..cc0d548ef 100755 --- a/ci/release/builders/aws_copy_keys.sh +++ b/ci/release/builders/aws_copy_keys.sh @@ -12,8 +12,7 @@ EOF } main() { - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -27,15 +26,12 @@ main() { flag_nonemptyarg && shift "$FLAGSHIFT" KEY_FILE=$FLAGARG ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done + shift "$FLAGSHIFT" if [ -z "${KEY_FILE-}" ]; then echoerr "-i is required" exit 1 diff --git a/ci/release/builders/aws_ensure.sh b/ci/release/builders/aws_ensure.sh index 3a875d832..bfb630ed0 100755 --- a/ci/release/builders/aws_ensure.sh +++ b/ci/release/builders/aws_ensure.sh @@ -12,8 +12,7 @@ EOF } main() { - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -27,15 +26,12 @@ main() { flag_noarg && shift "$FLAGSHIFT" SKIP_CREATE=1 ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done + shift "$FLAGSHIFT" if [ $# -gt 0 ]; then flag_errusage "no arguments are accepted" fi @@ -204,7 +200,7 @@ init_remote_hosts() { header macos-arm64 REMOTE_HOST=$TSTRUCT_MACOS_ARM64_BUILDER init_remote_macos - COLOR=2 header summary + FGCOLOR=2 header summary log "export TSTRUCT_LINUX_AMD64_BUILDER=$TSTRUCT_LINUX_AMD64_BUILDER" log "export TSTRUCT_LINUX_ARM64_BUILDER=$TSTRUCT_LINUX_ARM64_BUILDER" log "export TSTRUCT_MACOS_AMD64_BUILDER=$TSTRUCT_MACOS_AMD64_BUILDER" diff --git a/ci/release/builders/ssh.sh b/ci/release/builders/ssh.sh index 0325a99dd..3d0e7e995 100755 --- a/ci/release/builders/ssh.sh +++ b/ci/release/builders/ssh.sh @@ -12,8 +12,7 @@ EOF } main() { - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -27,15 +26,12 @@ main() { flag_reqarg && shift "$FLAGSHIFT" JOBFILTER="$FLAGARG" ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done + shift "$FLAGSHIFT" REMOTE_HOST=$TSTRUCT_LINUX_AMD64_BUILDER; runjob linux-amd64 ssh "$REMOTE_HOST" "$@" REMOTE_HOST=$TSTRUCT_LINUX_ARM64_BUILDER; runjob linux-arm64 ssh "$REMOTE_HOST" "$@" diff --git a/ci/release/gen_template_lib.sh b/ci/release/gen_template_lib.sh new file mode 100755 index 000000000..1f523767a --- /dev/null +++ b/ci/release/gen_template_lib.sh @@ -0,0 +1,29 @@ +#!/bin/sh +set -eu +cd -- "$(dirname "$0")/../.." +. ./ci/sub/lib.sh + +sh_c chmod +w ./ci/release/template/scripts/lib.sh +sh_c cat >./ci/release/template/scripts/lib.sh <\>./ci/release/template/scripts/lib.sh +sh_c chmod -w ./ci/release/template/scripts/lib.sh diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 0a232eb74..95119c42a 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -1,8 +1,21 @@ #!/bin/sh -if [ -n "${DEBUG-}" ]; then - set -x +# ************* +# DO NOT EDIT +# +# lib.sh was bundled together from +# +# - ./ci/sub/lib/rand.sh +# - ./ci/sub/lib/log.sh +# +# Generated by ./ci/release/gen_template_lib.sh. +# ************* + +#!/bin/sh +if [ "${LIB_RAND-}" ]; then + return 0 fi +LIB_RAND=1 rand() { seed="$1" @@ -14,15 +27,40 @@ rand() { } pick() { + if ! command -v shuf >/dev/null || ! command -v md5sum >/dev/null; then + eval "_echo \"\$3\"" + return + fi + seed="$1" shift i="$(rand "$seed" "1-$#")" eval "_echo \"\$$i\"" } +#!/bin/sh +if [ "${LIB_LOG-}" ]; then + return 0 +fi +LIB_LOG=1 + +if [ -n "${DEBUG-}" ]; then + set -x +fi + +export COLOR +if ! [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + export _COLOR=1 + fi +fi tput() { - if [ -n "$TERM" ]; then - command tput "$@" + if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + return 0 + fi + + if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + TERM=${TERM:-xterm-256color} command tput "$@" fi } @@ -59,10 +97,10 @@ printfp() {( prefix="$1" shift - if [ -z "${COLOR:-}" ]; then - COLOR="$(get_rand_color "$prefix")" + if [ -z "${FGCOLOR-}" ]; then + FGCOLOR="$(get_rand_color "$prefix")" fi - printf '%s' "$(setaf "$COLOR" "$prefix")" + printf '%s' "$(setaf "$FGCOLOR" "$prefix")" if [ $# -gt 0 ]; then printf ': ' @@ -74,13 +112,7 @@ catp() { prefix="$1" shift - printfp "$prefix" - printf ': ' - read -r line - _echo "$line" - - indent=$(repeat ' ' 2) - sed "s/^/$indent/" + sed "s/^/$(printfp "$prefix" '')/" } repeat() { @@ -94,48 +126,137 @@ strlen() { } echoerr() { - COLOR=1 echop err "$*" >&2 + FGCOLOR=1 echop err "$*" | humanpath>&2 } caterr() { - COLOR=1 catp err "$@" >&2 + FGCOLOR=1 catp err "$@" | humanpath >&2 } printferr() { - COLOR=1 printfp err "$@" >&2 + FGCOLOR=1 printfp err "$@" | humanpath >&2 } logp() { - echop "$@" >&2 + echop "$@" | humanpath >&2 } logfp() { - printfp "$@" >&2 + printfp "$@" | humanpath >&2 } logpcat() { - catp "$@" >&2 + catp "$@" | humanpath >&2 } log() { - COLOR=5 logp log "$@" + FGCOLOR=5 logp log "$@" } logf() { - COLOR=5 logfp log "$@" + FGCOLOR=5 logfp log "$@" } logcat() { - COLOR=5 catp log "$@" >&2 + FGCOLOR=5 logpcat log "$@" +} + +warn() { + FGCOLOR=3 logp warn "$@" +} + +warnf() { + FGCOLOR=3 logfp warn "$@" } sh_c() { - COLOR=3 logp exec "$*" + FGCOLOR=3 logp exec "$*" if [ -z "${DRY_RUN-}" ]; then - "$@" + eval "$@" + fi +} + +sudo_sh_c() { + if [ "$(id -u)" -eq 0 ]; then + sh_c "$@" + elif command -v doas >/dev/null; then + sh_c "doas $*" + elif command -v sudo >/dev/null; then + sh_c "sudo $*" + elif command -v su >/dev/null; then + sh_c "su root -c '$*'" + else + caterr <"$out" 2>&1 + code="$?" + set -e + if [ "$code" -eq 0 ]; then + return + fi + cat "$out" >&2 + return "$code" +} + +echo_dur() { + local dur=$1 + local h=$((dur/60/60)) + local m=$((dur/60%60)) + local s=$((dur%60)) + printf '%dh%dm%ds' "$h" "$m" "$s" +} + +sponge() { + dst="$1" + tmp="$(mktemp)" + cat > "$tmp" + cat "$tmp" > "$dst" +} + +stripansi() { + # First regex gets rid of standard xterm escape sequences for controlling + # visual attributes. + # The second regex I'm not 100% sure, the reference says it selects the US + # encoding but I'm not sure why that's necessary or why it always occurs + # in tput sgr0 before the standard escape sequence. + # See tput sgr0 | xxd + sed -e $'s/\x1b\[[0-9;]*m//g' -e $'s/\x1b(.//g' +} + +runtty() { + case "$(uname)" in + Darwin) + script -q /dev/null "$@" + ;; + Linux) + script -eqc "$*" + ;; + *) + echoerr "runtty: unsupported OS $(uname)" + return 1 + esac +} diff --git a/ci/sub b/ci/sub index df51b9089..c5e5d53ca 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit df51b90892737ebe9feca3dd982bcdfc7f684834 +Subproject commit c5e5d53caa3241629a56b95eaf9780a68546ad61 diff --git a/go.sum b/go.sum index c9976a706..a7e3307a5 100644 --- a/go.sum +++ b/go.sum @@ -769,6 +769,8 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 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/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E= +oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37 h1:Xy1JKJHc4hcuwi57s0BvGUY16GjxTtBmLUybsuGDU7E= +oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg= oss.terrastruct.com/cmdlog v0.0.0-20221116181457-07977d95ac37/go.mod h1:ROL3yxl2X+S3O+Rls00qdX6aMh+p1dF8IdxDRwDDpsg= oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541 h1:I9B1O1IJ6spivIQxbFRZmbhAwVeLwrcQRR1JbYUOvrI= oss.terrastruct.com/diff v1.0.2-0.20221116222035-8bf4dd3ab541/go.mod h1:ags2QDy/T6jr69hT6bpmAmhr2H98n9o8Atf3QlUJPiU= diff --git a/install.sh b/install.sh index 5cc51950f..a9eb6bec3 100755 --- a/install.sh +++ b/install.sh @@ -53,9 +53,20 @@ if [ -n "${DEBUG-}" ]; then set -x fi +export COLOR +if ! [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + export _COLOR=1 + fi +fi + tput() { - if [ -n "$TERM" ]; then - command tput "$@" + if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + return 0 + fi + + if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + TERM=${TERM:-xterm-256color} command tput "$@" fi } @@ -92,10 +103,10 @@ printfp() {( prefix="$1" shift - if [ -z "${COLOR:-}" ]; then - COLOR="$(get_rand_color "$prefix")" + if [ -z "${FGCOLOR-}" ]; then + FGCOLOR="$(get_rand_color "$prefix")" fi - printf '%s' "$(setaf "$COLOR" "$prefix")" + printf '%s' "$(setaf "$FGCOLOR" "$prefix")" if [ $# -gt 0 ]; then printf ': ' @@ -121,15 +132,15 @@ strlen() { } echoerr() { - COLOR=1 echop err "$*" | humanpath>&2 + FGCOLOR=1 echop err "$*" | humanpath>&2 } caterr() { - COLOR=1 catp err "$@" | humanpath >&2 + FGCOLOR=1 catp err "$@" | humanpath >&2 } printferr() { - COLOR=1 printfp err "$@" | humanpath >&2 + FGCOLOR=1 printfp err "$@" | humanpath >&2 } logp() { @@ -145,27 +156,27 @@ logpcat() { } log() { - COLOR=5 logp log "$@" + FGCOLOR=5 logp log "$@" } logf() { - COLOR=5 logfp log "$@" + FGCOLOR=5 logfp log "$@" } logcat() { - COLOR=5 logpcat log "$@" + FGCOLOR=5 logpcat log "$@" } warn() { - COLOR=3 logp warn "$@" + FGCOLOR=3 logp warn "$@" } warnf() { - COLOR=3 logfp warn "$@" + FGCOLOR=3 logfp warn "$@" } sh_c() { - COLOR=3 logp exec "$*" + FGCOLOR=3 logp exec "$*" if [ -z "${DRY_RUN-}" ]; then eval "$@" fi @@ -280,9 +291,8 @@ LIB_FLAG=1 # FLAGSHIFT contains the number by which the arguments should be shifted to # start at the next flag/argument # -# After each call check $FLAG for the name of the parsed flag. -# If empty, then no more flags are left. -# Still, call shift "$FLAGSHIFT" in case there was a -- +# flag_parse exits with a non zero code when there are no more flags +# to be parsed. Still, call shift "$FLAGSHIFT" in case there was a -- # # If the argument for the flag is optional, then use ${FLAGARG-} to access # the argument if one was passed. Use ${FLAGARG+x} = x to check if it was set. @@ -310,18 +320,15 @@ flag_parse() { # Remove everything before first equal sign. FLAGARG="${1#*=}" FLAGSHIFT=1 + return 0 ;; -) - FLAG= - FLAGRAW= - unset FLAGARG FLAGSHIFT=0 + return 1 ;; --) - FLAG= - FLAGRAW= - unset FLAGARG FLAGSHIFT=1 + return 1 ;; -*) # Remove leading hyphens. @@ -343,15 +350,13 @@ flag_parse() { ;; esac fi + return 0 ;; *) - FLAG= - FLAGRAW= - unset FLAGARG FLAGSHIFT=0 + return 1 ;; esac - return 0 } flag_reqarg() { @@ -520,8 +525,7 @@ EOF main() { METHOD=standalone - while :; do - flag_parse "$@" + while flag_parse "$@"; do case "$FLAG" in h|help) help @@ -563,15 +567,12 @@ main() { flag_noarg && shift "$FLAGSHIFT" UNINSTALL=1 ;; - '') - shift "$FLAGSHIFT" - break - ;; *) flag_errusage "unrecognized flag $FLAGRAW" ;; esac done + shift "$FLAGSHIFT" if [ $# -gt 0 ]; then flag_errusage "no arguments are accepted" @@ -610,7 +611,7 @@ install() { TALA_VERSION="$( install_tala && echo "$VERSION" )" fi - COLOR=2 header success + FGCOLOR=2 header success log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX" if [ -n "${TALA-}" ]; then log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX" From 0df3d4d15b1522f2ad0e85c2237acea2ac945f61 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 02:09:32 -0800 Subject: [PATCH 11/28] ci: Many fixes and updates --- Makefile | 3 ++ ci/release/build.sh | 10 +++-- ci/release/build_docker.sh | 1 - ci/release/gen_install.sh | 4 ++ ci/release/gen_sh.sh | 11 ++++++ ci/release/gen_template_lib.sh | 4 ++ ci/release/template/scripts/lib.sh | 59 +++++++++++++++++++----------- install.sh | 59 +++++++++++++++++++----------- 8 files changed, 104 insertions(+), 47 deletions(-) create mode 100755 ci/release/gen_sh.sh diff --git a/Makefile b/Makefile index 46577c730..a1c404ed4 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,9 @@ endif .PHONY: fmt fmt: prefix "$@" ./ci/sub/fmt/make.sh +.PHONY: fmt-release +fmt-release_sh: + prefix "$@" ./ci/release/gen_sh.sh .PHONY: lint lint: prefix "$@" go vet --composites=false ./... diff --git a/ci/release/build.sh b/ci/release/build.sh index 04cdc7217..591dbd320 100755 --- a/ci/release/build.sh +++ b/ci/release/build.sh @@ -167,13 +167,14 @@ build_remote_macos() { trap unlockfile_ssh EXIT sh_c ssh "$REMOTE_HOST" mkdir -p src sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/" - sh_c ssh "$REMOTE_HOST" "DRY_RUN=${DRY_RUN-} \ + sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \ +TERM=${TERM-} \ +DRY_RUN=${DRY_RUN-} \ HW_BUILD_DIR=$HW_BUILD_DIR \ VERSION=$VERSION \ OS=$OS \ ARCH=$ARCH \ ARCHIVE=$ARCHIVE \ -TERM=$TERM \ PATH=\\\"/usr/local/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin\\\${PATH+:\\\$PATH}\\\" \ ./src/d2/ci/release/_build.sh" sh_c mkdir -p "$HW_BUILD_DIR" @@ -185,13 +186,14 @@ build_remote_linux() { trap unlockfile_ssh EXIT sh_c ssh "$REMOTE_HOST" mkdir -p src sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/" - sh_c ssh "$REMOTE_HOST" "DRY_RUN=${DRY_RUN-} \ + sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \ +TERM=${TERM-} \ +DRY_RUN=${DRY_RUN-} \ HW_BUILD_DIR=$HW_BUILD_DIR \ VERSION=$VERSION \ OS=$OS \ ARCH=$ARCH \ ARCHIVE=$ARCHIVE \ -TERM=$TERM \ ./src/d2/ci/release/build_docker.sh" sh_c mkdir -p "$HW_BUILD_DIR" sh_c rsync --archive --human-readable "$REMOTE_HOST:src/d2/$ARCHIVE" "$ARCHIVE" diff --git a/ci/release/build_docker.sh b/ci/release/build_docker.sh index 7c3751af2..2939c5dd0 100755 --- a/ci/release/build_docker.sh +++ b/ci/release/build_docker.sh @@ -13,5 +13,4 @@ docker_run \ -e OS \ -e ARCH \ -e ARCHIVE \ - -e TERM \ "$tag" ./src/d2/ci/release/_build.sh diff --git a/ci/release/gen_install.sh b/ci/release/gen_install.sh index 082bdcc52..ee5249d87 100755 --- a/ci/release/gen_install.sh +++ b/ci/release/gen_install.sh @@ -37,3 +37,7 @@ sh_c cat \ sh_c cat ./ci/release/_install.sh \ \| sed -n "'/cd -- \"\$(dirname/,/cd -/!p'" \>\> install.sh sh_c chmod -w install.sh + +if [ -n "${CI-}" ]; then + git_assert_clean +fi diff --git a/ci/release/gen_sh.sh b/ci/release/gen_sh.sh new file mode 100755 index 000000000..63d9a0d3c --- /dev/null +++ b/ci/release/gen_sh.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -eu +cd -- "$(dirname "$0")/../.." +. ./ci/sub/lib.sh + +./ci/release/gen_install.sh +./ci/release/gen_template_lib.sh + +if [ -n "${CI-}" ]; then + git_assert_clean +fi diff --git a/ci/release/gen_template_lib.sh b/ci/release/gen_template_lib.sh index 1f523767a..b79c8ab61 100755 --- a/ci/release/gen_template_lib.sh +++ b/ci/release/gen_template_lib.sh @@ -27,3 +27,7 @@ sh_c cat \ ./ci/sub/lib/log.sh \ \| sed "-e'/^\. /d'" \>\>./ci/release/template/scripts/lib.sh sh_c chmod -w ./ci/release/template/scripts/lib.sh + +if [ -n "${CI-}" ]; then + git_assert_clean +fi diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 95119c42a..8c6d1ec06 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -47,23 +47,32 @@ if [ -n "${DEBUG-}" ]; then set -x fi -export COLOR -if ! [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then - export _COLOR=1 - fi -fi - tput() { - if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - return 0 - fi - - if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + if should_color; then TERM=${TERM:-xterm-256color} command tput "$@" fi } +should_color() { + if [ -n "${COLOR-}" ]; then + if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + _COLOR=0 + return 1 + elif [ "${COLOR-}" = 1 -o "${COLOR-}" = true ]; then + _COLOR=1 + return 0 + else + printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2 + fi + fi + if [ -t 1 ]; then + _COLOR=1 + return 0 + else + return 1 + fi +} + setaf() { tput setaf "$1" shift @@ -100,10 +109,10 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi - printf '%s' "$(setaf "$FGCOLOR" "$prefix")" + setaf "$FGCOLOR" "[$prefix]" if [ $# -gt 0 ]; then - printf ': ' + printf ' ' printf "$@" fi )} @@ -112,7 +121,8 @@ catp() { prefix="$1" shift - sed "s/^/$(printfp "$prefix" '')/" + should_color || true + sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/" } repeat() { @@ -126,27 +136,30 @@ strlen() { } echoerr() { - FGCOLOR=1 echop err "$*" | humanpath>&2 + FGCOLOR=1 logp err "$*" | humanpath>&2 } caterr() { - FGCOLOR=1 catp err "$@" | humanpath >&2 + FGCOLOR=1 logpcat err "$@" | humanpath >&2 } printferr() { - FGCOLOR=1 printfp err "$@" | humanpath >&2 + FGCOLOR=1 logfp err "$@" | humanpath >&2 } logp() { - echop "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} echop "$@" | humanpath >&2 } logfp() { - printfp "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} printfp "$@" | humanpath >&2 } logpcat() { - catp "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} catp "$@" | humanpath >&2 } log() { @@ -169,6 +182,10 @@ warnf() { FGCOLOR=3 logfp warn "$@" } +warncat() { + FGCOLOR=3 logpcat warn "$@" +} + sh_c() { FGCOLOR=3 logp exec "$*" if [ -z "${DRY_RUN-}" ]; then diff --git a/install.sh b/install.sh index a9eb6bec3..a1ac95b45 100755 --- a/install.sh +++ b/install.sh @@ -53,23 +53,32 @@ if [ -n "${DEBUG-}" ]; then set -x fi -export COLOR -if ! [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then - export _COLOR=1 - fi -fi - tput() { - if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - return 0 - fi - - if [ "${COLOR-}" = 1 -o "${COLOR-}" = true -o -t 1 ]; then + if should_color; then TERM=${TERM:-xterm-256color} command tput "$@" fi } +should_color() { + if [ -n "${COLOR-}" ]; then + if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then + _COLOR=0 + return 1 + elif [ "${COLOR-}" = 1 -o "${COLOR-}" = true ]; then + _COLOR=1 + return 0 + else + printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2 + fi + fi + if [ -t 1 ]; then + _COLOR=1 + return 0 + else + return 1 + fi +} + setaf() { tput setaf "$1" shift @@ -106,10 +115,10 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi - printf '%s' "$(setaf "$FGCOLOR" "$prefix")" + setaf "$FGCOLOR" "[$prefix]" if [ $# -gt 0 ]; then - printf ': ' + printf ' ' printf "$@" fi )} @@ -118,7 +127,8 @@ catp() { prefix="$1" shift - sed "s/^/$(printfp "$prefix" '')/" + should_color || true + sed "s/^/$(COLOR=${_COLOR-} printfp "$prefix" '')/" } repeat() { @@ -132,27 +142,30 @@ strlen() { } echoerr() { - FGCOLOR=1 echop err "$*" | humanpath>&2 + FGCOLOR=1 logp err "$*" | humanpath>&2 } caterr() { - FGCOLOR=1 catp err "$@" | humanpath >&2 + FGCOLOR=1 logpcat err "$@" | humanpath >&2 } printferr() { - FGCOLOR=1 printfp err "$@" | humanpath >&2 + FGCOLOR=1 logfp err "$@" | humanpath >&2 } logp() { - echop "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} echop "$@" | humanpath >&2 } logfp() { - printfp "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} printfp "$@" | humanpath >&2 } logpcat() { - catp "$@" | humanpath >&2 + should_color >&2 || true + COLOR=${_COLOR-} catp "$@" | humanpath >&2 } log() { @@ -175,6 +188,10 @@ warnf() { FGCOLOR=3 logfp warn "$@" } +warncat() { + FGCOLOR=3 logpcat warn "$@" +} + sh_c() { FGCOLOR=3 logp exec "$*" if [ -z "${DRY_RUN-}" ]; then From c09064dc602c55b2491bece62589222469d7994f Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 02:11:15 -0800 Subject: [PATCH 12/28] ci: Many more fixes --- .github/workflows/ci.yml | 14 ++++++++++++++ Makefile | 8 ++++---- ci/{release/gen_sh.sh => gen.sh} | 2 +- ci/release/gen_install.sh | 4 ---- ci/release/gen_template_lib.sh | 4 ---- 5 files changed, 19 insertions(+), 13 deletions(-) rename ci/{release/gen_sh.sh => gen.sh} (82%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1727e3638..a18a4bd80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,20 @@ jobs: env: GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + gen: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + - uses: actions/setup-go@v3 + with: + go-version-file: ./go.mod + cache: true + - run: COLOR=1 ./make.sh gen + env: + GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }} + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} lint: runs-on: ubuntu-latest steps: diff --git a/Makefile b/Makefile index a1c404ed4..9780b7bc0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .POSIX: .PHONY: all -all: fmt lint build test +all: fmt gen lint build test ifdef CI all: assert-linear endif @@ -9,9 +9,9 @@ endif .PHONY: fmt fmt: prefix "$@" ./ci/sub/fmt/make.sh -.PHONY: fmt-release -fmt-release_sh: - prefix "$@" ./ci/release/gen_sh.sh +.PHONY: gen +gen: + prefix "$@" ./ci/gen.sh .PHONY: lint lint: prefix "$@" go vet --composites=false ./... diff --git a/ci/release/gen_sh.sh b/ci/gen.sh similarity index 82% rename from ci/release/gen_sh.sh rename to ci/gen.sh index 63d9a0d3c..db0c557ab 100755 --- a/ci/release/gen_sh.sh +++ b/ci/gen.sh @@ -1,6 +1,6 @@ #!/bin/sh set -eu -cd -- "$(dirname "$0")/../.." +cd -- "$(dirname "$0")/.." . ./ci/sub/lib.sh ./ci/release/gen_install.sh diff --git a/ci/release/gen_install.sh b/ci/release/gen_install.sh index ee5249d87..082bdcc52 100755 --- a/ci/release/gen_install.sh +++ b/ci/release/gen_install.sh @@ -37,7 +37,3 @@ sh_c cat \ sh_c cat ./ci/release/_install.sh \ \| sed -n "'/cd -- \"\$(dirname/,/cd -/!p'" \>\> install.sh sh_c chmod -w install.sh - -if [ -n "${CI-}" ]; then - git_assert_clean -fi diff --git a/ci/release/gen_template_lib.sh b/ci/release/gen_template_lib.sh index b79c8ab61..1f523767a 100755 --- a/ci/release/gen_template_lib.sh +++ b/ci/release/gen_template_lib.sh @@ -27,7 +27,3 @@ sh_c cat \ ./ci/sub/lib/log.sh \ \| sed "-e'/^\. /d'" \>\>./ci/release/template/scripts/lib.sh sh_c chmod -w ./ci/release/template/scripts/lib.sh - -if [ -n "${CI-}" ]; then - git_assert_clean -fi From c74d32fce9535110ca9db2a2769f942fbcdffa32 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 03:19:40 -0800 Subject: [PATCH 13/28] ci: fmt/gen --- ci/release/template/scripts/lib.sh | 19 +++++++++++-------- ci/sub | 2 +- install.sh | 19 +++++++++++-------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 8c6d1ec06..9b2b4b7d7 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -55,20 +55,22 @@ tput() { should_color() { if [ -n "${COLOR-}" ]; then - if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - _COLOR=0 + if [ "$COLOR" = 0 -o "$COLOR" = false ]; then + _COLOR= return 1 - elif [ "${COLOR-}" = 1 -o "${COLOR-}" = true ]; then + elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then _COLOR=1 return 0 else printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2 fi fi + if [ -t 1 ]; then _COLOR=1 return 0 else + _COLOR= return 1 fi } @@ -109,11 +111,12 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi - setaf "$FGCOLOR" "[$prefix]" - - if [ $# -gt 0 ]; then - printf ' ' - printf "$@" + if [ $# -eq 0 ]; then + should_color || true + printf '%s' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") + else + should_color || true + printf '%s: %s\n' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") "$(printf "$@")" fi )} diff --git a/ci/sub b/ci/sub index c5e5d53ca..2eb7e4fa5 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit c5e5d53caa3241629a56b95eaf9780a68546ad61 +Subproject commit 2eb7e4fa5e2457a62140ed3e95c1d75f75cb583c diff --git a/install.sh b/install.sh index a1ac95b45..ff0742739 100755 --- a/install.sh +++ b/install.sh @@ -61,20 +61,22 @@ tput() { should_color() { if [ -n "${COLOR-}" ]; then - if [ "${COLOR-}" = 0 -o "${COLOR-}" = false ]; then - _COLOR=0 + if [ "$COLOR" = 0 -o "$COLOR" = false ]; then + _COLOR= return 1 - elif [ "${COLOR-}" = 1 -o "${COLOR-}" = true ]; then + elif [ "$COLOR" = 1 -o "$COLOR" = true ]; then _COLOR=1 return 0 else printf '$COLOR must be 0, 1, false or true but got %s' "$COLOR" >&2 fi fi + if [ -t 1 ]; then _COLOR=1 return 0 else + _COLOR= return 1 fi } @@ -115,11 +117,12 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi - setaf "$FGCOLOR" "[$prefix]" - - if [ $# -gt 0 ]; then - printf ' ' - printf "$@" + if [ $# -eq 0 ]; then + should_color || true + printf '%s' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") + else + should_color || true + printf '%s: %s\n' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") "$(printf "$@")" fi )} From e751e1242636e46c130a6e699fb16b8491cf721a Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 06:33:07 -0800 Subject: [PATCH 14/28] ci/sub: Update --- ci/release/build.sh | 2 -- ci/sub | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ci/release/build.sh b/ci/release/build.sh index 591dbd320..ce2e4eaf4 100755 --- a/ci/release/build.sh +++ b/ci/release/build.sh @@ -164,7 +164,6 @@ build_local() { build_remote_macos() { sh_c lockfile_ssh "$REMOTE_HOST" .d2-build-lock - trap unlockfile_ssh EXIT sh_c ssh "$REMOTE_HOST" mkdir -p src sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/" sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \ @@ -183,7 +182,6 @@ PATH=\\\"/usr/local/bin:/usr/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin\\\$ build_remote_linux() { sh_c lockfile_ssh "$REMOTE_HOST" .d2-build-lock - trap unlockfile_ssh EXIT sh_c ssh "$REMOTE_HOST" mkdir -p src sh_c rsync --archive --human-readable --delete ./ "$REMOTE_HOST:src/d2/" sh_c ssh "$REMOTE_HOST" "COLOR=${COLOR-} \ diff --git a/ci/sub b/ci/sub index 2eb7e4fa5..8f9a24045 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 2eb7e4fa5e2457a62140ed3e95c1d75f75cb583c +Subproject commit 8f9a24045546e76e39175ed148fdadd770c750ba From 17dd64d89d5633095292d2655779defe30121953 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 05:19:39 -0800 Subject: [PATCH 15/28] install.sh: Add brew integration and documentation Updates #80 --- README.md | 5 +- ci/release/_install.sh | 247 ++++++++++++++++++--------- ci/release/template/scripts/lib.sh | 13 +- ci/sub | 2 +- docs/INSTALL.md | 83 +++++++++ install.sh | 260 +++++++++++++++++++---------- lib/version/version.go | 2 +- 7 files changed, 441 insertions(+), 171 deletions(-) create mode 100644 docs/INSTALL.md diff --git a/README.md b/README.md index df9cd9564..7f31f52f5 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,9 @@ A browser window will open with `out.svg` and live-reload on changes to `in.d2`. ## Install +For more detailed installation docs with examples for if you do not wish to use the +install script see [./doc/INSTALL.md](./doc/INSTALL.md). + ### Install script The recommended way to install is to run our install script, which will figure out the @@ -92,7 +95,7 @@ curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall Alternatively, you can install from source: ```sh -go install oss.terrastruct.com/d2 +go install oss.terrastruct.com/d2/cmd/d2@latest ``` ## D2 as a library diff --git a/ci/release/_install.sh b/ci/release/_install.sh index 833f965f8..f1432a555 100755 --- a/ci/release/_install.sh +++ b/ci/release/_install.sh @@ -18,8 +18,11 @@ usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix [--tala latest] [--force] [--uninstall] install.sh automates the installation of D2 onto your system. It currently only supports -the installation of standalone releases from GitHub. If you pass --edge, it will clone the -source, build a release and install from it. +the installation of standalone releases from GitHub and via Homebrew on macOS. See the +docs for --detect below for more information + +If you pass --edge, it will clone the source, build a release and install from it. +--edge is incompatible with --tala and currently unimplemented. Flags: @@ -29,6 +32,8 @@ Flags: --version vX.X.X Pass to have install.sh install the given version instead of the latest version. + warn: The version may not be obeyed with package manager installations. Use + --method=standalone to enforce the version. --edge Pass to build and install D2 from source. This will still use --method if set to detect @@ -36,14 +41,15 @@ Flags: if an unsupported package manager is used. To install from source like a dev would, use go install oss.terrastruct.com/d2 note: currently unimplemented. + warn: incompatible with --tala as TALA is closed source. ---method [detect | standalone] +--method [detect | standalone | homebrew ] Pass to control the method by which to install. Right now we only support standalone releases from GitHub but later we'll add support for brew, rpm, deb and more. - note: currently unimplemented. - - detect is currently unimplemented but would use your OS's package manager - automatically. + - detect will use your OS's package manager automatically. + So far it only detects macOS and automatically uses homebrew. + - homebrew uses https://brew.sh/ which is a macOS and Linux package manager. - standalone installs a standalone release archive into the unix hierarchy path specified by --prefix which defaults to /usr/local Ensure /usr/local/bin is in your \$PATH to use it. @@ -51,16 +57,19 @@ Flags: --prefix /usr/local Controls the unix hierarchy path into which standalone releases are installed. Defaults to /usr/local. You may also want to use ~/.local to avoid needing sudo. - Remember that whatever you use, you must have the bin directory of your prefix - path in \$PATH to execute the d2 binary. For example, if my prefix directory is + We use ~/.local by default on arm64 macOS machines as SIP now disables access to + /usr/local. Remember that whatever you use, you must have the bin directory of your + prefix path in \$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then my \$PATH must contain /usr/local/bin. --tala [latest] Install Terrastruct's closed source TALA for improved layouts. - See https://github.com/terrastruct/TALA + See https://github.com/terrastruct/tala It optionally takes an argument of the TALA version to install. Installation obeys all other flags, just like the installation of d2. For example, the d2plugin-tala binary will be installed into /usr/local/bin/d2plugin-tala + warn: The version may not be obeyed with package manager installations. Use + --method=standalone to enforce the version. --force: Force installation over the existing version even if they match. It will attempt a @@ -85,7 +94,6 @@ EOF } main() { - METHOD=standalone while flag_parse "$@"; do case "$FLAG" in h|help) @@ -113,8 +121,6 @@ main() { method) flag_nonemptyarg && shift "$FLAGSHIFT" METHOD=$FLAGARG - echoerr "$FLAGRAW is currently unimplemented" - return 1 ;; prefix) flag_nonemptyarg && shift "$FLAGSHIFT" @@ -149,49 +155,75 @@ main() { PREFIX=${PREFIX:-/usr/local} CACHE_DIR=$(cache_dir) mkdir -p "$CACHE_DIR" + METHOD=${METHOD:-detect} INSTALL_DIR=$PREFIX/lib/d2 + case $METHOD in + detect) + case "$OS" in + macos) + log "detected macOS, using homebrew for (un)installation" + METHOD=homebrew + ;; + *) + warn "unrecognized OS $OS, falling back to --method=standalone" + METHOD=standalone + ;; + esac + ;; + standalone) ;; + homebrew) ;; + *) + echoerr "unknown (un)installation method $METHOD" + return 1 + ;; + esac + if [ -n "${UNINSTALL-}" ]; then uninstall - return 0 + else + install fi - - VERSION=${VERSION:-latest} - if [ "$VERSION" = latest ]; then - header "fetching latest release info" - fetch_release_info - fi - - install } install() { - install_d2 - if [ -n "${TALA-}" ]; then - # Run in subshell to avoid overwriting VERSION. - TALA_VERSION="$( install_tala && echo "$VERSION" )" - fi + case $METHOD in + standalone) + install_d2_standalone + if [ -n "${TALA-}" ]; then + # Run in subshell to avoid overwriting VERSION. + TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )" + fi + ;; + homebrew) + install_d2_brew + if [ -n "${TALA-}" ]; then install_tala_brew; fi + ;; + esac - FGCOLOR=2 header success + FGCOLOR=2 bigheader 'next steps' + case $METHOD in + standalone) install_post_standalone ;; + homebrew) install_post_brew ;; + esac +} + +install_post_standalone() { log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX" if [ -n "${TALA-}" ]; then log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX" fi - log "Rerun this install script with --uninstall to uninstall" + log "Rerun this install script with --uninstall to uninstall." + log if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then logcat >&2 <&2 <&2 <&2 </dev/null; then INSTALLED_VERSION="$(d2 version)" if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then - log "skipping installation as version $VERSION is already installed." + log "skipping installation as d2 $VERSION is already installed." return 0 fi - log "uninstalling $INSTALLED_VERSION to install $VERSION" - if ! uninstall_d2; then - warn "failed to uninstall $INSTALLED_VERSION" + log "uninstalling d2 $INSTALLED_VERSION to install $VERSION" + if ! uninstall_d2_standalone; then + warn "failed to uninstall d2 $INSTALLED_VERSION" fi fi - header "installing d2-$VERSION" - install_standalone_d2 -} - -install_standalone_d2() { ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz" log "installing standalone release $ARCHIVE from github" @@ -252,19 +309,39 @@ install_standalone_d2() { "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'" } -install_tala() { - REPO="${REPO_TALA:-terrastruct/TALA}" - VERSION=$TALA - RELEASE_INFO= - fetch_release_info - header "installing tala-$VERSION" - install_standalone_tala +install_d2_brew() { + header "installing d2 with homebrew" + sh_c brew tap terrastruct/d2 + sh_c brew install d2 + sh_c brew test d2 } -install_standalone_tala() { +install_tala_standalone() { + REPO="${REPO_TALA:-terrastruct/tala}" + VERSION=$TALA + + header "installing tala-$VERSION" + + if [ "$VERSION" = latest ]; then + fetch_release_info + fi + + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then + log "skipping installation as tala $VERSION is already installed." + return 0 + fi + log "uninstalling tala $INSTALLED_VERSION to install $VERSION" + if ! uninstall_tala_standalone; then + warn "failed to uninstall tala $INSTALLED_VERSION" + fi + fi + ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz" log "installing standalone release $ARCHIVE from github" + fetch_release_info asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1') asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"') @@ -280,36 +357,43 @@ install_standalone_tala() { "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'" } +install_tala_brew() { + header "installing tala with homebrew" + sh_c brew tap terrastruct/d2 + sh_c brew install tala + sh_c brew test tala +} + uninstall() { + # We uninstall tala first as package managers require that it be uninstalled before + # uninstalling d2 as TALA depends on d2. + if [ "${TALA-}" ]; then + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + header "uninstalling tala-$INSTALLED_VERSION" + case $METHOD in + standalone) uninstall_tala_standalone ;; + homebrew) uninstall_tala_brew ;; + esac + else + warn "no version of tala installed" + fi + fi + if ! command -v d2 >/dev/null; then warn "no version of d2 installed" return 0 fi + INSTALLED_VERSION="$(d2 --version)" - if ! uninstall_d2; then - echoerr "failed to uninstall $INSTALLED_VERSION" - return 1 - fi - if [ "${TALA-}" ]; then - if ! command -v d2plugin-tala >/dev/null; then - warn "no version of tala installed" - return 0 - fi - INSTALLED_VERSION="$(d2plugin-tala --version)" - if ! uninstall_tala; then - echoerr "failed to uninstall tala $INSTALLED_VERSION" - return 1 - fi - fi - return 0 -} - -uninstall_d2() { header "uninstalling d2-$INSTALLED_VERSION" - uninstall_standalone_d2 + case $METHOD in + standalone) uninstall_d2_standalone ;; + homebrew) uninstall_d2_brew ;; + esac } -uninstall_standalone_d2() { +uninstall_d2_standalone() { log "uninstalling standalone release of d2-$INSTALLED_VERSION" if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then @@ -327,12 +411,11 @@ uninstall_standalone_d2() { "$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION" } -uninstall_tala() { - header "uninstalling tala-$INSTALLED_VERSION" - uninstall_standalone_tala +uninstall_d2_brew() { + sh_c brew remove d2 } -uninstall_standalone_tala() { +uninstall_tala_standalone() { log "uninstalling standalone release tala-$INSTALLED_VERSION" if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then @@ -350,6 +433,10 @@ uninstall_standalone_tala() { "$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION" } +uninstall_tala_brew() { + sh_c brew remove tala +} + is_prefix_writable() { sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true # The reason for checking whether $INSTALL_DIR is writable is that on macOS you have diff --git a/ci/release/template/scripts/lib.sh b/ci/release/template/scripts/lib.sh index 9b2b4b7d7..61f7c1138 100644 --- a/ci/release/template/scripts/lib.sh +++ b/ci/release/template/scripts/lib.sh @@ -111,12 +111,11 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi + should_color || true if [ $# -eq 0 ]; then - should_color || true - printf '%s' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") + printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" else - should_color || true - printf '%s: %s\n' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") "$(printf "$@")" + printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")" fi )} @@ -219,6 +218,12 @@ header() { logp "/* $1 */" } +bigheader() { + logp "/** + * $1 + **/" +} + # humanpath replaces all occurrences of " $HOME" with " ~" # and all occurrences of '$HOME' with the literal '$HOME'. humanpath() { diff --git a/ci/sub b/ci/sub index 8f9a24045..55e97e541 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 8f9a24045546e76e39175ed148fdadd770c750ba +Subproject commit 55e97e5416c5a63737c44d9123441340bc4b210b diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 000000000..38fa92748 --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,83 @@ +# install + +This file documents how the install.sh script installs d2 and how you can +manually install yourself. + + + +- [install.sh](#installsh) +- [Standalone](#standalone) +- [macOS (Homebrew)](#macos-homebrew) + + + +## install.sh + +```sh +# With --dry-run the install script will print the commands it will use +# to install without actually installing so you know what it's going to do. +curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run +# If things look good, install for real. +curl -fsSL https://d2lang.com/install.sh | sh -s -- +``` + +For help on the terminal run including the supported package managers +and detection methods see: + +```sh +curl -fsSL https://d2lang.com/install.sh | sh -s -- --help +``` + +## Standalone + +We publish standalone release archives with every release on github. +Download the `.tar.gz` release for your OS/ARCH combination and then run: + +```sh +make install +``` + +Inside the extracted directory to install. + +```sh +make uninstall +``` + +To uninstall. You will be prompted for sudo/su/doas if root permissions +are required for installation. You can control the unix hierarchy installation +path with `PREFIX=`. For example: + +``` +# Install under ~/.local. +# Binaries will be at ~/.local/bin +# And manpages will be under ~/.local/share/man +# And supporting data like icons and fonts at ~/.local/share/d2 +make install PREFIX=$HOME/.local +``` + +The install script places the standalone release into `$PREFIX/lib/d2/d2-` +and we recommend doing the same with manually installed releases so that you +know where the release directory is for easy uninstall. + +We have releases with identical process for tala at https://github.com/terrastruct/TALA/releases + +## macOS (Homebrew) + +For macOS you may install as so: + +```sh +brew tap terrastruct/d2 +brew install d2 +brew test d2 +``` + +For closed source TALA [https://github.com/terrastruct/tala]: + +```sh +brew tap terrastruct/d2 +brew install tala +brew test tala +``` + +You don't have to run the `brew test` command but we recommend it to ensure d2 is +functioning correctly after installation. diff --git a/install.sh b/install.sh index ff0742739..78c315935 100755 --- a/install.sh +++ b/install.sh @@ -117,12 +117,11 @@ printfp() {( if [ -z "${FGCOLOR-}" ]; then FGCOLOR="$(get_rand_color "$prefix")" fi + should_color || true if [ $# -eq 0 ]; then - should_color || true - printf '%s' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") + printf '%s' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" else - should_color || true - printf '%s: %s\n' $(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix") "$(printf "$@")" + printf '%s: %s\n' "$(COLOR=${_COLOR-} setaf "$FGCOLOR" "$prefix")" "$(printf "$@")" fi )} @@ -225,6 +224,12 @@ header() { logp "/* $1 */" } +bigheader() { + logp "/** + * $1 + **/" +} + # humanpath replaces all occurrences of " $HOME" with " ~" # and all occurrences of '$HOME' with the literal '$HOME'. humanpath() { @@ -477,8 +482,11 @@ usage: $arg0 [--dry-run] [--version vX.X.X] [--edge] [--method detect] [--prefix [--tala latest] [--force] [--uninstall] install.sh automates the installation of D2 onto your system. It currently only supports -the installation of standalone releases from GitHub. If you pass --edge, it will clone the -source, build a release and install from it. +the installation of standalone releases from GitHub and via Homebrew on macOS. See the +docs for --detect below for more information + +If you pass --edge, it will clone the source, build a release and install from it. +--edge is incompatible with --tala and currently unimplemented. Flags: @@ -488,6 +496,8 @@ Flags: --version vX.X.X Pass to have install.sh install the given version instead of the latest version. + warn: The version may not be obeyed with package manager installations. Use + --method=standalone to enforce the version. --edge Pass to build and install D2 from source. This will still use --method if set to detect @@ -495,14 +505,15 @@ Flags: if an unsupported package manager is used. To install from source like a dev would, use go install oss.terrastruct.com/d2 note: currently unimplemented. + warn: incompatible with --tala as TALA is closed source. ---method [detect | standalone] +--method [detect | standalone | homebrew ] Pass to control the method by which to install. Right now we only support standalone releases from GitHub but later we'll add support for brew, rpm, deb and more. - note: currently unimplemented. - - detect is currently unimplemented but would use your OS's package manager - automatically. + - detect will use your OS's package manager automatically. + So far it only detects macOS and automatically uses homebrew. + - homebrew uses https://brew.sh/ which is a macOS and Linux package manager. - standalone installs a standalone release archive into the unix hierarchy path specified by --prefix which defaults to /usr/local Ensure /usr/local/bin is in your \$PATH to use it. @@ -510,16 +521,19 @@ Flags: --prefix /usr/local Controls the unix hierarchy path into which standalone releases are installed. Defaults to /usr/local. You may also want to use ~/.local to avoid needing sudo. - Remember that whatever you use, you must have the bin directory of your prefix - path in \$PATH to execute the d2 binary. For example, if my prefix directory is + We use ~/.local by default on arm64 macOS machines as SIP now disables access to + /usr/local. Remember that whatever you use, you must have the bin directory of your + prefix path in \$PATH to execute the d2 binary. For example, if my prefix directory is /usr/local then my \$PATH must contain /usr/local/bin. --tala [latest] Install Terrastruct's closed source TALA for improved layouts. - See https://github.com/terrastruct/TALA + See https://github.com/terrastruct/tala It optionally takes an argument of the TALA version to install. Installation obeys all other flags, just like the installation of d2. For example, the d2plugin-tala binary will be installed into /usr/local/bin/d2plugin-tala + warn: The version may not be obeyed with package manager installations. Use + --method=standalone to enforce the version. --force: Force installation over the existing version even if they match. It will attempt a @@ -544,7 +558,6 @@ EOF } main() { - METHOD=standalone while flag_parse "$@"; do case "$FLAG" in h|help) @@ -572,8 +585,6 @@ main() { method) flag_nonemptyarg && shift "$FLAGSHIFT" METHOD=$FLAGARG - echoerr "$FLAGRAW is currently unimplemented" - return 1 ;; prefix) flag_nonemptyarg && shift "$FLAGSHIFT" @@ -608,49 +619,75 @@ main() { PREFIX=${PREFIX:-/usr/local} CACHE_DIR=$(cache_dir) mkdir -p "$CACHE_DIR" + METHOD=${METHOD:-detect} INSTALL_DIR=$PREFIX/lib/d2 + case $METHOD in + detect) + case "$OS" in + macos) + log "detected macOS, using homebrew for (un)installation" + METHOD=homebrew + ;; + *) + warn "unrecognized OS $OS, falling back to --method=standalone" + METHOD=standalone + ;; + esac + ;; + standalone) ;; + homebrew) ;; + *) + echoerr "unknown (un)installation method $METHOD" + return 1 + ;; + esac + if [ -n "${UNINSTALL-}" ]; then uninstall - return 0 + else + install fi - - VERSION=${VERSION:-latest} - if [ "$VERSION" = latest ]; then - header "fetching latest release info" - fetch_release_info - fi - - install } install() { - install_d2 - if [ -n "${TALA-}" ]; then - # Run in subshell to avoid overwriting VERSION. - TALA_VERSION="$( install_tala && echo "$VERSION" )" - fi + case $METHOD in + standalone) + install_d2_standalone + if [ -n "${TALA-}" ]; then + # Run in subshell to avoid overwriting VERSION. + TALA_VERSION="$( RELEASE_INFO= install_tala_standalone && echo "$VERSION" )" + fi + ;; + homebrew) + install_d2_brew + if [ -n "${TALA-}" ]; then install_tala_brew; fi + ;; + esac - FGCOLOR=2 header success + FGCOLOR=2 bigheader 'next steps' + case $METHOD in + standalone) install_post_standalone ;; + homebrew) install_post_brew ;; + esac +} + +install_post_standalone() { log "d2-$VERSION-$OS-$ARCH has been successfully installed into $PREFIX" if [ -n "${TALA-}" ]; then log "tala-$TALA_VERSION-$OS-$ARCH has been successfully installed into $PREFIX" fi - log "Rerun this install script with --uninstall to uninstall" + log "Rerun this install script with --uninstall to uninstall." + log if ! echo "$PATH" | grep -qF "$PREFIX/bin"; then logcat >&2 <&2 <&2 <&2 </dev/null; then INSTALLED_VERSION="$(d2 version)" if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then - log "skipping installation as version $VERSION is already installed." + log "skipping installation as d2 $VERSION is already installed." return 0 fi - log "uninstalling $INSTALLED_VERSION to install $VERSION" - if ! uninstall_d2; then - warn "failed to uninstall $INSTALLED_VERSION" + log "uninstalling d2 $INSTALLED_VERSION to install $VERSION" + if ! uninstall_d2_standalone; then + warn "failed to uninstall d2 $INSTALLED_VERSION" fi fi - header "installing d2-$VERSION" - install_standalone_d2 -} - -install_standalone_d2() { ARCHIVE="d2-$VERSION-$OS-$ARCH.tar.gz" log "installing standalone release $ARCHIVE from github" @@ -711,19 +773,39 @@ install_standalone_d2() { "$sh_c" sh -c "'cd \"$INSTALL_DIR/d2-$VERSION\" && make install PREFIX=\"$PREFIX\"'" } -install_tala() { - REPO="${REPO_TALA:-terrastruct/TALA}" - VERSION=$TALA - RELEASE_INFO= - fetch_release_info - header "installing tala-$VERSION" - install_standalone_tala +install_d2_brew() { + header "installing d2 with homebrew" + sh_c brew tap terrastruct/d2 + sh_c brew install d2 + sh_c brew test d2 } -install_standalone_tala() { +install_tala_standalone() { + REPO="${REPO_TALA:-terrastruct/tala}" + VERSION=$TALA + + header "installing tala-$VERSION" + + if [ "$VERSION" = latest ]; then + fetch_release_info + fi + + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + if [ ! "${FORCE-}" -a "$VERSION" = "$INSTALLED_VERSION" ]; then + log "skipping installation as tala $VERSION is already installed." + return 0 + fi + log "uninstalling tala $INSTALLED_VERSION to install $VERSION" + if ! uninstall_tala_standalone; then + warn "failed to uninstall tala $INSTALLED_VERSION" + fi + fi + ARCHIVE="tala-$VERSION-$OS-$ARCH.tar.gz" log "installing standalone release $ARCHIVE from github" + fetch_release_info asset_line=$(sh_c 'cat "$RELEASE_INFO" | grep -n "$ARCHIVE" | cut -d: -f1 | head -n1') asset_url=$(sh_c 'sed -n $((asset_line-3))p "$RELEASE_INFO" | sed "s/^.*: \"\(.*\)\",$/\1/g"') @@ -739,36 +821,43 @@ install_standalone_tala() { "$sh_c" sh -c "'cd \"$INSTALL_DIR/tala-$VERSION\" && make install PREFIX=\"$PREFIX\"'" } +install_tala_brew() { + header "installing tala with homebrew" + sh_c brew tap terrastruct/d2 + sh_c brew install tala + sh_c brew test tala +} + uninstall() { + # We uninstall tala first as package managers require that it be uninstalled before + # uninstalling d2 as TALA depends on d2. + if [ "${TALA-}" ]; then + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + header "uninstalling tala-$INSTALLED_VERSION" + case $METHOD in + standalone) uninstall_tala_standalone ;; + homebrew) uninstall_tala_brew ;; + esac + else + warn "no version of tala installed" + fi + fi + if ! command -v d2 >/dev/null; then warn "no version of d2 installed" return 0 fi + INSTALLED_VERSION="$(d2 --version)" - if ! uninstall_d2; then - echoerr "failed to uninstall $INSTALLED_VERSION" - return 1 - fi - if [ "${TALA-}" ]; then - if ! command -v d2plugin-tala >/dev/null; then - warn "no version of tala installed" - return 0 - fi - INSTALLED_VERSION="$(d2plugin-tala --version)" - if ! uninstall_tala; then - echoerr "failed to uninstall tala $INSTALLED_VERSION" - return 1 - fi - fi - return 0 -} - -uninstall_d2() { header "uninstalling d2-$INSTALLED_VERSION" - uninstall_standalone_d2 + case $METHOD in + standalone) uninstall_d2_standalone ;; + homebrew) uninstall_d2_brew ;; + esac } -uninstall_standalone_d2() { +uninstall_d2_standalone() { log "uninstalling standalone release of d2-$INSTALLED_VERSION" if [ ! -e "$INSTALL_DIR/d2-$INSTALLED_VERSION" ]; then @@ -786,12 +875,11 @@ uninstall_standalone_d2() { "$sh_c" rm -rf "$INSTALL_DIR/d2-$INSTALLED_VERSION" } -uninstall_tala() { - header "uninstalling tala-$INSTALLED_VERSION" - uninstall_standalone_tala +uninstall_d2_brew() { + sh_c brew remove d2 } -uninstall_standalone_tala() { +uninstall_tala_standalone() { log "uninstalling standalone release tala-$INSTALLED_VERSION" if [ ! -e "$INSTALL_DIR/tala-$INSTALLED_VERSION" ]; then @@ -809,6 +897,10 @@ uninstall_standalone_tala() { "$sh_c" rm -rf "$INSTALL_DIR/tala-$INSTALLED_VERSION" } +uninstall_tala_brew() { + sh_c brew remove tala +} + is_prefix_writable() { sh_c "mkdir -p '$INSTALL_DIR' 2>/dev/null" || true # The reason for checking whether $INSTALL_DIR is writable is that on macOS you have diff --git a/lib/version/version.go b/lib/version/version.go index 34ab1a5bf..2f1ff62e5 100644 --- a/lib/version/version.go +++ b/lib/version/version.go @@ -1,4 +1,4 @@ package version // Pre-built binaries will have version set during build time. -var Version = "master (built from source)" +var Version = "????" From cd193e52c71f21bcb14df747b6480e5d26c97ec2 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 19:45:18 -0800 Subject: [PATCH 16/28] ci/sub: Update --- ci/sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/sub b/ci/sub index 55e97e541..f1f144c73 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 55e97e5416c5a63737c44d9123441340bc4b210b +Subproject commit f1f144c738f3015f3cafc7d566666f83a0916320 From 7ecdc828248020ec59f2763401d8931f3b78bbc0 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 20:16:29 -0800 Subject: [PATCH 17/28] install.sh: Improve brew integration and documentation --- ci/release/_install.sh | 22 ++++++++++++++++++---- docs/CONTRIBUTING.md | 3 +-- docs/INSTALL.md | 17 +---------------- install.sh | 22 ++++++++++++++++++---- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/ci/release/_install.sh b/ci/release/_install.sh index f1432a555..524d317be 100755 --- a/ci/release/_install.sh +++ b/ci/release/_install.sh @@ -162,8 +162,13 @@ main() { detect) case "$OS" in macos) - log "detected macOS, using homebrew for (un)installation" - METHOD=homebrew + if command -v brew >/dev/null; then + log "detected macOS with homebrew, using homebrew for (un)installation" + METHOD=homebrew + else + warn "detected macOS without homebrew, falling back to --method=standalone" + METHOD=standalone + fi ;; *) warn "unrecognized OS $OS, falling back to --method=standalone" @@ -181,8 +186,14 @@ main() { if [ -n "${UNINSTALL-}" ]; then uninstall + if [ -n "${DRY_RUN-}" ]; then + log "Rerun without --dry-run to execute printed commands and perform uninstall." + fi else install + if [ -n "${DRY_RUN-}" ]; then + log "Rerun without --dry-run to execute printed commands and perform install." + fi fi } @@ -313,7 +324,6 @@ install_d2_brew() { header "installing d2 with homebrew" sh_c brew tap terrastruct/d2 sh_c brew install d2 - sh_c brew test d2 } install_tala_standalone() { @@ -361,7 +371,6 @@ install_tala_brew() { header "installing tala with homebrew" sh_c brew tap terrastruct/d2 sh_c brew install tala - sh_c brew test tala } uninstall() { @@ -492,4 +501,9 @@ fetch_gh() { sh_c mv "$file.inprogress" "$file" } +brew() { + # Makes brew sane. + HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@" +} + main "$@" diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 41c82f89a..bd8851e7b 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -65,8 +65,7 @@ language. Sometimes it gives controversial sentences -- don't use those. Script to generate one line of random text: ``` ipsum1() { - fortune | head -n1 | sed 's/^ *//;s/ *$//' | tr -d '\n' | pbcopy - echo "$(pbpaste -Prefer txt)" + fortune | head -n1 | sed 's/^ *//;s/ *$//' | tr -d '\n' | tee /dev/stderr | pbcopy } ``` diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 38fa92748..57828c04e 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,7 +1,6 @@ # install -This file documents how the install.sh script installs d2 and how you can -manually install yourself. +This file documents all the ways by which you can install d2. @@ -59,8 +58,6 @@ The install script places the standalone release into `$PREFIX/lib/d2/d2-/dev/null; then + log "detected macOS with homebrew, using homebrew for (un)installation" + METHOD=homebrew + else + warn "detected macOS without homebrew, falling back to --method=standalone" + METHOD=standalone + fi ;; *) warn "unrecognized OS $OS, falling back to --method=standalone" @@ -645,8 +650,14 @@ main() { if [ -n "${UNINSTALL-}" ]; then uninstall + if [ -n "${DRY_RUN-}" ]; then + log "Rerun without --dry-run to execute printed commands and perform uninstall." + fi else install + if [ -n "${DRY_RUN-}" ]; then + log "Rerun without --dry-run to execute printed commands and perform install." + fi fi } @@ -777,7 +788,6 @@ install_d2_brew() { header "installing d2 with homebrew" sh_c brew tap terrastruct/d2 sh_c brew install d2 - sh_c brew test d2 } install_tala_standalone() { @@ -825,7 +835,6 @@ install_tala_brew() { header "installing tala with homebrew" sh_c brew tap terrastruct/d2 sh_c brew install tala - sh_c brew test tala } uninstall() { @@ -956,4 +965,9 @@ fetch_gh() { sh_c mv "$file.inprogress" "$file" } +brew() { + # Makes brew sane. + HOMEBREW_NO_INSTALL_CLEANUP=1 HOMEBREW_NO_AUTO_UPDATE=1 command brew "$@" +} + main "$@" From c9184cf5a754490a8731433d2d4660727b22214e Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 20:31:24 -0800 Subject: [PATCH 18/28] ci/sub: Update --- ci/sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/sub b/ci/sub index f1f144c73..ce6a94973 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit f1f144c738f3015f3cafc7d566666f83a0916320 +Subproject commit ce6a94973984ababdd1fb4b923578a7cba216436 From af63417892087e35439d97c5c9c8d6acd48767e0 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 20:37:06 -0800 Subject: [PATCH 19/28] install.sh: --uninstall implies uninstall tala too --- ci/release/_install.sh | 21 ++++++++++----------- install.sh | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/ci/release/_install.sh b/ci/release/_install.sh index 524d317be..4cd840ca9 100755 --- a/ci/release/_install.sh +++ b/ci/release/_install.sh @@ -82,6 +82,7 @@ Flags: as for installation. i.e if you used --method standalone you must again use --method standalone for uninstallation. With detect, the install script will try to use the OS package manager to uninstall instead. + note: tala will also be uninstalled if installed. All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change path of the cached assets. Release archives are unarchived into /usr/local/lib/d2/d2- @@ -376,17 +377,15 @@ install_tala_brew() { uninstall() { # We uninstall tala first as package managers require that it be uninstalled before # uninstalling d2 as TALA depends on d2. - if [ "${TALA-}" ]; then - if command -v d2plugin-tala >/dev/null; then - INSTALLED_VERSION="$(d2plugin-tala --version)" - header "uninstalling tala-$INSTALLED_VERSION" - case $METHOD in - standalone) uninstall_tala_standalone ;; - homebrew) uninstall_tala_brew ;; - esac - else - warn "no version of tala installed" - fi + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + header "uninstalling tala-$INSTALLED_VERSION" + case $METHOD in + standalone) uninstall_tala_standalone ;; + homebrew) uninstall_tala_brew ;; + esac + elif [ "${TALA-}" ]; then + warn "no version of tala installed" fi if ! command -v d2 >/dev/null; then diff --git a/install.sh b/install.sh index bce7507b5..678dabfe5 100755 --- a/install.sh +++ b/install.sh @@ -546,6 +546,7 @@ Flags: as for installation. i.e if you used --method standalone you must again use --method standalone for uninstallation. With detect, the install script will try to use the OS package manager to uninstall instead. + note: tala will also be uninstalled if installed. All downloaded archives are cached into ~/.cache/d2/release. use \$XDG_CACHE_HOME to change path of the cached assets. Release archives are unarchived into /usr/local/lib/d2/d2- @@ -840,17 +841,15 @@ install_tala_brew() { uninstall() { # We uninstall tala first as package managers require that it be uninstalled before # uninstalling d2 as TALA depends on d2. - if [ "${TALA-}" ]; then - if command -v d2plugin-tala >/dev/null; then - INSTALLED_VERSION="$(d2plugin-tala --version)" - header "uninstalling tala-$INSTALLED_VERSION" - case $METHOD in - standalone) uninstall_tala_standalone ;; - homebrew) uninstall_tala_brew ;; - esac - else - warn "no version of tala installed" - fi + if command -v d2plugin-tala >/dev/null; then + INSTALLED_VERSION="$(d2plugin-tala --version)" + header "uninstalling tala-$INSTALLED_VERSION" + case $METHOD in + standalone) uninstall_tala_standalone ;; + homebrew) uninstall_tala_brew ;; + esac + elif [ "${TALA-}" ]; then + warn "no version of tala installed" fi if ! command -v d2 >/dev/null; then From 1b1454194d68bc2cf20cfc08e440c27fdc7e018f Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 21:33:31 -0800 Subject: [PATCH 20/28] ci/sub: Update --- ci/sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/sub b/ci/sub index ce6a94973..091138b62 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit ce6a94973984ababdd1fb4b923578a7cba216436 +Subproject commit 091138b62dd351807d5c4ab033a7d7859e9dd121 From 58780d6c5cbb07a9823622031733e1853b503ad6 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 22:24:21 -0800 Subject: [PATCH 21/28] ci/sub: Update --- ci/sub | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/sub b/ci/sub index 091138b62..824046d95 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 091138b62dd351807d5c4ab033a7d7859e9dd121 +Subproject commit 824046d952b1442c76a057553591652c889fb7cb From 9b8bd434e9da780865358c141f0f9eafef7dfc28 Mon Sep 17 00:00:00 2001 From: Anmol Sethi Date: Sun, 20 Nov 2022 23:03:22 -0800 Subject: [PATCH 22/28] README: Update --- README.md | 8 ++++---- docs/INSTALL.md | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7f31f52f5..d2f53ae24 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,7 @@ A browser window will open with `out.svg` and live-reload on changes to `in.d2`. ## Install -For more detailed installation docs with examples for if you do not wish to use the -install script see [./doc/INSTALL.md](./doc/INSTALL.md). +For detailed installation docs with examples see [./docs/INSTALL.md](./docs/INSTALL.md). ### Install script @@ -76,8 +75,9 @@ curl -fsSL https://d2lang.com/install.sh | sh -s -- We have precompiled binaries on the [releases](https://github.com/terrastruct/d2/releases) page for macOS and Linux. For both amd64 and arm64. We will release package manager -distributions like .rpm, .deb soon. We also want to get D2 on Homebrew for macOS -and release a docker image. +distributions like .rpm, .deb soon. d2 is on homebrew with our tap. See [./docs/INSTALL.md](./docs/INSTALL.md). + +We'll have a docker image soon too. To uninstall: diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 57828c04e..220b79dab 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -7,6 +7,7 @@ This file documents all the ways by which you can install d2. - [install.sh](#installsh) - [Standalone](#standalone) - [macOS (Homebrew)](#macos-homebrew) +- [From Source](#from-source) @@ -66,3 +67,9 @@ For macOS you may install as so: brew tap terrastruct/d2 brew install d2 ``` + +## From Source + +```sh +go install oss.terrastruct.com/d2/cmd/d2@latest +``` From 92e5f0f86f36e435beb6c96c3b5d639257f854c4 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 21 Nov 2022 00:02:39 -0800 Subject: [PATCH 23/28] 2022-11-21 12:02:39AM --- README.md | 6 ++++-- docs/INSTALL.md | 42 +++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d2f53ae24..4dc18eb71 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,11 @@ curl -fsSL https://d2lang.com/install.sh | sh -s -- We have precompiled binaries on the [releases](https://github.com/terrastruct/d2/releases) page for macOS and Linux. For both amd64 and arm64. We will release package manager -distributions like .rpm, .deb soon. d2 is on homebrew with our tap. See [./docs/INSTALL.md](./docs/INSTALL.md). +distributions like .rpm, .deb soon. D2 is on Homebrew with our tap (the install script +uses this automatically if it detects you have `brew`). See +[./docs/INSTALL.md](./docs/INSTALL.md). -We'll have a docker image soon too. +Docker image coming soon. To uninstall: diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 220b79dab..adea2dad6 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -1,13 +1,13 @@ # install -This file documents all the ways by which you can install d2. +This file documents all the ways by which you can install D2. - [install.sh](#installsh) -- [Standalone](#standalone) - [macOS (Homebrew)](#macos-homebrew) -- [From Source](#from-source) +- [Standalone](#standalone) +- [From source](#from-source) @@ -21,31 +21,41 @@ curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run curl -fsSL https://d2lang.com/install.sh | sh -s -- ``` -For help on the terminal run including the supported package managers -and detection methods see: +For help on the terminal run, including the supported package managers and detection +methods: ```sh curl -fsSL https://d2lang.com/install.sh | sh -s -- --help ``` +## macOS (Homebrew) + +If you're on macOS, you can alternatively install with `brew`. (the install script above +does this automatically if you have `brew` installed). + +```sh +brew tap terrastruct/d2 +brew install d2 +``` + ## Standalone -We publish standalone release archives with every release on github. -Download the `.tar.gz` release for your OS/ARCH combination and then run: +We publish standalone release archives with every release on Github. +Download the `.tar.gz` release for your OS/ARCH combination and then run the following +inside the extracted directory to install: ```sh make install ``` -Inside the extracted directory to install. +Run the following to uninstall: ```sh make uninstall ``` -To uninstall. You will be prompted for sudo/su/doas if root permissions -are required for installation. You can control the unix hierarchy installation -path with `PREFIX=`. For example: +You will be prompted for sudo/su/doas if root permissions are required for installation. +You can control the Unix hierarchy installation path with `PREFIX=`. For example: ``` # Install under ~/.local. @@ -59,16 +69,10 @@ The install script places the standalone release into `$PREFIX/lib/d2/d2- Date: Mon, 21 Nov 2022 00:03:50 -0800 Subject: [PATCH 24/28] 2022-11-21 12:03:50AM --- docs/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index adea2dad6..0dda53b17 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -72,7 +72,7 @@ know where the release directory is for easy uninstall. ## From source -Alternatively, you can always install from source as well with `go`. +Alternatively, you can always install from source: ```sh go install oss.terrastruct.com/d2/cmd/d2@latest From 72b8a48ba42b35680f59e5294008d8b94599d4d1 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 21 Nov 2022 09:31:05 -0800 Subject: [PATCH 25/28] 2022-11-21 09:31:05AM --- README.md | 32 -------------------------------- docs/INSTALL.md | 9 +++++++++ 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 4dc18eb71..7c4ce599c 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,6 @@ - [Quickstart](#quickstart) - [Install](#install) - * [Install script](#install-script) - * [Install from source](#install-from-source) - [D2 as a library](#d2-as-a-library) - [Themes](#themes) - [Fonts](#fonts) @@ -60,46 +58,16 @@ A browser window will open with `out.svg` and live-reload on changes to `in.d2`. For detailed installation docs with examples see [./docs/INSTALL.md](./docs/INSTALL.md). -### Install script - -The recommended way to install is to run our install script, which will figure out the -best way to install based on your machine. - ```sh -# With --dry-run the install script will print the commands it will use -# to install without actually installing so you know what it's going to do. -curl -fsSL https://d2lang.com/install.sh | sh -s -- --dry-run -# If things look good, install for real. curl -fsSL https://d2lang.com/install.sh | sh -s -- ``` -We have precompiled binaries on the [releases](https://github.com/terrastruct/d2/releases) -page for macOS and Linux. For both amd64 and arm64. We will release package manager -distributions like .rpm, .deb soon. D2 is on Homebrew with our tap (the install script -uses this automatically if it detects you have `brew`). See -[./docs/INSTALL.md](./docs/INSTALL.md). - -Docker image coming soon. - To uninstall: ```sh -curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall --dry-run -# If things look good, uninstall for real. curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall ``` -> warn: Our binary releases aren't fully portable like normal Go binaries due to the C -> dependency on v8go for executing dagre. - -### Install from source - -Alternatively, you can install from source: - -```sh -go install oss.terrastruct.com/d2/cmd/d2@latest -``` - ## D2 as a library In addition to being a runnable CLI tool, D2 can also be used to produce diagrams from diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 0dda53b17..97af0744a 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -69,6 +69,8 @@ The install script places the standalone release into `$PREFIX/lib/d2/d2- warn: Our binary releases aren't fully portable like normal Go binaries due to the C +> dependency on v8go for executing dagre. ## From source @@ -77,3 +79,10 @@ Alternatively, you can always install from source: ```sh go install oss.terrastruct.com/d2/cmd/d2@latest ``` + +## Coming soon + +- Docker image +- Windows install +- rpm and deb packages +- homebrew core From c5ae5fa6e7daf7463d45955aa5ed6aeae9224421 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 21 Nov 2022 09:32:59 -0800 Subject: [PATCH 26/28] 2022-11-21 09:32:59AM --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c4ce599c..ea86143fa 100644 --- a/README.md +++ b/README.md @@ -56,12 +56,15 @@ A browser window will open with `out.svg` and live-reload on changes to `in.d2`. ## Install -For detailed installation docs with examples see [./docs/INSTALL.md](./docs/INSTALL.md). +The easiest way to install is with our install script: ```sh curl -fsSL https://d2lang.com/install.sh | sh -s -- ``` +For detailed installation docs, with alternative methods and examples for each OS, see +[./docs/INSTALL.md](./docs/INSTALL.md). + To uninstall: ```sh From 2568b6d534cc1ddffae16a0736562ff634680d24 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 21 Nov 2022 09:33:20 -0800 Subject: [PATCH 27/28] 2022-11-21 09:33:20AM --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea86143fa..14fa751fa 100644 --- a/README.md +++ b/README.md @@ -62,15 +62,15 @@ The easiest way to install is with our install script: curl -fsSL https://d2lang.com/install.sh | sh -s -- ``` -For detailed installation docs, with alternative methods and examples for each OS, see -[./docs/INSTALL.md](./docs/INSTALL.md). - To uninstall: ```sh curl -fsSL https://d2lang.com/install.sh | sh -s -- --uninstall ``` +For detailed installation docs, with alternative methods and examples for each OS, see +[./docs/INSTALL.md](./docs/INSTALL.md). + ## D2 as a library In addition to being a runnable CLI tool, D2 can also be used to produce diagrams from From a68434c28f7bd7a8b68984166255d498e0d23f4c Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 21 Nov 2022 09:35:49 -0800 Subject: [PATCH 28/28] 2022-11-21 09:35:49AM --- docs/INSTALL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 97af0744a..cf6599adb 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -13,6 +13,9 @@ This file documents all the ways by which you can install D2. ## install.sh +The recommended and easiest way to install is with our install script, which will detect +the OS and architecture you're on and use the best method: + ```sh # With --dry-run the install script will print the commands it will use # to install without actually installing so you know what it's going to do.