diff --git a/src/dvcs.rs b/src/dvcs.rs index 8946cd5..76ffc90 100644 --- a/src/dvcs.rs +++ b/src/dvcs.rs @@ -1,7 +1,8 @@ +use std::env::{set_current_dir as cd, current_dir as pwd}; use std::path::{Path, PathBuf}; use std::process::Command; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Backend { Git, Mercurial, @@ -55,13 +56,14 @@ impl Repo { unreachable!("UNIMPLEMENTED!"); } Backend::Unknown(name) => { - panic!("Uknown backend: {}. Cannot find ranch", name); + // TODO: This should be a forgebuild error message + panic!("Uknown backend: {}. Cannot find branch", name); } } } pub fn clone(&self) -> bool { - match &self.backend { + let success = match &self.backend { Backend::Git => { let output = Command::new("git") .arg("clone") @@ -76,14 +78,40 @@ impl Repo { unreachable!("Unimplemented"); } Backend::Unknown(name) => { + // TODO: This should be a forgebuild error message eprintln!("Unknown DVCS: {}", name); false } + }; + + // If the clone was successful and subupdates is enabled, + // List submodules and check them for updates + if success && self.subupdates { + self.subupdate(); } + + return success + } + + /// Checks for updates from submodules. If any update was found, download them and return true. + pub fn subupdate(&self) -> bool { + let mut found_subupdates = false; + for subpath in self.submodules() { + // Move into the submodule + cd(self.dest.join(subpath)).unwrap(); + // Generate a Repo instance for the submodule but don't enable subupdates + // Just in case someone would trigger an infinite loop by accident + let subrepo = Repo::new(self.backend.clone(), "irrelevant", &pwd().expect("Failed to read pwd"), false); + if subrepo.update() { + found_subupdates = true; + } + // Move back into main repo + cd(&self.dest).unwrap(); + } + found_subupdates } pub fn checkout(&self, target: &str) -> bool { - println!("CHECKOUT TO {}", target); match &self.backend { Backend::Git => { let status = Command::new("git") @@ -97,12 +125,51 @@ impl Repo { unreachable!("UNIMPLEMENTED"); } Backend::Unknown(name) => { + // TODO: This should be a forgebuild error message eprintln!("Unknown DVCS: {}", name); false } } } + /// Parse the list of submodules in the current repository + /// When there is no submodules (eg. no .gitmodules file), return an empty Vec + pub fn submodules(&self) -> Vec { + match &self.backend { + Backend::Git => { + let cmd = Command::new("git") + .arg("config") + .arg("--file") + .arg(&format!("{}/.gitmodules", self.dest.to_str().unwrap())) + .arg("--get-regexp") + .arg("path") + .output() + .expect("WTF"); + if cmd.status.success() { + // Command succeded, split by the first space character to find the path to the submodule + let out = std::string::String::from_utf8(cmd.stdout).expect("Wrong unicode"); + let mut results = Vec::new(); + for line in out.lines() { + results.push(PathBuf::from(line.split_once(" ").expect("Misformed .gitmodules").1)); + } + results + } else { + // No submodules found (even if .gitmodules exist but is empty) + Vec::new() + } + }, + Backend::Mercurial => { + unimplemented!("Soon"); + }, + Backend::Unknown(name) => { + // TODO: This should be a forgebuild error message + eprintln!("Unknown DVCS:{}!", name); + Vec::new() + } + } + } + + /// Check for updates on the main repository pub fn has_updates(&self) -> bool { match &self.backend { Backend::Git => { @@ -116,6 +183,7 @@ impl Repo { .success() { // FAILED, no internet?? + // TODO: This should be a forgebuild error message eprintln!("Fetching updates failed"); return false; } @@ -123,7 +191,7 @@ impl Repo { if Command::new("git") .arg("diff") .arg("--quiet") - .arg(format!("remotes/origin/{}", &branch)) + .arg(&format!("remotes/origin/{}", &branch)) .status() .expect("WTF") .success() @@ -133,31 +201,57 @@ impl Repo { } // Updates return true; - } + }, Backend::Mercurial => { - unreachable!("unimplemented"); - } + unimplemented!(); + }, Backend::Unknown(name) => { + // TODO: This should be a forgebuild error message eprintln!("Unknown DVCS:{}!", name); false } } } - /* - /// Runs an update on the repository in dest. Returns true - /// when an update was performed, false otherwise - pub fn update(&self, dest: &str) -> bool { - match self { + + /// Checks if the repository has updates, if so download them + /// Returns true if updates have been applied, false otherwise + /// Also applies submodules updates if self.subupdates is true + pub fn update(&self) -> bool { + match &self.backend { Backend::Git => { + // First try to run submodules updates + let had_subupdates = if self.subupdates { self.subupdate() } else { false }; - }, + // Now run main repo updates + if self.has_updates() { + if Command::new("git") + .arg("pull") + .arg("--ff-only") + .arg("origin") + .arg(self.branch()) + .status() + .expect("WTF") + .success() + { + // Main updates succeeded + return true; + } else { + // Main updates failed, return true if there were some submodule updates + return had_subupdates; + } + } + + // No main updates + return had_subupdates; + } Backend::Mercurial => { - - }, + unreachable!("unimplemented"); + } Backend::Unknown(name) => { - eprintln!("Unknown DVCS: {}", name); + // TODO: This should be a forgebuild error message + eprintln!("Unknown DVCS:{}!", name); false } } - }*/ + } } diff --git a/src/task.rs b/src/task.rs index 60cf455..7d080b8 100644 --- a/src/task.rs +++ b/src/task.rs @@ -64,10 +64,14 @@ impl Task { let dest = source_dir_from_basedir(&basedir, &name); let cloned = source.clone().map_or(false, |_| dest.is_dir()); let subupdates = read_extension(path, "subupdates").is_some(); + let checkout = read_extension(path, "checkout"); // Copy the global context so we have a more local scope let mut context = context.clone(); context.insert("$i18n_task".to_string(), name.clone()); + if let Some(branch) = &checkout { + context.insert("$i18n_branch".to_string(), branch.to_string()); + } if let Some(source_url) = &source { context.insert("$i18n_source".to_string(), source_url.clone()); } @@ -86,7 +90,7 @@ impl Task { }), source, config: HashMap::new(), - branch: read_extension(path, "checkout"), + branch: checkout, hosts: read_extension(path, "hosts").map_or(Vec::new(), |c| { c.split("\n").map(|line| line.to_string()).collect() }), @@ -99,6 +103,8 @@ impl Task { pub fn checkout(&self) { if let Some(branch) = &self.branch { if let Some(repo) = &self.repo { + self.info("to_branch"); + self.debug("checkout"); repo.checkout(branch); } } @@ -120,7 +126,7 @@ impl Task { pub fn update_and_run(&self, force: &bool) { if let Some(repo) = &self.repo { - if repo.has_updates() { + if repo.update() { self.run(); } else if *force { self.debug("forcing");