Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix(cli): speed up CLI over SSH #7885

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions cli/cliui/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ type message struct {

func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *message) {
m = &message{
Spin: fmt.Sprintf("Waiting for connection from %s...", Styles.Field.Render(agent.Name)),
Spin: fmt.Sprintf("Waiting for connection from %s...", DefaultStyles.Field.Render(agent.Name)),
Prompt: "Don't panic, your workspace is booting up!",
}
defer func() {
Expand All @@ -192,7 +192,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag

// We don't want to wrap the troubleshooting URL, so we'll handle word
// wrapping ourselves (vs using lipgloss).
w := wordwrap.NewWriter(Styles.Paragraph.GetWidth() - Styles.Paragraph.GetMarginLeft()*2)
w := wordwrap.NewWriter(DefaultStyles.Paragraph.GetWidth() - DefaultStyles.Paragraph.GetMarginLeft()*2)
w.Breakpoints = []rune{' ', '\n'}

_, _ = fmt.Fprint(w, m.Prompt)
Expand All @@ -208,7 +208,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
// We want to prefix the prompt with a caret, but we want text on the
// following lines to align with the text on the first line (i.e. added
// spacing).
ind := " " + Styles.Prompt.String()
ind := " " + DefaultStyles.Prompt.String()
iw := indent.NewWriter(1, func(w io.Writer) {
_, _ = w.Write([]byte(ind))
ind = " " // Set indentation to space after initial prompt.
Expand All @@ -223,7 +223,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
case codersdk.WorkspaceAgentDisconnected:
m.Prompt = "The workspace agent lost connection!"
case codersdk.WorkspaceAgentConnected:
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", Styles.Field.Render(agent.Name))
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", DefaultStyles.Field.Render(agent.Name))
m.Prompt = "Don't panic, your workspace agent has connected and the workspace is getting ready!"
if opts.NoWait {
m.Prompt = "Your workspace is still getting ready, it may be in an incomplete state."
Expand Down
81 changes: 48 additions & 33 deletions cli/cliui/cliui.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
package cliui

import (
"os"

"github.com/charmbracelet/charm/ui/common"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
"golang.org/x/xerrors"
)

var (
Canceled = xerrors.New("canceled")

defaultStyles = common.DefaultStyles()
)
var Canceled = xerrors.New("canceled")

// ValidateNotEmpty is a helper function to disallow empty inputs!
func ValidateNotEmpty(s string) error {
if s == "" {
return xerrors.New("Must be provided!")
}
return nil
}
// DefaultStyles compose visual elements of the UI.
var DefaultStyles Styles

// Styles compose visual elements of the UI!
var Styles = struct {
type Styles struct {
Bold,
Checkmark,
Code,
Expand All @@ -38,23 +31,45 @@ var Styles = struct {
Logo,
Warn,
Wrap lipgloss.Style
}{
Bold: lipgloss.NewStyle().Bold(true),
Checkmark: defaultStyles.Checkmark,
Code: defaultStyles.Code,
Crossmark: defaultStyles.Error.Copy().SetString("✘"),
DateTimeStamp: defaultStyles.LabelDim,
Error: defaultStyles.Error,
Field: defaultStyles.Code.Copy().Foreground(lipgloss.AdaptiveColor{Light: "#000000", Dark: "#FFFFFF"}),
Keyword: defaultStyles.Keyword,
Paragraph: defaultStyles.Paragraph,
Placeholder: lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#585858", Dark: "#4d46b3"}),
Prompt: defaultStyles.Prompt.Copy().Foreground(lipgloss.AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}),
FocusedPrompt: defaultStyles.FocusedPrompt.Copy().Foreground(lipgloss.Color("#651fff")),
Fuchsia: defaultStyles.SelectedMenuItem.Copy(),
Logo: defaultStyles.Logo.Copy().SetString("Coder"),
Warn: lipgloss.NewStyle().Foreground(
lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"},
),
Wrap: lipgloss.NewStyle().Width(80),
}

func init() {
lipgloss.SetDefaultRenderer(
lipgloss.NewRenderer(os.Stdout, termenv.WithColorCache(true)),
)

// 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.

charmStyles := common.DefaultStyles()

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),
}
}

// ValidateNotEmpty is a helper function to disallow empty inputs!
func ValidateNotEmpty(s string) error {
if s == "" {
return xerrors.New("Must be provided!")
}
return nil
}
4 changes: 2 additions & 2 deletions cli/cliui/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -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: Styles.Warn.Copy(),
Style: DefaultStyles.Warn.Copy(),
Prefix: "WARN: ",
Header: header,
Lines: lines,
Expand Down Expand Up @@ -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: Styles.Error.Copy(),
Style: DefaultStyles.Error.Copy(),
Prefix: "ERROR: ",
Header: header,
Lines: lines,
Expand Down
8 changes: 4 additions & 4 deletions cli/cliui/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te
label = templateVersionParameter.DisplayName
}

_, _ = fmt.Fprintln(inv.Stdout, Styles.Bold.Render(label))
_, _ = fmt.Fprintln(inv.Stdout, DefaultStyles.Bold.Render(label))
if templateVersionParameter.DescriptionPlaintext != "" {
_, _ = fmt.Fprintln(inv.Stdout, " "+strings.TrimSpace(strings.Join(strings.Split(templateVersionParameter.DescriptionPlaintext, "\n"), "\n "))+"\n")
}
Expand All @@ -40,7 +40,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te
}

_, _ = fmt.Fprintln(inv.Stdout)
_, _ = fmt.Fprintln(inv.Stdout, " "+Styles.Prompt.String()+Styles.Field.Render(strings.Join(values, ", ")))
_, _ = fmt.Fprintln(inv.Stdout, " "+DefaultStyles.Prompt.String()+DefaultStyles.Field.Render(strings.Join(values, ", ")))
value = string(v)
}
} else if len(templateVersionParameter.Options) > 0 {
Expand All @@ -54,7 +54,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te
})
if err == nil {
_, _ = fmt.Fprintln(inv.Stdout)
_, _ = fmt.Fprintln(inv.Stdout, " "+Styles.Prompt.String()+Styles.Field.Render(richParameterOption.Name))
_, _ = fmt.Fprintln(inv.Stdout, " "+DefaultStyles.Prompt.String()+DefaultStyles.Field.Render(richParameterOption.Name))
value = richParameterOption.Value
}
} else {
Expand All @@ -65,7 +65,7 @@ func RichParameter(inv *clibase.Invocation, templateVersionParameter codersdk.Te
text += ":"

value, err = Prompt(inv, PromptOptions{
Text: Styles.Bold.Render(text),
Text: DefaultStyles.Bold.Render(text),
Validate: func(value string) error {
return validateRichPrompt(value, templateVersionParameter)
},
Expand Down
16 changes: 8 additions & 8 deletions cli/cliui/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ func Prompt(inv *clibase.Invocation, opts PromptOptions) (string, error) {
}
}

_, _ = fmt.Fprint(inv.Stdout, Styles.FocusedPrompt.String()+opts.Text+" ")
_, _ = fmt.Fprint(inv.Stdout, DefaultStyles.FocusedPrompt.String()+opts.Text+" ")
if opts.IsConfirm {
if len(opts.Default) == 0 {
opts.Default = ConfirmYes
}
renderedYes := Styles.Placeholder.Render(ConfirmYes)
renderedNo := Styles.Placeholder.Render(ConfirmNo)
renderedYes := DefaultStyles.Placeholder.Render(ConfirmYes)
renderedNo := DefaultStyles.Placeholder.Render(ConfirmNo)
if opts.Default == ConfirmYes {
renderedYes = Styles.Bold.Render(ConfirmYes)
renderedYes = DefaultStyles.Bold.Render(ConfirmYes)
} else {
renderedNo = Styles.Bold.Render(ConfirmNo)
renderedNo = DefaultStyles.Bold.Render(ConfirmNo)
}
_, _ = fmt.Fprint(inv.Stdout, Styles.Placeholder.Render("("+renderedYes+Styles.Placeholder.Render("/"+renderedNo+Styles.Placeholder.Render(") "))))
_, _ = fmt.Fprint(inv.Stdout, DefaultStyles.Placeholder.Render("("+renderedYes+DefaultStyles.Placeholder.Render("/"+renderedNo+DefaultStyles.Placeholder.Render(") "))))
} else if opts.Default != "" {
_, _ = fmt.Fprint(inv.Stdout, Styles.Placeholder.Render("("+opts.Default+") "))
_, _ = fmt.Fprint(inv.Stdout, DefaultStyles.Placeholder.Render("("+opts.Default+") "))
}
interrupt := make(chan os.Signal, 1)

Expand Down Expand Up @@ -126,7 +126,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, DefaultStyles.Error.Render(err.Error()))
return Prompt(inv, opts)
}
}
Expand Down
18 changes: 9 additions & 9 deletions cli/cliui/provisionerjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp
)

printStage := func() {
_, _ = fmt.Fprintf(writer, Styles.Prompt.Render("⧗")+"%s\n", Styles.Field.Render(currentStage))
_, _ = fmt.Fprintf(writer, DefaultStyles.Prompt.Render("⧗")+"%s\n", DefaultStyles.Field.Render(currentStage))
}

updateStage := func(stage string, startedAt time.Time) {
Expand All @@ -80,11 +80,11 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp
if !didLogBetweenStage {
prefix = "\033[1A\r"
}
mark := Styles.Checkmark
mark := DefaultStyles.Checkmark
if job.CompletedAt != nil && job.Status != codersdk.ProvisionerJobSucceeded {
mark = Styles.Crossmark
mark = DefaultStyles.Crossmark
}
_, _ = fmt.Fprintf(writer, prefix+mark.String()+Styles.Placeholder.Render(" %s [%dms]")+"\n", currentStage, startedAt.Sub(currentStageStartedAt).Milliseconds())
_, _ = fmt.Fprintf(writer, prefix+mark.String()+DefaultStyles.Placeholder.Render(" %s [%dms]")+"\n", currentStage, startedAt.Sub(currentStageStartedAt).Milliseconds())
}
if stage == "" {
return
Expand Down Expand Up @@ -129,7 +129,7 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp
return
}
}
_, _ = fmt.Fprintf(writer, "\033[2K\r\n"+Styles.FocusedPrompt.String()+Styles.Bold.Render("Gracefully canceling...")+"\n\n")
_, _ = fmt.Fprintf(writer, "\033[2K\r\n"+DefaultStyles.FocusedPrompt.String()+DefaultStyles.Bold.Render("Gracefully canceling...")+"\n\n")
err := opts.Cancel()
if err != nil {
errChan <- xerrors.Errorf("cancel: %w", err)
Expand Down Expand Up @@ -207,11 +207,11 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp
if !opts.Verbose {
continue
}
output = Styles.Placeholder.Render(log.Output)
output = DefaultStyles.Placeholder.Render(log.Output)
case codersdk.LogLevelError:
output = defaultStyles.Error.Render(log.Output)
output = DefaultStyles.Error.Render(log.Output)
case codersdk.LogLevelWarn:
output = Styles.Warn.Render(log.Output)
output = DefaultStyles.Warn.Render(log.Output)
case codersdk.LogLevelInfo:
output = log.Output
}
Expand All @@ -222,7 +222,7 @@ func ProvisionerJob(ctx context.Context, writer io.Writer, opts ProvisionerJobOp
jobMutex.Unlock()
continue
}
_, _ = fmt.Fprintf(logOutput, "%s %s\n", Styles.Placeholder.Render(" "), output)
_, _ = fmt.Fprintf(logOutput, "%s %s\n", DefaultStyles.Placeholder.Render(" "), output)
if !opts.Silent {
didLogBetweenStage = true
}
Expand Down
26 changes: 13 additions & 13 deletions cli/cliui/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource

// Display a line for the resource.
tableWriter.AppendRow(table.Row{
Styles.Bold.Render(resourceAddress),
DefaultStyles.Bold.Render(resourceAddress),
"",
"",
})
Expand Down Expand Up @@ -106,7 +106,7 @@ func WorkspaceResources(writer io.Writer, resources []codersdk.WorkspaceResource
if totalAgents > 1 {
sshCommand += "." + agent.Name
}
sshCommand = Styles.Code.Render(sshCommand)
sshCommand = DefaultStyles.Code.Render(sshCommand)
row = append(row, sshCommand)
}
tableWriter.AppendRow(row)
Expand All @@ -121,23 +121,23 @@ func renderAgentStatus(agent codersdk.WorkspaceAgent) string {
switch agent.Status {
case codersdk.WorkspaceAgentConnecting:
since := database.Now().Sub(agent.CreatedAt)
return Styles.Warn.Render("⦾ connecting") + " " +
Styles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]")
return DefaultStyles.Warn.Render("⦾ connecting") + " " +
DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]")
case codersdk.WorkspaceAgentDisconnected:
since := database.Now().Sub(*agent.DisconnectedAt)
return Styles.Error.Render("⦾ disconnected") + " " +
Styles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]")
return DefaultStyles.Error.Render("⦾ disconnected") + " " +
DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]")
case codersdk.WorkspaceAgentTimeout:
since := database.Now().Sub(agent.CreatedAt)
return fmt.Sprintf(
"%s %s",
Styles.Warn.Render("⦾ timeout"),
Styles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]"),
DefaultStyles.Warn.Render("⦾ timeout"),
DefaultStyles.Placeholder.Render("["+strconv.Itoa(int(since.Seconds()))+"s]"),
)
case codersdk.WorkspaceAgentConnected:
return Styles.Keyword.Render("⦿ connected")
return DefaultStyles.Keyword.Render("⦿ connected")
default:
return Styles.Warn.Render("○ unknown")
return DefaultStyles.Warn.Render("○ unknown")
}
}

Expand All @@ -146,11 +146,11 @@ func renderAgentVersion(agentVersion, serverVersion string) string {
agentVersion = "(unknown)"
}
if !semver.IsValid(serverVersion) || !semver.IsValid(agentVersion) {
return Styles.Placeholder.Render(agentVersion)
return DefaultStyles.Placeholder.Render(agentVersion)
}
outdated := semver.Compare(agentVersion, serverVersion) < 0
if outdated {
return Styles.Warn.Render(agentVersion + " (outdated)")
return DefaultStyles.Warn.Render(agentVersion + " (outdated)")
}
return Styles.Keyword.Render(agentVersion)
return DefaultStyles.Keyword.Render(agentVersion)
}
Loading