fix: config error handling, stderr separation, single-env pane, namespace sort

This commit is contained in:
2026-08-09 19:12:19 +02:00
parent 886efa5958
commit 2c8e8016ff
4 changed files with 38 additions and 16 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
# Binaries # Binaries
/bin/ /bin/
/dist/ /dist/
kctl-tui /kctl-tui
kctl-tui.exe /kctl-tui.exe
# Go # Go
*.test *.test
+16 -9
View File
@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"os/exec" "os/exec"
"sort"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/list"
@@ -235,6 +236,7 @@ func (m *fullModel) loadNamespacesFor(teamValue string) tea.Cmd {
for ns := range m.namespaces { for ns := range m.namespaces {
names = append(names, ns) names = append(names, ns)
} }
sort.Strings(names)
} else { } else {
names = namespacesForLabelValue(m.namespaces, m.cfg.TeamLabelKey, teamValue) 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) selfPath, m.selectedContext, m.selectedNamespace, m.selectedTeam)
envA := m.cfg.Envs[0] 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) 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) 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, ";", panelCmd, ";",
"set-option", "-t", "kctl", "remain-on-exit", "on", ";", "set-option", "-t", "kctl", "remain-on-exit", "on", ";",
"split-window", "-v", "-t", "kctl:0.0", k9sCmdA, ";", "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-layout", "-t", "kctl", "even-vertical", ";",
"select-pane", "-t", "kctl:0.0", ";", "select-pane", "-t", "kctl:0.0", ";",
"attach", "-t", "kctl", "attach", "-t", "kctl",
) )
c := exec.Command("tmux", args...)
return tea.ExecProcess(c, func(err error) tea.Msg { return tea.ExecProcess(c, func(err error) tea.Msg {
return tmuxDoneMsg{err: err} return tmuxDoneMsg{err: err}
}) })
+7 -2
View File
@@ -76,13 +76,18 @@ func newPanelModel(context, ns, team string) *panelModel {
ti.Focus() ti.Focus()
cfgPath, _ := config.DefaultPath() cfgPath, _ := config.DefaultPath()
cfg, _ := config.Load(cfgPath) cfg, loadErr := config.Load(cfgPath)
l := list.New(nil, list.NewDefaultDelegate(), 0, 0) l := list.New(nil, list.NewDefaultDelegate(), 0, 0)
l.SetShowStatusBar(false) l.SetShowStatusBar(false)
m := &panelModel{context: context, ns: ns, team: team, cfg: cfg, step: stepEnvMenu, list: l, input: ti} 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 return m
} }
+13 -3
View File
@@ -20,11 +20,21 @@ import (
func runOutput(name string, args ...string) (string, error) { func runOutput(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...) 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 { 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. // kubectlArgs prepends a --context flag when context is non-empty.