build.rs/src/backend/git.rs

150 lines
4.5 KiB
Rust

use std::boxed::Box;
use std::env::{set_current_dir as cd, current_dir as pwd};
use std::path::{Path, PathBuf};
use std::process::Command;
use crate::backend::{Backend, Repo};
#[derive(Debug, Clone, Copy)]
pub struct Git;
impl Backend for Git {
fn download(&self, source: &str, dest: &Path) -> bool {
Command::new("git")
.arg("clone")
.arg("--recursive")
.arg(source)
.arg(dest)
.output() // To suppress (capture) output
.expect("PROCESS ERROR!")
.status
.success()
}
fn submodules(&self) -> Vec<PathBuf> {
let cmd = Command::new("git")
.arg("config")
.arg("--file")
.arg(".gitmodules")
.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));
}
return results;
} else {
// No submodules found (even if .gitmodules exist but is empty)
return Vec::new();
}
}
fn branch(&self) -> String {
let output = Command::new("git")
.arg("rev-parse")
.arg("--abbrev-ref")
.arg("HEAD")
.output()
.expect("WTF");
if !output.status.success() {
panic!("Corrupted git repository???");
}
String::from_utf8(output.stdout).unwrap().trim().to_string()
}
fn checkout(&self, target: &str) -> bool {
let status = Command::new("git")
.arg("checkout")
.arg(target)
.status()
.expect("PROCESS ERROR!");
status.success()
}
fn has_updates(&self) -> bool {
// Refresh remote
if !Command::new("git")
.arg("fetch")
.arg("--quiet")
.arg("origin")
.status()
.expect("WTF")
.success()
{
// FAILED, no internet??
// TODO: This should be a forgebuild error message
eprintln!("Fetching updates failed");
return false;
}
let branch = self.branch();
if Command::new("git")
.arg("diff")
.arg("--quiet")
.arg(&format!("remotes/origin/{}", &branch))
.status()
.expect("WTF")
.success()
{
// Command succeeeded, no updates
return false;
}
// Updates
return true;
}
fn update(&self) -> bool {
if Command::new("git")
.arg("pull")
.arg("--ff-only")
.arg("origin")
.arg(self.branch())
.status()
.expect("WTF")
.success()
{
// Main updates succeeded. If new submodules were added, initialize them
if !Command::new("git")
.arg("submodule")
.arg("update")
.arg("--init")
.arg("--recursive")
.status()
.expect("WTF")
.success()
{
// TODO: Should be forgebuild error message
eprintln!("Failed to initialize submodules which were added to the repo");
}
return true;
} else {
// Main updates failed
return false;
}
}
// TODO: Maybe this should be a generic implementation part of the trait?
fn subupdate(&self) -> bool {
let mut found_subupdates = false;
let prev_dir = pwd().expect("failed to pwd");
for subpath in self.submodules() {
// Move into the submodule
cd(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(Box::new(Git), "irrelevant", &pwd().expect("failed to pwd"), false);
if subrepo.backend.update() {
found_subupdates = true;
}
// Move back into main repo
cd(&prev_dir).unwrap();
}
found_subupdates
}
}