diff --git a/src/executor.rs b/src/executor.rs index 70472811..7e14533a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -264,3 +264,26 @@ impl CommandExt for Command { Ok(String::from_utf8(output.stdout)?) } } + +impl CommandExt for Executor { + fn check_output(&mut self) -> Result { + let output = match self.output()? { + ExecutorOutput::Wet(output) => output, + ExecutorOutput::Dry => unreachable!(), + }; + let status = output.status; + if !status.success() { + let stderr = String::from_utf8(output.stderr).unwrap_or_default(); + return Err(TopgradeError::ProcessFailedWithOutput(status, stderr).into()); + } + Ok(String::from_utf8(output.stdout)?) + } + + fn string_output(&mut self) -> Result { + let output = match self.output()? { + ExecutorOutput::Wet(output) => output, + ExecutorOutput::Dry => unreachable!(), + }; + Ok(String::from_utf8(output.stdout)?) + } +} diff --git a/src/main.rs b/src/main.rs index e3fb0f83..d2aecd36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,6 +136,8 @@ fn run() -> Result<()> { runner.execute(Step::System, "etc-update", || { linux::run_etc_update(sudo.as_ref(), run_type) })?; + + runner.execute(Step::Brew, "Brew", || unix::run_brew(&ctx, unix::BrewVariant::Linux))?; } #[cfg(windows)] @@ -144,18 +146,22 @@ fn run() -> Result<()> { runner.execute(Step::Scoop, "Scoop", || windows::run_scoop(config.cleanup(), run_type))?; } + #[cfg(target_os = "macos")] + { + runner.execute(Step::Brew, "Brew (ARM)", || { + unix::run_brew(&ctx, unix::BrewVariant::MacArm) + })?; + runner.execute(Step::Brew, "Brew (Intel)", || { + unix::run_brew(&ctx, unix::BrewVariant::MacIntel) + })?; + runner.execute(Step::MacPorts, "MacPorts", || macos::run_macports(&ctx))?; + runner.execute(Step::MicrosoftAutoUpdate, "Microsoft AutoUpdate", || { + macos::run_msupdate(&ctx) + })?; + } + #[cfg(unix)] { - runner.execute(Step::Brew, "Brew", || unix::run_brew(&ctx))?; - - #[cfg(target_os = "macos")] - { - runner.execute(Step::MacPorts, "MacPorts", || macos::run_macports(&ctx))?; - runner.execute(Step::MicrosoftAutoUpdate, "Microsoft AutoUpdate", || { - macos::run_msupdate(&ctx) - })?; - } - runner.execute(Step::Yadm, "yadm", || unix::run_yadm(&ctx))?; runner.execute(Step::Nix, "nix", || unix::run_nix(&ctx))?; runner.execute(Step::HomeManager, "home-manager", || unix::run_home_manager(run_type))?; diff --git a/src/steps/os/unix.rs b/src/steps/os/unix.rs index 3ad872db..fc27ac9f 100644 --- a/src/steps/os/unix.rs +++ b/src/steps/os/unix.rs @@ -2,7 +2,7 @@ use crate::error::SkipStep; use crate::error::TopgradeError; use crate::execution_context::ExecutionContext; -use crate::executor::{CommandExt, ExecutorExitStatus, RunType}; +use crate::executor::{CommandExt, Executor, ExecutorExitStatus, RunType}; use crate::terminal::{print_separator, print_warning}; use crate::utils::{require, PathExt}; use anyhow::Result; @@ -14,6 +14,53 @@ use std::path::PathBuf; use std::process::Command; use std::{env, path::Path}; +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum BrewVariant { + Linux, + MacIntel, + MacArm, +} + +impl BrewVariant { + fn binary_name(self) -> &'static str { + match self { + BrewVariant::Linux => "/home/linuxbrew/.linuxbrew/bin/brew", + BrewVariant::MacIntel => "/usr/local/bin/brew", + BrewVariant::MacArm => "/opt/homebrew/bin/brew", + } + } + + fn both_both_exist() -> bool { + Path::new("/usr/local/bin/brew").exists() && Path::new("/opt/homebrew/bin/brew").exists() + } + + pub fn step_title(self) -> &'static str { + let both_exists = Self::both_both_exist(); + match self { + BrewVariant::MacArm if both_exists => "Brew (ARM)", + BrewVariant::MacIntel if both_exists => "Brew (Intel)", + _ => "Brew", + } + } + + fn execute(self, run_type: RunType) -> Executor { + match self { + BrewVariant::MacIntel if cfg!(target_arch = "aarch64") => { + let mut command = run_type.execute("arch"); + command.arg("-x86_64").arg(self.binary_name()); + command + } + BrewVariant::MacArm if cfg!(target_arch = "x86_64") => { + let mut command = run_type.execute("arch"); + command.arg("-arm64e").arg(self.binary_name()); + command + } + _ => run_type.execute(self.binary_name()), + } + } +} + pub fn run_fisher(base_dirs: &BaseDirs, run_type: RunType) -> Result<()> { let fish = require("fish")?; base_dirs @@ -38,12 +85,13 @@ pub fn run_oh_my_fish(ctx: &ExecutionContext) -> Result<()> { ctx.run_type().execute(&fish).args(&["-c", "omf update"]).check_run() } -pub fn run_brew(ctx: &ExecutionContext) -> Result<()> { - let brew = require("brew")?; - print_separator("Brew"); +pub fn run_brew(ctx: &ExecutionContext, variant: BrewVariant) -> Result<()> { + require(variant.binary_name())?; + print_separator(variant.step_title()); let run_type = ctx.run_type(); - let cask_upgrade_exists = Command::new(&brew) + let cask_upgrade_exists = variant + .execute(RunType::Wet) .args(&["--repository", "buo/cask-upgrade"]) .check_output() .map(|p| Path::new(p.trim()).exists())?; @@ -56,8 +104,8 @@ pub fn run_brew(ctx: &ExecutionContext) -> Result<()> { brew_args.push("--greedy"); } - run_type.execute(&brew).arg("update").check_run()?; - run_type.execute(&brew).args(&brew_args).check_run()?; + variant.execute(run_type).arg("update").check_run()?; + variant.execute(run_type).args(&brew_args).check_run()?; if cask_upgrade_exists { let mut args = vec!["cu", "-y"]; @@ -65,11 +113,11 @@ pub fn run_brew(ctx: &ExecutionContext) -> Result<()> { args.push("-a"); } - run_type.execute(&brew).args(&args).check_run()?; + variant.execute(run_type).args(&args).check_run()?; } if ctx.config().cleanup() { - run_type.execute(&brew).arg("cleanup").check_run()?; + variant.execute(run_type).arg("cleanup").check_run()?; } Ok(())