File: command.go

package info (click to toggle)
gum 0.17.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 668 kB
  • sloc: sh: 232; ruby: 24; makefile: 17; javascript: 9; python: 4
file content (82 lines) | stat: -rw-r--r-- 2,409 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package spin

import (
	"fmt"
	"os"

	"github.com/charmbracelet/bubbles/spinner"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/gum/internal/exit"
	"github.com/charmbracelet/gum/internal/timeout"
	"github.com/charmbracelet/gum/style"
	"github.com/charmbracelet/x/term"
)

// Run provides a shell script interface for the spinner bubble.
// https://github.com/charmbracelet/bubbles/spinner
func (o Options) Run() error {
	isOutTTY := term.IsTerminal(os.Stdout.Fd())
	isErrTTY := term.IsTerminal(os.Stderr.Fd())

	s := spinner.New()
	s.Style = o.SpinnerStyle.ToLipgloss()
	s.Spinner = spinnerMap[o.Spinner]
	top, right, bottom, left := style.ParsePadding(o.Padding)
	m := model{
		spinner:    s,
		title:      o.TitleStyle.ToLipgloss().Render(o.Title),
		command:    o.Command,
		align:      o.Align,
		showStdout: (o.ShowOutput || o.ShowStdout) && isOutTTY,
		showStderr: (o.ShowOutput || o.ShowStderr) && isErrTTY,
		showError:  o.ShowError,
		isTTY:      isErrTTY,
		padding:    []int{top, right, bottom, left},
	}

	ctx, cancel := timeout.Context(o.Timeout)
	defer cancel()

	tm, err := tea.NewProgram(
		m,
		tea.WithOutput(os.Stderr),
		tea.WithContext(ctx),
		tea.WithInput(nil),
	).Run()
	if err != nil {
		return fmt.Errorf("unable to run action: %w", err)
	}

	m = tm.(model)
	// If the command succeeds, and we are printing output and we are in a TTY then push the STDOUT we got to the actual
	// STDOUT for piping or other things.
	//nolint:nestif
	if m.err != nil {
		if _, err := fmt.Fprintf(os.Stderr, "%s\n", m.err.Error()); err != nil {
			return fmt.Errorf("failed to write to stdout: %w", err)
		}
		return exit.ErrExit(1)
	} else if m.status == 0 {
		var output string
		if o.ShowOutput || (o.ShowStdout && o.ShowStderr) {
			output = m.output
		} else if o.ShowStdout {
			output = m.stdout
		} else if o.ShowStderr {
			output = m.stderr
		}
		if output != "" {
			if _, err := os.Stdout.WriteString(output); err != nil {
				return fmt.Errorf("failed to write to stdout: %w", err)
			}
		}
	} else if o.ShowError {
		// Otherwise if we are showing errors and the command did not exit with a 0 status code then push all of the command
		// output to the terminal. This way failed commands can be debugged.
		if _, err := os.Stdout.WriteString(m.output); err != nil {
			return fmt.Errorf("failed to write to stdout: %w", err)
		}
	}

	return exit.ErrExit(m.status)
}