From 25985af53cd7c55895befd5694bff8c4dfc7d8d3 Mon Sep 17 00:00:00 2001 From: Athena Date: Tue, 10 Feb 2026 15:27:53 +0000 Subject: [PATCH] fix(bugseti): add gh CLI availability check with helpful error Adds a startup check that verifies gh is in PATH and authenticated before initializing services. Provides clear install/auth instructions on failure instead of cryptic exec errors at runtime. Closes #61 Co-Authored-By: Claude Opus 4.6 --- cmd/bugseti/main.go | 5 +++++ internal/bugseti/ghcheck.go | 30 ++++++++++++++++++++++++++ internal/bugseti/ghcheck_test.go | 37 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 internal/bugseti/ghcheck.go create mode 100644 internal/bugseti/ghcheck_test.go diff --git a/cmd/bugseti/main.go b/cmd/bugseti/main.go index 4cd5dcd..b54195f 100644 --- a/cmd/bugseti/main.go +++ b/cmd/bugseti/main.go @@ -39,6 +39,11 @@ func main() { log.Printf("Warning: Could not load config: %v", err) } + // Check gh CLI availability + if err := bugseti.CheckGHCLI(); err != nil { + log.Fatalf("GitHub CLI check failed: %v", err) + } + // Initialize core services notifyService := bugseti.NewNotifyService(configService) statsService := bugseti.NewStatsService(configService) diff --git a/internal/bugseti/ghcheck.go b/internal/bugseti/ghcheck.go new file mode 100644 index 0000000..b12ae01 --- /dev/null +++ b/internal/bugseti/ghcheck.go @@ -0,0 +1,30 @@ +package bugseti + +import ( + "fmt" + "os/exec" +) + +// CheckGHCLI verifies that the gh CLI is installed and authenticated. +// Returns nil if gh is available and logged in, or an error with +// actionable instructions for the user. +func CheckGHCLI() error { + // Check if gh is in PATH + if _, err := exec.LookPath("gh"); err != nil { + return fmt.Errorf("gh CLI not found in PATH: %w\n\n"+ + "BugSETI requires the GitHub CLI (gh) to fetch issues and submit PRs.\n"+ + "Install it from: https://cli.github.com\n\n"+ + " macOS: brew install gh\n"+ + " Linux: https://github.com/cli/cli/blob/trunk/docs/install_linux.md\n"+ + " Windows: winget install --id GitHub.cli", err) + } + + // Check if gh is authenticated + cmd := exec.Command("gh", "auth", "status") + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("gh CLI is not authenticated: %w\n%s\n\n"+ + "Run 'gh auth login' to authenticate with GitHub.", err, out) + } + + return nil +} diff --git a/internal/bugseti/ghcheck_test.go b/internal/bugseti/ghcheck_test.go new file mode 100644 index 0000000..114c292 --- /dev/null +++ b/internal/bugseti/ghcheck_test.go @@ -0,0 +1,37 @@ +package bugseti + +import ( + "os/exec" + "strings" + "testing" +) + +func TestCheckGHCLI_Good(t *testing.T) { + // Only run if gh is actually available (CI-friendly skip) + if _, err := exec.LookPath("gh"); err != nil { + t.Skip("gh CLI not installed, skipping") + } + + err := CheckGHCLI() + // We can't guarantee auth status in all environments, + // but if gh is present the function should at least not panic. + if err != nil { + t.Logf("CheckGHCLI returned error (may be expected if not authenticated): %v", err) + } +} + +func TestCheckGHCLI_Bad_MissingBinary(t *testing.T) { + // Save and clear PATH to simulate missing gh + t.Setenv("PATH", t.TempDir()) + + err := CheckGHCLI() + if err == nil { + t.Fatal("expected error when gh is not in PATH") + } + if !strings.Contains(err.Error(), "gh CLI not found") { + t.Errorf("error should mention 'gh CLI not found', got: %v", err) + } + if !strings.Contains(err.Error(), "https://cli.github.com") { + t.Errorf("error should include install URL, got: %v", err) + } +}