Files
gastown/internal/cmd/version.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

91 lines
2.0 KiB
Go

package cmd
import (
"fmt"
"os/exec"
"runtime/debug"
"strings"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/version"
)
// Version information - set at build time via ldflags
var (
Version = "0.2.3"
// Build can be set via ldflags at compile time
Build = "dev"
// Commit and Branch - the git revision the binary was built from (optional ldflag)
Commit = ""
Branch = ""
)
var versionCmd = &cobra.Command{
Use: "version",
GroupID: GroupDiag,
Short: "Print version information",
Run: func(cmd *cobra.Command, args []string) {
commit := resolveCommitHash()
branch := resolveBranch()
if commit != "" && branch != "" {
fmt.Printf("gt version %s (%s: %s@%s)\n", Version, Build, branch, version.ShortCommit(commit))
} else if commit != "" {
fmt.Printf("gt version %s (%s: %s)\n", Version, Build, version.ShortCommit(commit))
} else {
fmt.Printf("gt version %s (%s)\n", Version, Build)
}
},
}
func init() {
rootCmd.AddCommand(versionCmd)
// Pass the build-time commit to the version package for stale binary checks
if Commit != "" {
version.SetCommit(Commit)
}
}
func resolveCommitHash() string {
if Commit != "" {
return Commit
}
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" && setting.Value != "" {
return setting.Value
}
}
}
return ""
}
func resolveBranch() string {
if Branch != "" {
return Branch
}
// Try to get branch from build info (build-time VCS detection)
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.branch" && setting.Value != "" {
return setting.Value
}
}
}
// Fallback: try to get branch from git at runtime
cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD")
cmd.Dir = "."
if output, err := cmd.Output(); err == nil {
if branch := strings.TrimSpace(string(output)); branch != "" && branch != "HEAD" {
return branch
}
}
return ""
}