diff --git a/internal/cmd/stale.go b/internal/cmd/stale.go index 068024ff..1a019652 100644 --- a/internal/cmd/stale.go +++ b/internal/cmd/stale.go @@ -43,6 +43,7 @@ func init() { // 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"` @@ -77,8 +78,9 @@ func runStale(cmd *cobra.Command, args []string) 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 { + if info.IsStale && !info.IsForkBuild { os.Exit(0) } os.Exit(1) @@ -87,6 +89,7 @@ func runStale(cmd *cobra.Command, args []string) error { // Build output output := StaleOutput{ Stale: info.IsStale, + ForkBuild: info.IsForkBuild, BinaryCommit: info.BinaryCommit, RepoCommit: info.RepoCommit, CommitsBehind: info.CommitsBehind, @@ -106,7 +109,11 @@ func outputStaleJSON(output StaleOutput) error { } func outputStaleText(output StaleOutput) error { - if output.Stale { + 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)) diff --git a/internal/doctor/stale_binary_check.go b/internal/doctor/stale_binary_check.go index 60a64c75..cf7ed0f9 100644 --- a/internal/doctor/stale_binary_check.go +++ b/internal/doctor/stale_binary_check.go @@ -46,6 +46,16 @@ func (c *StaleBinaryCheck) Run(ctx *CheckContext) *CheckResult { } } + // Fork builds are fine - the binary was built from a different repo + if info.IsForkBuild { + return &CheckResult{ + Name: c.Name(), + Status: StatusOK, + Message: fmt.Sprintf("Binary built from fork (%s)", version.ShortCommit(info.BinaryCommit)), + Details: []string{"Binary commit not in rig repo - likely built from local fork"}, + } + } + if info.IsStale { msg := fmt.Sprintf("Binary is stale (built from %s, repo at %s)", version.ShortCommit(info.BinaryCommit), version.ShortCommit(info.RepoCommit)) diff --git a/internal/version/stale.go b/internal/version/stale.go index 2c62f188..bc10c390 100644 --- a/internal/version/stale.go +++ b/internal/version/stale.go @@ -21,6 +21,7 @@ var ( // StaleBinaryInfo contains information about binary staleness. type StaleBinaryInfo struct { IsStale bool // True if binary commit doesn't match repo HEAD + IsForkBuild bool // True if binary was built from a different repo (fork) BinaryCommit string // Commit hash the binary was built from RepoCommit string // Current repo HEAD commit CommitsBehind int // Number of commits binary is behind (0 if unknown) @@ -66,6 +67,15 @@ func commitsMatch(a, b string) bool { return strings.HasPrefix(a, b[:minLen]) || strings.HasPrefix(b, a[:minLen]) } +// commitExistsInRepo checks if a commit hash exists in the given repository. +// This is used to detect fork builds - if the binary's commit doesn't exist +// in the target repo, the binary was likely built from a different repo. +func commitExistsInRepo(commit, repoDir string) bool { + cmd := exec.Command("git", "cat-file", "-t", commit) + cmd.Dir = repoDir + return cmd.Run() == nil +} + // CheckStaleBinary compares the binary's embedded commit with the repo HEAD. // It returns staleness info including whether the binary needs rebuilding. // This check is designed to be fast and non-blocking - errors are captured @@ -93,6 +103,14 @@ func CheckStaleBinary(repoDir string) *StaleBinaryInfo { // Compare commits using prefix matching (handles short vs full hash) // Use the shorter of the two commit lengths for comparison if !commitsMatch(info.BinaryCommit, info.RepoCommit) { + // Check if the binary's commit exists in this repo. + // If it doesn't, the binary was built from a different repo (fork). + // Don't warn about staleness in that case. + if !commitExistsInRepo(info.BinaryCommit, repoDir) { + info.IsForkBuild = true + return info + } + info.IsStale = true // Try to count commits between binary and HEAD