1
0
Fork 0
tplp-planning-benchmark/benchmark-new/benchmark_repository/src/lib.rs

326 lines
9.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extern crate git2;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;
extern crate indicatif;
use git2::{Cred, Error, FetchOptions, Index, IndexEntry, IndexTime, Progress, PushOptions, RemoteCallbacks, ResetType, Repository, Signature, TreeBuilder};
use git2::build::{CheckoutBuilder, RepoBuilder};
use std::fs::read;
use std::path::Path;
use std::string::String;
use std::str;
use indicatif::{ProgressBar, ProgressStyle};
pub struct BenchmarkRepository
{
repository: Repository,
ssh_user: String,
user_name: String,
user_email: String,
}
impl BenchmarkRepository
{
fn progress_bar_style() -> ProgressStyle
{
ProgressStyle::default_bar()
.template("{bar:74.on_black} {percent:>3} %")
.progress_chars("█▉▊▋▌▍▎▏ ")
}
fn transfer_progress_callback(progress: Progress, progress_bar: &mut ProgressBar) -> bool
{
progress_bar.set_length(progress.total_objects() as u64);
progress_bar.set_position(progress.received_objects() as u64);
// continue the transfer
true
}
fn push_update_reference_callback(reference: &str, status: Option<&str>) -> Result<(), Error>
{
match status
{
None => trace!("Reference “{}” pushed successfully to remote “origin”", reference),
Some(error) => panic!("Couldnt push reference “{}” to remote “origin”: {}", reference, error),
};
Ok(())
}
fn checkout_progress_callback(path: Option<&Path>, current: usize, total: usize, progress_bar: &mut ProgressBar)
{
progress_bar.set_length(total as u64);
progress_bar.set_position(current as u64);
}
fn reset_origin(&self, remote_url: &str) -> &Self
{
let remote = match self.repository.find_remote("origin")
{
Ok(remote) => remote,
Err(_) =>
match self.repository.remote("origin", remote_url)
{
Ok(remote) => remote,
Err(error) => panic!("Could not reset remote “origin”: {}", error),
},
};
info!("Reset origin to “{}”", remote_url);
self
}
fn fetch_branch(&self, branch_name: &str) -> &Self
{
let mut progress_bar = ProgressBar::new(0);
progress_bar.set_style(BenchmarkRepository::progress_bar_style());
{
let mut remote_callbacks = RemoteCallbacks::new();
remote_callbacks.credentials(
|_, _, _|
{
match Cred::ssh_key_from_agent(&self.ssh_user)
{
Ok(credentials) => Ok(credentials),
Err(error) => panic!("could not retrieve key pair for SSH authentication as user “{}”: {}", self.ssh_user, error),
}
});
remote_callbacks.transfer_progress(
|progress| BenchmarkRepository::transfer_progress_callback(progress, &mut progress_bar));
let mut fetch_options = FetchOptions::new();
fetch_options.remote_callbacks(remote_callbacks);
let mut origin = self.repository.find_remote("origin").expect("could not find remote “origin”");
info!("Updating branch “{}”", branch_name);
if let Err(error) = origin.fetch(&[branch_name], Some(&mut fetch_options), None)
{
panic!("failed to fetch branch “{}” from remote “origin”: {}", branch_name, error);
}
}
progress_bar.finish_and_clear();
trace!("Branch “{}” is up-to-date", branch_name);
self
}
fn update_branch_storage(&self, branch_name: &str) -> &Self
{
self.fetch_branch(branch_name);
let mut progress_bar = ProgressBar::new(0);
progress_bar.set_style(BenchmarkRepository::progress_bar_style());
{
let mut checkout_builder = CheckoutBuilder::new();
let workdir = self.repository.path().parent().expect("invalid repository storage path");
let branch_storage_path = workdir.join(branch_name);
checkout_builder.force();
checkout_builder.target_dir(&branch_storage_path);
checkout_builder.progress(
|path, current, total| BenchmarkRepository::checkout_progress_callback(path, current, total, &mut progress_bar));
let object_id = match self.repository.refname_to_id(&format!("refs/remotes/origin/{}", branch_name))
{
Ok(object_id) => object_id,
Err(error) => panic!("could not look up branch “{}” from remote “origin”: {}", branch_name, error),
};
let object = match self.repository.find_object(object_id, None)
{
Ok(object) => object,
Err(error) => panic!("could not retrieve branch “{}” from remote “origin”: {}", branch_name, error),
};
info!("Updating branch storage “{}”", branch_name);
if let Err(error) = self.repository.checkout_tree(&object, Some(&mut checkout_builder))
{
panic!("failed to update branch storage for “{}”: {}", branch_name, error);
}
}
progress_bar.finish_and_clear();
trace!("Branch storage “{}” is up-to-date", branch_name);
self
}
fn init(base_path: &Path) -> Repository
{
let repository = match Repository::init_bare(base_path)
{
Ok(repository) => repository,
Err(error) => panic!("failed to initialize Git repository in “{}”: {}", base_path.display(), error),
};
info!("Initialized Git repository in “{}”", base_path.display());
repository
}
pub fn new(remote_url: &str, base_path: &Path, ssh_user: &str, user_name: &str, user_email: &str) -> BenchmarkRepository
{
let repository = match Repository::open(base_path)
{
Ok(repository) =>
{
info!("Using existing Git repository");
repository
},
Err(_) => BenchmarkRepository::init(base_path),
};
let benchmark_repository =
BenchmarkRepository
{
repository: repository,
ssh_user: ssh_user.to_string(),
user_name: user_name.to_string(),
user_email: user_email.to_string(),
};
benchmark_repository
.reset_origin(remote_url)
.fetch_branch("test-config")
.fetch_branch("test-results")
.fetch_branch("test-status");
benchmark_repository
}
pub fn read_file_as_index_entry(&self, file_path: &Path, result_file_path: &Path) -> IndexEntry
{
// create a new blob with the file contents
let object_id = match self.repository.blob_path(file_path)
{
Ok(object_id) => object_id,
Err(error) => panic!("Could not write blob for “{}”: {}", file_path.display(), error),
};
info!("Created object “{}” from “{}”", object_id, file_path.display());
IndexEntry
{
ctime: IndexTime::new(0, 0),
mtime: IndexTime::new(0, 0),
dev: 0,
ino: 0,
mode: 0o100644,
uid: 0,
gid: 0,
file_size: 0,
id: object_id,
flags: 0,
flags_extended: 0,
path: result_file_path.to_string_lossy().as_bytes().to_owned(),
}
}
pub fn commit_file(&self, file_path: &Path, result_file_path: &Path, branch_name: &str)
{
let reference_name = format!("refs/remotes/origin/{}", branch_name);
let reference = match self.repository.find_reference(&reference_name)
{
Ok(value) => value,
Err(error) => panic!("Could not find reference “{}”: {}", reference_name, error),
};
let parent_tree = match reference.peel_to_tree()
{
Ok(value) => value,
Err(error) => panic!("Could not peel reference to tree: {}", error),
};
let mut index = match Index::new()
{
Ok(value) => value,
Err(error) => panic!("Could not create index: {}", error),
};
match index.read_tree(&parent_tree)
{
Ok(value) => value,
Err(error) => panic!("Could not read parent tree into index: {}", error),
};
let index_entry = self.read_file_as_index_entry(file_path, result_file_path);
if let Err(error) = index.add(&index_entry)
{
panic!("Could not add index entry: {}", error);
}
let tree_object_id = match index.write_tree_to(&self.repository)
{
Ok(value) => value,
Err(error) => panic!("Could not write index to tree: {}", error),
};
let tree = match self.repository.find_tree(tree_object_id)
{
Ok(value) => value,
Err(error) => panic!("Could obtain tree: {}", error),
};
info!("Created tree object “{}”", tree_object_id);
let signature = Signature::now(&self.user_name, &self.user_email).expect("Could not create signature");
let message = format!("Add file “{}", result_file_path.display());
let parent = match reference.peel_to_commit()
{
Ok(value) => value,
Err(error) => panic!("Could not peel reference: {}", error),
};
let commit_id = match self.repository.commit(Some(&reference_name), &signature, &signature, &message, &tree, &[&parent])
{
Ok(value) => value,
Err(error) => panic!("Could not write commit: {}", error),
};
let push_refspec = format!("refs/remotes/origin/{}:refs/heads/{}", branch_name, branch_name);
trace!("Created commit “{}”, using refspec “{}”", commit_id, push_refspec);
let mut remote_callbacks = RemoteCallbacks::new();
remote_callbacks.credentials(
|_, _, _|
{
match Cred::ssh_key_from_agent(&self.ssh_user)
{
Ok(credentials) => Ok(credentials),
Err(error) => panic!("could not retrieve key pair for SSH authentication as user “{}”: {}", self.ssh_user, error),
}
});
remote_callbacks.push_update_reference(
|reference, status| BenchmarkRepository::push_update_reference_callback(reference, status));
let mut push_options = PushOptions::new();
push_options.remote_callbacks(remote_callbacks);
let mut remote = self.repository.find_remote("origin").expect("");
remote.push(&[&push_refspec], Some(&mut push_options)).expect("couldnt push");
}
}
#[cfg(test)]
mod tests
{
}