2022-11-08 05:54:35 -05:00
|
|
|
use crate::command::CommandExt;
|
2020-02-27 15:32:13 +02:00
|
|
|
use crate::execution_context::ExecutionContext;
|
2020-06-15 15:43:59 +03:00
|
|
|
use crate::terminal::{print_separator, prompt_yesno};
|
2024-10-03 12:47:35 +02:00
|
|
|
use crate::utils::{get_require_sudo_string, require_option};
|
2022-11-08 05:54:35 -05:00
|
|
|
use crate::{utils::require, Step};
|
2022-11-11 09:39:29 -05:00
|
|
|
use color_eyre::eyre::Result;
|
2024-10-03 12:47:35 +02:00
|
|
|
use rust_i18n::t;
|
2024-02-29 15:58:24 -08:00
|
|
|
use std::collections::HashSet;
|
2022-06-17 11:10:21 +03:00
|
|
|
use std::fs;
|
2021-04-28 10:55:54 +03:00
|
|
|
use std::process::Command;
|
2022-11-16 13:43:57 -05:00
|
|
|
use tracing::debug;
|
2018-06-28 12:16:54 +03:00
|
|
|
|
2020-02-27 20:28:50 +02:00
|
|
|
pub fn run_macports(ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
require("port")?;
|
2024-10-03 12:47:35 +02:00
|
|
|
let sudo = require_option(ctx.sudo().as_ref(), get_require_sudo_string())?;
|
2023-06-13 22:15:57 +08:00
|
|
|
|
2020-02-27 20:28:50 +02:00
|
|
|
print_separator("MacPorts");
|
2022-11-08 05:54:35 -05:00
|
|
|
ctx.run_type()
|
|
|
|
|
.execute(sudo)
|
|
|
|
|
.args(["port", "selfupdate"])
|
|
|
|
|
.status_checked()?;
|
2020-02-27 20:28:50 +02:00
|
|
|
ctx.run_type()
|
|
|
|
|
.execute(sudo)
|
2022-11-03 04:26:20 +08:00
|
|
|
.args(["port", "-u", "upgrade", "outdated"])
|
2022-11-08 05:54:35 -05:00
|
|
|
.status_checked()?;
|
2020-02-27 20:28:50 +02:00
|
|
|
if ctx.config().cleanup() {
|
|
|
|
|
ctx.run_type()
|
|
|
|
|
.execute(sudo)
|
2022-11-03 04:26:20 +08:00
|
|
|
.args(["port", "-N", "reclaim"])
|
2022-11-08 05:54:35 -05:00
|
|
|
.status_checked()?;
|
2020-02-27 20:28:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-25 15:09:23 +08:00
|
|
|
pub fn run_mas(ctx: &ExecutionContext) -> Result<()> {
|
2020-01-28 16:22:17 +02:00
|
|
|
let mas = require("mas")?;
|
2024-10-03 12:47:35 +02:00
|
|
|
print_separator(t!("macOS App Store"));
|
2020-01-28 16:22:17 +02:00
|
|
|
|
2023-05-25 15:09:23 +08:00
|
|
|
ctx.run_type().execute(mas).arg("upgrade").status_checked()
|
2020-01-28 16:22:17 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-15 15:43:59 +03:00
|
|
|
pub fn upgrade_macos(ctx: &ExecutionContext) -> Result<()> {
|
2024-10-03 12:47:35 +02:00
|
|
|
print_separator(t!("macOS system update"));
|
2018-08-19 14:45:23 +03:00
|
|
|
|
2024-01-26 22:57:10 -08:00
|
|
|
let should_ask = !(ctx.config().yes(Step::System) || ctx.config().dry_run());
|
2020-06-15 15:43:59 +03:00
|
|
|
if should_ask {
|
2024-10-03 12:47:35 +02:00
|
|
|
println!("{}", t!("Finding available software"));
|
2020-06-15 15:43:59 +03:00
|
|
|
if system_update_available()? {
|
2024-10-03 12:47:35 +02:00
|
|
|
let answer = prompt_yesno(t!("A system update is available. Do you wish to install it?").as_ref())?;
|
2020-06-15 15:43:59 +03:00
|
|
|
if !answer {
|
2020-08-21 23:04:36 +03:00
|
|
|
return Ok(());
|
2020-06-15 15:43:59 +03:00
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
} else {
|
2024-10-03 12:47:35 +02:00
|
|
|
println!("{}", t!("No new software available."));
|
2020-08-11 10:49:33 +03:00
|
|
|
return Ok(());
|
2020-06-15 15:43:59 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut command = ctx.run_type().execute("softwareupdate");
|
2022-11-03 04:26:20 +08:00
|
|
|
command.args(["--install", "--all"]);
|
2020-06-15 15:43:59 +03:00
|
|
|
|
|
|
|
|
if should_ask {
|
|
|
|
|
command.arg("--no-scan");
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()
|
2020-06-15 15:43:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn system_update_available() -> Result<bool> {
|
2022-11-08 05:54:35 -05:00
|
|
|
let output = Command::new("softwareupdate").arg("--list").output_checked_utf8()?;
|
|
|
|
|
|
2020-06-15 15:43:59 +03:00
|
|
|
debug!("{:?}", output);
|
|
|
|
|
|
2022-11-08 05:54:35 -05:00
|
|
|
Ok(!output.stderr.contains("No new software available"))
|
2018-06-28 12:16:54 +03:00
|
|
|
}
|
2022-06-17 11:10:21 +03:00
|
|
|
|
|
|
|
|
pub fn run_sparkle(ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let sparkle = require("sparkle")?;
|
|
|
|
|
|
|
|
|
|
print_separator("Sparkle");
|
|
|
|
|
|
|
|
|
|
for application in (fs::read_dir("/Applications")?).flatten() {
|
|
|
|
|
let probe = Command::new(&sparkle)
|
2022-11-03 04:26:20 +08:00
|
|
|
.args(["--probe", "--application"])
|
2022-06-17 11:10:21 +03:00
|
|
|
.arg(application.path())
|
2022-11-08 05:54:35 -05:00
|
|
|
.output_checked_utf8();
|
2022-06-17 11:10:21 +03:00
|
|
|
if probe.is_ok() {
|
|
|
|
|
let mut command = ctx.run_type().execute(&sparkle);
|
2022-11-03 04:26:20 +08:00
|
|
|
command.args(["bundle", "--check-immediately", "--application"]);
|
2022-06-17 11:10:21 +03:00
|
|
|
command.arg(application.path());
|
2022-11-08 05:54:35 -05:00
|
|
|
command.status_checked()?;
|
2022-06-17 11:10:21 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2024-02-29 15:58:24 -08:00
|
|
|
|
|
|
|
|
pub fn update_xcodes(ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let xcodes = require("xcodes")?;
|
|
|
|
|
print_separator("Xcodes");
|
|
|
|
|
|
|
|
|
|
let should_ask = !(ctx.config().yes(Step::Xcodes) || ctx.config().dry_run());
|
|
|
|
|
|
|
|
|
|
let releases = ctx
|
|
|
|
|
.run_type()
|
|
|
|
|
.execute(&xcodes)
|
|
|
|
|
.args(["update"])
|
|
|
|
|
.output_checked_utf8()?
|
|
|
|
|
.stdout;
|
|
|
|
|
|
|
|
|
|
let releases_installed: Vec<String> = releases
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|r| r.contains("(Installed)"))
|
|
|
|
|
.map(String::from)
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
if releases_installed.is_empty() {
|
2024-10-03 12:47:35 +02:00
|
|
|
println!("{}", t!("No Xcode releases installed."));
|
2024-02-29 15:58:24 -08:00
|
|
|
return Ok(());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (installed_gm, installed_beta, installed_regular) =
|
|
|
|
|
releases_installed
|
|
|
|
|
.iter()
|
|
|
|
|
.fold((false, false, false), |(gm, beta, regular), release| {
|
|
|
|
|
(
|
|
|
|
|
gm || release.contains("GM") || release.contains("Release Candidate"),
|
|
|
|
|
beta || release.contains("Beta"),
|
|
|
|
|
regular
|
|
|
|
|
|| !(release.contains("GM")
|
|
|
|
|
|| release.contains("Release Candidate")
|
|
|
|
|
|| release.contains("Beta")),
|
|
|
|
|
)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let releases_gm = releases
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|&r| r.matches("GM").count() > 0 || r.matches("Release Candidate").count() > 0)
|
|
|
|
|
.map(String::from)
|
|
|
|
|
.collect();
|
|
|
|
|
let releases_beta = releases
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|&r| r.matches("Beta").count() > 0)
|
|
|
|
|
.map(String::from)
|
|
|
|
|
.collect();
|
|
|
|
|
let releases_regular = releases
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|&r| {
|
|
|
|
|
r.matches("GM").count() == 0
|
|
|
|
|
&& r.matches("Release Candidate").count() == 0
|
|
|
|
|
&& r.matches("Beta").count() == 0
|
|
|
|
|
})
|
|
|
|
|
.map(String::from)
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
if installed_gm {
|
|
|
|
|
process_xcodes_releases(releases_gm, should_ask, ctx)?;
|
|
|
|
|
}
|
|
|
|
|
if installed_beta {
|
|
|
|
|
process_xcodes_releases(releases_beta, should_ask, ctx)?;
|
|
|
|
|
}
|
|
|
|
|
if installed_regular {
|
|
|
|
|
process_xcodes_releases(releases_regular, should_ask, ctx)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let releases_new = ctx
|
|
|
|
|
.run_type()
|
|
|
|
|
.execute(&xcodes)
|
|
|
|
|
.args(["list"])
|
|
|
|
|
.output_checked_utf8()?
|
|
|
|
|
.stdout;
|
|
|
|
|
|
|
|
|
|
let releases_gm_new_installed: HashSet<_> = releases_new
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|release| {
|
|
|
|
|
release.contains("(Installed)") && (release.contains("GM") || release.contains("Release Candidate"))
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let releases_beta_new_installed: HashSet<_> = releases_new
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|release| release.contains("(Installed)") && release.contains("Beta"))
|
|
|
|
|
.collect();
|
|
|
|
|
let releases_regular_new_installed: HashSet<_> = releases_new
|
|
|
|
|
.lines()
|
|
|
|
|
.filter(|release| {
|
|
|
|
|
release.contains("(Installed)")
|
|
|
|
|
&& !(release.contains("GM") || release.contains("Release Candidate") || release.contains("Beta"))
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
for releases_new_installed in [
|
|
|
|
|
releases_gm_new_installed,
|
|
|
|
|
releases_beta_new_installed,
|
|
|
|
|
releases_regular_new_installed,
|
|
|
|
|
] {
|
|
|
|
|
if should_ask && releases_new_installed.len() == 2 {
|
2024-10-03 12:47:35 +02:00
|
|
|
let answer_uninstall =
|
|
|
|
|
prompt_yesno(t!("Would you like to move the former Xcode release to the trash?").as_ref())?;
|
2024-02-29 15:58:24 -08:00
|
|
|
if answer_uninstall {
|
|
|
|
|
let _ = ctx
|
|
|
|
|
.run_type()
|
|
|
|
|
.execute(&xcodes)
|
|
|
|
|
.args([
|
|
|
|
|
"uninstall",
|
2025-02-02 19:24:57 -08:00
|
|
|
releases_new_installed.iter().next().copied().unwrap_or_default(),
|
2024-02-29 15:58:24 -08:00
|
|
|
])
|
|
|
|
|
.status_checked();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn process_xcodes_releases(releases_filtered: Vec<String>, should_ask: bool, ctx: &ExecutionContext) -> Result<()> {
|
|
|
|
|
let xcodes = require("xcodes")?;
|
|
|
|
|
|
2025-02-02 19:24:57 -08:00
|
|
|
if releases_filtered.last().map_or(true, |s| !s.contains("(Installed)")) && !releases_filtered.is_empty() {
|
2024-02-29 15:58:24 -08:00
|
|
|
println!(
|
2024-10-03 12:47:35 +02:00
|
|
|
"{} {}",
|
|
|
|
|
t!("New Xcode release detected:"),
|
2024-02-29 15:58:24 -08:00
|
|
|
releases_filtered.last().cloned().unwrap_or_default()
|
|
|
|
|
);
|
|
|
|
|
if should_ask {
|
2024-10-03 12:47:35 +02:00
|
|
|
let answer_install = prompt_yesno(t!("Would you like to install it?").as_ref())?;
|
2024-02-29 15:58:24 -08:00
|
|
|
if answer_install {
|
|
|
|
|
let _ = ctx
|
|
|
|
|
.run_type()
|
|
|
|
|
.execute(xcodes)
|
|
|
|
|
.args(["install", &releases_filtered.last().cloned().unwrap_or_default()])
|
|
|
|
|
.status_checked();
|
|
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|