aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar BanceDev 2026-02-27 23:36:59 -0500
committerGravatar BanceDev 2026-02-27 23:36:59 -0500
commitea45b8b4207772826a7232f244e6bb2d9a46feeb (patch)
treef4c8c2f74bccf7cdfb5ef78665c927c6a7671a21 /src
parentadd the clean subcommand (diff)
added update command
Diffstat (limited to 'src')
-rw-r--r--src/action.rs51
-rw-r--r--src/util.rs42
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();