diff --git a/.gitignore b/.gitignore index a6bb9c4..73cc89a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ # Binaries /bin/ /dist/ -kctl-tui -kctl-tui.exe +/kctl-tui +/kctl-tui.exe # Go *.test diff --git a/cmd/kctl-tui/full.go b/cmd/kctl-tui/full.go index 2e7fb16..10ea1b6 100644 --- a/cmd/kctl-tui/full.go +++ b/cmd/kctl-tui/full.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os/exec" + "sort" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/bubbles/list" @@ -235,6 +236,7 @@ func (m *fullModel) loadNamespacesFor(teamValue string) tea.Cmd { for ns := range m.namespaces { names = append(names, ns) } + sort.Strings(names) } else { names = namespacesForLabelValue(m.namespaces, m.cfg.TeamLabelKey, teamValue) } @@ -257,26 +259,31 @@ func (m *fullModel) startTmuxSession() tea.Cmd { selfPath, m.selectedContext, m.selectedNamespace, m.selectedTeam) envA := m.cfg.Envs[0] - envB := m.cfg.Envs[0] - if len(m.cfg.Envs) > 1 { - envB = m.cfg.Envs[1] - } ctxA := m.cfg.ResolveContext(envA, m.selectedContext) - ctxB := m.cfg.ResolveContext(envB, m.selectedContext) - k9sCmdA := fmt.Sprintf("k9s --context %s -n %s", ctxA, m.selectedNamespace) - k9sCmdB := fmt.Sprintf("k9s --context %s -n %s", ctxB, m.selectedNamespace) - c := exec.Command("tmux", "new-session", "-d", "-s", "kctl", + args := []string{ + "new-session", "-d", "-s", "kctl", panelCmd, ";", "set-option", "-t", "kctl", "remain-on-exit", "on", ";", "split-window", "-v", "-t", "kctl:0.0", k9sCmdA, ";", - "split-window", "-v", "-t", "kctl:0.1", k9sCmdB, ";", + } + if len(m.cfg.Envs) > 1 { + envB := m.cfg.Envs[1] + ctxB := m.cfg.ResolveContext(envB, m.selectedContext) + k9sCmdB := fmt.Sprintf("k9s --context %s -n %s", ctxB, m.selectedNamespace) + args = append(args, + "split-window", "-v", "-t", "kctl:0.1", k9sCmdB, ";", + ) + } + args = append(args, "select-layout", "-t", "kctl", "even-vertical", ";", "select-pane", "-t", "kctl:0.0", ";", "attach", "-t", "kctl", ) + c := exec.Command("tmux", args...) + return tea.ExecProcess(c, func(err error) tea.Msg { return tmuxDoneMsg{err: err} }) diff --git a/cmd/kctl-tui/panel.go b/cmd/kctl-tui/panel.go index 971495a..c04d31d 100644 --- a/cmd/kctl-tui/panel.go +++ b/cmd/kctl-tui/panel.go @@ -76,13 +76,18 @@ func newPanelModel(context, ns, team string) *panelModel { ti.Focus() cfgPath, _ := config.DefaultPath() - cfg, _ := config.Load(cfgPath) + cfg, loadErr := config.Load(cfgPath) l := list.New(nil, list.NewDefaultDelegate(), 0, 0) l.SetShowStatusBar(false) m := &panelModel{context: context, ns: ns, team: team, cfg: cfg, step: stepEnvMenu, list: l, input: ti} - m.showEnvMenu() + if loadErr != nil { + m.err = fmt.Errorf("config load failed: %w", loadErr) + m.step = stepError + } else { + m.showEnvMenu() + } return m } diff --git a/internal/kubeexec/kubeexec.go b/internal/kubeexec/kubeexec.go index 1e7d4f7..b6ee7e5 100644 --- a/internal/kubeexec/kubeexec.go +++ b/internal/kubeexec/kubeexec.go @@ -20,11 +20,21 @@ import ( func runOutput(name string, args ...string) (string, error) { cmd := exec.Command(name, args...) - out, err := cmd.CombinedOutput() + var stdout, stderr strings.Builder + cmd.Stdout = &stdout + cmd.Stderr = &stderr + err := cmd.Run() if err != nil { - return "", fmt.Errorf("%s %s failed: %w\n%s", name, strings.Join(args, " "), err, string(out)) + msg := fmt.Sprintf("%s %s failed: %v", name, strings.Join(args, " "), err) + if s := strings.TrimSpace(stderr.String()); s != "" { + msg += "\n" + s + } + if s := strings.TrimSpace(stdout.String()); s != "" { + msg += "\n" + s + } + return "", fmt.Errorf("%s", msg) } - return strings.TrimSpace(string(out)), nil + return strings.TrimSpace(stdout.String()), nil } // kubectlArgs prepends a --context flag when context is non-empty.