Skip to content

Commit 59de9fb

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
Merge remote-tracking branch 'upstream/main'
2 parents 26001da + 00c68d5 commit 59de9fb

26 files changed

+1791
-357
lines changed

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,14 @@ install-darwin: app-bundle
170170
install-unix: build
171171
@echo "Installing on $(shell uname)..."
172172
@echo "Installing binary to /usr/local/bin..."
173-
@sudo install -m 755 out/$(APP_NAME) /usr/local/bin/
173+
@if command -v sudo >/dev/null 2>&1; then \
174+
sudo install -m 755 out/$(APP_NAME) /usr/local/bin/; \
175+
elif command -v doas >/dev/null 2>&1; then \
176+
doas install -m 755 out/$(APP_NAME) /usr/local/bin/; \
177+
else \
178+
echo "Error: Neither sudo nor doas found. Please install the binary manually."; \
179+
exit 1; \
180+
fi
174181
@echo "Installation complete! $(APP_NAME) has been installed to /usr/local/bin"
175182

176183
# Install on Windows

README.md

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,6 @@ The only PR tracker that honks at you when you're the bottleneck. Now shipping w
1212
Lives in your menubar like a tiny waterfowl of productivity shame, watching your GitHub PRs and making aggressive bird sounds when you're blocking someone's code from seeing the light of production.
1313

1414
![Review Goose Screenshot](media/screenshot.png)
15-
16-
## What It Does
17-
18-
- **Honks** when you're blocking someone's PR (authentic goose noises included)
19-
- **Jet sounds** when your own PR is ready for the next stage
20-
- **Smart turn-based assignment** - knows who is blocking a PR, knows when tests are failing, etc.
21-
- **Auto-start** on login (macOS)
22-
- **Auto-open** incoming PRs in your browser (off by default, rate-limited)
23-
- **Org Filtering** for orgs you may not care about in a home or work context
24-
* **Multi-platform** support for Linux, macOS, Windows, FreeBSD, whatever.
25-
26-
You can also visit the web-based dashboard at https://dash.ready-to-review.dev/
27-
28-
## Dependencies
29-
30-
* [go](https://go.dev/) 1.23.4 or higher
31-
* [gh](https://cli.github.com/), AKA the GitHub command-line utility
32-
3315
## macOS Quick Start ⚡ (Get Honked At)
3416

3517
Install dependencies:
@@ -53,7 +35,19 @@ cd goose && make run
5335

5436
This will will cause the goose to implant itself into `/Applications/Review Goose.app` for future invocations. To be persistently annoyed every time you login, click the `Start at Login` menu item.
5537

56-
### Using a fine-grained access token
38+
## Linux/BSD/Windows Quick Start
39+
40+
1. Install the GitHub CLI and Go via your platforms recommended methods
41+
2. Compile and install Goose:
42+
43+
```bash
44+
go install github.com/codeGROOVE-dev/goose/cmd/goose@latest
45+
```
46+
47+
3. Copy goose from $HOME/go/bin to wherever you prefer
48+
4. Add goose to your auto-login so you never foget about PRs again
49+
50+
## Using a fine-grained access token
5751

5852
If you want more control over which repositories the goose can access, you can use a [fine-grained personal access token](https://github.com/settings/personal-access-tokens/new) with the following permissions:
5953

@@ -68,13 +62,19 @@ env GITHUB_TOKEN=your_token_here goose
6862

6963
We don't yet persist fine-grained tokens to disk - PR's welcome!
7064

65+
## Usage
66+
67+
- **macOS/Windows**: Click the tray icon to show the menu
68+
- **Linux/BSD**: Right-click the tray icon to show the menu (left-click refreshes PRs)
69+
7170
## Known Issues
7271

73-
- Visual notifications won't work on macOS until we release signed binaries.
72+
- Visual notifications won't work reliably on macOS until we release signed binaries.
73+
- Tray icons on GNOME require [snixembed](https://git.sr.ht/~steef/snixembed) and enabling the [Legacy Tray extension](https://www.omgubuntu.co.uk/2024/08/gnome-official-status-icons-extension). Goose will automatically launch snixembed if needed, but you must install it first (e.g., `apt install snixembed` or `yay -S snixembed`).
7474

7575
## Pricing
7676

77-
- Free forever for public repositories ❤️
77+
- Free forever for public open-source repositories ❤️
7878
- Private repo access will soon be a supporter-only feature to ensure the goose is fed. ($1/mo)
7979

8080
## Privacy
@@ -85,6 +85,10 @@ We don't yet persist fine-grained tokens to disk - PR's welcome!
8585
- No data is resold to anyone. We don't even want it.
8686
- No telemetry is collected
8787

88+
## License
89+
90+
This project is licensed under the GPL-3.0 License - see the [LICENSE](LICENSE) file for details.
91+
8892
---
8993

9094
Built with 🪿 by [codeGROOVE](https://codegroove.dev/) - PRs welcome!

cmd/goose/cache.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313
"time"
1414

15+
"github.com/codeGROOVE-dev/goose/pkg/safebrowse"
1516
"github.com/codeGROOVE-dev/retry"
1617
"github.com/codeGROOVE-dev/turnclient/pkg/turn"
1718
)
@@ -97,7 +98,7 @@ func (app *App) checkCache(cacheFile, url string, updatedAt time.Time) (cachedDa
9798
func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (*turn.CheckResponse, bool, error) {
9899
hasRunningTests := false
99100
// Validate URL before processing
100-
if err := validateURL(url); err != nil {
101+
if err := safebrowse.ValidateURL(url); err != nil {
101102
return nil, false, fmt.Errorf("invalid URL: %w", err)
102103
}
103104

cmd/goose/icons.go

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import (
44
"log/slog"
55
)
66

7-
// Icon variables are defined in platform-specific files:
8-
// - icons_windows.go: uses .ico files.
9-
// - icons_unix.go: uses .png files.
7+
// Icon implementations are in platform-specific files:
8+
// - icons_darwin.go: macOS (static PNG icons, counts shown in title)
9+
// - icons_badge.go: Linux/BSD/Windows (dynamic circle badges with counts)
1010

1111
// IconType represents different icon states.
1212
type IconType int
@@ -21,33 +21,20 @@ const (
2121
IconLock // Authentication error
2222
)
2323

24-
// getIcon returns the icon bytes for the given type.
25-
func getIcon(iconType IconType) []byte {
26-
switch iconType {
27-
case IconGoose, IconBoth:
28-
// For both, we'll use the goose icon as primary
29-
return iconGoose
30-
case IconPopper:
31-
return iconPopper
32-
case IconCockroach:
33-
return iconCockroach
34-
case IconWarning:
35-
return iconWarning
36-
case IconLock:
37-
return iconLock
38-
default:
39-
return iconSmiling
40-
}
41-
}
24+
// getIcon returns icon bytes for the given type and counts.
25+
// Implementation is platform-specific:
26+
// - macOS: returns static icons (counts displayed in title bar)
27+
// - Linux/Windows: generates dynamic badges with embedded counts.
28+
// Implemented in icons_darwin.go and icons_badge.go.
4229

43-
// setTrayIcon updates the system tray icon based on PR counts.
44-
func (app *App) setTrayIcon(iconType IconType) {
45-
iconBytes := getIcon(iconType)
30+
// setTrayIcon updates the system tray icon.
31+
func (app *App) setTrayIcon(iconType IconType, counts PRCounts) {
32+
iconBytes := getIcon(iconType, counts)
4633
if len(iconBytes) == 0 {
47-
slog.Warn("Icon bytes are empty, skipping icon update", "type", iconType)
34+
slog.Warn("icon bytes empty, skipping update", "type", iconType)
4835
return
4936
}
5037

5138
app.systrayInterface.SetIcon(iconBytes)
52-
slog.Debug("[TRAY] Setting icon", "type", iconType)
39+
slog.Debug("tray icon updated", "type", iconType, "incoming", counts.IncomingBlocked, "outgoing", counts.OutgoingBlocked)
5340
}

cmd/goose/icons_badge.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//go:build (linux || freebsd || openbsd || netbsd || dragonfly || solaris || illumos || aix || windows) && !darwin
2+
3+
package main
4+
5+
import (
6+
_ "embed"
7+
"log/slog"
8+
"sync"
9+
10+
"github.com/codeGROOVE-dev/goose/pkg/icon"
11+
)
12+
13+
// Linux, BSD, and Windows use dynamic circle badges since they don't support title text.
14+
15+
//go:embed icons/smiling-face.png
16+
var iconSmilingSource []byte
17+
18+
//go:embed icons/warning.png
19+
var iconWarning []byte
20+
21+
//go:embed icons/lock.png
22+
var iconLock []byte
23+
24+
var (
25+
cache = icon.NewCache()
26+
27+
smiling []byte
28+
smilingOnce sync.Once
29+
)
30+
31+
func getIcon(iconType IconType, counts PRCounts) []byte {
32+
// Static icons for error states
33+
if iconType == IconWarning {
34+
return iconWarning
35+
}
36+
if iconType == IconLock {
37+
return iconLock
38+
}
39+
40+
incoming := counts.IncomingBlocked
41+
outgoing := counts.OutgoingBlocked
42+
43+
// Happy face when nothing is blocked
44+
if incoming == 0 && outgoing == 0 {
45+
smilingOnce.Do(func() {
46+
scaled, err := icon.Scale(iconSmilingSource)
47+
if err != nil {
48+
slog.Error("failed to scale happy face icon", "error", err)
49+
smiling = iconSmilingSource
50+
return
51+
}
52+
smiling = scaled
53+
})
54+
return smiling
55+
}
56+
57+
// Check cache
58+
if cached, ok := cache.Get(incoming, outgoing); ok {
59+
return cached
60+
}
61+
62+
// Generate badge
63+
badge, err := icon.Badge(incoming, outgoing)
64+
if err != nil {
65+
slog.Error("failed to generate badge", "error", err, "incoming", incoming, "outgoing", outgoing)
66+
return smiling
67+
}
68+
69+
cache.Put(incoming, outgoing, badge)
70+
return badge
71+
}

cmd/goose/icons_darwin.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//go:build darwin
2+
3+
package main
4+
5+
import _ "embed"
6+
7+
// macOS displays counts in the title bar, so icons remain static.
8+
9+
//go:embed icons/goose.png
10+
var iconGoose []byte
11+
12+
//go:embed icons/popper.png
13+
var iconPopper []byte
14+
15+
//go:embed icons/smiling-face.png
16+
var iconSmiling []byte
17+
18+
//go:embed icons/lock.png
19+
var iconLock []byte
20+
21+
//go:embed icons/warning.png
22+
var iconWarning []byte
23+
24+
//go:embed icons/cockroach.png
25+
var iconCockroach []byte
26+
27+
func getIcon(iconType IconType, counts PRCounts) []byte {
28+
switch iconType {
29+
case IconGoose, IconBoth:
30+
return iconGoose
31+
case IconPopper:
32+
return iconPopper
33+
case IconCockroach:
34+
return iconCockroach
35+
case IconWarning:
36+
return iconWarning
37+
case IconLock:
38+
return iconLock
39+
default:
40+
return iconSmiling
41+
}
42+
}

cmd/goose/icons_unix.go

Lines changed: 0 additions & 27 deletions
This file was deleted.

cmd/goose/icons_windows.go

Lines changed: 0 additions & 27 deletions
This file was deleted.

cmd/goose/loginitem_other.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//go:build !darwin
22

3-
// Package main - loginitem_other.go provides stub functions for non-macOS platforms.
43
package main
54

65
import "context"

0 commit comments

Comments
 (0)