feat(v0.5.3): doctor -- checkTmux three-state helper
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warrenronsiek/ctask/internal/config"
|
||||
"github.com/warrenronsiek/ctask/internal/shell"
|
||||
"github.com/warrenronsiek/ctask/internal/workspace"
|
||||
)
|
||||
|
||||
@@ -203,6 +204,9 @@ func runDoctor(cmd *cobra.Command, args []string) error {
|
||||
// Check 8: CTASK_PROJECT_ROOT (v0.5).
|
||||
checkProjectRoot(&passed, &failed)
|
||||
|
||||
// Check 9: tmux availability for persistent session mode (v0.5.3).
|
||||
checkTmux(&passed, &failed)
|
||||
|
||||
// Summary
|
||||
fmt.Println()
|
||||
fmt.Printf("%d checks passed, %d failed\n", passed, failed)
|
||||
@@ -254,3 +258,29 @@ func checkSeedDir(label, envValue, resolved, envName string, passed, failed *int
|
||||
fmt.Printf(" Fix: create the directory or unset %s to use built-in defaults.\n", envName)
|
||||
*failed++
|
||||
}
|
||||
|
||||
// checkTmux reports the three-state tmux check (v0.5.3):
|
||||
// - CTASK_SESSION_MODE != "persistent" -> INFO (direct mode, tmux optional)
|
||||
// - persistent + tmux on PATH + version OK -> two INFO lines
|
||||
// - persistent + tmux missing or too old -> FAIL with install/update hint
|
||||
func checkTmux(passed, failed *int) {
|
||||
_ = passed
|
||||
mode := config.ResolveSessionMode()
|
||||
if mode != "persistent" {
|
||||
fmt.Printf(" [INFO] Session mode: direct (tmux not required)\n")
|
||||
return
|
||||
}
|
||||
fmt.Printf(" [INFO] Session mode: persistent\n")
|
||||
tmuxPath, ver, err := shell.LookupTmux()
|
||||
if err != nil {
|
||||
fmt.Printf(" [FAIL] tmux not found on PATH or unsupported version: %v\n", err)
|
||||
fmt.Printf(" Fix: install tmux 3.0+ (apt/brew/pacman/dnf), or unset CTASK_SESSION_MODE\n")
|
||||
*failed++
|
||||
return
|
||||
}
|
||||
rawVer := ver.Raw
|
||||
if rawVer == "" {
|
||||
rawVer = "unknown version"
|
||||
}
|
||||
fmt.Printf(" [INFO] tmux found: %s (%s)\n", rawVer, tmuxPath)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,31 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// captureStdout runs fn while capturing os.Stdout and returns the output.
|
||||
func captureStdout(t *testing.T, fn func()) string {
|
||||
t.Helper()
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
t.Fatalf("pipe: %v", err)
|
||||
}
|
||||
orig := os.Stdout
|
||||
os.Stdout = w
|
||||
defer func() { os.Stdout = orig }()
|
||||
|
||||
fn()
|
||||
w.Close()
|
||||
data, _ := io.ReadAll(r)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// This file swaps process-global os.Stdout and env vars. Do not call
|
||||
// t.Parallel() in this file.
|
||||
|
||||
@@ -161,3 +180,50 @@ func TestDoctorProjectRootSetButMissingFails(t *testing.T) {
|
||||
t.Errorf("expected FAIL line, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTmuxNotConfigured(t *testing.T) {
|
||||
os.Unsetenv("CTASK_SESSION_MODE")
|
||||
out := captureStdout(t, func() {
|
||||
passed, failed := 0, 0
|
||||
checkTmux(&passed, &failed)
|
||||
})
|
||||
if !strings.Contains(out, "Session mode: direct") {
|
||||
t.Errorf("expected 'Session mode: direct' info line: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTmuxConfiguredAndPresent(t *testing.T) {
|
||||
if _, err := exec.LookPath("tmux"); err != nil {
|
||||
t.Skip("tmux not on PATH")
|
||||
}
|
||||
os.Setenv("CTASK_SESSION_MODE", "persistent")
|
||||
defer os.Unsetenv("CTASK_SESSION_MODE")
|
||||
out := captureStdout(t, func() {
|
||||
passed, failed := 0, 0
|
||||
checkTmux(&passed, &failed)
|
||||
})
|
||||
if !strings.Contains(out, "Session mode: persistent") {
|
||||
t.Errorf("expected 'Session mode: persistent': %q", out)
|
||||
}
|
||||
if !strings.Contains(out, "tmux found") {
|
||||
t.Errorf("expected 'tmux found' info line: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckTmuxConfiguredAndMissing(t *testing.T) {
|
||||
orig := os.Getenv("PATH")
|
||||
defer os.Setenv("PATH", orig)
|
||||
os.Setenv("PATH", "")
|
||||
os.Setenv("CTASK_SESSION_MODE", "persistent")
|
||||
defer os.Unsetenv("CTASK_SESSION_MODE")
|
||||
|
||||
failed := 0
|
||||
passed := 0
|
||||
out := captureStdout(t, func() { checkTmux(&passed, &failed) })
|
||||
if failed != 1 {
|
||||
t.Errorf("missing tmux must increment failed counter; got %d", failed)
|
||||
}
|
||||
if !strings.Contains(out, "tmux not found") {
|
||||
t.Errorf("expected 'tmux not found' fail line: %q", out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user