Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/goose/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package main implements a cross-platform system tray application for monitoring GitHub pull requests.

Check failure on line 1 in cmd/goose/main.go

View workflow job for this annotation

GitHub Actions / golangci-lint

package has more than one godoc ("main") (godoclint)
// It displays incoming and outgoing PRs, highlighting those that are blocked and need attention.
// The app integrates with the Turn API to provide additional PR metadata and uses the GitHub API
// for fetching PR data.
Expand Down Expand Up @@ -39,7 +39,7 @@
maxPRsToProcess = 200
minUpdateInterval = 10 * time.Second
defaultUpdateInterval = 1 * time.Minute
blockedPRIconDuration = 5 * time.Minute
blockedPRIconDuration = 15 * time.Minute
maxRetryDelay = 2 * time.Minute
maxRetries = 10
minorFailureThreshold = 3
Expand Down
28 changes: 14 additions & 14 deletions cmd/goose/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"sync"
"testing"
"time"

"github.com/codeGROOVE-dev/turnclient/pkg/turn"
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -198,7 +200,7 @@ func TestMenuItemTitleTransition(t *testing.T) {
_ = ctx // Unused in this test but would be used for real menu operations
}

// TestWorkflowStateNewlyPublished tests that PRs with NEWLY_PUBLISHED workflow state get "- NEW!" suffix.
// TestWorkflowStateNewlyPublished tests that PRs with NEWLY_PUBLISHED workflow state get a 💎 bullet.
func TestWorkflowStateNewlyPublished(t *testing.T) {
tests := []struct {
name string
Expand All @@ -211,32 +213,32 @@ func TestWorkflowStateNewlyPublished(t *testing.T) {
Repository: "test/repo",
Number: 123,
ActionKind: "review",
WorkflowState: "NEWLY_PUBLISHED",
WorkflowState: string(turn.StateNewlyPublished),
NeedsReview: true,
UpdatedAt: time.Now(),
},
expectedTitle: " test/repo #123 — review - NEW!",
expectedTitle: "💎 test/repo #123 — review",
},
{
name: "newly_published_without_action",
pr: PR{
Repository: "test/repo",
Number: 456,
WorkflowState: "NEWLY_PUBLISHED",
WorkflowState: string(turn.StateNewlyPublished),
UpdatedAt: time.Now(),
},
expectedTitle: "test/repo #456 - NEW!",
expectedTitle: "💎 test/repo #456",
},
{
name: "newly_published_with_running_tests",
pr: PR{
Repository: "test/repo",
Number: 789,
TestState: "running",
WorkflowState: "NEWLY_PUBLISHED",
WorkflowState: string(turn.StateNewlyPublished),
UpdatedAt: time.Now(),
},
expectedTitle: "test/repo #789 — tests running... - NEW!",
expectedTitle: "💎 test/repo #789 — tests running...",
},
{
name: "not_newly_published_with_action",
Expand Down Expand Up @@ -266,13 +268,11 @@ func TestWorkflowStateNewlyPublished(t *testing.T) {
title = fmt.Sprintf("%s — tests running...", title)
}

// Add "- NEW!" suffix if workflow state is NEWLY_PUBLISHED
if pr.WorkflowState == "NEWLY_PUBLISHED" {
title = fmt.Sprintf("%s - NEW!", title)
}

// Add prefix based on blocked status
if pr.NeedsReview || pr.IsBlocked {
// Add prefix based on workflow state or blocked status
switch {
case pr.WorkflowState == string(turn.StateNewlyPublished):
title = fmt.Sprintf("💎 %s", title)
case pr.NeedsReview || pr.IsBlocked:
title = fmt.Sprintf("■ %s", title)
}

Expand Down
87 changes: 56 additions & 31 deletions cmd/goose/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"strings"
"time"

"github.com/codeGROOVE-dev/turnclient/pkg/turn"
"github.com/energye/systray" // needed for MenuItem type
)

Expand Down Expand Up @@ -265,7 +266,7 @@
}

// addPRSection adds a section of PRs to the menu.
func (app *App) addPRSection(ctx context.Context, prs []PR, sectionTitle string, blockedCount int) {

Check failure on line 269 in cmd/goose/ui.go

View workflow job for this annotation

GitHub Actions / golangci-lint

Function name: addPRSection, Cyclomatic Complexity: 34, Halstead Volume: 6022.05, Maintainability Index: 19 (maintidx)
slog.Debug("[MENU] addPRSection called",
"section", sectionTitle,
"pr_count", len(prs),
Expand Down Expand Up @@ -342,13 +343,12 @@
title = fmt.Sprintf("%s — tests running...", title)
}

// Add "- NEW!" suffix if workflow state is NEWLY_PUBLISHED
if sortedPRs[prIndex].WorkflowState == "NEWLY_PUBLISHED" {
title = fmt.Sprintf("%s - NEW!", title)
}

// Add bullet point or emoji based on PR status
if sortedPRs[prIndex].NeedsReview || sortedPRs[prIndex].IsBlocked {
switch {
case sortedPRs[prIndex].WorkflowState == string(turn.StateNewlyPublished):
// Use gem emoji for newly published PRs
title = fmt.Sprintf("💎 %s", title)
case sortedPRs[prIndex].NeedsReview || sortedPRs[prIndex].IsBlocked:
// Get the blocked time from state manager
prState, hasState := app.stateManager.PRState(sortedPRs[prIndex].URL)

Expand All @@ -358,16 +358,27 @@
time.Since(prState.FirstBlockedAt) < blockedPRIconDuration &&
!prState.IsInitialDiscovery {
timeSinceBlocked := time.Since(prState.FirstBlockedAt)
// Use party popper for outgoing PRs, goose for incoming PRs
// Use cockroach for fix_tests, party popper for other outgoing PRs, goose for incoming PRs
if sectionTitle == "Outgoing" {
title = fmt.Sprintf("🎉 %s", title)
slog.Info("[MENU] Adding party popper to outgoing PR",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
if sortedPRs[prIndex].ActionKind == "fix_tests" {
title = fmt.Sprintf("🪳 %s", title)
slog.Info("[MENU] Adding cockroach to outgoing PR with broken tests",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
} else {
title = fmt.Sprintf("🎉 %s", title)
slog.Info("[MENU] Adding party popper to outgoing PR",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
}
} else {
title = fmt.Sprintf("🪿 %s", title)
slog.Debug("[MENU] Adding goose to incoming PR",
Expand All @@ -394,9 +405,11 @@
}
}
}
} else if sortedPRs[prIndex].ActionKind != "" {
case sortedPRs[prIndex].ActionKind != "":
// PR has an action but isn't blocked - add bullet to indicate it could use input
title = fmt.Sprintf("• %s", title)
default:
// No special prefix needed
}
// Format age inline for tooltip
duration := time.Since(sortedPRs[prIndex].UpdatedAt)
Expand Down Expand Up @@ -538,28 +551,38 @@
title = fmt.Sprintf("%s — tests running...", title)
}

// Add "- NEW!" suffix if workflow state is NEWLY_PUBLISHED
if sortedPRs[prIndex].WorkflowState == "NEWLY_PUBLISHED" {
title = fmt.Sprintf("%s - NEW!", title)
}

// Add bullet point or emoji for blocked PRs (same logic as in addPRSection)
if sortedPRs[prIndex].NeedsReview || sortedPRs[prIndex].IsBlocked {
switch {
case sortedPRs[prIndex].WorkflowState == string(turn.StateNewlyPublished):
// Use gem emoji for newly published PRs
title = fmt.Sprintf("💎 %s", title)
case sortedPRs[prIndex].NeedsReview || sortedPRs[prIndex].IsBlocked:
prState, hasState := app.stateManager.PRState(sortedPRs[prIndex].URL)

if hasState && !prState.FirstBlockedAt.IsZero() &&
time.Since(prState.FirstBlockedAt) < blockedPRIconDuration &&
!prState.IsInitialDiscovery {
timeSinceBlocked := time.Since(prState.FirstBlockedAt)
if sectionTitle == "Outgoing" {
title = fmt.Sprintf("🎉 %s", title)
slog.Info("[MENU] Adding party popper to outgoing PR in generateMenuTitles",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
if sortedPRs[prIndex].ActionKind == "fix_tests" {
title = fmt.Sprintf("🪳 %s", title)
slog.Info("[MENU] Adding cockroach to outgoing PR with broken tests in generateMenuTitles",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
} else {
title = fmt.Sprintf("🎉 %s", title)
slog.Info("[MENU] Adding party popper to outgoing PR in generateMenuTitles",
"repo", sortedPRs[prIndex].Repository,
"number", sortedPRs[prIndex].Number,
"url", sortedPRs[prIndex].URL,
"firstBlockedAt", prState.FirstBlockedAt.Format(time.RFC3339),
"blocked_ago", timeSinceBlocked.Round(time.Second),
"remaining", (blockedPRIconDuration - timeSinceBlocked).Round(time.Second))
}
} else {
title = fmt.Sprintf("🪿 %s", title)
slog.Debug("[MENU] Adding goose to incoming PR in generateMenuTitles",
Expand All @@ -586,9 +609,11 @@
"number", sortedPRs[prIndex].Number)
}
}
} else if sortedPRs[prIndex].ActionKind != "" {
case sortedPRs[prIndex].ActionKind != "":
// PR has an action but isn't blocked - add bullet to indicate it could use input
title = fmt.Sprintf("• %s", title)
default:
// No special prefix needed
}

titles = append(titles, title)
Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ module github.com/codeGROOVE-dev/goose
go 1.24.0

require (
github.com/codeGROOVE-dev/retry v1.2.0
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027122631-1d61827b70ca
github.com/codeGROOVE-dev/turnclient v0.0.0-20251022064427-5a712e1e10e6
github.com/codeGROOVE-dev/retry v1.3.0
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027213037-05bb80a9db89
github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4
github.com/energye/systray v1.0.2
github.com/gen2brain/beeep v0.11.1
github.com/google/go-github/v57 v57.0.0
Expand All @@ -14,7 +14,7 @@ require (

require (
git.sr.ht/~jackmordaunt/go-toast v1.1.2 // indirect
github.com/codeGROOVE-dev/prx v0.0.0-20251027012315-7b273aabfc7d // indirect
github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5 // indirect
github.com/esiqveland/notify v0.13.3 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ git.sr.ht/~jackmordaunt/go-toast v1.1.2 h1:/yrfI55LRt1M7H1vkaw+NaH1+L1CDxrqDltwm
git.sr.ht/~jackmordaunt/go-toast v1.1.2/go.mod h1:jA4OqHKTQ4AFBdwrSnwnskUIIS3HYzlJSgdzCKqfavo=
github.com/codeGROOVE-dev/prx v0.0.0-20251027012315-7b273aabfc7d h1:kUaCKFRxWFrWEyl4fVHi+eY/D5tKhBU29a8YbQyihEk=
github.com/codeGROOVE-dev/prx v0.0.0-20251027012315-7b273aabfc7d/go.mod h1:7qLbi18baOyS8yO/6/64SBIqtyzSzLFdsDST15NPH3w=
github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5 h1:tjxTLJ5NXx1xhReL4M+J4LTl/JGNSZjPrznAoci06OA=
github.com/codeGROOVE-dev/prx v0.0.0-20251027204543-4e6165f046e5/go.mod h1:FEy3gz9IYDXWnKWkoDSL+pWu6rujxbBSrF4w5A8QSK0=
github.com/codeGROOVE-dev/retry v1.2.0 h1:xYpYPX2PQZmdHwuiQAGGzsBm392xIMl4nfMEFApQnu8=
github.com/codeGROOVE-dev/retry v1.2.0/go.mod h1:8OgefgV1XP7lzX2PdKlCXILsYKuz6b4ZpHa/20iLi8E=
github.com/codeGROOVE-dev/retry v1.3.0 h1:/+ipAWRJLL6y1R1vprYo0FSjSBvH6fE5j9LKXjpD54g=
github.com/codeGROOVE-dev/retry v1.3.0/go.mod h1:8OgefgV1XP7lzX2PdKlCXILsYKuz6b4ZpHa/20iLi8E=
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027122631-1d61827b70ca h1:NDBJTf69PxMsZkZLUjvnfiMQHWL6Y2T4jQT5YNzXTXA=
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027122631-1d61827b70ca/go.mod h1:/kd3ncsRNldD0MUpbtp5ojIzfCkyeXB7JdOrpuqG7Gg=
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027213037-05bb80a9db89 h1:8Z3SM90hy1nuK2r2yhtv4HwitnO9si4GzVRktRDQ68g=
github.com/codeGROOVE-dev/sprinkler v0.0.0-20251027213037-05bb80a9db89/go.mod h1:/kd3ncsRNldD0MUpbtp5ojIzfCkyeXB7JdOrpuqG7Gg=
github.com/codeGROOVE-dev/turnclient v0.0.0-20251022064427-5a712e1e10e6 h1:7FCmaftkl362oTZHVJyUg+xhxqfQFx+JisBf7RgklL8=
github.com/codeGROOVE-dev/turnclient v0.0.0-20251022064427-5a712e1e10e6/go.mod h1:fYwtN9Ql6lY8t2WvCfENx+mP5FUwjlqwXCLx9CVLY20=
github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4 h1:si9tMEo5SXpDuDXGkJ1zNnnpP8TbmakrkNujAbpKlqA=
github.com/codeGROOVE-dev/turnclient v0.0.0-20251028130307-1f85c9aa43c4/go.mod h1:bFWMd0JeaJY0kSIO5AcRQdJLXF3Fo3eKclE49vmIZes=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
Loading