fix formatting, use git2 fork until/if merged in
This commit is contained in:
parent
5d06e08f5b
commit
f37549748c
7 changed files with 299 additions and 158 deletions
|
@ -1,4 +1,4 @@
|
|||
edition = "2021"
|
||||
tab_spaces = 2
|
||||
match_block_trailing_comma = true
|
||||
wrap_comments = true
|
||||
#wrap_comments = true
|
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 += "=";
|
||||
|
|
137
src/config.rs
137
src/config.rs
|
@ -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(())
|
||||
|
|
50
src/main.rs
50
src/main.rs
|
@ -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(())
|
||||
|
|
242
src/refractr.rs
242
src/refractr.rs
|
@ -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(())
|
||||
|
|
Loading…
Add table
Reference in a new issue