Skip to content
Open
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
28 changes: 28 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3717,6 +3717,26 @@ func downloadAttachmentCmd(account *config.Account, uid uint32, folderName strin
}
}

// isLocalDevBuild reports whether this binary is an unstamped build running
// outside of any package sandbox, e.g. `make build` or `go run .` from a
// clone. It matters for the update checks: detectInstalledVersion falls back
// to asking the system package manager, so a contributor running their own
// build is told the version of the *packaged* Matcha they happen to have
// installed, which almost never matches the latest release tag.
//
// Version alone isn't a reliable signal: snap and flatpak builds are also
// unstamped (snapcraft.yaml and the flatpak manifest build without ldflags),
// but they *are* managed by the update path (trySnapRefresh, tryFlatpakUpdate).
// SNAP and FLATPAK_ID are always injected by their respective runtimes
// (snapenv.basicEnv; flatpak-run.c's "FLATPAK_ID is always set"), so their
// presence rules out a local dev build even when the version is unstamped.
func isLocalDevBuild() bool {
v := strings.TrimSpace(version)
unstamped := v == "" || v == "dev"
sandboxed := os.Getenv("SNAP") != "" || os.Getenv("FLATPAK_ID") != ""
return unstamped && !sandboxed
}

/*
detectInstalledVersion returns a best-effort installed version string.
Priority:
Expand Down Expand Up @@ -3808,6 +3828,10 @@ installed version. This runs in the background when the TUI initializes.
*/
func checkForUpdatesCmd() tea.Cmd {
return func() tea.Msg {
// A local dev build is not something `matcha update` can upgrade.
if isLocalDevBuild() {
return nil
}
// Non-fatal: if anything goes wrong we just don't show the update message.
const api = "https://api.github.com/repos/floatpane/matcha/releases/latest"
resp, err := httpClient.Get(api)
Expand All @@ -3834,6 +3858,10 @@ func checkForUpdatesCmd() tea.Cmd {
// a V1RCAvailableMsg when the installed version is still pre-v1.
func checkForV1RCCmd() tea.Cmd {
return func() tea.Msg {
// Same reasoning as checkForUpdatesCmd.
if isLocalDevBuild() {
return nil
}
const api = "https://api.github.com/repos/floatpane/matcha/releases"
resp, err := httpClient.Get(api)
if err != nil {
Expand Down
54 changes: 54 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,60 @@ func TestUndoDeleteRestoresFolderCounter(t *testing.T) {
}
}

func TestIsLocalDevBuildDetectsUnstampedVersions(t *testing.T) {
original := version
t.Cleanup(func() { version = original })

for _, tc := range []struct {
name string
version string
snap string
flatpak string
want bool
}{
{name: "plain go build", version: "dev", want: true},
{name: "empty ldflags value", version: "", want: true},
{name: "whitespace only", version: " ", want: true},
{name: "released version", version: "0.44.0", want: false},
{name: "tagged version", version: "v1.0.0-rc1", want: false},
{name: "unstamped snap build", version: "dev", snap: "/snap/matcha/42", want: false},
{name: "unstamped flatpak build", version: "dev", flatpak: "com.floatpane.matcha", want: false},
} {
t.Run(tc.name, func(t *testing.T) {
version = tc.version
// An empty value stands in for "unset": isLocalDevBuild compares
// against "", so absent and set-but-empty are equivalent. Setting
// both also isolates the test from a runner that is itself inside a
// snap or flatpak.
t.Setenv("SNAP", tc.snap)
t.Setenv("FLATPAK_ID", tc.flatpak)

if got := isLocalDevBuild(); got != tc.want {
t.Fatalf("isLocalDevBuild() with version %q, SNAP=%q, FLATPAK_ID=%q = %t, want %t",
tc.version, tc.snap, tc.flatpak, got, tc.want)
}
})
}
}

func TestUpdateChecksAreSkippedOnLocalDevBuilds(t *testing.T) {
original := version
t.Cleanup(func() { version = original })
version = "dev"
t.Setenv("SNAP", "")
t.Setenv("FLATPAK_ID", "")

// Both checks must bail out before reaching the GitHub API, so a dev build
// neither advertises an update nor makes a network call to find one.
if msg := checkForUpdatesCmd()(); msg != nil {
t.Fatalf("checkForUpdatesCmd() on a dev build = %#v, want nil", msg)
}

if msg := checkForV1RCCmd()(); msg != nil {
t.Fatalf("checkForV1RCCmd() on a dev build = %#v, want nil", msg)
}
}

func TestUnreadBadgeCountDeduplicatesOverlappingStores(t *testing.T) {
email := fetcher.Email{UID: 42, AccountID: "acct-a"}
got := unreadBadgeCount(
Expand Down
Loading