From b838817c5154e00420ffe8aa14f90880ce07b1fe Mon Sep 17 00:00:00 2001 From: Ammar Bandukwala Date: Thu, 7 Sep 2023 16:09:34 -0500 Subject: [PATCH] chore(cli): replace lipgloss with coder/pretty This change will improve over CLI performance and "snappiness" as well as substantially reduce our test times. Preliminary benchmarks show `coder server --help` times cut from 300ms to 120ms on my dogfood instance. The inefficiency of lipgloss disproportionately impacts our system, as all help text for every command is generated whenever any command is invoked. The `pretty` API could clean up a lot of the code (e.g., by replacing complex string concatenations with Printf), but this commit is too expansive as is so that work will be done in a follow up. --- Makefile | 2 +- cli/clitest/golden.go | 8 +- cli/cliui/cliui.go | 157 ++++++++++++++---- cli/cliui/log.go | 14 +- cli/cliui/parameter.go | 14 +- cli/cliui/prompt.go | 20 ++- cli/cliui/provisionerjob.go | 21 ++- cli/cliui/resources.go | 31 ++-- cli/create.go | 13 +- cli/delete.go | 6 +- cli/dotfiles.go | 6 +- cli/gitssh.go | 14 +- cli/help.go | 2 +- cli/list.go | 6 +- cli/login.go | 28 ++-- cli/login_test.go | 4 +- cli/parameterresolver.go | 4 +- cli/ping.go | 16 +- cli/publickey.go | 10 +- cli/rename.go | 4 +- cli/resetpassword.go | 8 +- cli/restart.go | 7 +- cli/root.go | 24 +-- cli/server.go | 14 +- cli/start.go | 5 +- cli/stop.go | 7 +- cli/templatecreate.go | 20 ++- cli/templatedelete.go | 8 +- cli/templatedelete_test.go | 6 +- cli/templateedit.go | 4 +- cli/templateinit.go | 28 +++- cli/templatepush.go | 15 +- cli/templates.go | 4 +- cli/templateversions.go | 2 +- cli/testdata/coder_--help.golden | 8 +- cli/testdata/coder_config-ssh_--help.golden | 10 +- cli/testdata/coder_create_--help.golden | 4 +- cli/testdata/coder_dotfiles_--help.golden | 4 +- cli/testdata/coder_port-forward_--help.golden | 24 +-- ...coder_schedule_override-stop_--help.golden | 2 +- .../coder_schedule_start_--help.golden | 4 +- .../coder_schedule_stop_--help.golden | 2 +- cli/testdata/coder_templates_--help.golden | 10 +- .../coder_templates_versions_--help.golden | 4 +- cli/testdata/coder_tokens_--help.golden | 12 +- .../coder_users_activate_--help.golden | 2 +- cli/testdata/coder_users_show_--help.golden | 2 +- .../coder_users_suspend_--help.golden | 2 +- cli/usercreate.go | 12 +- cli/userstatus.go | 4 +- cli/version.go | 6 +- cli/version_test.go | 13 +- cmd/cliui/main.go | 14 +- coderd/devtunnel/tunnel.go | 9 +- docs/cli.md | 4 +- docs/cli/config-ssh.md | 6 +- docs/cli/create.md | 2 +- docs/cli/dotfiles.md | 2 +- docs/cli/port-forward.md | 16 +- docs/cli/schedule_override-stop.md | 2 +- docs/cli/schedule_start.md | 2 +- docs/cli/schedule_stop.md | 2 +- docs/cli/templates.md | 6 +- docs/cli/templates_versions.md | 2 +- docs/cli/tokens.md | 6 +- docs/cli/users_activate.md | 2 +- docs/cli/users_show.md | 2 +- docs/cli/users_suspend.md | 2 +- enterprise/cli/groupcreate.go | 3 +- enterprise/cli/groupcreate_test.go | 4 +- enterprise/cli/groupdelete.go | 3 +- enterprise/cli/groupdelete_test.go | 4 +- enterprise/cli/groupedit.go | 4 +- enterprise/cli/groupedit_test.go | 4 +- enterprise/cli/provisionerdaemons.go | 2 +- enterprise/cli/proxyserver.go | 2 +- enterprise/cli/testdata/coder_--help.golden | 8 +- enterprise/cli/workspaceproxy.go | 10 +- go.mod | 8 +- go.sum | 28 +--- 80 files changed, 488 insertions(+), 328 deletions(-) diff --git a/Makefile b/Makefile index 65196d0a4bd1d..943334c780f5a 100644 --- a/Makefile +++ b/Makefile @@ -559,7 +559,7 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me pnpm run format:write:only ./docs/admin/prometheus.md docs/cli.md: scripts/clidocgen/main.go examples/examples.gen.json $(GO_SRC_FILES) - BASE_PATH="." go run ./scripts/clidocgen + CI=true BASE_PATH="." go run ./scripts/clidocgen pnpm run format:write:only ./docs/cli.md ./docs/cli/*.md ./docs/manifest.json docs/admin/audit-logs.md: scripts/auditdocgen/main.go enterprise/audit/table.go coderd/rbac/object_gen.go diff --git a/cli/clitest/golden.go b/cli/clitest/golden.go index 8abaaeb3d154f..61dbb5e9f991a 100644 --- a/cli/clitest/golden.go +++ b/cli/clitest/golden.go @@ -11,11 +11,11 @@ import ( "strings" "testing" - "github.com/charmbracelet/lipgloss" "github.com/muesli/termenv" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/cli/clibase" + "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/config" "github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/database/dbtestutil" @@ -53,13 +53,9 @@ func DefaultCases() []CommandHelpCase { // //nolint:tparallel,paralleltest func TestCommandHelp(t *testing.T, getRoot func(t *testing.T) *clibase.Cmd, cases []CommandHelpCase) { - ogColorProfile := lipgloss.ColorProfile() // ANSI256 escape codes are far easier for humans to parse in a diff, // but TrueColor is probably more popular with modern terminals. - lipgloss.SetColorProfile(termenv.ANSI) - t.Cleanup(func() { - lipgloss.SetColorProfile(ogColorProfile) - }) + cliui.TestColor(t, termenv.ANSI) rootClient, replacements := prepareTestData(t) root := getRoot(t) diff --git a/cli/cliui/cliui.go b/cli/cliui/cliui.go index b59ccd61298cc..ad8ee442b6ea8 100644 --- a/cli/cliui/cliui.go +++ b/cli/cliui/cliui.go @@ -2,11 +2,13 @@ package cliui import ( "os" + "testing" + "time" - "github.com/charmbracelet/charm/ui/common" - "github.com/charmbracelet/lipgloss" "github.com/muesli/termenv" "golang.org/x/xerrors" + + "github.com/coder/pretty" ) var Canceled = xerrors.New("canceled") @@ -15,55 +17,142 @@ var Canceled = xerrors.New("canceled") var DefaultStyles Styles type Styles struct { - Bold, - Checkmark, Code, - Crossmark, DateTimeStamp, Error, Field, Keyword, - Paragraph, Placeholder, Prompt, FocusedPrompt, Fuchsia, - Logo, Warn, - Wrap lipgloss.Style + Wrap pretty.Style } -func init() { - lipgloss.SetDefaultRenderer( - lipgloss.NewRenderer(os.Stdout, termenv.WithColorCache(true)), - ) +var color = termenv.NewOutput(os.Stdout).ColorProfile() + +// TestColor sets the color profile to the given profile for the duration of the +// test. +// WARN: Must not be used in parallel tests. +func TestColor(t *testing.T, tprofile termenv.Profile) { + old := color + color = tprofile + t.Cleanup(func() { + color = old + }) +} + +var ( + Green = color.Color("#04B575") + Red = color.Color("#ED567A") + Fuchsia = color.Color("#EE6FF8") + Yellow = color.Color("#ECFD65") + Blue = color.Color("#5000ff") +) + +func isTerm() bool { + return color != termenv.Ascii +} + +// Bold returns a formatter that renders text in bold +// if the terminal supports it. +func Bold(s string) string { + if !isTerm() { + return s + } + return pretty.Sprint(pretty.Bold(), s) +} - // All Styles are set after we change the DefaultRenderer so that the ColorCache - // is in effect, mitigating the severe performance issue seen here: - // https://github.com/coder/coder/issues/7884. +// BoldFmt returns a formatter that renders text in bold +// if the terminal supports it. +func BoldFmt() pretty.Formatter { + if !isTerm() { + return pretty.Style{} + } + return pretty.Bold() +} - charmStyles := common.DefaultStyles() +// Timestamp formats a timestamp for display. +func Timestamp(t time.Time) string { + return pretty.Sprint(DefaultStyles.DateTimeStamp, t.Format(time.Stamp)) +} +// Keyword formats a keyword for display. +func Keyword(s string) string { + return pretty.Sprint(DefaultStyles.Keyword, s) +} + +// Placeholder formats a placeholder for display. +func Placeholder(s string) string { + return pretty.Sprint(DefaultStyles.Placeholder, s) +} + +// Wrap prevents the text from overflowing the terminal. +func Wrap(s string) string { + return pretty.Sprint(DefaultStyles.Wrap, s) +} + +// Code formats code for display. +func Code(s string) string { + return pretty.Sprint(DefaultStyles.Code, s) +} + +// Field formats a field for display. +func Field(s string) string { + return pretty.Sprint(DefaultStyles.Field, s) +} + +func ifTerm(fmt pretty.Formatter) pretty.Formatter { + if !isTerm() { + return pretty.Nop + } + return fmt +} + +func init() { + // We do not adapt the color based on whether the terminal is light or dark. + // Doing so would require a round-trip between the program and the terminal + // due to the OSC query and response. DefaultStyles = Styles{ - Bold: lipgloss.NewStyle().Bold(true), - Checkmark: charmStyles.Checkmark, - Code: charmStyles.Code, - Crossmark: charmStyles.Error.Copy().SetString("✘"), - DateTimeStamp: charmStyles.LabelDim, - Error: charmStyles.Error, - Field: charmStyles.Code.Copy().Foreground(lipgloss.AdaptiveColor{Light: "#000000", Dark: "#FFFFFF"}), - Keyword: charmStyles.Keyword, - Paragraph: charmStyles.Paragraph, - Placeholder: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#585858", Dark: "#4d46b3"}), - Prompt: charmStyles.Prompt.Copy().Foreground(lipgloss.AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}), - FocusedPrompt: charmStyles.FocusedPrompt.Copy().Foreground(lipgloss.Color("#651fff")), - Fuchsia: charmStyles.SelectedMenuItem.Copy(), - Logo: charmStyles.Logo.Copy().SetString("Coder"), - Warn: lipgloss.NewStyle().Foreground( - lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"}, - ), - Wrap: lipgloss.NewStyle().Width(80), + Code: pretty.Style{ + ifTerm(pretty.XPad(1, 1)), + pretty.FgColor(Red), + pretty.BgColor(color.Color("#2c2c2c")), + }, + DateTimeStamp: pretty.Style{ + pretty.FgColor(color.Color("#7571F9")), + }, + Error: pretty.Style{ + pretty.FgColor(Red), + }, + Field: pretty.Style{ + pretty.XPad(1, 1), + pretty.FgColor(color.Color("#FFFFFF")), + pretty.BgColor(color.Color("#2b2a2a")), + }, + Keyword: pretty.Style{ + pretty.FgColor(Green), + }, + Placeholder: pretty.Style{ + pretty.FgColor(color.Color("#4d46b3")), + }, + Prompt: pretty.Style{ + pretty.FgColor(color.Color("#5C5C5C")), + pretty.Wrap("> ", ""), + }, + Warn: pretty.Style{ + pretty.FgColor(Yellow), + }, + Wrap: pretty.Style{ + pretty.LineWrap(80), + }, } + + DefaultStyles.FocusedPrompt = append( + DefaultStyles.Prompt, + pretty.FgColor(Blue), + ) } // ValidateNotEmpty is a helper function to disallow empty inputs! diff --git a/cli/cliui/log.go b/cli/cliui/log.go index fcc1d9b317c17..675aed0adc53c 100644 --- a/cli/cliui/log.go +++ b/cli/cliui/log.go @@ -5,12 +5,12 @@ import ( "io" "strings" - "github.com/charmbracelet/lipgloss" + "github.com/coder/pretty" ) // cliMessage provides a human-readable message for CLI errors and messages. type cliMessage struct { - Style lipgloss.Style + Style pretty.Style Header string Prefix string Lines []string @@ -21,13 +21,13 @@ func (m cliMessage) String() string { var str strings.Builder if m.Prefix != "" { - _, _ = str.WriteString(m.Style.Bold(true).Render(m.Prefix)) + _, _ = str.WriteString(Bold(m.Prefix)) } - _, _ = str.WriteString(m.Style.Bold(false).Render(m.Header)) + pretty.Fprint(&str, m.Style, m.Header) _, _ = str.WriteString("\r\n") for _, line := range m.Lines { - _, _ = fmt.Fprintf(&str, " %s %s\r\n", m.Style.Render("|"), line) + _, _ = fmt.Fprintf(&str, " %s %s\r\n", pretty.Sprint(m.Style, "|"), line) } return str.String() } @@ -35,7 +35,7 @@ func (m cliMessage) String() string { // Warn writes a log to the writer provided. func Warn(wtr io.Writer, header string, lines ...string) { _, _ = fmt.Fprint(wtr, cliMessage{ - Style: DefaultStyles.Warn.Copy(), + Style: DefaultStyles.Warn, Prefix: "WARN: ", Header: header, Lines: lines, @@ -63,7 +63,7 @@ func Infof(wtr io.Writer, fmtStr string, args ...interface{}) { // Error writes a log to the writer provided. func Error(wtr io.Writer, header string, lines ...string) { _, _ = fmt.Fprint(wtr, cliMessage{ - Style: DefaultStyles.Error.Copy(), + Style: DefaultStyles.Error, Prefix: "ERROR: ", Header: header, Lines: lines, diff --git a/cli/cliui/parameter.go b/cli/cliui/parameter.go index a4c8d8e817d59..3482e285e002d 100644 --- a/cli/cliui/parameter.go +++ b/cli/cliui/parameter.go @@ -7,6 +7,7 @@ import ( "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/codersdk" + "github.com/coder/pretty" ) func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.TemplateVersionParameter) (string, error) { @@ -16,10 +17,10 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te } if templateVersionParameter.Ephemeral { - label += DefaultStyles.Warn.Render(" (build option)") + label += pretty.Sprint(DefaultStyles.Warn, " (build option)") } - _, _ = fmt.Fprintln(inv.Stdout, DefaultStyles.Bold.Render(label)) + _, _ = fmt.Fprintln(inv.Stdout, Bold(label)) if templateVersionParameter.DescriptionPlaintext != "" { _, _ = fmt.Fprintln(inv.Stdout, " "+strings.TrimSpace(strings.Join(strings.Split(templateVersionParameter.DescriptionPlaintext, "\n"), "\n "))+"\n") @@ -45,7 +46,10 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te } _, _ = fmt.Fprintln(inv.Stdout) - _, _ = fmt.Fprintln(inv.Stdout, " "+DefaultStyles.Prompt.String()+DefaultStyles.Field.Render(strings.Join(values, ", "))) + pretty.Fprintf( + inv.Stdout, + DefaultStyles.Prompt, "%s\n", strings.Join(values, ", "), + ) value = string(v) } } else if len(templateVersionParameter.Options) > 0 { @@ -59,7 +63,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te }) if err == nil { _, _ = fmt.Fprintln(inv.Stdout) - _, _ = fmt.Fprintln(inv.Stdout, " "+DefaultStyles.Prompt.String()+DefaultStyles.Field.Render(richParameterOption.Name)) + pretty.Fprintf(inv.Stdout, DefaultStyles.Prompt, "%s\n", richParameterOption.Name) value = richParameterOption.Value } } else { @@ -70,7 +74,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te text += ":" value, err = Prompt(inv, PromptOptions{ - Text: DefaultStyles.Bold.Render(text), + Text: Bold(text), Validate: func(value string) error { return validateRichPrompt(value, templateVersionParameter) }, diff --git a/cli/cliui/prompt.go b/cli/cliui/prompt.go index ef859814f6299..4d7cb6d4166df 100644 --- a/cli/cliui/prompt.go +++ b/cli/cliui/prompt.go @@ -14,6 +14,7 @@ import ( "golang.org/x/xerrors" "github.com/coder/coder/v2/cli/clibase" + "github.com/coder/pretty" ) // PromptOptions supply a set of options to the prompt. @@ -55,21 +56,24 @@ func Prompt(inv *clibase.Invocation, opts PromptOptions) (string, error) { } } - _, _ = fmt.Fprint(inv.Stdout, DefaultStyles.FocusedPrompt.String()+opts.Text+" ") + pretty.Fprintf(inv.Stdout, DefaultStyles.FocusedPrompt, "") + pretty.Fprintf(inv.Stdout, pretty.Nop, "%s ", opts.Text) if opts.IsConfirm { if len(opts.Default) == 0 { opts.Default = ConfirmYes } - renderedYes := DefaultStyles.Placeholder.Render(ConfirmYes) - renderedNo := DefaultStyles.Placeholder.Render(ConfirmNo) + var ( + renderedYes = pretty.Sprint(DefaultStyles.Placeholder, ConfirmYes) + renderedNo = pretty.Sprint(DefaultStyles.Placeholder, ConfirmNo) + ) if opts.Default == ConfirmYes { - renderedYes = DefaultStyles.Bold.Render(ConfirmYes) + renderedYes = Bold(ConfirmYes) } else { - renderedNo = DefaultStyles.Bold.Render(ConfirmNo) + renderedNo = Bold(ConfirmNo) } - _, _ = fmt.Fprint(inv.Stdout, DefaultStyles.Placeholder.Render("("+renderedYes+DefaultStyles.Placeholder.Render("/"+renderedNo+DefaultStyles.Placeholder.Render(") ")))) + pretty.Fprintf(inv.Stdout, DefaultStyles.Placeholder, "(%s/%s)", renderedYes, renderedNo) } else if opts.Default != "" { - _, _ = fmt.Fprint(inv.Stdout, DefaultStyles.Placeholder.Render("("+opts.Default+") ")) + _, _ = fmt.Fprint(inv.Stdout, pretty.Sprint(DefaultStyles.Placeholder, "("+opts.Default+") ")) } interrupt := make(chan os.Signal, 1) @@ -126,7 +130,7 @@ func Prompt(inv *clibase.Invocation, opts PromptOptions) (string, error) { if opts.Validate != nil { err := opts.Validate(line) if err != nil { - _, _ = fmt.Fprintln(inv.Stdout, DefaultStyles.Error.Render(err.Error())) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(DefaultStyles.Error, err.Error())) return Prompt(inv, opts) } } diff --git a/cli/cliui/provisionerjob.go b/cli/cliui/provisionerjob.go index b09ac6bc73cad..aeaea7a34cf45 100644 --- a/cli/cliui/provisionerjob.go +++ b/cli/cliui/provisionerjob.go @@ -15,6 +15,7 @@ import ( "golang.org/x/xerrors" "github.com/coder/coder/v2/codersdk" + "github.com/coder/pretty" ) func WorkspaceBuild(ctx context.Context, writer io.Writer, client *codersdk.Client, build uuid.UUID) error { @@ -54,7 +55,7 @@ func (err *ProvisionerJobError) Error() string { } // ProvisionerJob renders a provisioner job with interactive cancellation. -func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOptions) error { +func ProvisionerJob(ctx context.Context, wr io.Writer, opts ProvisionerJobOptions) error { if opts.FetchInterval == 0 { opts.FetchInterval = time.Second } @@ -70,7 +71,7 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp jobMutex sync.Mutex ) - sw := &stageWriter{w: writer, verbose: opts.Verbose, silentLogs: opts.Silent} + sw := &stageWriter{w: wr, verbose: opts.Verbose, silentLogs: opts.Silent} printStage := func() { sw.Start(currentStage) @@ -127,7 +128,11 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp return } } - _, _ = fmt.Fprintf(writer, DefaultStyles.FocusedPrompt.String()+DefaultStyles.Bold.Render("Gracefully canceling...")+"\n\n") + pretty.Fprintf( + wr, + DefaultStyles.FocusedPrompt.With(BoldFmt()), + "Gracefully canceling...\n\n", + ) err := opts.Cancel() if err != nil { errChan <- xerrors.Errorf("cancel: %w", err) @@ -236,7 +241,7 @@ func (s *stageWriter) Log(createdAt time.Time, level codersdk.LogLevel, line str w = &s.logBuf } - render := func(s ...string) string { return strings.Join(s, " ") } + var style pretty.Style var lines []string if !createdAt.IsZero() { @@ -249,14 +254,14 @@ func (s *stageWriter) Log(createdAt time.Time, level codersdk.LogLevel, line str if !s.verbose { return } - render = DefaultStyles.Placeholder.Render + style = DefaultStyles.Placeholder case codersdk.LogLevelError: - render = DefaultStyles.Error.Render + style = DefaultStyles.Error case codersdk.LogLevelWarn: - render = DefaultStyles.Warn.Render + style = DefaultStyles.Warn case codersdk.LogLevelInfo: } - _, _ = fmt.Fprintf(w, "%s\n", render(lines...)) + pretty.Fprintf(w, style, "%s\n", strings.Join(lines, " ")) } func (s *stageWriter) flushLogs() { diff --git a/cli/cliui/resources.go b/cli/cliui/resources.go index a4eb805d8817e..a9204c968c10a 100644 --- a/cli/cliui/resources.go +++ b/cli/cliui/resources.go @@ -11,6 +11,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/codersdk" + "github.com/coder/pretty" ) type WorkspaceResourcesOptions struct { @@ -78,7 +79,7 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource // Display a line for the resource. tableWriter.AppendRow(table.Row{ - DefaultStyles.Bold.Render(resourceAddress), + Bold(resourceAddress), "", "", "", @@ -107,7 +108,7 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource if totalAgents > 1 { sshCommand += "." + agent.Name } - sshCommand = DefaultStyles.Code.Render(sshCommand) + sshCommand = pretty.Sprint(DefaultStyles.Code, sshCommand) row = append(row, sshCommand) } tableWriter.AppendRow(row) @@ -122,31 +123,31 @@ func renderAgentStatus(agent codersdk.WorkspaceAgent) string { switch agent.Status { case codersdk.WorkspaceAgentConnecting: since := dbtime.Now().Sub(agent.CreatedAt) - return DefaultStyles.Warn.Render("⦾ connecting") + " " + - DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]") + return pretty.Sprint(DefaultStyles.Warn, "⦾ connecting") + " " + + pretty.Sprint(DefaultStyles.Placeholder, "["+strconv.Itoa(int(since.Seconds()))+"s]") case codersdk.WorkspaceAgentDisconnected: since := dbtime.Now().Sub(*agent.DisconnectedAt) - return DefaultStyles.Error.Render("⦾ disconnected") + " " + - DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]") + return pretty.Sprint(DefaultStyles.Error, "⦾ disconnected") + " " + + pretty.Sprint(DefaultStyles.Placeholder, "["+strconv.Itoa(int(since.Seconds()))+"s]") case codersdk.WorkspaceAgentTimeout: since := dbtime.Now().Sub(agent.CreatedAt) return fmt.Sprintf( "%s %s", - DefaultStyles.Warn.Render("⦾ timeout"), - DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]"), + pretty.Sprint(DefaultStyles.Warn, "⦾ timeout"), + pretty.Sprint(DefaultStyles.Placeholder, "["+strconv.Itoa(int(since.Seconds()))+"s]"), ) case codersdk.WorkspaceAgentConnected: - return DefaultStyles.Keyword.Render("⦿ connected") + return pretty.Sprint(DefaultStyles.Keyword, "⦿ connected") default: - return DefaultStyles.Warn.Render("○ unknown") + return pretty.Sprint(DefaultStyles.Warn, "○ unknown") } } func renderAgentHealth(agent codersdk.WorkspaceAgent) string { if agent.Health.Healthy { - return DefaultStyles.Keyword.Render("✔ healthy") + return pretty.Sprint(DefaultStyles.Keyword, "✔ healthy") } - return DefaultStyles.Error.Render("✘ " + agent.Health.Reason) + return pretty.Sprint(DefaultStyles.Error, "✘ "+agent.Health.Reason) } func renderAgentVersion(agentVersion, serverVersion string) string { @@ -154,11 +155,11 @@ func renderAgentVersion(agentVersion, serverVersion string) string { agentVersion = "(unknown)" } if !semver.IsValid(serverVersion) || !semver.IsValid(agentVersion) { - return DefaultStyles.Placeholder.Render(agentVersion) + return pretty.Sprint(DefaultStyles.Placeholder, agentVersion) } outdated := semver.Compare(agentVersion, serverVersion) < 0 if outdated { - return DefaultStyles.Warn.Render(agentVersion + " (outdated)") + return pretty.Sprint(DefaultStyles.Warn, agentVersion+" (outdated)") } - return DefaultStyles.Keyword.Render(agentVersion) + return pretty.Sprint(DefaultStyles.Keyword, agentVersion) } diff --git a/cli/create.go b/cli/create.go index 971fbc27aac36..d66e89fc48786 100644 --- a/cli/create.go +++ b/cli/create.go @@ -10,6 +10,8 @@ import ( "golang.org/x/exp/slices" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/util/ptr" @@ -75,7 +77,7 @@ func (r *RootCmd) create() *clibase.Cmd { var template codersdk.Template if templateName == "" { - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Wrap.Render("Select a template below to preview the provisioned infrastructure:")) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Wrap, "Select a template below to preview the provisioned infrastructure:")) templates, err := client.TemplatesByOrganization(inv.Context(), organization.ID) if err != nil { @@ -93,7 +95,7 @@ func (r *RootCmd) create() *clibase.Cmd { templateName := template.Name if template.ActiveUserCount > 0 { - templateName += cliui.DefaultStyles.Placeholder.Render( + templateName += cliui.Placeholder( fmt.Sprintf( " (used by %s)", formatActiveDevelopers(template.ActiveUserCount), @@ -177,7 +179,12 @@ func (r *RootCmd) create() *clibase.Cmd { return xerrors.Errorf("watch build: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "\nThe %s workspace has been created at %s!\n", cliui.DefaultStyles.Keyword.Render(workspace.Name), cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf( + inv.Stdout, + "\nThe %s workspace has been created at %s!\n", + cliui.Keyword(workspace.Name), + cliui.Timestamp(time.Now()), + ) return nil }, } diff --git a/cli/delete.go b/cli/delete.go index 760c0c4e77dd0..a29a821490d9f 100644 --- a/cli/delete.go +++ b/cli/delete.go @@ -54,7 +54,11 @@ func (r *RootCmd) deleteWorkspace() *clibase.Cmd { return err } - _, _ = fmt.Fprintf(inv.Stdout, "\n%s has been deleted at %s!\n", cliui.DefaultStyles.Keyword.Render(workspace.FullName()), cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf( + inv.Stdout, + "\n%s has been deleted at %s!\n", cliui.Keyword(workspace.FullName()), + cliui.Timestamp(time.Now()), + ) return nil }, } diff --git a/cli/dotfiles.go b/cli/dotfiles.go index 88d57c7089eaf..cf3b1391d5e9a 100644 --- a/cli/dotfiles.go +++ b/cli/dotfiles.go @@ -13,6 +13,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" ) @@ -143,7 +145,7 @@ func (r *RootCmd) dotfiles() *clibase.Cmd { return err } // if the repo exists we soft fail the update operation and try to continue - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Error.Render("Failed to update repo, continuing...")) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Error, "Failed to update repo, continuing...")) } if dotfilesExists && gitbranch != "" { @@ -159,7 +161,7 @@ func (r *RootCmd) dotfiles() *clibase.Cmd { if err != nil { // Do not block on this error, just log it and continue _, _ = fmt.Fprintln(inv.Stdout, - cliui.DefaultStyles.Error.Render(fmt.Sprintf("Failed to use branch %q (%s), continuing...", err.Error(), gitbranch))) + pretty.Sprint(cliui.DefaultStyles.Error, fmt.Sprintf("Failed to use branch %q (%s), continuing...", err.Error(), gitbranch))) } } diff --git a/cli/gitssh.go b/cli/gitssh.go index de5482a8ae387..ea461394c3241 100644 --- a/cli/gitssh.go +++ b/cli/gitssh.go @@ -16,6 +16,7 @@ import ( "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" + "github.com/coder/pretty" ) func (r *RootCmd) gitssh() *clibase.Cmd { @@ -90,12 +91,15 @@ func (r *RootCmd) gitssh() *clibase.Cmd { exitErr := &exec.ExitError{} if xerrors.As(err, &exitErr) && exitErr.ExitCode() == 255 { _, _ = fmt.Fprintln(inv.Stderr, - "\n"+cliui.DefaultStyles.Wrap.Render("Coder authenticates with "+cliui.DefaultStyles.Field.Render("git")+ - " using the public key below. All clones with SSH are authenticated automatically 🪄.")+"\n") - _, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Code.Render(strings.TrimSpace(key.PublicKey))+"\n") + "\n"+pretty.Sprintf( + cliui.DefaultStyles.Wrap, + "Coder authenticates with "+pretty.Sprint(cliui.DefaultStyles.Field, "git")+ + " using the public key below. All clones with SSH are authenticated automatically 🪄.")+"\n", + ) + _, _ = fmt.Fprintln(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Code, strings.TrimSpace(key.PublicKey))+"\n") _, _ = fmt.Fprintln(inv.Stderr, "Add to GitHub and GitLab:") - _, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Prompt.String()+"https://github.com/settings/ssh/new") - _, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Prompt.String()+"https://gitlab.com/-/profile/keys") + pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "%s", "https://github.com/settings/ssh/new\n\n") + pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "%s", "https://gitlab.com/-/profile/keys\n\n") _, _ = fmt.Fprintln(inv.Stderr) return err } diff --git a/cli/help.go b/cli/help.go index 3741dbfc28119..cd19ecd78844b 100644 --- a/cli/help.go +++ b/cli/help.go @@ -127,7 +127,7 @@ var usageTemplate = template.Must( return opt.Flag }, "prettyHeader": func(s string) string { - return cliui.DefaultStyles.Bold.Render(s) + return cliui.Bold(s) }, "isEnterprise": func(opt clibase.Option) bool { return opt.Annotations.IsSet("enterprise") diff --git a/cli/list.go b/cli/list.go index e6a1f6fee6d7f..b82d6f31579bf 100644 --- a/cli/list.go +++ b/cli/list.go @@ -7,6 +7,8 @@ import ( "github.com/google/uuid" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/schedule/cron" @@ -119,9 +121,9 @@ func (r *RootCmd) list() *clibase.Cmd { return err } if len(res.Workspaces) == 0 { - _, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Prompt.String()+"No workspaces found! Create one:") + pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Prompt, "No workspaces found! Create one:\n") _, _ = fmt.Fprintln(inv.Stderr) - _, _ = fmt.Fprintln(inv.Stderr, " "+cliui.DefaultStyles.Code.Render("coder create ")) + _, _ = fmt.Fprintln(inv.Stderr, " "+pretty.Sprint(cliui.DefaultStyles.Code, "coder create ")) _, _ = fmt.Fprintln(inv.Stderr) return nil } diff --git a/cli/login.go b/cli/login.go index a68603763151e..73a18a1c40009 100644 --- a/cli/login.go +++ b/cli/login.go @@ -16,6 +16,8 @@ import ( "github.com/pkg/browser" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/userpassword" @@ -43,7 +45,7 @@ func promptFirstUsername(inv *clibase.Invocation) (string, error) { return "", xerrors.Errorf("get current user: %w", err) } username, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "What " + cliui.DefaultStyles.Field.Render("username") + " would you like?", + Text: "What " + pretty.Sprint(cliui.DefaultStyles.Field, "username") + " would you like?", Default: currentUser.Username, }) if errors.Is(err, cliui.Canceled) { @@ -59,7 +61,7 @@ func promptFirstUsername(inv *clibase.Invocation) (string, error) { func promptFirstPassword(inv *clibase.Invocation) (string, error) { retry: password, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Enter a " + cliui.DefaultStyles.Field.Render("password") + ":", + Text: "Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", Secret: true, Validate: func(s string) error { return userpassword.Validate(s) @@ -69,7 +71,7 @@ retry: return "", xerrors.Errorf("specify password prompt: %w", err) } confirm, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Confirm " + cliui.DefaultStyles.Field.Render("password") + ":", + Text: "Confirm " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", Secret: true, Validate: cliui.ValidateNotEmpty, }) @@ -78,7 +80,7 @@ retry: } if confirm != password { - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Error.Render("Passwords do not match")) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Error, "Passwords do not match")) goto retry } @@ -115,12 +117,8 @@ func (r *RootCmd) loginWithPassword( _, _ = fmt.Fprintf( inv.Stdout, - cliui.DefaultStyles.Paragraph.Render( - fmt.Sprintf( - "Welcome to Coder, %s! You're authenticated.", - cliui.DefaultStyles.Keyword.Render(u.Username), - ), - )+"\n", + "Welcome to Coder, %s! You're authenticated.", + pretty.Sprint(cliui.DefaultStyles.Keyword, u.Username), ) return nil @@ -177,7 +175,7 @@ func (r *RootCmd) login() *clibase.Cmd { if err != nil { // Checking versions isn't a fatal error so we print a warning // and proceed. - _, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Warn.Render(err.Error())) + _, _ = fmt.Fprintln(inv.Stderr, pretty.Sprint(cliui.DefaultStyles.Warn, err.Error())) } hasFirstUser, err := client.HasFirstUser(ctx) @@ -209,7 +207,7 @@ func (r *RootCmd) login() *clibase.Cmd { if email == "" { email, err = cliui.Prompt(inv, cliui.PromptOptions{ - Text: "What's your " + cliui.DefaultStyles.Field.Render("email") + "?", + Text: "What's your " + pretty.Sprint(cliui.DefaultStyles.Field, "email") + "?", Validate: func(s string) error { err := validator.New().Var(s, "email") if err != nil { @@ -261,7 +259,9 @@ func (r *RootCmd) login() *clibase.Cmd { _, _ = fmt.Fprintf( inv.Stdout, - cliui.DefaultStyles.Paragraph.Render("Get started by creating a template: "+cliui.DefaultStyles.Code.Render("coder templates init"))+"\n") + "Get started by creating a template: %s\n", + pretty.Sprint(cliui.DefaultStyles.Code, "coder templates init"), + ) return nil } @@ -327,7 +327,7 @@ func (r *RootCmd) login() *clibase.Cmd { return xerrors.Errorf("write server url: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, Caret+"Welcome to Coder, %s! You're authenticated.\n", cliui.DefaultStyles.Keyword.Render(resp.Username)) + _, _ = fmt.Fprintf(inv.Stdout, Caret+"Welcome to Coder, %s! You're authenticated.\n", pretty.Sprint(cliui.DefaultStyles.Keyword, resp.Username)) return nil }, } diff --git a/cli/login_test.go b/cli/login_test.go index 910f58ea1f2c7..89d4cd87b37d9 100644 --- a/cli/login_test.go +++ b/cli/login_test.go @@ -8,6 +8,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/coderdtest" @@ -141,7 +143,7 @@ func TestLogin(t *testing.T) { // Validate that we reprompt for matching passwords. pty.ExpectMatch("Passwords do not match") - pty.ExpectMatch("Enter a " + cliui.DefaultStyles.Field.Render("password")) + pty.ExpectMatch("Enter a " + pretty.Sprint(cliui.DefaultStyles.Field, "password")) pty.WriteLine("SomeSecurePassword!") pty.ExpectMatch("Confirm") diff --git a/cli/parameterresolver.go b/cli/parameterresolver.go index 486188d52a27a..97cf622b75c33 100644 --- a/cli/parameterresolver.go +++ b/cli/parameterresolver.go @@ -5,6 +5,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -203,7 +205,7 @@ func (pr *ParameterResolver) resolveWithInput(resolved []codersdk.WorkspaceBuild Value: parameterValue, }) } else if action == WorkspaceUpdate && !tvp.Mutable && !firstTimeUse { - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Warn.Render(fmt.Sprintf("Parameter %q is not mutable, and cannot be customized after workspace creation.", tvp.Name))) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Warn, fmt.Sprintf("Parameter %q is not mutable, and cannot be customized after workspace creation.", tvp.Name))) } } return resolved, nil diff --git a/cli/ping.go b/cli/ping.go index a3075a85ad3e4..2df0d57446780 100644 --- a/cli/ping.go +++ b/cli/ping.go @@ -10,6 +10,8 @@ import ( "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -104,14 +106,14 @@ func (r *RootCmd) ping() *clibase.Cmd { if p2p { if !didP2p { _, _ = fmt.Fprintln(inv.Stdout, "p2p connection established in", - cliui.DefaultStyles.DateTimeStamp.Render(time.Since(start).Round(time.Millisecond).String()), + pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, time.Since(start).Round(time.Millisecond).String()), ) } didP2p = true via = fmt.Sprintf("%s via %s", - cliui.DefaultStyles.Fuchsia.Render("p2p"), - cliui.DefaultStyles.Code.Render(pong.Endpoint), + pretty.Sprint(cliui.DefaultStyles.Fuchsia, "p2p"), + pretty.Sprint(cliui.DefaultStyles.Code, pong.Endpoint), ) } else { derpName := "unknown" @@ -120,15 +122,15 @@ func (r *RootCmd) ping() *clibase.Cmd { derpName = derpRegion.RegionName } via = fmt.Sprintf("%s via %s", - cliui.DefaultStyles.Fuchsia.Render("proxied"), - cliui.DefaultStyles.Code.Render(fmt.Sprintf("DERP(%s)", derpName)), + pretty.Sprint(cliui.DefaultStyles.Fuchsia, "proxied"), + pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("DERP(%s)", derpName)), ) } _, _ = fmt.Fprintf(inv.Stdout, "pong from %s %s in %s\n", - cliui.DefaultStyles.Keyword.Render(workspaceName), + pretty.Sprint(cliui.DefaultStyles.Keyword, workspaceName), via, - cliui.DefaultStyles.DateTimeStamp.Render(dur.String()), + pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, dur.String()), ) if n == int(pingNum) { diff --git a/cli/publickey.go b/cli/publickey.go index c41c5e2fd4d46..f6e145377e407 100644 --- a/cli/publickey.go +++ b/cli/publickey.go @@ -5,6 +5,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -44,13 +46,13 @@ func (r *RootCmd) publickey() *clibase.Cmd { } cliui.Infof(inv.Stdout, - "This is your public key for using "+cliui.DefaultStyles.Field.Render("git")+" in "+ + "This is your public key for using "+pretty.Sprint(cliui.DefaultStyles.Field, "git")+" in "+ "Coder. All clones with SSH will be authenticated automatically 🪄.", ) - cliui.Infof(inv.Stdout, cliui.DefaultStyles.Code.Render(strings.TrimSpace(key.PublicKey))+"\n") + cliui.Infof(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Code, strings.TrimSpace(key.PublicKey))+"\n") cliui.Infof(inv.Stdout, "Add to GitHub and GitLab:") - cliui.Infof(inv.Stdout, cliui.DefaultStyles.Prompt.String()+"https://github.com/settings/ssh/new") - cliui.Infof(inv.Stdout, cliui.DefaultStyles.Prompt.String()+"https://gitlab.com/-/profile/keys") + cliui.Infof(inv.Stdout, "> https://github.com/settings/ssh/new") + cliui.Infof(inv.Stdout, "> https://gitlab.com/-/profile/keys") return nil }, diff --git a/cli/rename.go b/cli/rename.go index 94d9fc5517278..24a201ab7d3d0 100644 --- a/cli/rename.go +++ b/cli/rename.go @@ -5,6 +5,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -27,7 +29,7 @@ func (r *RootCmd) rename() *clibase.Cmd { } _, _ = fmt.Fprintf(inv.Stdout, "%s\n\n", - cliui.DefaultStyles.Wrap.Render("WARNING: A rename can result in data loss if a resource references the workspace name in the template (e.g volumes). Please backup any data before proceeding."), + pretty.Sprint(cliui.DefaultStyles.Wrap, "WARNING: A rename can result in data loss if a resource references the workspace name in the template (e.g volumes). Please backup any data before proceeding."), ) _, _ = fmt.Fprintf(inv.Stdout, "See: %s\n\n", "https://coder.com/docs/coder-oss/latest/templates/resource-persistence#%EF%B8%8F-persistence-pitfalls") _, err = cliui.Prompt(inv, cliui.PromptOptions{ diff --git a/cli/resetpassword.go b/cli/resetpassword.go index aee1798a51db5..887aa9575a45e 100644 --- a/cli/resetpassword.go +++ b/cli/resetpassword.go @@ -8,6 +8,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/database" @@ -49,7 +51,7 @@ func (*RootCmd) resetPassword() *clibase.Cmd { } password, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Enter new " + cliui.DefaultStyles.Field.Render("password") + ":", + Text: "Enter new " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", Secret: true, Validate: func(s string) error { return userpassword.Validate(s) @@ -59,7 +61,7 @@ func (*RootCmd) resetPassword() *clibase.Cmd { return xerrors.Errorf("password prompt: %w", err) } confirmedPassword, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "Confirm " + cliui.DefaultStyles.Field.Render("password") + ":", + Text: "Confirm " + pretty.Sprint(cliui.DefaultStyles.Field, "password") + ":", Secret: true, Validate: cliui.ValidateNotEmpty, }) @@ -83,7 +85,7 @@ func (*RootCmd) resetPassword() *clibase.Cmd { return xerrors.Errorf("updating password: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "\nPassword has been reset for user %s!\n", cliui.DefaultStyles.Keyword.Render(user.Username)) + _, _ = fmt.Fprintf(inv.Stdout, "\nPassword has been reset for user %s!\n", pretty.Sprint(cliui.DefaultStyles.Keyword, user.Username)) return nil }, } diff --git a/cli/restart.go b/cli/restart.go index 2f5ca1fff7a77..a936c30594878 100644 --- a/cli/restart.go +++ b/cli/restart.go @@ -6,6 +6,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -92,7 +94,10 @@ func (r *RootCmd) restart() *clibase.Cmd { return err } - _, _ = fmt.Fprintf(out, "\nThe %s workspace has been restarted at %s!\n", cliui.DefaultStyles.Keyword.Render(workspace.Name), cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf(out, + "\nThe %s workspace has been restarted at %s!\n", + pretty.Sprint(cliui.DefaultStyles.Keyword, workspace.Name), cliui.Timestamp(time.Now()), + ) return nil }, } diff --git a/cli/root.go b/cli/root.go index 53b00e1f6df04..2da35c2f9832a 100644 --- a/cli/root.go +++ b/cli/root.go @@ -30,6 +30,8 @@ import ( "golang.org/x/exp/slices" "golang.org/x/xerrors" + "github.com/coder/pretty" + "cdr.dev/slog" "github.com/coder/coder/v2/buildinfo" "github.com/coder/coder/v2/cli/clibase" @@ -42,7 +44,7 @@ import ( ) var ( - Caret = cliui.DefaultStyles.Prompt.String() + Caret = pretty.Sprint(cliui.DefaultStyles.Prompt, "") // Applied as annotations to workspace commands // so they display in a separated "help" section. @@ -581,15 +583,13 @@ func (r *RootCmd) initClientInternal(client *codersdk.Client, allowTokenMissing if err = <-versionErr; err != nil { // Just log the error here. We never want to fail a command // due to a pre-run. - _, _ = fmt.Fprintf(inv.Stderr, - cliui.DefaultStyles.Warn.Render("check versions error: %s"), err) + pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Warn, "check versions error: %s", err) _, _ = fmt.Fprintln(inv.Stderr) } if err = <-warningErr; err != nil { // Same as above - _, _ = fmt.Fprintf(inv.Stderr, - cliui.DefaultStyles.Warn.Render("check entitlement warnings error: %s"), err) + pretty.Fprintf(inv.Stderr, cliui.DefaultStyles.Warn, "check entitlement warnings error: %s", err) _, _ = fmt.Fprintln(inv.Stderr) } @@ -753,18 +753,18 @@ type example struct { func formatExamples(examples ...example) string { var sb strings.Builder - padStyle := cliui.DefaultStyles.Wrap.Copy().PaddingLeft(4) + padStyle := cliui.DefaultStyles.Wrap.With(pretty.XPad(4, 0)) for i, e := range examples { if len(e.Description) > 0 { wordwrap.WrapString(e.Description, 80) _, _ = sb.WriteString( - " - " + padStyle.Render(e.Description + ":")[4:] + "\n\n ", + " - " + pretty.Sprint(padStyle, e.Description+":")[4:] + "\n\n ", ) } // We add 1 space here because `cliui.DefaultStyles.Code` adds an extra // space. This makes the code block align at an even 2 or 6 // spaces for symmetry. - _, _ = sb.WriteString(" " + cliui.DefaultStyles.Code.Render(fmt.Sprintf("$ %s", e.Command))) + _, _ = sb.WriteString(" " + pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("$ %s", e.Command))) if i < len(examples)-1 { _, _ = sb.WriteString("\n\n") } @@ -802,8 +802,8 @@ func (r *RootCmd) checkVersions(i *clibase.Invocation, client *codersdk.Client) } if !buildinfo.VersionsMatch(clientVersion, info.Version) { - warn := cliui.DefaultStyles.Warn.Copy().Align(lipgloss.Left) - _, _ = fmt.Fprintf(i.Stderr, warn.Render(fmtWarningText), clientVersion, info.Version, strings.TrimPrefix(info.CanonicalVersion(), "v")) + warn := cliui.DefaultStyles.Warn + _, _ = fmt.Fprintf(i.Stderr, pretty.Sprint(warn, fmtWarningText), clientVersion, info.Version, strings.TrimPrefix(info.CanonicalVersion(), "v")) _, _ = fmt.Fprintln(i.Stderr) } @@ -821,7 +821,7 @@ func (r *RootCmd) checkWarnings(i *clibase.Invocation, client *codersdk.Client) entitlements, err := client.Entitlements(ctx) if err == nil { for _, w := range entitlements.Warnings { - _, _ = fmt.Fprintln(i.Stderr, cliui.DefaultStyles.Warn.Render(w)) + _, _ = fmt.Fprintln(i.Stderr, pretty.Sprint(cliui.DefaultStyles.Warn, w)) } } return nil @@ -1022,7 +1022,7 @@ func (p *prettyErrorFormatter) printf(style lipgloss.Style, format string, a ... //nolint:unused func SlimUnsupported(w io.Writer, cmd string) { - _, _ = fmt.Fprintf(w, "You are using a 'slim' build of Coder, which does not support the %s subcommand.\n", cliui.DefaultStyles.Code.Render(cmd)) + _, _ = fmt.Fprintf(w, "You are using a 'slim' build of Coder, which does not support the %s subcommand.\n", pretty.Sprint(cliui.DefaultStyles.Code, cmd)) _, _ = fmt.Fprintln(w, "") _, _ = fmt.Fprintln(w, "Please use a build of Coder from GitHub releases:") _, _ = fmt.Fprintln(w, " https://github.com/coder/coder/releases") diff --git a/cli/server.go b/cli/server.go index 63fca59ec3fec..9526a944f277b 100644 --- a/cli/server.go +++ b/cli/server.go @@ -52,6 +52,8 @@ import ( "gopkg.in/yaml.v3" "tailscale.com/tailcfg" + "github.com/coder/pretty" + "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" "cdr.dev/slog/sloggers/slogjson" @@ -512,7 +514,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. cliui.Warnf( inv.Stderr, "The access URL %s %s, this may cause unexpected problems when creating workspaces. Generate a unique *.try.coder.app URL by not specifying an access URL.\n", - cliui.DefaultStyles.Field.Render(vals.AccessURL.String()), reason, + pretty.Sprint(cliui.DefaultStyles.Field, vals.AccessURL.String()), reason, ) } @@ -1026,9 +1028,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. select { case <-notifyCtx.Done(): exitErr = notifyCtx.Err() - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Bold.Render( - "Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit", - )) + _, _ = io.WriteString(inv.Stdout, cliui.Bold("Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit")) case <-tunnelDone: exitErr = xerrors.New("dev tunnel closed unexpectedly") case exitErr = <-errCh: @@ -1134,7 +1134,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. if pgRawURL { _, _ = fmt.Fprintf(inv.Stdout, "%s\n", url) } else { - _, _ = fmt.Fprintf(inv.Stdout, "%s\n", cliui.DefaultStyles.Code.Render(fmt.Sprintf("psql %q", url))) + _, _ = fmt.Fprintf(inv.Stdout, "%s\n", pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("psql %q", url))) } return nil }, @@ -1164,7 +1164,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd. if pgRawURL { _, _ = fmt.Fprintf(inv.Stdout, "%s\n", url) } else { - _, _ = fmt.Fprintf(inv.Stdout, "%s\n", cliui.DefaultStyles.Code.Render(fmt.Sprintf("psql %q", url))) + _, _ = fmt.Fprintf(inv.Stdout, "%s\n", pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("psql %q", url))) } <-ctx.Done() @@ -1403,7 +1403,7 @@ func PrintLogo(inv *clibase.Invocation, daemonTitle string) { return } - versionString := cliui.DefaultStyles.Bold.Render(daemonTitle + " " + buildinfo.Version()) + versionString := cliui.Bold(daemonTitle + " " + buildinfo.Version()) _, _ = fmt.Fprintf(inv.Stdout, "%s - Your Self-Hosted Remote Development Platform\n", versionString) } diff --git a/cli/start.go b/cli/start.go index cde5152e14dc2..32f14985c7991 100644 --- a/cli/start.go +++ b/cli/start.go @@ -71,7 +71,10 @@ func (r *RootCmd) start() *clibase.Cmd { return err } - _, _ = fmt.Fprintf(inv.Stdout, "\nThe %s workspace has been started at %s!\n", cliui.DefaultStyles.Keyword.Render(workspace.Name), cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf( + inv.Stdout, "\nThe %s workspace has been started at %s!\n", + cliui.Keyword(workspace.Name), cliui.Timestamp(time.Now()), + ) return nil }, } diff --git a/cli/stop.go b/cli/stop.go index 41265a859f489..ea26e426e6323 100644 --- a/cli/stop.go +++ b/cli/stop.go @@ -47,7 +47,12 @@ func (r *RootCmd) stop() *clibase.Cmd { return err } - _, _ = fmt.Fprintf(inv.Stdout, "\nThe %s workspace has been stopped at %s!\n", cliui.DefaultStyles.Keyword.Render(workspace.Name), cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf( + inv.Stdout, + "\nThe %s workspace has been stopped at %s!\n", cliui.Keyword(workspace.Name), + + cliui.Timestamp(time.Now()), + ) return nil }, } diff --git a/cli/templatecreate.go b/cli/templatecreate.go index 5089260c86245..2ac9cef963b3a 100644 --- a/cli/templatecreate.go +++ b/cli/templatecreate.go @@ -14,6 +14,8 @@ import ( "github.com/google/uuid" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/util/ptr" @@ -144,11 +146,13 @@ func (r *RootCmd) templateCreate() *clibase.Cmd { return err } - _, _ = fmt.Fprintln(inv.Stdout, "\n"+cliui.DefaultStyles.Wrap.Render( - "The "+cliui.DefaultStyles.Keyword.Render(templateName)+" template has been created at "+cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"! "+ + _, _ = fmt.Fprintln(inv.Stdout, "\n"+pretty.Sprint(cliui.DefaultStyles.Wrap, + "The "+pretty.Sprint( + cliui.DefaultStyles.Keyword, templateName)+" template has been created at "+ + pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, time.Now().Format(time.Stamp))+"! "+ "Developers can provision a workspace with this template using:")+"\n") - _, _ = fmt.Fprintln(inv.Stdout, " "+cliui.DefaultStyles.Code.Render(fmt.Sprintf("coder create --template=%q [workspace name]", templateName))) + _, _ = fmt.Fprintln(inv.Stdout, " "+pretty.Sprint(cliui.DefaultStyles.Code, fmt.Sprintf("coder create --template=%q [workspace name]", templateName))) _, _ = fmt.Fprintln(inv.Stdout) return nil @@ -331,12 +335,12 @@ func prettyDirectoryPath(dir string) string { if err != nil { return dir } - pretty := dir - if strings.HasPrefix(pretty, homeDir) { - pretty = strings.TrimPrefix(pretty, homeDir) - pretty = "~" + pretty + prettyDir := dir + if strings.HasPrefix(prettyDir, homeDir) { + prettyDir = strings.TrimPrefix(prettyDir, homeDir) + prettyDir = "~" + prettyDir } - return pretty + return prettyDir } func ParseProvisionerTags(rawTags []string) (map[string]string, error) { diff --git a/cli/templatedelete.go b/cli/templatedelete.go index 9380279d6af96..6cb4213a93895 100644 --- a/cli/templatedelete.go +++ b/cli/templatedelete.go @@ -7,6 +7,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -77,7 +79,7 @@ func (r *RootCmd) templateDelete() *clibase.Cmd { // Confirm deletion of the template. _, err = cliui.Prompt(inv, cliui.PromptOptions{ - Text: fmt.Sprintf("Delete these templates: %s?", cliui.DefaultStyles.Code.Render(strings.Join(templateNames, ", "))), + Text: fmt.Sprintf("Delete these templates: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(templateNames, ", "))), IsConfirm: true, Default: cliui.ConfirmNo, }) @@ -91,7 +93,9 @@ func (r *RootCmd) templateDelete() *clibase.Cmd { return xerrors.Errorf("delete template %q: %w", template.Name, err) } - _, _ = fmt.Fprintln(inv.Stdout, "Deleted template "+cliui.DefaultStyles.Code.Render(template.Name)+" at "+cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"!") + _, _ = fmt.Fprintln( + inv.Stdout, "Deleted template "+pretty.Sprint(cliui.DefaultStyles.Keyword, template.Name)+" at "+cliui.Timestamp(time.Now()), + ) } return nil diff --git a/cli/templatedelete_test.go b/cli/templatedelete_test.go index 963ece08ab4dc..1e7ecfc61cdc0 100644 --- a/cli/templatedelete_test.go +++ b/cli/templatedelete_test.go @@ -8,6 +8,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/coderdtest" @@ -37,7 +39,7 @@ func TestTemplateDelete(t *testing.T) { execDone <- inv.Run() }() - pty.ExpectMatch(fmt.Sprintf("Delete these templates: %s?", cliui.DefaultStyles.Code.Render(template.Name))) + pty.ExpectMatch(fmt.Sprintf("Delete these templates: %s?", pretty.Sprint(cliui.DefaultStyles.Code, template.Name))) pty.WriteLine("yes") require.NoError(t, <-execDone) @@ -95,7 +97,7 @@ func TestTemplateDelete(t *testing.T) { execDone <- inv.Run() }() - pty.ExpectMatch(fmt.Sprintf("Delete these templates: %s?", cliui.DefaultStyles.Code.Render(strings.Join(templateNames, ", ")))) + pty.ExpectMatch(fmt.Sprintf("Delete these templates: %s?", pretty.Sprint(cliui.DefaultStyles.Code, strings.Join(templateNames, ", ")))) pty.WriteLine("yes") require.NoError(t, <-execDone) diff --git a/cli/templateedit.go b/cli/templateedit.go index d44702b66485e..f64d2c5fc4b50 100644 --- a/cli/templateedit.go +++ b/cli/templateedit.go @@ -8,6 +8,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -114,7 +116,7 @@ func (r *RootCmd) templateEdit() *clibase.Cmd { if err != nil { return xerrors.Errorf("update template metadata: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "Updated template metadata at %s!\n", cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf(inv.Stdout, "Updated template metadata at %s!\n", pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, time.Now().Format(time.Stamp))) return nil }, } diff --git a/cli/templateinit.go b/cli/templateinit.go index 47addbf05e347..a9577733bc0fb 100644 --- a/cli/templateinit.go +++ b/cli/templateinit.go @@ -17,6 +17,7 @@ import ( "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/examples" "github.com/coder/coder/v2/provisionersdk" + "github.com/coder/pretty" ) func (*RootCmd) templateInit() *clibase.Cmd { @@ -42,17 +43,21 @@ func (*RootCmd) templateInit() *clibase.Cmd { for _, example := range exampleList { name := fmt.Sprintf( "%s\n%s\n%s\n", - cliui.DefaultStyles.Bold.Render(example.Name), - cliui.DefaultStyles.Wrap.Copy().PaddingLeft(6).Render(example.Description), - cliui.DefaultStyles.Keyword.Copy().PaddingLeft(6).Render(example.URL), + cliui.Bold(example.Name), + pretty.Sprint(cliui.DefaultStyles.Wrap.With(pretty.XPad(6, 0)), example.Description), + pretty.Sprint(cliui.DefaultStyles.Keyword.With(pretty.XPad(6, 0)), example.URL), ) optsToID[name] = example.ID } opts := maps.Keys(optsToID) sort.Strings(opts) - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Wrap.Render( - "A template defines infrastructure as code to be provisioned "+ - "for individual developer workspaces. Select an example to be copied to the active directory:\n")) + _, _ = fmt.Fprintln( + inv.Stdout, + pretty.Sprint( + cliui.DefaultStyles.Wrap, + "A template defines infrastructure as code to be provisioned "+ + "for individual developer workspaces. Select an example to be copied to the active directory:\n"), + ) selected, err := cliui.Select(inv, cliui.SelectOptions{ Options: opts, }) @@ -94,7 +99,7 @@ func (*RootCmd) templateInit() *clibase.Cmd { } else { relPath = "./" + relPath } - _, _ = fmt.Fprintf(inv.Stdout, "Extracting %s to %s...\n", cliui.DefaultStyles.Field.Render(selectedTemplate.ID), relPath) + _, _ = fmt.Fprintf(inv.Stdout, "Extracting %s to %s...\n", pretty.Sprint(cliui.DefaultStyles.Field, selectedTemplate.ID), relPath) err = os.MkdirAll(directory, 0o700) if err != nil { return err @@ -104,8 +109,13 @@ func (*RootCmd) templateInit() *clibase.Cmd { return err } _, _ = fmt.Fprintln(inv.Stdout, "Create your template by running:") - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Paragraph.Render(cliui.DefaultStyles.Code.Render("cd "+relPath+" && coder templates create"))+"\n") - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Wrap.Render("Examples provide a starting point and are expected to be edited! 🎨")) + _, _ = fmt.Fprintln( + inv.Stdout, + pretty.Sprint( + cliui.DefaultStyles.Code, + "cd "+relPath+" && coder templates create"), + ) + _, _ = fmt.Fprintln(inv.Stdout, pretty.Sprint(cliui.DefaultStyles.Wrap, "\nExamples provide a starting point and are expected to be edited! 🎨")) return nil }, } diff --git a/cli/templatepush.go b/cli/templatepush.go index 66d61375af99b..ad4403324dfc4 100644 --- a/cli/templatepush.go +++ b/cli/templatepush.go @@ -11,6 +11,8 @@ import ( "github.com/briandowns/spinner" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -85,7 +87,7 @@ func (pf *templateUploadFlags) upload(inv *clibase.Invocation, client *codersdk. spin := spinner.New(spinner.CharSets[5], 100*time.Millisecond) spin.Writer = inv.Stdout - spin.Suffix = cliui.DefaultStyles.Keyword.Render(" Uploading directory...") + spin.Suffix = pretty.Sprint(cliui.DefaultStyles.Keyword, " Uploading directory...") spin.Start() defer spin.Stop() @@ -110,7 +112,7 @@ func (pf *templateUploadFlags) checkForLockfile(inv *clibase.Invocation) error { if !hasLockfile { cliui.Warn(inv.Stdout, "No .terraform.lock.hcl file found", "When provisioning, Coder will be unable to cache providers without a lockfile and must download them from the internet each time.", - "Create one by running "+cliui.DefaultStyles.Code.Render("terraform init")+" in your template directory.", + "Create one by running "+pretty.Sprint(cliui.DefaultStyles.Code, "terraform init")+" in your template directory.", ) } return nil @@ -246,9 +248,10 @@ func (r *RootCmd) templatePush() *clibase.Cmd { return err } - _, _ = fmt.Fprintln(inv.Stdout, "\n"+cliui.DefaultStyles.Wrap.Render( - "The "+cliui.DefaultStyles.Keyword.Render(name)+" template has been created at "+cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"! "+ - "Developers can provision a workspace with this template using:")+"\n") + _, _ = fmt.Fprintln( + inv.Stdout, "\n"+cliui.Wrap( + "The "+cliui.Keyword(name)+" template has been created at "+cliui.Timestamp(time.Now())+"! "+ + "Developers can provision a workspace with this template using:")+"\n") } else if activate { err = client.UpdateActiveTemplateVersion(inv.Context(), template.ID, codersdk.UpdateActiveTemplateVersion{ ID: job.ID, @@ -258,7 +261,7 @@ func (r *RootCmd) templatePush() *clibase.Cmd { } } - _, _ = fmt.Fprintf(inv.Stdout, "Updated version at %s!\n", cliui.DefaultStyles.DateTimeStamp.Render(time.Now().Format(time.Stamp))) + _, _ = fmt.Fprintf(inv.Stdout, "Updated version at %s!\n", pretty.Sprint(cliui.DefaultStyles.DateTimeStamp, time.Now().Format(time.Stamp))) return nil }, } diff --git a/cli/templates.go b/cli/templates.go index 7ded6a7e5ee2b..3d24ec14b5ccc 100644 --- a/cli/templates.go +++ b/cli/templates.go @@ -5,6 +5,8 @@ import ( "github.com/google/uuid" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -75,7 +77,7 @@ func templatesToRows(templates ...codersdk.Template) []templateTableRow { OrganizationID: template.OrganizationID, Provisioner: template.Provisioner, ActiveVersionID: template.ActiveVersionID, - UsedBy: cliui.DefaultStyles.Fuchsia.Render(formatActiveDevelopers(template.ActiveUserCount)), + UsedBy: pretty.Sprint(cliui.DefaultStyles.Fuchsia, formatActiveDevelopers(template.ActiveUserCount)), DefaultTTL: (time.Duration(template.DefaultTTLMillis) * time.Millisecond), } } diff --git a/cli/templateversions.go b/cli/templateversions.go index 622854c4afedc..299ae98e96b23 100644 --- a/cli/templateversions.go +++ b/cli/templateversions.go @@ -101,7 +101,7 @@ func templateVersionsToRows(activeVersionID uuid.UUID, templateVersions ...coder for i, templateVersion := range templateVersions { activeStatus := "" if templateVersion.ID == activeVersionID { - activeStatus = cliui.DefaultStyles.Code.Render(cliui.DefaultStyles.Keyword.Render("Active")) + activeStatus = cliui.Keyword("Active") } rows[i] = templateVersionRow{ diff --git a/cli/testdata/coder_--help.golden b/cli/testdata/coder_--help.golden index 6e988a9f568fd..a0915fc75957c 100644 --- a/cli/testdata/coder_--help.golden +++ b/cli/testdata/coder_--help.golden @@ -1,13 +1,13 @@ Usage: coder [global-flags] Coder v0.0.0-devel — A tool for provisioning self-hosted development environments with Terraform. - - Start a Coder server: + - Start a Coder server: -  $ coder server  + $ coder server - - Get started by creating a template from an example: + - Get started by creating a template from an example: -  $ coder templates init  + $ coder templates init Subcommands config-ssh Add an SSH Host entry for your workspaces "ssh diff --git a/cli/testdata/coder_config-ssh_--help.golden b/cli/testdata/coder_config-ssh_--help.golden index 8aa547ccb467f..2cb7d69f3e62b 100644 --- a/cli/testdata/coder_config-ssh_--help.golden +++ b/cli/testdata/coder_config-ssh_--help.golden @@ -2,14 +2,14 @@ Usage: coder config-ssh [flags] Add an SSH Host entry for your workspaces "ssh coder.workspace" -- You can use -o (or --ssh-option) so set SSH options to be used for all your - workspaces: +- You can use -o (or --ssh-option) so set SSH options to be used for all your +workspaces: -  $ coder config-ssh -o ForwardAgent=yes  + $ coder config-ssh -o ForwardAgent=yes - - You can use --dry-run (or -n) to see the changes that would be made: + - You can use --dry-run (or -n) to see the changes that would be made: -  $ coder config-ssh --dry-run  + $ coder config-ssh --dry-run Options --coder-binary-path string, $CODER_SSH_CONFIG_BINARY_PATH diff --git a/cli/testdata/coder_create_--help.golden b/cli/testdata/coder_create_--help.golden index 2e080b6e85ca7..005c913529f18 100644 --- a/cli/testdata/coder_create_--help.golden +++ b/cli/testdata/coder_create_--help.golden @@ -2,9 +2,9 @@ Usage: coder create [flags] [name] Create a workspace -- Create a workspace for another user (if you have permission): +- Create a workspace for another user (if you have permission): -  $ coder create /  + $ coder create / Options --parameter string-array, $CODER_RICH_PARAMETER diff --git a/cli/testdata/coder_dotfiles_--help.golden b/cli/testdata/coder_dotfiles_--help.golden index 0ff91b7dd1870..b72d47860b5c4 100644 --- a/cli/testdata/coder_dotfiles_--help.golden +++ b/cli/testdata/coder_dotfiles_--help.golden @@ -2,9 +2,9 @@ Usage: coder dotfiles [flags] Personalize your workspace by applying a canonical dotfiles repository -- Check out and install a dotfiles repository without prompts: +- Check out and install a dotfiles repository without prompts: -  $ coder dotfiles --yes git@github.com:example/dotfiles.git  + $ coder dotfiles --yes git@github.com:example/dotfiles.git Options -b, --branch string diff --git a/cli/testdata/coder_port-forward_--help.golden b/cli/testdata/coder_port-forward_--help.golden index 35c314d400699..ff65b059f5edd 100644 --- a/cli/testdata/coder_port-forward_--help.golden +++ b/cli/testdata/coder_port-forward_--help.golden @@ -5,27 +5,27 @@ forwarding, use "coder ssh -R". Aliases: tunnel -- Port forward a single TCP port from 1234 in the workspace to port 5678 on - your local machine: +- Port forward a single TCP port from 1234 in the workspace to port 5678 on your +local machine: -  $ coder port-forward --tcp 5678:1234  + $ coder port-forward --tcp 5678:1234 - - Port forward a single UDP port from port 9000 to port 9000 on your local - machine: + - Port forward a single UDP port from port 9000 to port 9000 on your local +machine: -  $ coder port-forward --udp 9000  + $ coder port-forward --udp 9000 - - Port forward multiple TCP ports and a UDP port: + - Port forward multiple TCP ports and a UDP port: -  $ coder port-forward --tcp 8080:8080 --tcp 9000:3000 --udp 5353:53  + $ coder port-forward --tcp 8080:8080 --tcp 9000:3000 --udp 5353:53 - - Port forward multiple ports (TCP or UDP) in condensed syntax: + - Port forward multiple ports (TCP or UDP) in condensed syntax: -  $ coder port-forward --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012  + $ coder port-forward --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012 - - Port forward specifying the local address to bind to: + - Port forward specifying the local address to bind to: -  $ coder port-forward --tcp 1.2.3.4:8080:8080  + $ coder port-forward --tcp 1.2.3.4:8080:8080 Options -p, --tcp string-array, $CODER_PORT_FORWARD_TCP diff --git a/cli/testdata/coder_schedule_override-stop_--help.golden b/cli/testdata/coder_schedule_override-stop_--help.golden index b0b5ca3050ca9..da5d84210c9af 100644 --- a/cli/testdata/coder_schedule_override-stop_--help.golden +++ b/cli/testdata/coder_schedule_override-stop_--help.golden @@ -6,7 +6,7 @@ Override the stop time of a currently running workspace instance. * The new stop time must be at least 30 minutes in the future. * The workspace template may restrict the maximum workspace runtime. -  $ coder schedule override-stop my-workspace 90m  + $ coder schedule override-stop my-workspace 90m --- Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_schedule_start_--help.golden b/cli/testdata/coder_schedule_start_--help.golden index 21854d2e39081..7420e9a31e2e9 100644 --- a/cli/testdata/coder_schedule_start_--help.golden +++ b/cli/testdata/coder_schedule_start_--help.golden @@ -12,9 +12,9 @@ Schedule format: [day-of-week] [location]. If omitted, we will fall back to either the TZ environment variable or /etc/localtime. You can check your corresponding location by visiting https://ipinfo.io - it shows in the demo widget on the right. - - Set the workspace to start at 9:30am (in Dublin) from Monday to Friday: + - Set the workspace to start at 9:30am (in Dublin) from Monday to Friday: -  $ coder schedule start my-workspace 9:30AM Mon-Fri Europe/Dublin  + $ coder schedule start my-workspace 9:30AM Mon-Fri Europe/Dublin --- Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_schedule_stop_--help.golden b/cli/testdata/coder_schedule_stop_--help.golden index 44cda6c083d72..73cb9ce2fe23e 100644 --- a/cli/testdata/coder_schedule_stop_--help.golden +++ b/cli/testdata/coder_schedule_stop_--help.golden @@ -15,7 +15,7 @@ When enabling scheduled stop, enter a duration in one of the following formats: * 2m (2 minutes) * 2 (2 minutes) -  $ coder schedule stop my-workspace 2h30m  + $ coder schedule stop my-workspace 2h30m --- Run `coder --help` for a list of global options. diff --git a/cli/testdata/coder_templates_--help.golden b/cli/testdata/coder_templates_--help.golden index 352695e26fb57..edd422dd9da49 100644 --- a/cli/testdata/coder_templates_--help.golden +++ b/cli/testdata/coder_templates_--help.golden @@ -5,17 +5,17 @@ Manage templates Aliases: template Templates are written in standard Terraform and describe the infrastructure for workspaces - - Create a template for developers to create workspaces: + - Create a template for developers to create workspaces: -  $ coder templates create  + $ coder templates create - - Make changes to your template, and plan the changes: + - Make changes to your template, and plan the changes: -  $ coder templates plan my-template  + $ coder templates plan my-template - Push an update to the template. Your developers can update their workspaces: -  $ coder templates push my-template  + $ coder templates push my-template Subcommands create Create a template from the current directory or as specified by diff --git a/cli/testdata/coder_templates_versions_--help.golden b/cli/testdata/coder_templates_versions_--help.golden index 58636e2ccdf6c..306d6e386952a 100644 --- a/cli/testdata/coder_templates_versions_--help.golden +++ b/cli/testdata/coder_templates_versions_--help.golden @@ -4,9 +4,9 @@ Manage different versions of the specified template Aliases: version -- List versions of a specific template: +- List versions of a specific template: -  $ coder templates versions list my-template  + $ coder templates versions list my-template Subcommands list List all the versions of the specified template diff --git a/cli/testdata/coder_tokens_--help.golden b/cli/testdata/coder_tokens_--help.golden index 0ba7c3f1fe316..9a98c819c3f59 100644 --- a/cli/testdata/coder_tokens_--help.golden +++ b/cli/testdata/coder_tokens_--help.golden @@ -5,17 +5,17 @@ Manage personal access tokens Aliases: token Tokens are used to authenticate automated clients to Coder. - - Create a token for automation: + - Create a token for automation: -  $ coder tokens create  + $ coder tokens create - - List your tokens: + - List your tokens: -  $ coder tokens ls  + $ coder tokens ls - - Remove a token by ID: + - Remove a token by ID: -  $ coder tokens rm WuoWs4ZsMX  + $ coder tokens rm WuoWs4ZsMX Subcommands create Create a token diff --git a/cli/testdata/coder_users_activate_--help.golden b/cli/testdata/coder_users_activate_--help.golden index 2d76f7257242e..f4954ea205617 100644 --- a/cli/testdata/coder_users_activate_--help.golden +++ b/cli/testdata/coder_users_activate_--help.golden @@ -5,7 +5,7 @@ platform Aliases: active - $ coder users activate example_user  +$ coder users activate example_user Options -c, --column string-array (default: username,email,created_at,status) diff --git a/cli/testdata/coder_users_show_--help.golden b/cli/testdata/coder_users_show_--help.golden index 2d20c844dd5f6..47cdb3b2890df 100644 --- a/cli/testdata/coder_users_show_--help.golden +++ b/cli/testdata/coder_users_show_--help.golden @@ -2,7 +2,7 @@ Usage: coder users show [flags] Show a single user. Use 'me' to indicate the currently authenticated user. - $ coder users show me  +$ coder users show me Options -o, --output string (default: table) diff --git a/cli/testdata/coder_users_suspend_--help.golden b/cli/testdata/coder_users_suspend_--help.golden index b5abb4a2c908e..7d3d41ea855c1 100644 --- a/cli/testdata/coder_users_suspend_--help.golden +++ b/cli/testdata/coder_users_suspend_--help.golden @@ -5,7 +5,7 @@ platform Aliases: rm, delete - $ coder users suspend example_user  +$ coder users suspend example_user Options -c, --column string-array (default: username,email,created_at,status) diff --git a/cli/usercreate.go b/cli/usercreate.go index 768e87d826783..478cc98e16e47 100644 --- a/cli/usercreate.go +++ b/cli/usercreate.go @@ -7,6 +7,8 @@ import ( "github.com/go-playground/validator/v10" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -88,7 +90,7 @@ func (r *RootCmd) userCreate() *clibase.Cmd { authenticationMethod := "" switch codersdk.LoginType(strings.ToLower(string(userLoginType))) { case codersdk.LoginTypePassword: - authenticationMethod = `Your password is: ` + cliui.DefaultStyles.Field.Render(password) + authenticationMethod = `Your password is: ` + pretty.Sprint(cliui.DefaultStyles.Field, password) case codersdk.LoginTypeNone: authenticationMethod = "Login has been disabled for this user. Contact your administrator to authenticate." case codersdk.LoginTypeGithub: @@ -99,16 +101,16 @@ func (r *RootCmd) userCreate() *clibase.Cmd { _, _ = fmt.Fprintln(inv.Stderr, `A new user has been created! Share the instructions below to get them started. -`+cliui.DefaultStyles.Placeholder.Render("—————————————————————————————————————————————————")+` +`+pretty.Sprint(cliui.DefaultStyles.Placeholder, "—————————————————————————————————————————————————")+` Download the Coder command line for your operating system: https://github.com/coder/coder/releases -Run `+cliui.DefaultStyles.Code.Render("coder login "+client.URL.String())+` to authenticate. +Run `+pretty.Sprint(cliui.DefaultStyles.Code, "coder login "+client.URL.String())+` to authenticate. -Your email is: `+cliui.DefaultStyles.Field.Render(email)+` +Your email is: `+pretty.Sprint(cliui.DefaultStyles.Field, email)+` `+authenticationMethod+` -Create a workspace `+cliui.DefaultStyles.Code.Render("coder create")+`!`) +Create a workspace `+pretty.Sprint(cliui.DefaultStyles.Code, "coder create")+`!`) return nil }, } diff --git a/cli/userstatus.go b/cli/userstatus.go index ac3bbaa0929a6..5f516e196b365 100644 --- a/cli/userstatus.go +++ b/cli/userstatus.go @@ -6,6 +6,8 @@ import ( "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -89,7 +91,7 @@ func (r *RootCmd) createUserStatusCommand(sdkStatus codersdk.UserStatus) *clibas return xerrors.Errorf("%s user: %w", verb, err) } - _, _ = fmt.Fprintf(inv.Stdout, "\nUser %s has been %s!\n", cliui.DefaultStyles.Keyword.Render(user.Username), pastVerb) + _, _ = fmt.Fprintf(inv.Stdout, "\nUser %s has been %s!\n", pretty.Sprint(cliui.DefaultStyles.Keyword, user.Username), pastVerb) return nil }, } diff --git a/cli/version.go b/cli/version.go index 70cac4f78d8e6..76ae3ffcf6de9 100644 --- a/cli/version.go +++ b/cli/version.go @@ -5,6 +5,8 @@ import ( "strings" "time" + "github.com/coder/pretty" + "github.com/coder/coder/v2/buildinfo" "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" @@ -39,9 +41,9 @@ func (vi versionInfo) String() string { _, _ = str.WriteString("\r\n" + vi.ExternalURL + "\r\n\r\n") if vi.Slim { - _, _ = str.WriteString(fmt.Sprintf("Slim build of Coder, does not support the %s subcommand.", cliui.DefaultStyles.Code.Render("server"))) + _, _ = str.WriteString(fmt.Sprintf("Slim build of Coder, does not support the %s subcommand.", pretty.Sprint(cliui.DefaultStyles.Code, "server"))) } else { - _, _ = str.WriteString(fmt.Sprintf("Full build of Coder, supports the %s subcommand.", cliui.DefaultStyles.Code.Render("server"))) + _, _ = str.WriteString(fmt.Sprintf("Full build of Coder, supports the %s subcommand.", pretty.Sprint(cliui.DefaultStyles.Code, "server"))) } return str.String() } diff --git a/cli/version_test.go b/cli/version_test.go index 76c4f4392fbd7..5802fff6f10f0 100644 --- a/cli/version_test.go +++ b/cli/version_test.go @@ -6,28 +6,19 @@ import ( "strings" "testing" - "github.com/charmbracelet/lipgloss" - "github.com/muesli/termenv" "github.com/stretchr/testify/require" "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/testutil" ) -// We need to override the global color profile to test escape codes. -// -//nolint:tparallel,paralleltest func TestVersion(t *testing.T) { - ogColorProfile := lipgloss.ColorProfile() - lipgloss.SetColorProfile(termenv.ANSI) - t.Cleanup(func() { - lipgloss.SetColorProfile(ogColorProfile) - }) + t.Parallel() expectedText := `Coder v0.0.0-devel https://github.com/coder/coder -Full build of Coder, supports the  server  subcommand. +Full build of Coder, supports the server subcommand. ` expectedJSON := `{ "version": "v0.0.0-devel", diff --git a/cmd/cliui/main.go b/cmd/cliui/main.go index 749060908f7d3..16137d5733c8e 100644 --- a/cmd/cliui/main.go +++ b/cmd/cliui/main.go @@ -22,16 +22,26 @@ import ( ) func main() { - root := &clibase.Cmd{ + var root *clibase.Cmd + root = &clibase.Cmd{ Use: "cliui", Short: "Used for visually testing UI components for the CLI.", + HelpHandler: func(inv *clibase.Invocation) error { + _, _ = fmt.Fprintln(inv.Stdout, "This command is used for visually testing UI components for the CLI.") + _, _ = fmt.Fprintln(inv.Stdout, "It is not intended to be used by end users.") + _, _ = fmt.Fprintln(inv.Stdout, "Subcommands: ") + for _, child := range root.Children { + _, _ = fmt.Fprintf(inv.Stdout, "- %s\n", child.Use) + } + return nil + }, } root.Children = append(root.Children, &clibase.Cmd{ Use: "prompt", Handler: func(inv *clibase.Invocation) error { _, err := cliui.Prompt(inv, cliui.PromptOptions{ - Text: "What is our " + cliui.DefaultStyles.Field.Render("company name") + "?", + Text: "What is our " + cliui.Field("company name") + "?", Default: "acme-corp", Validate: func(s string) error { if !strings.EqualFold(s, "coder") { diff --git a/coderd/devtunnel/tunnel.go b/coderd/devtunnel/tunnel.go index d61976cef4f32..89ceace6e4849 100644 --- a/coderd/devtunnel/tunnel.go +++ b/coderd/devtunnel/tunnel.go @@ -17,6 +17,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cryptorand" + "github.com/coder/pretty" "github.com/coder/wgtunnel/tunnelsdk" ) @@ -114,8 +115,8 @@ func readOrGenerateConfig(customTunnelHost string) (Config, error) { if cfg.Version == 0 { _, _ = fmt.Println() - _, _ = fmt.Println(cliui.DefaultStyles.Error.Render("You're running a deprecated tunnel version!")) - _, _ = fmt.Println(cliui.DefaultStyles.Error.Render("Upgrading you to the new version now. You will need to rebuild running workspaces.")) + pretty.Printf(cliui.DefaultStyles.Error, "You're running a deprecated tunnel version.\n") + pretty.Printf(cliui.DefaultStyles.Error, "Upgrading you to the new version now. You will need to rebuild running workspaces.") _, _ = fmt.Println() cfg, err := GenerateConfig(customTunnelHost) @@ -172,8 +173,8 @@ func GenerateConfig(customTunnelHost string) (Config, error) { spin.Stop() _, _ = fmt.Printf("Using tunnel in %s with latency %s.\n", - cliui.DefaultStyles.Keyword.Render(locationName), - cliui.DefaultStyles.Code.Render(node.AvgLatency.String()), + cliui.Keyword(locationName), + cliui.Code(node.AvgLatency.String()), ) return Config{ diff --git a/docs/cli.md b/docs/cli.md index c9ffdc7c46421..a63ccad623a6f 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -14,11 +14,11 @@ coder [global-flags] Coder — A tool for provisioning self-hosted development environments with Terraform. - Start a Coder server: - $ coder server + $ coder server - Get started by creating a template from an example: - $ coder templates init + $ coder templates init ``` ## Subcommands diff --git a/docs/cli/config-ssh.md b/docs/cli/config-ssh.md index 9d5196c824827..b46d6bf55b37f 100644 --- a/docs/cli/config-ssh.md +++ b/docs/cli/config-ssh.md @@ -14,13 +14,13 @@ coder config-ssh [flags] ```console - You can use -o (or --ssh-option) so set SSH options to be used for all your - workspaces: +workspaces: - $ coder config-ssh -o ForwardAgent=yes + $ coder config-ssh -o ForwardAgent=yes - You can use --dry-run (or -n) to see the changes that would be made: - $ coder config-ssh --dry-run + $ coder config-ssh --dry-run ``` ## Options diff --git a/docs/cli/create.md b/docs/cli/create.md index 8c36ea44a3dd7..8cb182528d5ea 100644 --- a/docs/cli/create.md +++ b/docs/cli/create.md @@ -15,7 +15,7 @@ coder create [flags] [name] ```console - Create a workspace for another user (if you have permission): - $ coder create / + $ coder create / ``` ## Options diff --git a/docs/cli/dotfiles.md b/docs/cli/dotfiles.md index 641b292415adc..59446a8b84d77 100644 --- a/docs/cli/dotfiles.md +++ b/docs/cli/dotfiles.md @@ -15,7 +15,7 @@ coder dotfiles [flags] ```console - Check out and install a dotfiles repository without prompts: - $ coder dotfiles --yes git@github.com:example/dotfiles.git + $ coder dotfiles --yes git@github.com:example/dotfiles.git ``` ## Options diff --git a/docs/cli/port-forward.md b/docs/cli/port-forward.md index 791960452398d..3419269c220fc 100644 --- a/docs/cli/port-forward.md +++ b/docs/cli/port-forward.md @@ -17,27 +17,27 @@ coder port-forward [flags] ## Description ```console - - Port forward a single TCP port from 1234 in the workspace to port 5678 on - your local machine: + - Port forward a single TCP port from 1234 in the workspace to port 5678 on your +local machine: - $ coder port-forward --tcp 5678:1234 + $ coder port-forward --tcp 5678:1234 - Port forward a single UDP port from port 9000 to port 9000 on your local - machine: +machine: - $ coder port-forward --udp 9000 + $ coder port-forward --udp 9000 - Port forward multiple TCP ports and a UDP port: - $ coder port-forward --tcp 8080:8080 --tcp 9000:3000 --udp 5353:53 + $ coder port-forward --tcp 8080:8080 --tcp 9000:3000 --udp 5353:53 - Port forward multiple ports (TCP or UDP) in condensed syntax: - $ coder port-forward --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012 + $ coder port-forward --tcp 8080,9000:3000,9090-9092,10000-10002:10010-10012 - Port forward specifying the local address to bind to: - $ coder port-forward --tcp 1.2.3.4:8080:8080 + $ coder port-forward --tcp 1.2.3.4:8080:8080 ``` ## Options diff --git a/docs/cli/schedule_override-stop.md b/docs/cli/schedule_override-stop.md index 2caf7661f11ea..8c565d734a585 100644 --- a/docs/cli/schedule_override-stop.md +++ b/docs/cli/schedule_override-stop.md @@ -18,5 +18,5 @@ coder schedule override-stop * The new stop time must be at least 30 minutes in the future. * The workspace template may restrict the maximum workspace runtime. - $ coder schedule override-stop my-workspace 90m + $ coder schedule override-stop my-workspace 90m ``` diff --git a/docs/cli/schedule_start.md b/docs/cli/schedule_start.md index d967b8448746e..771bb995e65b0 100644 --- a/docs/cli/schedule_start.md +++ b/docs/cli/schedule_start.md @@ -25,5 +25,5 @@ Schedule format: [day-of-week] [location]. - Set the workspace to start at 9:30am (in Dublin) from Monday to Friday: - $ coder schedule start my-workspace 9:30AM Mon-Fri Europe/Dublin + $ coder schedule start my-workspace 9:30AM Mon-Fri Europe/Dublin ``` diff --git a/docs/cli/schedule_stop.md b/docs/cli/schedule_stop.md index f00bff4bfbd30..399bc69cd5fc9 100644 --- a/docs/cli/schedule_stop.md +++ b/docs/cli/schedule_stop.md @@ -26,5 +26,5 @@ When enabling scheduled stop, enter a duration in one of the following formats: * 2m (2 minutes) * 2 (2 minutes) - $ coder schedule stop my-workspace 2h30m + $ coder schedule stop my-workspace 2h30m ``` diff --git a/docs/cli/templates.md b/docs/cli/templates.md index 4426625363ed7..602b4c4fb6ef5 100644 --- a/docs/cli/templates.md +++ b/docs/cli/templates.md @@ -20,15 +20,15 @@ coder templates Templates are written in standard Terraform and describe the infrastructure for workspaces - Create a template for developers to create workspaces: - $ coder templates create + $ coder templates create - Make changes to your template, and plan the changes: - $ coder templates plan my-template + $ coder templates plan my-template - Push an update to the template. Your developers can update their workspaces: - $ coder templates push my-template + $ coder templates push my-template ``` ## Subcommands diff --git a/docs/cli/templates_versions.md b/docs/cli/templates_versions.md index 9114ce1646ccf..5779c22a764c9 100644 --- a/docs/cli/templates_versions.md +++ b/docs/cli/templates_versions.md @@ -19,7 +19,7 @@ coder templates versions ```console - List versions of a specific template: - $ coder templates versions list my-template + $ coder templates versions list my-template ``` ## Subcommands diff --git a/docs/cli/tokens.md b/docs/cli/tokens.md index a6314c05c9137..4e74eb9516057 100644 --- a/docs/cli/tokens.md +++ b/docs/cli/tokens.md @@ -20,15 +20,15 @@ coder tokens Tokens are used to authenticate automated clients to Coder. - Create a token for automation: - $ coder tokens create + $ coder tokens create - List your tokens: - $ coder tokens ls + $ coder tokens ls - Remove a token by ID: - $ coder tokens rm WuoWs4ZsMX + $ coder tokens rm WuoWs4ZsMX ``` ## Subcommands diff --git a/docs/cli/users_activate.md b/docs/cli/users_activate.md index 07c001e81d67f..f5b2d3e8b85ca 100644 --- a/docs/cli/users_activate.md +++ b/docs/cli/users_activate.md @@ -17,7 +17,7 @@ coder users activate [flags] ## Description ```console - $ coder users activate example_user + $ coder users activate example_user ``` ## Options diff --git a/docs/cli/users_show.md b/docs/cli/users_show.md index 77f9c914c6924..dc941a9728c8b 100644 --- a/docs/cli/users_show.md +++ b/docs/cli/users_show.md @@ -13,7 +13,7 @@ coder users show [flags] ## Description ```console - $ coder users show me + $ coder users show me ``` ## Options diff --git a/docs/cli/users_suspend.md b/docs/cli/users_suspend.md index 3c9676b8b41d0..20c0a3713c650 100644 --- a/docs/cli/users_suspend.md +++ b/docs/cli/users_suspend.md @@ -18,7 +18,7 @@ coder users suspend [flags] ## Description ```console - $ coder users suspend example_user + $ coder users suspend example_user ``` ## Options diff --git a/enterprise/cli/groupcreate.go b/enterprise/cli/groupcreate.go index 5f40b79bb4502..e5f7bbd8a3bb9 100644 --- a/enterprise/cli/groupcreate.go +++ b/enterprise/cli/groupcreate.go @@ -9,6 +9,7 @@ import ( "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" + "github.com/coder/pretty" ) func (r *RootCmd) groupCreate() *clibase.Cmd { @@ -42,7 +43,7 @@ func (r *RootCmd) groupCreate() *clibase.Cmd { return xerrors.Errorf("create group: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "Successfully created group %s!\n", cliui.DefaultStyles.Keyword.Render(group.Name)) + _, _ = fmt.Fprintf(inv.Stdout, "Successfully created group %s!\n", pretty.Sprint(cliui.DefaultStyles.Keyword, group.Name)) return nil }, } diff --git a/enterprise/cli/groupcreate_test.go b/enterprise/cli/groupcreate_test.go index 4e9bdfdfb5ed2..783ce12f7cf8e 100644 --- a/enterprise/cli/groupcreate_test.go +++ b/enterprise/cli/groupcreate_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -43,6 +45,6 @@ func TestCreateGroup(t *testing.T) { err := inv.Run() require.NoError(t, err) - pty.ExpectMatch(fmt.Sprintf("Successfully created group %s!", cliui.DefaultStyles.Keyword.Render(groupName))) + pty.ExpectMatch(fmt.Sprintf("Successfully created group %s!", pretty.Sprint(cliui.DefaultStyles.Keyword, groupName))) }) } diff --git a/enterprise/cli/groupdelete.go b/enterprise/cli/groupdelete.go index adada073e32a6..e7ca01ba36de8 100644 --- a/enterprise/cli/groupdelete.go +++ b/enterprise/cli/groupdelete.go @@ -9,6 +9,7 @@ import ( "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" + "github.com/coder/pretty" ) func (r *RootCmd) groupDelete() *clibase.Cmd { @@ -41,7 +42,7 @@ func (r *RootCmd) groupDelete() *clibase.Cmd { return xerrors.Errorf("delete group: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "Successfully deleted group %s!\n", cliui.DefaultStyles.Keyword.Render(group.Name)) + _, _ = fmt.Fprintf(inv.Stdout, "Successfully deleted group %s!\n", pretty.Sprint(cliui.DefaultStyles.Keyword, group.Name)) return nil }, } diff --git a/enterprise/cli/groupdelete_test.go b/enterprise/cli/groupdelete_test.go index c3ff2593e0b56..21aa6391bd4ba 100644 --- a/enterprise/cli/groupdelete_test.go +++ b/enterprise/cli/groupdelete_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -45,7 +47,7 @@ func TestGroupDelete(t *testing.T) { err = inv.Run() require.NoError(t, err) - pty.ExpectMatch(fmt.Sprintf("Successfully deleted group %s", cliui.DefaultStyles.Keyword.Render(group.Name))) + pty.ExpectMatch(fmt.Sprintf("Successfully deleted group %s", pretty.Sprint(cliui.DefaultStyles.Keyword, group.Name))) }) t.Run("NoArg", func(t *testing.T) { diff --git a/enterprise/cli/groupedit.go b/enterprise/cli/groupedit.go index a1c8db9ab8995..8811378bc0a34 100644 --- a/enterprise/cli/groupedit.go +++ b/enterprise/cli/groupedit.go @@ -7,6 +7,8 @@ import ( "github.com/google/uuid" "golang.org/x/xerrors" + "github.com/coder/pretty" + agpl "github.com/coder/coder/v2/cli" "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" @@ -77,7 +79,7 @@ func (r *RootCmd) groupEdit() *clibase.Cmd { return xerrors.Errorf("patch group: %w", err) } - _, _ = fmt.Fprintf(inv.Stdout, "Successfully patched group %s!\n", cliui.DefaultStyles.Keyword.Render(group.Name)) + _, _ = fmt.Fprintf(inv.Stdout, "Successfully patched group %s!\n", pretty.Sprint(cliui.DefaultStyles.Keyword, group.Name)) return nil }, } diff --git a/enterprise/cli/groupedit_test.go b/enterprise/cli/groupedit_test.go index a6bf396338147..a0d192854f85a 100644 --- a/enterprise/cli/groupedit_test.go +++ b/enterprise/cli/groupedit_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/require" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/coderd/coderdtest" @@ -65,7 +67,7 @@ func TestGroupEdit(t *testing.T) { err = inv.Run() require.NoError(t, err) - pty.ExpectMatch(fmt.Sprintf("Successfully patched group %s", cliui.DefaultStyles.Keyword.Render(expectedName))) + pty.ExpectMatch(fmt.Sprintf("Successfully patched group %s", pretty.Sprint(cliui.DefaultStyles.Keyword, expectedName))) }) t.Run("InvalidUserInput", func(t *testing.T) { diff --git a/enterprise/cli/provisionerdaemons.go b/enterprise/cli/provisionerdaemons.go index 124c253595ac2..e63e7cd46ce36 100644 --- a/enterprise/cli/provisionerdaemons.go +++ b/enterprise/cli/provisionerdaemons.go @@ -147,7 +147,7 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd { select { case <-notifyCtx.Done(): exitErr = notifyCtx.Err() - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Bold.Render( + _, _ = fmt.Fprintln(inv.Stdout, cliui.Bold( "Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit", )) case exitErr = <-errCh: diff --git a/enterprise/cli/proxyserver.go b/enterprise/cli/proxyserver.go index e7f4e8650783c..b3608d1500faf 100644 --- a/enterprise/cli/proxyserver.go +++ b/enterprise/cli/proxyserver.go @@ -305,7 +305,7 @@ func (*RootCmd) proxyServer() *clibase.Cmd { case exitErr = <-errCh: case <-notifyCtx.Done(): exitErr = notifyCtx.Err() - _, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Bold.Render( + _, _ = fmt.Fprintln(inv.Stdout, cliui.Bold( "Interrupt caught, gracefully exiting. Use ctrl+\\ to force quit", )) } diff --git a/enterprise/cli/testdata/coder_--help.golden b/enterprise/cli/testdata/coder_--help.golden index ae24592079a69..86a50ec2976c2 100644 --- a/enterprise/cli/testdata/coder_--help.golden +++ b/enterprise/cli/testdata/coder_--help.golden @@ -1,13 +1,13 @@ Usage: coder [global-flags] Coder v0.0.0-devel — A tool for provisioning self-hosted development environments with Terraform. - - Start a Coder server: + - Start a Coder server: -  $ coder server  + $ coder server - - Get started by creating a template from an example: + - Get started by creating a template from an example: -  $ coder templates init  + $ coder templates init Subcommands features List Enterprise features diff --git a/enterprise/cli/workspaceproxy.go b/enterprise/cli/workspaceproxy.go index a0f2004d7a824..95f17a251c5ee 100644 --- a/enterprise/cli/workspaceproxy.go +++ b/enterprise/cli/workspaceproxy.go @@ -8,6 +8,8 @@ import ( "github.com/fatih/color" "golang.org/x/xerrors" + "github.com/coder/pretty" + "github.com/coder/coder/v2/cli/clibase" "github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/codersdk" @@ -206,7 +208,7 @@ func (r *RootCmd) deleteProxy() *clibase.Cmd { // Confirm deletion of the template. _, err = cliui.Prompt(inv, cliui.PromptOptions{ - Text: fmt.Sprintf("Delete this workspace proxy: %s?", cliui.DefaultStyles.Code.Render(wsproxy.DisplayName)), + Text: fmt.Sprintf("Delete this workspace proxy: %s?", pretty.Sprint(cliui.DefaultStyles.Code, wsproxy.DisplayName)), IsConfirm: true, Default: cliui.ConfirmNo, }) @@ -428,14 +430,14 @@ func newUpdateProxyResponseFormatter() *updateProxyResponseFormatter { } return fmt.Sprintf("Workspace Proxy %[1]q updated successfully.\n"+ - cliui.DefaultStyles.Placeholder.Render("—————————————————————————————————————————————————")+"\n"+ + pretty.Sprint(cliui.DefaultStyles.Placeholder, "—————————————————————————————————————————————————")+"\n"+ "Save this authentication token, it will not be shown again.\n"+ "Token: %[2]s\n"+ "\n"+ "Start the proxy by running:\n"+ - cliui.DefaultStyles.Code.Render("CODER_PROXY_SESSION_TOKEN=%[2]s coder wsproxy server --primary-access-url %[3]s --http-address=0.0.0.0:3001")+ + cliui.Code("CODER_PROXY_SESSION_TOKEN=%[2]s coder wsproxy server --primary-access-url %[3]s --http-address=0.0.0.0:3001")+ // This is required to turn off the code style. Otherwise it appears in the code block until the end of the line. - cliui.DefaultStyles.Placeholder.Render(""), + pretty.Sprint(cliui.DefaultStyles.Placeholder, ""), response.Proxy.Name, response.ProxyToken, up.primaryAccessURL), nil }), cliui.JSONFormat(), diff --git a/go.mod b/go.mod index 82c6266b84b51..0974bae8832ea 100644 --- a/go.mod +++ b/go.mod @@ -79,7 +79,6 @@ require ( github.com/briandowns/spinner v1.18.1 github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 github.com/cenkalti/backoff/v4 v4.2.1 - github.com/charmbracelet/charm v0.12.4 github.com/charmbracelet/glamour v0.6.0 // In later at least v0.7.1, lipgloss changes its terminal detection // which breaks most of our CLI golden files tests. @@ -245,11 +244,9 @@ require ( github.com/bep/godartsass/v2 v2.0.0 // indirect github.com/bep/golibsass v1.1.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/charmbracelet/bubbles v0.15.0 // indirect - github.com/charmbracelet/bubbletea v0.23.2 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/coder/pretty v0.0.0-20230907185834-1d3e21235f75 github.com/containerd/continuity v0.4.2-0.20230616210509-1e0d26eb2381 // indirect github.com/coreos/go-iptables v0.6.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect @@ -321,7 +318,6 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect @@ -336,8 +332,6 @@ require ( github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/term v0.5.0 // indirect - github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a // indirect - github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/niklasfasching/go-org v1.7.0 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect diff --git a/go.sum b/go.sum index 92ef6075c4526..8f71a9d4ac842 100644 --- a/go.sum +++ b/go.sum @@ -124,7 +124,6 @@ github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloD github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= github.com/aws/aws-sdk-go-v2 v1.20.0 h1:INUDpYLt4oiPOJl0XwZDK2OVAVf0Rzo+MGVTv9f+gy8= @@ -154,7 +153,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.21.1/go.mod h1:G8SbvL0rFk4WOJroU8tKB github.com/aws/smithy-go v1.14.0 h1:+X90sB94fizKjDmwb4vyl2cTTPXTE5E2G/1mjByb0io= github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= @@ -184,17 +182,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= -github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= -github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps= -github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= -github.com/charmbracelet/charm v0.12.4 h1:YEB64WxLvdnmmGLiejXlC/e4hAd4a5SY4TqW8bdErv4= -github.com/charmbracelet/charm v0.12.4/go.mod h1:BOvE692iyhnFctYs6Es3gb7xjx/JBgKpR9gxUmqXo3A= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= @@ -230,6 +219,8 @@ github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136 h1:0RgB61LcNs github.com/coder/go-scim/pkg/v2 v2.0.0-20230221055123-1d63c1222136/go.mod h1:VkD1P761nykiq75dz+4iFqIQIZka189tx1BQLOp0Skc= github.com/coder/gvisor v0.0.0-20230714132058-be2e4ac102c3 h1:gtuDFa+InmMVUYiurBV+XYu24AeMGv57qlZ23i6rmyE= github.com/coder/gvisor v0.0.0-20230714132058-be2e4ac102c3/go.mod h1:pzr6sy8gDLfVmDAg8OYrlKvGEHw5C3PGTiBXBTCx76Q= +github.com/coder/pretty v0.0.0-20230907185834-1d3e21235f75 h1:cTpm2TCHrJmijom7EUqf5PI3PRQTPz4JDXKWH1XJfpQ= +github.com/coder/pretty v0.0.0-20230907185834-1d3e21235f75/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc= github.com/coder/retry v1.4.0 h1:g0fojHFxcdgM3sBULqgjFDxw1UIvaCqk4ngUDu0EWag= github.com/coder/retry v1.4.0/go.mod h1:blHMk9vs6LkoRT9ZHyuZo360cufXEhrxqvEzeMtRGoY= github.com/coder/ssh v0.0.0-20230621095435-9a7e23486f1c h1:TI7TzdFI0UvQmwgyQhtI1HeyYNRxAQpr8Tw/rjT8VSA= @@ -242,7 +233,6 @@ github.com/coder/wgtunnel v0.1.5 h1:WP3sCj/3iJ34eKvpMQEp1oJHvm24RYh0NHbj1kfUKfs= github.com/coder/wgtunnel v0.1.5/go.mod h1:bokoUrHnUFY4lu9KOeSYiIcHTI2MO1KwqumU4DPDyJI= github.com/coder/wireguard-go v0.0.0-20230807234434-d825b45ccbf5 h1:eDk/42Kj4xN4yfE504LsvcFEo3dWUiCOaBiWJ2uIH2A= github.com/coder/wireguard-go v0.0.0-20230807234434-d825b45ccbf5/go.mod h1:QRIcq2+DbdIC5sKh/gcAZhuqu6WT6L6G8/ALPN5wqYw= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.4.2-0.20230616210509-1e0d26eb2381 h1:a5jOuoZHKBi2oH9JsfNqrrPpHhmrYU0NAte3M/EPudw= github.com/containerd/continuity v0.4.2-0.20230616210509-1e0d26eb2381/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= @@ -661,13 +651,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -717,17 +703,9 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= -github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= -github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -805,7 +783,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= @@ -1171,7 +1148,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=