Files
gastown/internal/doctor/stale_binary_check.go
slit be35b3eaab feat(version): add stale binary detection with startup warning
Add detection for when the installed gt binary is out of date with the
source repository. This helps catch issues where commands fail mysteriously
because the installed binary doesn't have recent fixes.

Changes:
- Add internal/version package with stale binary detection logic
- Add startup warning in PersistentPreRunE when binary is stale
- Add gt doctor check for stale-binary
- Use prefix matching for commit comparison (handles short vs full hash)

The warning is non-blocking and only shows once per shell session via
the GT_STALE_WARNED environment variable.

Resolves: gt-ud912

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 18:14:17 -08:00

84 lines
2.2 KiB
Go

package doctor
import (
"fmt"
"github.com/steveyegge/gastown/internal/version"
)
// StaleBinaryCheck verifies the installed gt binary is up to date with the repo.
type StaleBinaryCheck struct {
FixableCheck
}
// NewStaleBinaryCheck creates a new stale binary check.
func NewStaleBinaryCheck() *StaleBinaryCheck {
return &StaleBinaryCheck{
FixableCheck: FixableCheck{
BaseCheck: BaseCheck{
CheckName: "stale-binary",
CheckDescription: "Check if gt binary is up to date with repo",
},
},
}
}
// Run checks if the binary is stale.
func (c *StaleBinaryCheck) Run(ctx *CheckContext) *CheckResult {
repoRoot, err := version.GetRepoRoot()
if err != nil {
return &CheckResult{
Name: c.Name(),
Status: StatusOK,
Message: "Cannot locate gt source repo (not a development environment)",
Details: []string{err.Error()},
}
}
info := version.CheckStaleBinary(repoRoot)
if info.Error != nil {
return &CheckResult{
Name: c.Name(),
Status: StatusOK,
Message: "Cannot determine binary version (dev build?)",
Details: []string{info.Error.Error()},
}
}
if info.IsStale {
msg := fmt.Sprintf("Binary is stale (built from %s, repo at %s)",
version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit))
if info.CommitsBehind > 0 {
msg = fmt.Sprintf("Binary is %d commits behind (built from %s, repo at %s)",
info.CommitsBehind, version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit))
}
return &CheckResult{
Name: c.Name(),
Status: StatusWarning,
Message: msg,
FixHint: "Run 'gt install' to rebuild and install",
}
}
return &CheckResult{
Name: c.Name(),
Status: StatusOK,
Message: fmt.Sprintf("Binary is up to date (%s)", version.ShortCommit(info.BinaryCommit)),
}
}
// Fix rebuilds and installs gt.
func (c *StaleBinaryCheck) Fix(ctx *CheckContext) error {
// Note: We don't auto-fix this because:
// 1. It requires building and installing, which takes time
// 2. It modifies system files outside the workspace
// 3. User should explicitly run 'gt install'
return fmt.Errorf("run 'gt install' manually to rebuild")
}
// CanFix returns false - stale binary should be fixed manually.
func (c *StaleBinaryCheck) CanFix() bool {
return false
}