diff options
| author | 2026-03-02 14:22:55 -0500 | |
|---|---|---|
| committer | 2026-03-02 14:22:55 -0500 | |
| commit | f76baa98acffc6a0d637ff360dd890a5fa10a973 (patch) | |
| tree | d153197e38c5f26c0398a296259f0b1a98ce2ab4 /src | |
| parent | dont fail on empty command (diff) | |
use lockfile for update tracking instead of trusting repo head
Diffstat (limited to 'src')
| -rw-r--r-- | src/action.rs | 38 | ||||
| -rw-r--r-- | src/lock.rs | 46 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/util.rs | 33 |
4 files changed, 108 insertions, 10 deletions
diff --git a/src/action.rs b/src/action.rs index dacf289..9816a6a 100644 --- a/src/action.rs +++ b/src/action.rs @@ -1,4 +1,5 @@ use crate::config::{self, Config, ConfigCommand, TEMP_CONFIG_PATH}; +use crate::lock::{self, Lockfile, Package}; use crate::util::{self, BASE_CONFIG_PATH, BASE_REPO_PATH, PackageList}; use git2::Repository; use std::fs; @@ -112,6 +113,17 @@ fn add(url: &str) -> Result<(), String> { config::run_config_command(&config_path, &clone_path, ConfigCommand::PostInstall)?; } + // only add to lockfile if installed + let oid = util::get_commit_hash_full(&clone_path) + .map_err(|e| format!("could not get commit hash: {e}"))?; + + let mut lockfile = Lockfile::new(); + lockfile.update_pkg(Package { + name: repo_name.to_string(), + source: url.to_string(), + checksum: oid.to_string(), + })?; + Ok(()) } @@ -123,8 +135,19 @@ fn update() -> Result<(), String> { let package_paths = util::collect_packages()?; let mut update_pkgs: PackageList = vec![]; + let mut lockfile = Lockfile::new(); for (name, path, config_path) in package_paths { - if util::pull_repo(&path).map_err(|e| format!("failed to update repo: {e}"))? { + util::pull_repo(&path).map_err(|e| format!("failed to update repo: {e}"))?; + let oid = util::get_commit_hash_full(&path) + .map_err(|e| format!("could not get commit hash: {e}"))?; + let url = util::get_remote_url(&path) + .map_err(|e| format!("failed to get url for remote origin: {e}"))?; + let p = Package { + name: name.clone(), + source: url, + checksum: oid.to_string(), + }; + if lockfile.out_of_date(p) { update_pkgs.push((name, path, config_path)); } } @@ -135,6 +158,17 @@ fn update() -> Result<(), String> { for (name, path, cfg_path) in update_pkgs { config::run_config_command(&cfg_path, &path, ConfigCommand::Build)?; config::run_config_command(&cfg_path, &path, ConfigCommand::Install)?; + + let oid = util::get_commit_hash_full(&path) + .map_err(|e| format!("could not get commit hash: {e}"))?; + let url = util::get_remote_url(&path) + .map_err(|e| format!("failed to get url for remote origin: {e}"))?; + lockfile.update_pkg(lock::Package { + name: name.clone(), + source: url, + checksum: oid.to_string(), + })?; + println!("Upgraded {}", name); } } @@ -189,7 +223,7 @@ fn list() -> Result<(), String> { let entry = entry.map_err(|e| e.to_string())?; let path = entry.path(); if path.is_dir() { - let oid = util::get_commit_hash(&path) + let oid = util::get_commit_hash_short(&path) .map_err(|e| format!("failed to get commit hash: {e}"))?; let oid = oid.as_str().unwrap(); if let Some(stem) = path.file_stem() { diff --git a/src/lock.rs b/src/lock.rs new file mode 100644 index 0000000..3aaacac --- /dev/null +++ b/src/lock.rs @@ -0,0 +1,46 @@ +use serde::{Deserialize, Serialize}; +use std::fs; + +pub const LOCK_PATH: &str = "/var/db/forge/forge.lock"; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Package { + pub name: String, + pub source: String, + pub checksum: String, +} + +#[derive(Deserialize, Serialize, Default)] +pub struct Lockfile { + pub package: Vec<Package>, +} + +impl Lockfile { + pub fn new() -> Self { + if let Ok(contents) = fs::read_to_string(LOCK_PATH) { + toml::from_str(&contents).unwrap_or_default() + } else { + Lockfile::default() + } + } + + pub fn out_of_date(&self, update: Package) -> bool { + if let Some(existing) = self.package.iter().find(|p| p.name == update.name) { + return existing.checksum != update.checksum; + } + true + } + + pub fn update_pkg(&mut self, package: Package) -> Result<(), String> { + if let Some(existing) = self.package.iter_mut().find(|p| p.name == package.name) { + *existing = package; + } else { + self.package.push(package); + } + + let toml_string = toml::to_string_pretty(&self) + .map_err(|e| format!("failed to serialize lockfile: {e}"))?; + + fs::write(LOCK_PATH, toml_string).map_err(|e| format!("failed to write to lockfile: {e}")) + } +} diff --git a/src/main.rs b/src/main.rs index 529b875..37a308a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use crate::config::TEMP_CONFIG_PATH; mod action; mod config; +mod lock; mod util; fn main() { diff --git a/src/util.rs b/src/util.rs index 9d44e70..a829327 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -use git2::{Buf, Cred, FetchOptions, RemoteCallbacks, Repository, build::CheckoutBuilder}; +use git2::{Buf, Cred, FetchOptions, Oid, RemoteCallbacks, Repository, build::CheckoutBuilder}; use std::env; use std::fs; use std::io; @@ -71,7 +71,15 @@ pub fn dir_size(path: &Path) -> std::io::Result<u64> { Ok(size) } -pub fn get_commit_hash(path: &Path) -> Result<Buf, git2::Error> { +pub fn get_commit_hash_full(path: &Path) -> Result<Oid, git2::Error> { + let repo = Repository::open(path)?; + let head = repo.head()?; + + let commit = head.peel_to_commit()?; + Ok(commit.id()) +} + +pub fn get_commit_hash_short(path: &Path) -> Result<Buf, git2::Error> { let repo = Repository::open(path)?; let head = repo.head()?; @@ -85,6 +93,18 @@ pub fn get_editor() -> String { .unwrap_or_else(|_| "nano".to_string()) } +pub fn get_remote_url(path: &Path) -> Result<String, git2::Error> { + let repo = Repository::open(path)?; + + let remote = repo.find_remote("origin")?; + + if let Some(url) = remote.url() { + Ok(url.to_string()) + } else { + Err(git2::Error::from_str("Remote 'origin' has no URL")) + } +} + pub fn open_in_editor(editor: &str, file: &str) -> Result<(), String> { let status = Command::new(editor) .arg(file) @@ -110,7 +130,7 @@ pub fn print_collected_packages(packages: &PackageList, message: &str) { ); } -pub fn pull_repo(path: &Path) -> Result<bool, git2::Error> { +pub fn pull_repo(path: &Path) -> Result<(), git2::Error> { let repo = Repository::open(path)?; let head = repo.head()?; @@ -140,13 +160,10 @@ pub fn pull_repo(path: &Path) -> Result<bool, git2::Error> { reference.set_target(fetch_commit.id(), "Fast-Forward")?; repo.set_head(&refname)?; repo.checkout_head(Some(CheckoutBuilder::default().force()))?; - Ok(true) - } else if analysis.is_up_to_date() { - Ok(false) - } else { + } else if !analysis.is_up_to_date() { println!("Non fast-forward merge required (manual merge needed)."); - Ok(false) } + Ok(()) } pub fn yn_prompt(prompt: &str) -> bool { |
