Compare commits

...

6 commits
v0.1.0 ... main

Author SHA1 Message Date
zyl
b8d82cad6b undo version change 2024-12-09 22:46:56 -08:00
zyl
f4cc78031b update version number 2024-12-09 22:44:49 -08:00
zyl
caa0cf6799 configurable package managers, add pacman/yay support 2024-12-09 22:44:11 -08:00
zyl
405cf3aba8
add untested homebrew support 2024-10-17 18:27:41 -07:00
zyl
c1da19e352
bump version 2024-10-17 18:27:33 -07:00
zyl
58aab0eb08
add install instructions 2024-10-08 16:51:22 -07:00
5 changed files with 134 additions and 33 deletions

2
Cargo.lock generated
View file

@ -424,7 +424,7 @@ dependencies = [
[[package]] [[package]]
name = "pupdate" name = "pupdate"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"directories", "directories",

View file

@ -6,7 +6,7 @@ license = "WTFPL"
name = "pupdate" name = "pupdate"
readme = "README.md" readme = "README.md"
repository = "https://github.com/zyllian/pupdate" repository = "https://github.com/zyllian/pupdate"
version = "0.1.0" version = "0.2.0"
[dependencies] [dependencies]
clap = {version = "4", features = ["derive"]} clap = {version = "4", features = ["derive"]}

View file

@ -4,6 +4,8 @@ simple helper utility to update remote systems alongside the local system easily
## usage ## usage
you can install easily using `cargo install pupdate`.
run `pupdate -h` for help with arguments. with no arguments, pupdate will update the local system and any remotes configured in the config file (default ~/.pupdate). run `pupdate -h` for help with arguments. with no arguments, pupdate will update the local system and any remotes configured in the config file (default ~/.pupdate).
## config ## config

View file

@ -40,6 +40,41 @@ struct Config {
/// the directory to log to, no logs if missing /// the directory to log to, no logs if missing
#[serde(default)] #[serde(default)]
log_dir: Option<PathBuf>, log_dir: Option<PathBuf>,
/// the package managers to pupdate the local system with
#[serde(default)]
package_managers: Vec<PackageManager>,
}
/// enum for supported package managers
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
enum PackageManager {
AptGet,
Homebrew,
Pacman,
Yay,
}
impl PackageManager {
/// gets the packaage manager's name
pub fn get_name(&self) -> &'static str {
match self {
Self::AptGet => "apt-get",
Self::Homebrew => "homebrew",
Self::Pacman => "pacman",
Self::Yay => "yay",
}
}
/// pupdates the local system
pub async fn pupdate(&self, log_dir: Option<PathBuf>) -> eyre::Result<bool> {
match self {
Self::AptGet => pupdate_apt(log_dir).await,
Self::Homebrew => pupdate_homebrew(log_dir).await,
Self::Pacman => pupdate_pacman(log_dir).await,
Self::Yay => pupdate_yay(log_dir).await,
}
}
} }
/// pupdates a remote target through ssh /// pupdates a remote target through ssh
@ -77,9 +112,11 @@ async fn pupdate_remote(
Ok((remote, success)) Ok((remote, success))
} }
/// pupdates the local system using apt-get /// helper for writing log files from multiple processes
async fn pupdate_apt(log_dir: Option<PathBuf>) -> eyre::Result<bool> { async fn log_helper(
async fn log(outputs: &[std::process::Output], log_dir: Option<PathBuf>) -> eyre::Result<bool> { outputs: &[std::process::Output],
log_dir: Option<PathBuf>,
) -> eyre::Result<bool> {
if let Some(log_dir) = log_dir { if let Some(log_dir) = log_dir {
let mut stdout = File::create(log_dir.join("local.stdout.log")).await?; let mut stdout = File::create(log_dir.join("local.stdout.log")).await?;
let mut stderr = File::create(log_dir.join("local.stderr.log")).await?; let mut stderr = File::create(log_dir.join("local.stderr.log")).await?;
@ -96,21 +133,74 @@ async fn pupdate_apt(log_dir: Option<PathBuf>) -> eyre::Result<bool> {
Ok(true) Ok(true)
} }
let update_output = Command::new("sudo") /// helper to prefix a command with sudo if requested
.arg("apt-get") fn command_maybe_with_sudo(command: &str, with_sudo: bool) -> Command {
if with_sudo {
let mut cmd = Command::new("sudo");
cmd.arg(command);
cmd
} else {
Command::new(command)
}
}
/// helper for pupdates which both follow the "X update && X upgrade" set of commands (e.g. apt-get and brew)
async fn update_and_upgrade(
log_dir: Option<PathBuf>,
command: &str,
with_sudo: bool,
yes: bool,
) -> eyre::Result<bool> {
let update_output = command_maybe_with_sudo(command, with_sudo)
.arg("update") .arg("update")
.output() .output()
.await?; .await?;
if !update_output.status.success() { if !update_output.status.success() {
return log(&[update_output], log_dir).await; return log_helper(&[update_output], log_dir).await;
} }
let upgrade_output = Command::new("sudo") let mut upgrade_command = command_maybe_with_sudo(command, with_sudo);
.arg("apt-get") upgrade_command.arg("upgrade");
.arg("upgrade") if yes {
.arg("-y") upgrade_command.arg("-y");
.output() }
.await?; let upgrade_output = upgrade_command.output().await?;
log(&[update_output, upgrade_output], log_dir).await log_helper(&[update_output, upgrade_output], log_dir).await
}
/// pupdates the local system using apt-get
async fn pupdate_apt(log_dir: Option<PathBuf>) -> eyre::Result<bool> {
update_and_upgrade(log_dir, "apt-get", true, true).await
}
/// pupdates the local system using brew, untested
async fn pupdate_homebrew(log_dir: Option<PathBuf>) -> eyre::Result<bool> {
update_and_upgrade(log_dir, "brew", false, false).await
}
/// helper for pupdates which follow the pacman-style command format
async fn syu(
log_dir: Option<PathBuf>,
command: &str,
with_sudo: bool,
yes: bool,
) -> eyre::Result<bool> {
let mut command = command_maybe_with_sudo(command, with_sudo);
command.arg("-Syu");
if yes {
command.arg("--noconfirm");
}
let output = command.output().await?;
log_helper(&[output], log_dir).await
}
/// pupdates the local system using pacman
async fn pupdate_pacman(log_dir: Option<PathBuf>) -> eyre::Result<bool> {
syu(log_dir, "pacman", true, true).await
}
/// pupdates the local system using yay
async fn pupdate_yay(log_dir: Option<PathBuf>) -> eyre::Result<bool> {
syu(log_dir, "yay", false, true).await
} }
#[tokio::main] #[tokio::main]
@ -123,9 +213,13 @@ async fn main() -> eyre::Result<()> {
}; };
let config_path = args.config.or(base_config_path); let config_path = args.config.or(base_config_path);
let config: Config = if let Some(config) = config_path { let config: Config = if let Some(config) = config_path {
if config.exists() {
serde_json::from_str(&std::fs::read_to_string(config)?)? serde_json::from_str(&std::fs::read_to_string(config)?)?
} else { } else {
Config::default() Config::default()
}
} else {
Config::default()
}; };
let log_dir = args.log_dir.or(config.log_dir).map(|log_dir| { let log_dir = args.log_dir.or(config.log_dir).map(|log_dir| {
@ -199,8 +293,15 @@ async fn main() -> eyre::Result<()> {
if !args.skip_local { if !args.skip_local {
println!("running local pupdates, you may be pawmpted for your password"); println!("running local pupdates, you may be pawmpted for your password");
if config.package_managers.is_empty() {
eprintln!("no package managers defined in config, skipping local pupdates");
}
for package_manager in config.package_managers {
println!("pupdating with {}", package_manager.get_name());
let start = OffsetDateTime::now_utc(); let start = OffsetDateTime::now_utc();
if pupdate_apt(log_dir).await? { package_manager.pupdate(log_dir.clone()).await?;
let end = OffsetDateTime::now_utc(); let end = OffsetDateTime::now_utc();
let duration = end - start; let duration = end - start;
@ -208,8 +309,6 @@ async fn main() -> eyre::Result<()> {
"successfully pupdated the local system in {} seconds", "successfully pupdated the local system in {} seconds",
duration.whole_seconds() duration.whole_seconds()
); );
} else {
println!("failed to pupdate the local system");
} }
} }

View file

@ -4,4 +4,4 @@ some things that may or may not get done at some point:
- pupdate daemon to remotely pupdate without using ssh (use ssh keys for authentication though maybe?) - pupdate daemon to remotely pupdate without using ssh (use ssh keys for authentication though maybe?)
- pupdate docker compose containers (interactively?) - pupdate docker compose containers (interactively?)
- support other pupdate methods than just apt - support other pupdate methods than just apt and homebrew