Repository: cli/cli
File: pkg/iostreams/iostreams.go (Go)

Code before:
	}

	s.progressIndicatorMu.Lock()
	defer s.progressIndicatorMu.Unlock()

	if s.progressIndicator != nil {
		if label == "" {
			s.progressIndicator.Prefix = ""
		} else {
			s.progressIndicator.Prefix = label + " "
		}
		return
	}

	spinnerDisabledValue, spinnerDisabledIsSet := os.LookupEnv("GH_SPINNER_DISABLED")
	falseyValues := []string{"false", "0", "no", ""}

	var spinnerStyle []string
	if spinnerDisabledIsSet && !slices.Contains(falseyValues, spinnerDisabledValue) {
		// This progress indicator must be readable by screen readers
		spinnerStyle = []string{"Working..."}
	} else {
		// https://github.com/briandowns/spinner#available-character-sets
		// ⣾ ⣷ ⣽ ⣻ ⡿
		spinnerStyle = spinner.CharSets[11]
	}

	sp := spinner.New(spinnerStyle, 120*time.Millisecond, spinner.WithWriter(s.ErrOut), spinner.WithColor("fgCyan"))
	if label != "" {
		sp.Prefix = label + " "
	}

	sp.Start()
	s.progressIndicator = sp
}

func (s *IOStreams) StopProgressIndicator() {
	s.progressIndicatorMu.Lock()
	defer s.progressIndicatorMu.Unlock()
	if s.progressIndicator == nil {
		return
	}
	s.progressIndicator.Stop()
	s.progressIndicator = nil
}

func (s *IOStreams) RunWithProgress(label string, run func() error) error {
	s.StartProgressIndicatorWithLabel(label)
	defer s.StopProgressIndicator()

	return run()

Review comment (suggestion):
**question:** should we bypass `briandowns/spinner` all together if disabled?

Looking under the covers, `briandowns/spinner` requires a spinner character set in order to do anything.  In my mind, I was originally thinking `Working...` message would be treated as a default prefix if `StartProgressIndicator()` was called; not necessarily combined with a provided prefix.

As I mentioned in one of my other comments, `spinner` will still erase and redraw the screen as it doesn't appear to determine if an existing line matches the output.

Code after:

func (s *IOStreams) StartProgressIndicator() {
	s.StartProgressIndicatorWithLabel("")
}

func (s *IOStreams) StartProgressIndicatorWithLabel(label string) {
	if !s.progressIndicatorEnabled {
		return
	}

	if s.spinnerDisabled {
		// If the spinner is disabled, simply print a
		// textual progress indicator and return.
		// This means that s.ProgressIndicator will be nil.
		// See also: the comment on StopProgressIndicator()
		s.startTextualProgressIndicator(label)
		return
	}

	s.progressIndicatorMu.Lock()
	defer s.progressIndicatorMu.Unlock()

	if s.progressIndicator != nil {
		if label == "" {
			s.progressIndicator.Prefix = ""
		} else {
			s.progressIndicator.Prefix = label + " "
		}
		return
	}

	// https://github.com/briandowns/spinner#available-character-sets
	// ⣾ ⣷ ⣽ ⣻ ⡿
	spinnerStyle := spinner.CharSets[11]

	sp := spinner.New(spinnerStyle, 120*time.Millisecond, spinner.WithWriter(s.ErrOut), spinner.WithColor("fgCyan"))
	if label != "" {
		sp.Prefix = label + " "
	}

	sp.Start()
	s.progressIndicator = sp
}

func (s *IOStreams) startTextualProgressIndicator(label string) {
	s.progressIndicatorMu.Lock()
	defer s.progressIndicatorMu.Unlock()

	// Default label when spinner disabled is "Working..."
	if label == "" {
		label = "Working..."