diff options
| author | 2026-02-27 23:36:59 -0500 | |
|---|---|---|
| committer | 2026-02-27 23:36:59 -0500 | |
| commit | ea45b8b4207772826a7232f244e6bb2d9a46feeb (patch) | |
| tree | f4c8c2f74bccf7cdfb5ef78665c927c6a7671a21 /src | |
| parent | add the clean subcommand (diff) | |
added update command
Diffstat (limited to 'src')
| -rw-r--r-- | src/action.rs | 51 | ||||
| -rw-r--r-- | src/util.rs | 42 |
2 files changed, 89 insertions, 4 deletions
diff --git a/src/action.rs b/src/action.rs index a9c2563..d873fc9 100644 --- a/src/action.rs +++ b/src/action.rs @@ -1,5 +1,5 @@ use crate::config::{self, ConfigCommand, TEMP_CONFIG_PATH, create_config}; -use crate::util::{dir_size, get_editor, open_in_editor, yn_prompt}; +use crate::util::{dir_size, get_editor, open_in_editor, pull_repo, yn_prompt}; use git2::Repository; use std::fs; use std::path::PathBuf; @@ -65,7 +65,7 @@ impl Action { pub fn execute(self) -> Result<(), String> { match self { Action::Add { url } => add(url.as_str()), - Action::Update => Ok(update()), + Action::Update => update(), Action::Upgrade { packages } => Ok(upgrade(packages)), Action::Autoremove => Ok(autoremove()), Action::Remove { packages } => remove(packages), @@ -126,8 +126,51 @@ fn add(url: &str) -> Result<(), String> { Ok(()) } -fn update() { - println!("updating"); +fn update() -> Result<(), String> { + if !nix::unistd::geteuid().is_root() { + return Err("update must be run as root".to_string()); + } + + let package_paths: Vec<(String, PathBuf)> = fs::read_dir(BASE_CONFIG_PATH) + .map_err(|e| format!("failed to iterate package directory: {}", e))? + .map(|p| { + let entry = p.map_err(|e| e.to_string())?; + let path = entry.path(); + + let pkgname = path + .file_stem() + .ok_or_else(|| format!("invalid filename: {:?}", path))? + .to_string_lossy() + .into_owned(); + + let path = PathBuf::from(BASE_REPO_PATH).join(&pkgname); + + if !path.exists() { + Err(format!("no installed package: {}", pkgname)) + } else { + Ok((pkgname, path)) + } + }) + .collect::<Result<_, _>>()?; + + println!( + "Packages to update ({}): {}\n", + package_paths.len(), + package_paths + .iter() + .map(|(p, _)| p.as_str()) + .collect::<Vec<_>>() + .join(", ") + ); + + if yn_prompt("Proceed with update?") { + for (name, path) in package_paths { + pull_repo(&path).map_err(|e| format!("failed to update repo: {e}"))?; + println!("Updated {}", name); + } + } + + Ok(()) } fn upgrade(packages: Vec<String>) { diff --git a/src/util.rs b/src/util.rs index 6f04573..0958764 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,4 @@ +use git2::{Cred, FetchOptions, RemoteCallbacks, Repository, build::CheckoutBuilder}; use std::env; use std::fs; use std::io; @@ -40,6 +41,47 @@ pub fn open_in_editor(editor: &str, file: &str) -> Result<(), String> { Ok(()) } +pub fn pull_repo(path: &Path) -> Result<(), git2::Error> { + let repo = Repository::open(path)?; + + let head = repo.head()?; + let branch = head + .shorthand() + .ok_or_else(|| git2::Error::from_str("Could not determine current branch"))?; + + let mut callbacks = RemoteCallbacks::new(); + callbacks.credentials(|_url, username_from_url, _allowed| { + Cred::ssh_key_from_agent(username_from_url.unwrap()) + }); + + let mut fetch_options = FetchOptions::new(); + fetch_options.remote_callbacks(callbacks); + + let mut remote = repo.find_remote("origin")?; + remote.fetch(&[branch], Some(&mut fetch_options), None)?; + + let fetch_head = repo.find_reference("FETCH_HEAD")?; + let fetch_commit = repo.reference_to_annotated_commit(&fetch_head)?; + + let (analysis, _pref) = repo.merge_analysis(&[&fetch_commit])?; + + if analysis.is_fast_forward() { + println!("Fast-forwarding..."); + + let refname = format!("refs/heads/{}", branch); + let mut reference = repo.find_reference(&refname)?; + reference.set_target(fetch_commit.id(), "Fast-Forward")?; + repo.set_head(&refname)?; + repo.checkout_head(Some(CheckoutBuilder::default().force()))?; + } else if analysis.is_up_to_date() { + println!("Already up to date."); + } else { + println!("Non fast-forward merge required (manual merge needed)."); + } + + Ok(()) +} + pub fn yn_prompt(prompt: &str) -> bool { print!("{} [y/n]: ", prompt); io::stdout().flush().unwrap(); |
