Skip to content

Commit 23c40bd

Browse files
eddietejedaclaude
andcommitted
feat(update): execute brew upgrade directly for Homebrew installs
Instead of printing instructions and returning, run_update() now shells out to `brew upgrade <formula>` when a Homebrew install is detected. brew is located by checking common install paths (/opt/homebrew/bin, /usr/local/bin) before falling back to PATH. If brew is not found or the upgrade fails, falls back to printing the manual command. On success, busts the update-available cache so the notice clears on the next run, matching the behaviour of the non-Homebrew upgrade path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e2e151d commit 23c40bd

1 file changed

Lines changed: 66 additions & 2 deletions

File tree

src/update.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,76 @@ pub fn maybe_print_update_notice(rx: Option<std::sync::mpsc::Receiver<Option<Ver
164164
);
165165
}
166166

167+
/// Try to run `brew upgrade <formula>` directly. Falls back to printing the
168+
/// manual instruction if `brew` is not on PATH or the command fails.
169+
fn run_homebrew_upgrade() {
170+
println!("Updating via Homebrew...");
171+
172+
// Locate `brew` — prefer the common install paths so the upgrade works
173+
// even if the user's shell profile hasn't been sourced in this context.
174+
let brew = ["brew", "/opt/homebrew/bin/brew", "/usr/local/bin/brew"]
175+
.iter()
176+
.find(|&&p| {
177+
if p == "brew" {
178+
which_brew()
179+
} else {
180+
std::path::Path::new(p).exists()
181+
}
182+
})
183+
.copied();
184+
185+
let Some(brew_bin) = brew else {
186+
eprintln!(
187+
"{}",
188+
"brew not found — run manually:".yellow()
189+
);
190+
println!(" {}", format!("brew upgrade {HOMEBREW_FORMULA}").cyan());
191+
return;
192+
};
193+
194+
let status = std::process::Command::new(brew_bin)
195+
.args(["upgrade", HOMEBREW_FORMULA])
196+
.status();
197+
198+
match status {
199+
Ok(s) if s.success() => {
200+
// Cache bust so the update notice clears on the next run.
201+
if let Ok(v) = fetch_latest_version() {
202+
write_cache(&UpdateCheckCache {
203+
checked_at: now_secs(),
204+
latest_version: v.to_string(),
205+
});
206+
}
207+
}
208+
Ok(s) => {
209+
eprintln!(
210+
"{}",
211+
format!("brew upgrade exited with status {s}").red()
212+
);
213+
std::process::exit(s.code().unwrap_or(1));
214+
}
215+
Err(e) => {
216+
eprintln!("{}", format!("error running brew: {e}").red());
217+
eprintln!("Run manually:");
218+
println!(" {}", format!("brew upgrade {HOMEBREW_FORMULA}").cyan());
219+
std::process::exit(1);
220+
}
221+
}
222+
}
223+
224+
fn which_brew() -> bool {
225+
std::process::Command::new("which")
226+
.arg("brew")
227+
.output()
228+
.map(|o| o.status.success())
229+
.unwrap_or(false)
230+
}
231+
167232
pub fn run_update() {
168233
let current = Version::parse(CURRENT_VERSION).expect("invalid package version");
169234

170235
if detect_install_method() == InstallMethod::Homebrew {
171-
println!("hotdata was installed via Homebrew. Update with:");
172-
println!(" {}", format!("brew upgrade {HOMEBREW_FORMULA}").cyan());
236+
run_homebrew_upgrade();
173237
return;
174238
}
175239

0 commit comments

Comments
 (0)