fix formatting, use git2 fork until/if merged in

This commit is contained in:
Bryson Steck 2025-03-20 22:01:50 -06:00
parent 5d06e08f5b
commit f37549748c
Signed by: bryson
SSH key fingerprint: SHA256:XpKABw/nP4z8UVaH+weLaBnEOD86+cVwif+QjuYLGT4
7 changed files with 299 additions and 158 deletions

View file

@ -1,4 +1,4 @@
edition = "2021"
tab_spaces = 2
match_block_trailing_comma = true
wrap_comments = true
#wrap_comments = true

10
Cargo.lock generated
View file

@ -232,9 +232,8 @@ dependencies = [
[[package]]
name = "git2"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff"
version = "0.20.1"
source = "git+https://github.com/brysonsteck/git2-rs?branch=certificates#659e318aedf3de9c7be5ce2bf40c8aa850194792"
dependencies = [
"bitflags",
"libc",
@ -435,9 +434,8 @@ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "libgit2-sys"
version = "0.18.0+1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec"
version = "0.18.1+1.9.0"
source = "git+https://github.com/brysonsteck/git2-rs?branch=certificates#659e318aedf3de9c7be5ce2bf40c8aa850194792"
dependencies = [
"cc",
"libc",

View file

@ -8,7 +8,7 @@ edition = "2021"
clap = { version = "4.5.29", features = ["derive"] }
colored = "3.0.0"
ctrlc = "3.4.5"
git2 = "0.20.0"
git2 = { git = "https://github.com/brysonsteck/git2-rs", branch = "certificates" }
hex = "0.4.3"
quit = "2.0.0"
serde = "1.0.217"

View file

@ -15,7 +15,7 @@ pub enum ExitCode {
RemoteError = 5,
PushError = 6,
FetchError = 7,
ConfigError = 8
ConfigError = 8,
}
pub struct ReturnData {
@ -33,7 +33,9 @@ pub fn warning(msg: String) {
}
pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
if level < msg_lvl { return };
if level < msg_lvl {
return;
};
let mut prefix = String::new();
for _ in 0..msg_lvl {
prefix += "=";

View file

@ -20,7 +20,7 @@ use toml;
pub struct ConfigFile {
pub path: String,
pub file: Metadata,
pub config: Config
pub config: Config,
}
impl fmt::Display for ConfigFile {
@ -40,21 +40,24 @@ impl fmt::Display for ConfigFile {
if i < self.config.to.len() - 1 {
to_list.push_str(", ");
}
};
}
to_list.push(']');
let work_dir_path = match &self.config.work_dir {
None => {
if cfg!(windows) {
format!("Using default \"{}\\refractr\"", match env::var("TEMP") {
Ok(val) => val,
Err(_) => format!("This shouldn't happen!")
})
format!(
"Using default \"{}\\refractr\"",
match env::var("TEMP") {
Ok(val) => val,
Err(_) => format!("This shouldn't happen!"),
}
)
} else {
format!("Using default: /tmp/refractr")
}
},
Some(path) => format!("{}", path)
Some(path) => format!("{}", path),
};
let schedule_interval = match self.config.schedule.interval {
@ -65,10 +68,15 @@ impl fmt::Display for ConfigFile {
String::from("This shouldn't happen!\n")
}
},
Some(int) => format!("\n Scheduled interval in seconds: {}", int.to_string())
Some(int) => format!(
"\n Scheduled interval in seconds: {}",
int.to_string()
),
};
write!(f, "Config file: \"{}\"\n \
write!(
f,
"Config file: \"{}\"\n \
Is a file: {}\n \
Read only: {}\n \
Configuration:\n \
@ -78,32 +86,37 @@ impl fmt::Display for ConfigFile {
Working directory: {}\n \
SSH key for pushing clone: {}\n \
Schedule enabled: {}\
{}"
, self.path, self.file.is_file(), self.file.permissions().readonly(), self.config.from
, to_list, branches_list, work_dir_path, self.config.git.ssh_identity_file, self.config.schedule.enabled
, schedule_interval)
{}",
self.path,
self.file.is_file(),
self.file.permissions().readonly(),
self.config.from,
to_list,
branches_list,
work_dir_path,
self.config.git.ssh_identity_file,
self.config.schedule.enabled,
schedule_interval
)
}
}
#[derive(PartialEq)]
#[derive(Deserialize)]
#[derive(PartialEq, Deserialize)]
pub struct Config {
pub from: String,
pub to: Vec<String>,
pub branches: Vec<String>,
pub work_dir: Option<String>,
pub git: Git,
pub schedule: Schedule
pub schedule: Schedule,
}
#[derive(PartialEq)]
#[derive(Deserialize)]
#[derive(PartialEq, Deserialize)]
pub struct Git {
pub ssh_identity_file: String,
}
#[derive(PartialEq)]
#[derive(Deserialize)]
#[derive(PartialEq, Deserialize)]
pub struct Schedule {
pub enabled: bool,
pub interval: Option<i32>,
@ -112,45 +125,90 @@ pub struct Schedule {
pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<ConfigFile>, String> {
let mut config_files: Vec<ConfigFile> = vec![];
for path in paths {
common::verbose(refractr.verbose, 1, format!("Reading config file: \"{}\"", String::from(path.to_string_lossy())));
common::verbose(
refractr.verbose,
1,
format!(
"Reading config file: \"{}\"",
String::from(path.to_string_lossy())
),
);
let mut data = String::new();
let mut file = match File::open(path.as_path()) {
Err(e) => return Err(format!("unable to open {}: {}", path.as_path().display(), e)),
Ok(file) => file
Err(e) => {
return Err(format!(
"unable to open {}: {}",
path.as_path().display(),
e
))
},
Ok(file) => file,
};
if let Err(e) = file.read_to_string(&mut data) {
return Err(format!("unable to read {}: {}", path.as_path().display(), e))
return Err(format!(
"unable to read {}: {}",
path.as_path().display(),
e
));
}
let config: Config = match toml::from_str(&data) {
Ok(c) => c,
Err(e) => return Err(format!("issues parsing toml file {}: {}", path.as_path().display(), e))
Err(e) => {
return Err(format!(
"issues parsing toml file {}: {}",
path.as_path().display(),
e
))
},
};
let config_file = ConfigFile {
path: match fs::canonicalize(&path) {
Err(_) => return Err(format!("cannot get absolute path of config file: {}", path.as_path().display())),
Ok(abs) => abs.to_string_lossy().to_string()
Err(_) => {
return Err(format!(
"cannot get absolute path of config file: {}",
path.as_path().display()
))
},
Ok(abs) => abs.to_string_lossy().to_string(),
},
file: match fs::metadata(&path) {
Err(_) => return Err(format!("cannot obtain metadata for config file: {}", path.as_path().display())),
Ok(metadata) => metadata
Err(_) => {
return Err(format!(
"cannot obtain metadata for config file: {}",
path.as_path().display()
))
},
Ok(metadata) => metadata,
},
config: match verify_config(&config) {
Err(e) => return Err(format!("invalid config {}: {}", path.as_path().display(), e)),
Ok(_) => config
}
Err(e) => {
return Err(format!(
"invalid config {}: {}",
path.as_path().display(),
e
))
},
Ok(_) => config,
},
};
let mut dup = false;
for i in &config_files {
if i.path == config_file.path {
common::warning(format!("skipping config file \"{}\" as it was already read", path.as_path().display()));
common::warning(format!(
"skipping config file \"{}\" as it was already read",
path.as_path().display()
));
dup = true;
break;
} else if i.config == config_file.config {
common::warning(format!("config files \"{}\" and \"{}\" appear to have the same config", i.path, config_file.path));
common::warning(format!(
"config files \"{}\" and \"{}\" appear to have the same config",
i.path, config_file.path
));
}
}
@ -167,10 +225,10 @@ fn verify_config(config: &Config) -> Result<(), String> {
match config.schedule.interval {
Some(i) => {
if i < 60 {
return Err(format!("schedule is enabled, but less than 60"))
return Err(format!("schedule is enabled, but less than 60"));
}
},
None => return Err(format!("schedule is enabled, but no interval was defined"))
None => return Err(format!("schedule is enabled, but no interval was defined")),
}
}
@ -180,7 +238,7 @@ fn verify_config(config: &Config) -> Result<(), String> {
if cfg!(windows) {
match env::var("TEMP") {
Ok(val) => val,
Err(_) => return Err(format!("cannot determine the default temp dir"))
Err(_) => return Err(format!("cannot determine the default temp dir")),
}
} else {
format!("/tmp/refractr")
@ -190,8 +248,9 @@ fn verify_config(config: &Config) -> Result<(), String> {
if !&config.from.starts_with("ssh://")
&& !&config.from.starts_with("https://")
&& !&config.from.starts_with("http://") {
return Err(format!("'from' value does not use a supported protocol"))
&& !&config.from.starts_with("http://")
{
return Err(format!("'from' value does not use a supported protocol"));
}
Ok(())

View file

@ -15,10 +15,10 @@ use crate::refractr::Refractr;
use clap::Parser;
use std::path::PathBuf;
use std::process;
#[cfg(target_family = "unix")]
use users;
#[cfg(target_family = "windows")]
use username;
#[cfg(target_family = "unix")]
use users;
#[derive(Parser)]
#[command(name = "refractr")]
@ -32,7 +32,11 @@ struct Args {
#[arg(short, long, help = "Specify the level of verbosity", action = clap::ArgAction::Count)]
verbose: u8,
#[arg(short = 'e', long, help = "Output a full, commented config file and exit")]
#[arg(
short = 'e',
long,
help = "Output a full, commented config file and exit"
)]
create: bool,
#[arg(short = 's', long, help = "Exit on push errors instead of ignoring")]
@ -44,7 +48,8 @@ fn get_config_default() -> &'static str {
return "C:\\ProgramData\\refractr\\config.toml";
#[cfg(target_os = "linux")]
return "/etc/refractr/config.toml";
#[cfg(any(target_os = "freebsd",
#[cfg(any(
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "macos"
@ -61,12 +66,12 @@ fn main() -> Result<(), String> {
"true" => true,
_ => false,
},
None => false
None => false,
},
pid: process::id(),
strict: args.strict,
unix: cfg!(unix),
verbose: args.verbose
verbose: args.verbose,
};
// warn to avoid root/admin
@ -86,22 +91,37 @@ fn main() -> Result<(), String> {
Err(_) => common::warning(format!("failed to get process username")),
}
common::verbose(refractr.verbose, 1, format!("refractr started with level {} verbosity enabled", refractr.verbose.to_string()));
common::verbose(
refractr.verbose,
1,
format!(
"refractr started with level {} verbosity enabled",
refractr.verbose.to_string()
),
);
common::verbose(refractr.verbose, 3, format!("Process ID: {}", refractr.pid));
common::verbose(refractr.verbose, 3, format!("Running in Docker: {}", refractr.docker));
common::verbose(refractr.verbose, 3, format!("System is UNIX(-like): {}", refractr.unix));
common::verbose(
refractr.verbose,
3,
format!("Running in Docker: {}", refractr.docker),
);
common::verbose(
refractr.verbose,
3,
format!("System is UNIX(-like): {}", refractr.unix),
);
common::verbose(refractr.verbose, 2, format!("Checking for create flag"));
if args.create {
common::verbose(refractr.verbose, 3, format!("Printing sample config"));
let example = include_str!("example/config.toml");
println!("{}", example);
return Ok(())
return Ok(());
}
let mut cfgs = vec![];
match config::read_config(args.config, &refractr) {
Ok(c) => cfgs = c,
Err(e) => common::error(format!("{}", e), common::ExitCode::ConfigError)
Err(e) => common::error(format!("{}", e), common::ExitCode::ConfigError),
};
if refractr.verbose >= 2 {
@ -111,10 +131,14 @@ fn main() -> Result<(), String> {
}
}
common::verbose(refractr.verbose, 1, format!("Config file(s) read successfully"));
common::verbose(
refractr.verbose,
1,
format!("Config file(s) read successfully"),
);
match refractr.run(cfgs) {
Ok(_) => (),
Err(e) => common::error(format!("{}", e.msg), e.code)
Err(e) => common::error(format!("{}", e.msg), e.code),
};
Ok(())

View file

@ -6,20 +6,20 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use crate::common::{self, ReturnData, ExitCode};
use crate::common::{self, ExitCode, ReturnData};
use crate::config::{Config, ConfigFile};
use git2::string_array::StringArray;
use git2::{FetchOptions, CertificateCheckStatus, Cred, PushOptions, RemoteCallbacks, Repository};
use git2::{Error, ErrorCode};
use git2::build::RepoBuilder;
use git2::string_array::StringArray;
use git2::{CertificateCheckStatus, Cred, FetchOptions, PushOptions, RemoteCallbacks, Repository};
use git2::{Error, ErrorCode};
use hex;
use sha2::{Sha256, Digest};
use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time;
@ -28,7 +28,7 @@ pub struct Refractr {
pub pid: u32,
pub strict: bool,
pub unix: bool,
pub verbose: u8
pub verbose: u8,
}
struct OpenedRepository {
@ -41,7 +41,11 @@ struct OpenedRepository {
impl Refractr {
fn set_up_work_dir(&self, work_dir: PathBuf) -> Result<String, String> {
if let Err(e) = fs::create_dir_all(&work_dir) {
return Err(format!("could not create working directory: {}: {}", work_dir.to_string_lossy().to_string(), e))
return Err(format!(
"could not create working directory: {}: {}",
work_dir.to_string_lossy().to_string(),
e
));
}
Ok(work_dir.to_string_lossy().to_string())
}
@ -72,47 +76,58 @@ impl Refractr {
Ok(())
}
fn fetch(&self, repo: &Repository, branches: &Vec<String>, ssh: bool, ssh_key: &String) -> Result<(), Error> {
fn fetch(
&self,
repo: &Repository,
branches: &Vec<String>,
ssh: bool,
ssh_key: &String,
) -> Result<(), Error> {
let mut cb = RemoteCallbacks::new();
let mut fo = FetchOptions::new();
if ssh {
let key_string: String = ssh_key.clone();
cb.credentials(move |_,_,_| Cred::ssh_key(
"git",
None,
Path::new(&key_string),
None));
cb.credentials(move |_, _, _| Cred::ssh_key("git", None, Path::new(&key_string), None));
cb.certificate_check(|cert, url| {
let mut sha256 = String::new();
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
sha256.push_str(&hex::encode(i.to_string()));
}
common::warning(
format!("implicitly trusting unknown host {} with sha256 host key {}",
common::warning(format!(
"implicitly trusting unknown host {} with sha256 host key {}",
url,
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
common::warning(
format!("to ignore this error in the future, add this host to your known_hosts file"));
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())
));
common::warning(format!(
"to ignore this error in the future, add this host to your known_hosts file"
));
Ok(CertificateCheckStatus::CertificateOk)
});
}
fo.download_tags(git2::AutotagOption::All);
fo.remote_callbacks(cb);
repo.find_remote("origin")?.fetch(&branches, Some(&mut fo), None)?;
repo
.find_remote("origin")?
.fetch(&branches, Some(&mut fo), None)?;
Ok(())
}
fn set_up_refs(&self, repo: &Repository, branches: &Vec<String>) -> Result<(), Error> {
for branch in branches {
let mut fetch_head = repo.find_reference(format!("refs/remotes/origin/{}", branch).as_str())?;
let mut fetch_head =
repo.find_reference(format!("refs/remotes/origin/{}", branch).as_str())?;
fetch_head.rename(format!("refs/heads/{}", branch).as_str(), true, "")?;
}
Ok(())
}
fn make_remotes<'a> (&self, repo: &'a Repository, cfg: &ConfigFile) -> Result<Vec<String>, String> {
fn make_remotes<'a>(
&self,
repo: &'a Repository,
cfg: &ConfigFile,
) -> Result<Vec<String>, String> {
// create remotes for each "to" repo
let mut remote_list = Vec::new();
for to in &cfg.config.to {
@ -122,7 +137,8 @@ impl Refractr {
common::verbose(
self.verbose,
2,
format!("Attempting to create remote {} for url {}", remote_id, to));
format!("Attempting to create remote {} for url {}", remote_id, to),
);
match repo.remote(remote_id.as_str(), to) {
Ok(_) => remote_list.push(remote_id),
Err(e) => {
@ -132,37 +148,43 @@ impl Refractr {
} else {
return Err(format!("failed to create remote: {}", e));
}
}
},
}
}
Ok(remote_list)
}
fn push_remotes(&self, cfg: &Config, repo: &Repository, remote_list: &Vec<String>) -> Result<(), String> {
fn push_remotes(
&self,
cfg: &Config,
repo: &Repository,
remote_list: &Vec<String>,
) -> Result<(), String> {
for id in remote_list {
let mut remote = repo.find_remote(&id).unwrap();
common::verbose(
self.verbose,
1,
format!("Pushing to remote: {}", remote.url().unwrap()));
format!("Pushing to remote: {}", remote.url().unwrap()),
);
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|_,_,_| Cred::ssh_key(
"git",
None,
&Path::new(&cfg.git.ssh_identity_file),
None));
callbacks.credentials(|_, _, _| {
Cred::ssh_key("git", None, &Path::new(&cfg.git.ssh_identity_file), None)
});
callbacks.certificate_check(|cert, url| {
let mut sha256 = String::new();
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
sha256.push_str(&hex::encode(i.to_string()));
}
common::warning(
format!("implicitly trusting unknown host {} with sha256 host key {}",
common::warning(format!(
"implicitly trusting unknown host {} with sha256 host key {}",
url,
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
common::warning(
format!("to ignore this error in the future, add this host to your known_hosts file"));
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())
));
common::warning(format!(
"to ignore this error in the future, add this host to your known_hosts file"
));
Ok(CertificateCheckStatus::CertificateOk)
});
let mut push_options = PushOptions::new();
@ -182,11 +204,19 @@ impl Refractr {
Ok(_) => (),
Err(e) => {
if self.strict {
return Err(format!("failed to push to remote: {}: {}", remote.url().unwrap(), e))
return Err(format!(
"failed to push to remote: {}: {}",
remote.url().unwrap(),
e
));
} else {
common::warning(format!("failed to push to remote: {}: {}", remote.url().unwrap(), e))
common::warning(format!(
"failed to push to remote: {}: {}",
remote.url().unwrap(),
e
))
}
}
},
}
}
@ -199,13 +229,16 @@ impl Refractr {
let r = running.clone();
let count = repos.len();
for i in 0..repos.len() {
current_ints.push(u64::from(repos[i].cfg.schedule.interval.unwrap().unsigned_abs()));
};
current_ints.push(u64::from(
repos[i].cfg.schedule.interval.unwrap().unsigned_abs(),
));
}
let original_ints = current_ints.clone();
ctrlc::set_handler(move || {
r.store(false, Ordering::SeqCst);
}).expect("Failed to set ^C handler");
})
.expect("Failed to set ^C handler");
common::verbose(self.verbose, 1, format!("Starting scheduled loop"));
let min = *current_ints.iter().min().unwrap();
@ -218,7 +251,8 @@ impl Refractr {
common::verbose(
self.verbose,
2,
format!("Sleeping for {} seconds", sleep_int.as_secs()));
format!("Sleeping for {} seconds", sleep_int.as_secs()),
);
while running.load(Ordering::SeqCst) {
thread::sleep(time::Duration::from_secs(1));
if now.elapsed().as_secs() >= sleep_int.as_secs() {
@ -230,19 +264,17 @@ impl Refractr {
common::verbose(
self.verbose,
2,
format!("Interval for {} has arrived, pulling", repos[i].cfg.from));
format!("Interval for {} has arrived, pulling", repos[i].cfg.from),
);
let _ = self.fast_forward(&repos[i].path, &repos[i].cfg.branches);
if let Err(e) = self.push_remotes(
&repos[i].cfg,
&repos[i].repo,
&repos[i].remotes) {
common::error(e, ExitCode::PushError)
if let Err(e) = self.push_remotes(&repos[i].cfg, &repos[i].repo, &repos[i].remotes) {
common::error(e, ExitCode::PushError)
};
}
}
do_break = false;
break
break;
}
}
}
@ -266,25 +298,30 @@ impl Refractr {
PathBuf::from("/tmp/refractr")
}
},
Some(path) => PathBuf::from(path)
Some(path) => PathBuf::from(path),
});
let path_str = match work_dir {
Ok(p) => p,
Err(e) => return Err(ReturnData {
code: ExitCode::FilesystemError,
msg: e
})
Err(e) => {
return Err(ReturnData {
code: ExitCode::FilesystemError,
msg: e,
})
},
};
common::verbose(
self.verbose,
2,
format!("Created working directory: {}", &path_str));
format!("Created working directory: {}", &path_str),
);
let repo_name = match &cfg.config.from.split("/").last() {
Some(split) => split.to_string(),
None => return Err(ReturnData {
code: ExitCode::ParseError,
msg: format!("failed to parse repository name")
})
None => {
return Err(ReturnData {
code: ExitCode::ParseError,
msg: format!("failed to parse repository name"),
})
},
};
let ssh = cfg.config.from.starts_with("ssh://");
@ -295,22 +332,20 @@ impl Refractr {
// make initial clone
if ssh {
let key_string = cfg.config.git.ssh_identity_file.clone();
cb.credentials(move |_,_,_| Cred::ssh_key(
"git",
None,
Path::new(&key_string),
None));
cb.credentials(move |_, _, _| Cred::ssh_key("git", None, Path::new(&key_string), None));
cb.certificate_check(|cert, url| {
let mut sha256 = String::new();
for i in cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec() {
sha256.push_str(&hex::encode(i.to_string()));
}
common::warning(
format!("implicitly trusting unknown host {} with sha256 host key {}",
common::warning(format!(
"implicitly trusting unknown host {} with sha256 host key {}",
url,
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())));
common::warning(
format!("to ignore this error in the future, add this host to your known_hosts file"));
hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec())
));
common::warning(format!(
"to ignore this error in the future, add this host to your known_hosts file"
));
Ok(CertificateCheckStatus::CertificateOk)
});
}
@ -322,45 +357,63 @@ impl Refractr {
common::verbose(
self.verbose,
1,
format!("Cloning repository {} to {}", &cfg.config.from, &repo_dir));
format!("Cloning repository {} to {}", &cfg.config.from, &repo_dir),
);
let repo = match builder.clone(&cfg.config.from, Path::new(&repo_dir)) {
Ok(repo) => repo,
Err(e) => {
if e.code() != ErrorCode::Exists {
common::error(format!("failed to clone repo to {}: {}", repo_dir, e), ExitCode::FilesystemError);
common::error(
format!("failed to clone repo to {}: {}", repo_dir, e),
ExitCode::FilesystemError,
);
}
common::warning(format!("found existing repo at {}, attempting to use", repo_dir));
common::warning(format!(
"found existing repo at {}, attempting to use",
repo_dir
));
match self.fast_forward(&repo_dir, &cfg.config.branches) {
Ok(_) => if let Ok(repo) = Repository::open(Path::new(&repo_dir)) {
repo
} else {
Ok(_) => {
if let Ok(repo) = Repository::open(Path::new(&repo_dir)) {
repo
} else {
return Err(ReturnData {
code: ExitCode::RepositoryError,
msg: format!("failed to obtain existing repo"),
});
}
},
Err(e) => {
return Err(ReturnData {
code: ExitCode::RepositoryError,
msg: format!("failed to obtain existing repo")
msg: format!("failed to obtain existing repo: {}", e),
})
},
Err(e) => return Err(ReturnData {
code: ExitCode::RepositoryError,
msg: format!("failed to obtain existing repo: {}", e)
})
}
}
},
};
self.set_up_refs(&repo, &cfg.config.branches).unwrap();
if let Err(e) = self.fetch(&repo,
if let Err(e) = self.fetch(
&repo,
&cfg.config.branches,
ssh,
&cfg.config.git.ssh_identity_file) {
common::error(format!("failed to fetch repo {}: {}", cfg.config.from, e), ExitCode::FetchError);
&cfg.config.git.ssh_identity_file,
) {
common::error(
format!("failed to fetch repo {}: {}", cfg.config.from, e),
ExitCode::FetchError,
);
}
let remotes = match self.make_remotes(&repo, &cfg) {
Ok(v) => v,
Err(e) => return Err(ReturnData {
code: ExitCode::RemoteError,
msg: e
})
Err(e) => {
return Err(ReturnData {
code: ExitCode::RemoteError,
msg: e,
})
},
};
if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) {
common::error(e, ExitCode::PushError);
@ -371,7 +424,7 @@ impl Refractr {
repo,
path: repo_dir,
remotes,
cfg: cfg.config
cfg: cfg.config,
});
}
}
@ -380,13 +433,18 @@ impl Refractr {
common::verbose(
self.verbose,
2,
format!("{} configs have schedules enabled, setting up looper", loop_repos.len()));
format!(
"{} configs have schedules enabled, setting up looper",
loop_repos.len()
),
);
return self.looper(loop_repos);
} else {
common::verbose(
self.verbose,
2,
format!("No scheduled configs found, exiting refractr"));
format!("No scheduled configs found, exiting refractr"),
);
}
Ok(())