refactoring to use quit crate
This commit is contained in:
parent
8d1d3e514b
commit
6d3ff502a9
6 changed files with 140 additions and 59 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -146,6 +146,15 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.17"
|
||||
|
@ -539,6 +548,21 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c090c608233d81bd6b90e718cf34506c60a10e633dff2292c3d1029e798d669b"
|
||||
dependencies = [
|
||||
"quit_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quit_macros"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d4b27a0dd5d08ad7af2d17952fb360ec9c30eeade0b32df7a3c9b099ff37564"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
|
@ -553,9 +577,11 @@ name = "refractr"
|
|||
version = "0.3.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"colored",
|
||||
"ctrlc",
|
||||
"git2",
|
||||
"hex",
|
||||
"quit",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2",
|
||||
|
|
|
@ -5,9 +5,11 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.29", features = ["derive"] }
|
||||
colored = "3.0.0"
|
||||
ctrlc = "3.4.5"
|
||||
git2 = "0.20.0"
|
||||
hex = "0.4.3"
|
||||
quit = "2.0.0"
|
||||
serde = "1.0.217"
|
||||
serde_derive = "1.0.217"
|
||||
sha2 = "0.10.8"
|
||||
|
@ -16,4 +18,4 @@ toml = "0.8.20"
|
|||
username = "0.2.0"
|
||||
|
||||
[target.'cfg(target_family = "unix")'.dependencies]
|
||||
users = "0.11.0"
|
||||
users = "0.11.0"
|
||||
|
|
|
@ -1,13 +1,25 @@
|
|||
use colored::Colorize;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! freak_out {
|
||||
($msg:expr) => {
|
||||
return Err($msg)
|
||||
};
|
||||
pub enum ExitCode {
|
||||
ParseError = 2,
|
||||
FilesystemError = 3,
|
||||
RepositoryError = 4,
|
||||
RemoteError = 5,
|
||||
PushError = 6
|
||||
}
|
||||
|
||||
pub struct ReturnData {
|
||||
pub code: ExitCode,
|
||||
pub msg: String,
|
||||
}
|
||||
|
||||
pub fn error(msg: String, code: ExitCode) {
|
||||
eprintln!("{} {}", "error:".red().bold(), msg);
|
||||
quit::with_code(code as u8)
|
||||
}
|
||||
|
||||
pub fn warning(msg: String) {
|
||||
eprintln!("warning: {}", msg)
|
||||
eprintln!("{} {}", "warning:".yellow().bold(), msg)
|
||||
}
|
||||
|
||||
pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
|
||||
|
@ -18,5 +30,5 @@ pub fn verbose(level: u8, msg_lvl: u8, msg: String) {
|
|||
}
|
||||
|
||||
prefix += "> ";
|
||||
eprintln!("{}{}", prefix, msg);
|
||||
eprintln!("{}{}", prefix.purple().bold(), msg);
|
||||
}
|
||||
|
|
|
@ -107,24 +107,32 @@ pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<Confi
|
|||
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!("refractr: unable to open {}: {}", path.as_path().display(), e)),
|
||||
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!("refractr: 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))
|
||||
};
|
||||
|
||||
let config_file = ConfigFile {
|
||||
path: match fs::canonicalize(&path) {
|
||||
Err(_) => return Err(format!("refractr: cannot get absolute path of config file: {}", path.as_path().display())),
|
||||
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!("refractr: cannot obtain metadata for config file: {}", path.as_path().display())),
|
||||
Err(_) => return Err(format!("cannot obtain metadata for config file: {}", path.as_path().display())),
|
||||
Ok(metadata) => metadata
|
||||
},
|
||||
config: verify_config(toml::from_str(&data).unwrap())
|
||||
config: match verify_config(&config) {
|
||||
Err(e) => return Err(format!("invalid config {}: {}", path.as_path().display(), e)),
|
||||
Ok(_) => config
|
||||
}
|
||||
};
|
||||
|
||||
let mut dup = false;
|
||||
|
@ -146,25 +154,37 @@ pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<Confi
|
|||
return Ok(config_files);
|
||||
}
|
||||
|
||||
fn verify_config(config: Config) -> Config {
|
||||
fn verify_config(config: &Config) -> Result<(), String> {
|
||||
if config.schedule.enabled {
|
||||
assert_ne!(config.schedule.interval, None);
|
||||
assert!(config.schedule.interval.unwrap() >= 60);
|
||||
match config.schedule.interval {
|
||||
Some(i) => {
|
||||
if i < 60 {
|
||||
return Err(format!("schedule is enabled, but less than 60"))
|
||||
}
|
||||
},
|
||||
None => return Err(format!("schedule is enabled, but no interval was defined"))
|
||||
}
|
||||
}
|
||||
|
||||
assert_ne!("", match &config.work_dir {
|
||||
match &config.work_dir {
|
||||
Some(path) => format!("{}", path),
|
||||
None => {
|
||||
if cfg!(windows) {
|
||||
match env::var("TEMP") {
|
||||
Ok(val) => val,
|
||||
Err(_) => format!("")
|
||||
Err(_) => return Err(format!("cannot determine the default temp dir"))
|
||||
}
|
||||
} else {
|
||||
format!("/tmp/refractr")
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return config;
|
||||
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"))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -44,6 +44,7 @@ fn get_config_default() -> &'static str {
|
|||
return "/usr/local/etc/refractr/config.toml";
|
||||
}
|
||||
|
||||
#[quit::main]
|
||||
fn main() -> Result<(), String> {
|
||||
let args = Args::parse();
|
||||
let refractr = Refractr {
|
||||
|
@ -77,29 +78,35 @@ fn main() -> Result<(), String> {
|
|||
Err(_) => common::warning(format!("failed to get process username")),
|
||||
}
|
||||
|
||||
common::verbose(refractr.verbose, 1, format!("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, 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);
|
||||
Ok(())
|
||||
} else {
|
||||
let cfgs = match config::read_config(args.config, &refractr) {
|
||||
Ok(cfgs) => cfgs,
|
||||
Err(e) => freak_out!(e)
|
||||
};
|
||||
if refractr.verbose >= 2 {
|
||||
// no need to loop over configs if verbose is not at the correct level
|
||||
for i in &cfgs {
|
||||
common::verbose(refractr.verbose, 2, format!("{}", i));
|
||||
}
|
||||
}
|
||||
|
||||
common::verbose(refractr.verbose, 1, format!("Config file(s) read successfully"));
|
||||
refractr.run(cfgs)
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let cfgs = match config::read_config(args.config, &refractr) {
|
||||
Ok(cfgs) => cfgs,
|
||||
Err(e) => return Err(e)
|
||||
};
|
||||
if refractr.verbose >= 2 {
|
||||
// no need to loop over configs if verbose is not at the correct level
|
||||
for i in &cfgs {
|
||||
common::verbose(refractr.verbose, 2, format!("{}", i));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::freak_out;
|
||||
use crate::common;
|
||||
use crate::common::{self, ReturnData, ExitCode};
|
||||
use crate::config::{Config, ConfigFile};
|
||||
|
||||
use git2::{FetchOptions, CertificateCheckStatus, Cred, PushOptions, RemoteCallbacks, Repository};
|
||||
use git2::{Error, ErrorCode};
|
||||
use git2::build::{RepoBuilder, CheckoutBuilder};
|
||||
use git2::build::RepoBuilder;
|
||||
use hex;
|
||||
use sha2::{Sha256, Digest};
|
||||
use std::env;
|
||||
|
@ -33,7 +32,7 @@ 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) {
|
||||
freak_out!(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())
|
||||
}
|
||||
|
@ -121,7 +120,7 @@ impl Refractr {
|
|||
common::warning(format!("remote {} already exists, skipping", remote_id));
|
||||
remote_list.push(remote_id)
|
||||
} else {
|
||||
freak_out!(format!("failed to create remote: {}", e));
|
||||
return Err(format!("failed to create remote: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,7 +179,7 @@ impl Refractr {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn looper(&self, repos: Vec<OpenedRepository>) -> Result<(), String> {
|
||||
fn looper(&self, repos: Vec<OpenedRepository>) -> Result<(), ReturnData> {
|
||||
let mut current_ints = Vec::new();
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
|
@ -224,8 +223,8 @@ impl Refractr {
|
|||
&repos[i].cfg,
|
||||
&repos[i].repo,
|
||||
&repos[i].remotes) {
|
||||
return Err(e)
|
||||
}
|
||||
common::error(e, ExitCode::PushError)
|
||||
};
|
||||
}
|
||||
}
|
||||
do_break = false;
|
||||
|
@ -238,7 +237,7 @@ impl Refractr {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), String> {
|
||||
pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), ReturnData> {
|
||||
common::verbose(self.verbose, 3, format!("Starting main refractr loop"));
|
||||
let mut loop_repos = Vec::new();
|
||||
|
||||
|
@ -248,7 +247,7 @@ impl Refractr {
|
|||
let work_dir = self.set_up_work_dir(match &cfg.config.work_dir {
|
||||
None => {
|
||||
if cfg!(windows) {
|
||||
PathBuf::from(format!("{}\\refractr\"", env::var("TEMP").unwrap()))
|
||||
PathBuf::from(format!("{}\\refractr", env::var("TEMP").unwrap()))
|
||||
} else {
|
||||
PathBuf::from("/tmp/refractr")
|
||||
}
|
||||
|
@ -257,7 +256,10 @@ impl Refractr {
|
|||
});
|
||||
let path_str = match work_dir {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Err(e)
|
||||
Err(e) => return Err(ReturnData {
|
||||
code: ExitCode::FilesystemError,
|
||||
msg: e
|
||||
})
|
||||
};
|
||||
common::verbose(
|
||||
self.verbose,
|
||||
|
@ -265,19 +267,17 @@ impl Refractr {
|
|||
format!("Created working directory: {}", &path_str));
|
||||
let repo_name = match &cfg.config.from.split("/").last() {
|
||||
Some(split) => split.to_string(),
|
||||
None => freak_out!(format!("failed to parse repository name"))
|
||||
None => return Err(ReturnData {
|
||||
code: ExitCode::ParseError,
|
||||
msg: format!("failed to parse repository name")
|
||||
})
|
||||
};
|
||||
|
||||
// make initial clone
|
||||
common::verbose(
|
||||
self.verbose,
|
||||
1,
|
||||
format!("Cloning repository: {}", &cfg.config.from));
|
||||
|
||||
let mut builder = RepoBuilder::new();
|
||||
let mut cb = RemoteCallbacks::new();
|
||||
let mut fo = FetchOptions::new();
|
||||
|
||||
// make initial clone
|
||||
if cfg.config.from.starts_with("ssh://") {
|
||||
let key_string = cfg.config.git.ssh_identity_file.clone();
|
||||
cb.credentials(move |_,_,_| Cred::ssh_key(
|
||||
|
@ -304,17 +304,28 @@ impl Refractr {
|
|||
builder.fetch_options(fo);
|
||||
|
||||
let repo_dir = format!("{}{}{}", &path_str, MAIN_SEPARATOR_STR, repo_name);
|
||||
common::verbose(
|
||||
self.verbose,
|
||||
1,
|
||||
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(_) => {
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
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 {
|
||||
freak_out!(format!("failed to obtain existing repo"))
|
||||
return Err(ReturnData {
|
||||
code: ExitCode::RepositoryError,
|
||||
msg: format!("failed to obtain existing repo")
|
||||
})
|
||||
},
|
||||
Err(e) => freak_out!(format!("failed to obtain existing repo: {}", e))
|
||||
Err(e) => return Err(ReturnData {
|
||||
code: ExitCode::RepositoryError,
|
||||
msg: format!("failed to obtain existing repo: {}", e)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -324,10 +335,13 @@ impl Refractr {
|
|||
|
||||
let remotes = match self.make_remotes(&repo, &cfg) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(e)
|
||||
Err(e) => return Err(ReturnData {
|
||||
code: ExitCode::RemoteError,
|
||||
msg: e
|
||||
})
|
||||
};
|
||||
if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) {
|
||||
return Err(e)
|
||||
common::error(e, ExitCode::PushError);
|
||||
}
|
||||
|
||||
if cfg.config.schedule.enabled {
|
||||
|
|
Loading…
Add table
Reference in a new issue