Skip to content

Commit 7c96a07

Browse files
committed
fix(cli): handle brew outdated exit code 1 as outdated, not error
Signed-off-by: Drew Cain <groksrc@gmail.com>
1 parent 148e07c commit 7c96a07

3 files changed

Lines changed: 37 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
## v0.20.2 (2026-03-10)
6+
7+
### Bug Fixes
8+
9+
- Fix auto-update Homebrew detection: `brew outdated` exits 1 when a formula is outdated, not on error
10+
- Previously treated exit code 1 as a failure, causing "Automatic update check failed" instead of detecting the available update
11+
512
## v0.20.1 (2026-03-10)
613

714
### Bug Fixes

src/basic_memory/cli/auto_update.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,11 @@ def _check_homebrew_update_available(silent: bool) -> tuple[bool, str | None]:
134134
silent=silent,
135135
capture_output=True,
136136
)
137-
if result.returncode != 0:
138-
stderr = (result.stderr or "").strip()
139-
stdout = (result.stdout or "").strip()
140-
detail = stderr or stdout or "brew outdated failed"
141-
raise RuntimeError(detail)
142-
143-
is_outdated = bool((result.stdout or "").strip())
137+
# Trigger: brew outdated exits 1 when the formula IS outdated (with name on stdout).
138+
# Why: non-zero exit here means "outdated", not "error".
139+
# Outcome: check stdout for the package name to determine outdated status.
140+
stdout = (result.stdout or "").strip()
141+
is_outdated = PACKAGE_NAME in stdout
144142
return is_outdated, None
145143

146144

tests/cli/test_auto_update.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
AutoUpdateResult,
1313
AutoUpdateStatus,
1414
InstallSource,
15+
_check_homebrew_update_available,
1516
_is_interactive_session,
1617
detect_install_source,
1718
maybe_run_periodic_auto_update,
@@ -129,6 +130,30 @@ def test_force_bypasses_auto_update_disabled(monkeypatch, tmp_path):
129130
assert manager.save_calls == 1
130131

131132

133+
def test_check_homebrew_update_available_exit_code_1_means_outdated(monkeypatch):
134+
"""brew outdated exits 1 when the formula is outdated, not on error."""
135+
136+
def _fake_run(command, **kwargs):
137+
return subprocess.CompletedProcess(
138+
command, 1, stdout="basicmachines-co/basic-memory/basic-memory\n", stderr=""
139+
)
140+
141+
monkeypatch.setattr("basic_memory.cli.auto_update._run_subprocess", _fake_run)
142+
is_outdated, _ = _check_homebrew_update_available(silent=False)
143+
assert is_outdated is True
144+
145+
146+
def test_check_homebrew_update_available_exit_code_0_means_up_to_date(monkeypatch):
147+
"""brew outdated exits 0 when the formula is up to date."""
148+
149+
def _fake_run(command, **kwargs):
150+
return subprocess.CompletedProcess(command, 0, stdout="", stderr="")
151+
152+
monkeypatch.setattr("basic_memory.cli.auto_update._run_subprocess", _fake_run)
153+
is_outdated, _ = _check_homebrew_update_available(silent=False)
154+
assert is_outdated is False
155+
156+
132157
def test_homebrew_outdated_triggers_upgrade(monkeypatch, tmp_path):
133158
config = _base_config(tmp_path)
134159
manager = StubConfigManager(config)

0 commit comments

Comments
 (0)