feat(doctor): add env-vars check, remove redundant gtroot check
Adds a new `gt doctor` check that verifies tmux session environment variables match expected values from `config.RoleEnvVars()`. - Checks all Gas Town sessions (gt-*, hq-*) - Compares actual tmux env vars against expected for each role - Reports mismatches with guidance to restart sessions - Treats no sessions as success (valid when Gas Town is down) - Skips deacon (doesn't use standard env vars) Also: - Adds `tmux.GetAllEnvironment()` to retrieve all session env vars - Removes redundant gtroot_check (env-vars check covers GT_ROOT) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
1d88a73eaa
commit
52b9a95f98
147
internal/doctor/env_check.go
Normal file
147
internal/doctor/env_check.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/session"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
)
|
||||
|
||||
// SessionEnvReader abstracts tmux session environment access for testing.
|
||||
type SessionEnvReader interface {
|
||||
ListSessions() ([]string, error)
|
||||
GetAllEnvironment(session string) (map[string]string, error)
|
||||
}
|
||||
|
||||
// tmuxEnvReader wraps real tmux operations.
|
||||
type tmuxEnvReader struct {
|
||||
t *tmux.Tmux
|
||||
}
|
||||
|
||||
func (r *tmuxEnvReader) ListSessions() ([]string, error) {
|
||||
return r.t.ListSessions()
|
||||
}
|
||||
|
||||
func (r *tmuxEnvReader) GetAllEnvironment(session string) (map[string]string, error) {
|
||||
return r.t.GetAllEnvironment(session)
|
||||
}
|
||||
|
||||
// EnvVarsCheck verifies that tmux session environment variables match expected values.
|
||||
type EnvVarsCheck struct {
|
||||
BaseCheck
|
||||
reader SessionEnvReader // nil means use real tmux
|
||||
}
|
||||
|
||||
// NewEnvVarsCheck creates a new env vars check.
|
||||
func NewEnvVarsCheck() *EnvVarsCheck {
|
||||
return &EnvVarsCheck{
|
||||
BaseCheck: BaseCheck{
|
||||
CheckName: "env-vars",
|
||||
CheckDescription: "Verify tmux session environment variables match expected values",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewEnvVarsCheckWithReader creates a check with a custom reader (for testing).
|
||||
func NewEnvVarsCheckWithReader(reader SessionEnvReader) *EnvVarsCheck {
|
||||
c := NewEnvVarsCheck()
|
||||
c.reader = reader
|
||||
return c
|
||||
}
|
||||
|
||||
// Run checks environment variables for all Gas Town sessions.
|
||||
func (c *EnvVarsCheck) Run(ctx *CheckContext) *CheckResult {
|
||||
reader := c.reader
|
||||
if reader == nil {
|
||||
reader = &tmuxEnvReader{t: tmux.NewTmux()}
|
||||
}
|
||||
|
||||
sessions, err := reader.ListSessions()
|
||||
if err != nil {
|
||||
// No tmux server - treat as success (valid when Gas Town is down)
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusOK,
|
||||
Message: "No tmux sessions running",
|
||||
}
|
||||
}
|
||||
|
||||
// Filter to Gas Town sessions only (gt-* and hq-*)
|
||||
var gtSessions []string
|
||||
for _, sess := range sessions {
|
||||
if strings.HasPrefix(sess, "gt-") || strings.HasPrefix(sess, "hq-") {
|
||||
gtSessions = append(gtSessions, sess)
|
||||
}
|
||||
}
|
||||
|
||||
if len(gtSessions) == 0 {
|
||||
// No Gas Town sessions - treat as success (valid when Gas Town is down)
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusOK,
|
||||
Message: "No Gas Town sessions running",
|
||||
}
|
||||
}
|
||||
|
||||
var mismatches []string
|
||||
checkedCount := 0
|
||||
|
||||
for _, sess := range gtSessions {
|
||||
identity, err := session.ParseSessionName(sess)
|
||||
if err != nil {
|
||||
// Skip unparseable sessions
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip deacon - it doesn't use the standard env vars
|
||||
if identity.Role == session.RoleDeacon {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get expected env vars based on role
|
||||
expected := config.RoleEnvVars(string(identity.Role), identity.Rig, identity.Name)
|
||||
|
||||
// Get actual tmux env vars
|
||||
actual, err := reader.GetAllEnvironment(sess)
|
||||
if err != nil {
|
||||
mismatches = append(mismatches, fmt.Sprintf("%s: could not read env vars: %v", sess, err))
|
||||
continue
|
||||
}
|
||||
|
||||
checkedCount++
|
||||
|
||||
// Compare each expected var
|
||||
for key, expectedVal := range expected {
|
||||
actualVal, exists := actual[key]
|
||||
if !exists {
|
||||
mismatches = append(mismatches, fmt.Sprintf("%s: missing %s (expected %q)", sess, key, expectedVal))
|
||||
} else if actualVal != expectedVal {
|
||||
mismatches = append(mismatches, fmt.Sprintf("%s: %s=%q (expected %q)", sess, key, actualVal, expectedVal))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(mismatches) == 0 {
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusOK,
|
||||
Message: fmt.Sprintf("All %d session(s) have correct environment variables", checkedCount),
|
||||
}
|
||||
}
|
||||
|
||||
// Add explanation about needing restart
|
||||
details := append(mismatches,
|
||||
"",
|
||||
"Note: Mismatched session env vars won't affect running Claude until sessions restart.",
|
||||
)
|
||||
|
||||
return &CheckResult{
|
||||
Name: c.Name(),
|
||||
Status: StatusWarning,
|
||||
Message: fmt.Sprintf("Found %d env var mismatch(es) across %d session(s)", len(mismatches), checkedCount),
|
||||
Details: details,
|
||||
FixHint: "Run 'gt shutdown && gt up' to restart sessions with correct env vars",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user