When the gt binary is built from a local fork (e.g., ~/src/gastown), the staleness check would incorrectly warn about being stale because it compared against the rig's repo HEAD. This confused agents. The fix detects fork builds by checking if the binary's embedded commit exists in the target repo. If not, the binary was built from a different repo and we report "fork build" instead of "stale". Changes: - Add IsForkBuild field to StaleBinaryInfo - Add commitExistsInRepo helper to detect fork scenarios - Update gt stale command to show "Binary built from fork" status - Update doctor check to report fork builds as OK Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
130 lines
3.6 KiB
Go
130 lines
3.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/steveyegge/gastown/internal/style"
|
|
"github.com/steveyegge/gastown/internal/version"
|
|
)
|
|
|
|
var staleJSON bool
|
|
var staleQuiet bool
|
|
|
|
var staleCmd = &cobra.Command{
|
|
Use: "stale",
|
|
GroupID: GroupDiag,
|
|
Short: "Check if the gt binary is stale",
|
|
Long: `Check if the gt binary was built from an older commit than the current repo HEAD.
|
|
|
|
This command compares the commit hash embedded in the binary at build time
|
|
with the current HEAD of the gastown repository.
|
|
|
|
Examples:
|
|
gt stale # Human-readable output
|
|
gt stale --json # Machine-readable JSON output
|
|
gt stale --quiet # Exit code only (0=stale, 1=fresh)
|
|
|
|
Exit codes:
|
|
0 - Binary is stale (needs rebuild)
|
|
1 - Binary is fresh (up to date)
|
|
2 - Error (could not determine staleness)`,
|
|
RunE: runStale,
|
|
}
|
|
|
|
func init() {
|
|
staleCmd.Flags().BoolVar(&staleJSON, "json", false, "Output as JSON")
|
|
staleCmd.Flags().BoolVarP(&staleQuiet, "quiet", "q", false, "Exit code only (0=stale, 1=fresh)")
|
|
rootCmd.AddCommand(staleCmd)
|
|
}
|
|
|
|
// StaleOutput represents the JSON output structure.
|
|
type StaleOutput struct {
|
|
Stale bool `json:"stale"`
|
|
ForkBuild bool `json:"fork_build,omitempty"`
|
|
BinaryCommit string `json:"binary_commit"`
|
|
RepoCommit string `json:"repo_commit"`
|
|
CommitsBehind int `json:"commits_behind,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
func runStale(cmd *cobra.Command, args []string) error {
|
|
// Find the gastown repo
|
|
repoRoot, err := version.GetRepoRoot()
|
|
if err != nil {
|
|
if staleQuiet {
|
|
os.Exit(2)
|
|
}
|
|
if staleJSON {
|
|
return outputStaleJSON(StaleOutput{Error: err.Error()})
|
|
}
|
|
return fmt.Errorf("cannot find gastown repo: %w", err)
|
|
}
|
|
|
|
// Check staleness
|
|
info := version.CheckStaleBinary(repoRoot)
|
|
|
|
// Handle errors
|
|
if info.Error != nil {
|
|
if staleQuiet {
|
|
os.Exit(2)
|
|
}
|
|
if staleJSON {
|
|
return outputStaleJSON(StaleOutput{Error: info.Error.Error()})
|
|
}
|
|
return fmt.Errorf("staleness check failed: %w", info.Error)
|
|
}
|
|
|
|
// Quiet mode: just exit with appropriate code
|
|
// Fork builds are treated as "fresh" (exit 1) since they're intentional
|
|
if staleQuiet {
|
|
if info.IsStale && !info.IsForkBuild {
|
|
os.Exit(0)
|
|
}
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Build output
|
|
output := StaleOutput{
|
|
Stale: info.IsStale,
|
|
ForkBuild: info.IsForkBuild,
|
|
BinaryCommit: info.BinaryCommit,
|
|
RepoCommit: info.RepoCommit,
|
|
CommitsBehind: info.CommitsBehind,
|
|
}
|
|
|
|
if staleJSON {
|
|
return outputStaleJSON(output)
|
|
}
|
|
|
|
return outputStaleText(output)
|
|
}
|
|
|
|
func outputStaleJSON(output StaleOutput) error {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(output)
|
|
}
|
|
|
|
func outputStaleText(output StaleOutput) error {
|
|
if output.ForkBuild {
|
|
fmt.Printf("%s Binary built from fork\n", style.Success.Render("✓"))
|
|
fmt.Printf(" Commit: %s\n", version.ShortCommit(output.BinaryCommit))
|
|
fmt.Printf(" %s\n", style.Dim.Render("(commit not in rig repo - likely built from local fork)"))
|
|
} else if output.Stale {
|
|
fmt.Printf("%s Binary is stale\n", style.Warning.Render("⚠"))
|
|
fmt.Printf(" Binary: %s\n", version.ShortCommit(output.BinaryCommit))
|
|
fmt.Printf(" Repo: %s\n", version.ShortCommit(output.RepoCommit))
|
|
if output.CommitsBehind > 0 {
|
|
fmt.Printf(" %s\n", style.Dim.Render(fmt.Sprintf("(%d commits behind)", output.CommitsBehind)))
|
|
}
|
|
fmt.Printf("\n Run 'go install ./cmd/gt' to rebuild\n")
|
|
} else {
|
|
fmt.Printf("%s Binary is fresh\n", style.Success.Render("✓"))
|
|
fmt.Printf(" Commit: %s\n", version.ShortCommit(output.BinaryCommit))
|
|
}
|
|
return nil
|
|
}
|