clean up work dirs

This commit is contained in:
Bryson Steck 2025-03-25 21:21:11 -06:00
parent b98a3beec4
commit fb8ed1ae8c
Signed by: bryson
SSH key fingerprint: SHA256:XpKABw/nP4z8UVaH+weLaBnEOD86+cVwif+QjuYLGT4
4 changed files with 90 additions and 52 deletions

View file

@ -16,6 +16,7 @@ pub enum ExitCode {
PushError = 6, PushError = 6,
FetchError = 7, FetchError = 7,
ConfigError = 8, ConfigError = 8,
HaltError = 130,
} }
pub struct ReturnData { pub struct ReturnData {
@ -23,11 +24,15 @@ pub struct ReturnData {
pub msg: String, pub msg: String,
} }
pub fn error(msg: String, code: ExitCode) { pub fn error_quit(msg: String, code: ExitCode) {
eprintln!("{} {}", "error:".red().bold(), msg); eprintln!("{} {}", "error:".red().bold(), msg);
quit::with_code(code as u8) quit::with_code(code as u8)
} }
pub fn error(msg: String) {
eprintln!("{} {}", "error:".red().bold(), msg);
}
pub fn warning(msg: String) { pub fn warning(msg: String) {
eprintln!("{} {}", "warning:".yellow().bold(), msg) eprintln!("{} {}", "warning:".yellow().bold(), msg)
} }

View file

@ -123,6 +123,19 @@ pub struct Schedule {
pub interval: Option<i32>, pub interval: Option<i32>,
} }
pub fn get_work_dir(work_dir: &Option<String>) -> String {
match work_dir {
None => {
if cfg!(windows) {
format!("{}\\refractr", env::var("TEMP").unwrap())
} else {
format!("/tmp/refractr")
}
},
Some(path) => path.clone(),
}
}
pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<ConfigFile>, String> { pub fn read_config(paths: Vec<PathBuf>, refractr: &Refractr) -> Result<Vec<ConfigFile>, String> {
let mut config_files: Vec<ConfigFile> = vec![]; let mut config_files: Vec<ConfigFile> = vec![];
for path in paths { for path in paths {
@ -233,18 +246,12 @@ fn verify_config(config: &Config) -> Result<(), String> {
} }
} }
match &config.work_dir { if let None = &config.work_dir {
Some(path) => format!("{}", path), if cfg!(windows) {
None => { if let Err(e) = env::var("TEMP") {
if cfg!(windows) { return Err(format!("cannot determine the default temp dir: {}", e));
match env::var("TEMP") {
Ok(val) => val,
Err(_) => return Err(format!("cannot determine the default temp dir")),
}
} else {
format!("/tmp/refractr")
} }
}, }
}; };
if !&config.from.starts_with("ssh://") if !&config.from.starts_with("ssh://")

View file

@ -45,6 +45,13 @@ struct Args {
help = "Exit on push and unknown host errors instead of ignoring them" help = "Exit on push and unknown host errors instead of ignoring them"
)] )]
strict: bool, strict: bool,
#[arg(
short = 'p',
long,
help = "Do not clean the working directories specified in config(s)"
)]
persist: bool,
} }
fn get_config_default() -> &'static str { fn get_config_default() -> &'static str {
@ -72,6 +79,7 @@ fn main() -> Result<(), String> {
}, },
None => false, None => false,
}, },
persist: args.persist,
pid: process::id(), pid: process::id(),
strict: args.strict, strict: args.strict,
unix: cfg!(unix), unix: cfg!(unix),
@ -125,7 +133,7 @@ fn main() -> Result<(), String> {
let mut cfgs = vec![]; let mut cfgs = vec![];
match config::read_config(args.config, &refractr) { match config::read_config(args.config, &refractr) {
Ok(c) => cfgs = c, Ok(c) => cfgs = c,
Err(e) => common::error(format!("{}", e), common::ExitCode::ConfigError), Err(e) => common::error_quit(format!("{}", e), common::ExitCode::ConfigError),
}; };
if refractr.verbose >= 2 { if refractr.verbose >= 2 {
@ -142,7 +150,7 @@ fn main() -> Result<(), String> {
); );
match refractr.run(cfgs) { match refractr.run(cfgs) {
Ok(_) => (), Ok(_) => (),
Err(e) => common::error(format!("{}", e.msg), e.code), Err(e) => common::error_quit(format!("{}", e.msg), e.code),
}; };
Ok(()) Ok(())

View file

@ -7,7 +7,7 @@
*/ */
use crate::common::{self, ExitCode, ReturnData}; use crate::common::{self, ExitCode, ReturnData};
use crate::config::{Config, ConfigFile}; use crate::config::{self, Config, ConfigFile};
use git2::build::RepoBuilder; use git2::build::RepoBuilder;
use git2::string_array::StringArray; use git2::string_array::StringArray;
@ -15,8 +15,7 @@ use git2::{CertificateCheckStatus, Cred, FetchOptions, PushOptions, RemoteCallba
use git2::{Error, ErrorCode}; use git2::{Error, ErrorCode};
use hex; use hex;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::env; use std::fs::{self, remove_dir_all};
use std::fs;
use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR}; use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -25,6 +24,7 @@ use std::time;
pub struct Refractr { pub struct Refractr {
pub docker: bool, pub docker: bool,
pub persist: bool,
pub pid: u32, pub pid: u32,
pub strict: bool, pub strict: bool,
pub unix: bool, pub unix: bool,
@ -39,7 +39,7 @@ struct OpenedRepository {
} }
impl Refractr { impl Refractr {
fn set_up_work_dir(&self, work_dir: PathBuf) -> Result<String, String> { fn set_up_work_dir(&self, work_dir: PathBuf) -> Result<(), String> {
if let Err(e) = fs::create_dir_all(&work_dir) { if let Err(e) = fs::create_dir_all(&work_dir) {
return Err(format!( return Err(format!(
"could not create working directory: {}: {}", "could not create working directory: {}: {}",
@ -47,7 +47,7 @@ impl Refractr {
e e
)); ));
} }
Ok(work_dir.to_string_lossy().to_string()) Ok(())
} }
fn set_up_ssh(&self, key_path: String, strict: bool) -> Result<RemoteCallbacks, String> { fn set_up_ssh(&self, key_path: String, strict: bool) -> Result<RemoteCallbacks, String> {
@ -56,7 +56,7 @@ impl Refractr {
cb.certificate_check(move |cert, url| { cb.certificate_check(move |cert, url| {
let sha256 = hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec()); let sha256 = hex::encode(cert.as_hostkey().unwrap().hash_sha256().unwrap().to_vec());
if strict { if strict {
common::error( common::error_quit(
format!( format!(
"unknown host {} with sha256 host key {}, exiting", "unknown host {} with sha256 host key {}, exiting",
url, sha256 url, sha256
@ -120,7 +120,7 @@ impl Refractr {
fo.remote_callbacks(cb); fo.remote_callbacks(cb);
() ()
}, },
Err(e) => common::error( Err(e) => common::error_quit(
format!("error setting up ssh: {}", e), format!("error setting up ssh: {}", e),
ExitCode::ConfigError, ExitCode::ConfigError,
), ),
@ -195,7 +195,7 @@ impl Refractr {
po.remote_callbacks(cb); po.remote_callbacks(cb);
() ()
}, },
Err(e) => common::error( Err(e) => common::error_quit(
format!("error setting up ssh: {}", e), format!("error setting up ssh: {}", e),
ExitCode::ConfigError, ExitCode::ConfigError,
), ),
@ -294,7 +294,7 @@ impl Refractr {
&repos[i].cfg.git.ssh_identity_file, &repos[i].cfg.git.ssh_identity_file,
self.strict.clone(), self.strict.clone(),
) { ) {
common::error( common::error_quit(
format!("failed to fetch repo {}: {}", repos[i].cfg.from, e), format!("failed to fetch repo {}: {}", repos[i].cfg.from, e),
ExitCode::FetchError, ExitCode::FetchError,
); );
@ -302,7 +302,7 @@ impl Refractr {
let _ = self.fast_forward(&repos[i].repo, &repos[i].cfg.branches); let _ = self.fast_forward(&repos[i].repo, &repos[i].cfg.branches);
if let Err(e) = self.push_remotes(&repos[i].cfg, &repos[i].repo, &repos[i].remotes) { if let Err(e) = self.push_remotes(&repos[i].cfg, &repos[i].repo, &repos[i].remotes) {
common::error(e, ExitCode::PushError) common::error_quit(e, ExitCode::PushError)
}; };
current_ints[i] = original_ints[i].clone(); current_ints[i] = original_ints[i].clone();
} }
@ -322,37 +322,38 @@ impl Refractr {
Ok(()) Ok(())
} }
fn clean(&self, dirs: Vec<String>) -> Result<(), ReturnData> {
for dir in dirs {
common::verbose(self.verbose, 1, format!("removing working dir {}", dir));
if let Err(e) = remove_dir_all(&dir) {
common::warning(format!("could not clean up working dir: {}: {}", dir, e));
}
}
Ok(())
}
pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), ReturnData> { pub fn run(&self, cfgs: Vec<ConfigFile>) -> Result<(), ReturnData> {
common::verbose(self.verbose, 3, format!("Starting main refractr loop")); common::verbose(self.verbose, 3, format!("Starting main refractr loop"));
let mut loop_repos = Vec::new(); let mut loop_repos = Vec::new();
let mut work_dirs = Vec::new();
for cfg in cfgs { for cfg in cfgs {
// set up the working directory // set up the working directory
common::verbose(self.verbose, 3, format!("Loading config: {}", cfg.path)); common::verbose(self.verbose, 3, format!("Loading config: {}", cfg.path));
let work_dir = self.set_up_work_dir(match &cfg.config.work_dir { let work_dir = config::get_work_dir(&cfg.config.work_dir);
None => { if let Err(e) = self.set_up_work_dir(PathBuf::from(&work_dir)) {
if cfg!(windows) { return Err(ReturnData {
PathBuf::from(format!("{}\\refractr", env::var("TEMP").unwrap())) msg: e,
} else { code: ExitCode::FilesystemError,
PathBuf::from("/tmp/refractr") });
} }
},
Some(path) => PathBuf::from(path),
});
let path_str = match work_dir {
Ok(p) => p,
Err(e) => {
return Err(ReturnData {
code: ExitCode::FilesystemError,
msg: e,
})
},
};
common::verbose( common::verbose(
self.verbose, self.verbose,
2, 2,
format!("Created working directory: {}", &path_str), format!("Created working directory: {}", work_dir),
); );
let repo_name = match &cfg.config.from.split("/").last() { let repo_name = match &cfg.config.from.split("/").last() {
Some(split) => split.to_string(), Some(split) => split.to_string(),
None => { None => {
@ -377,7 +378,7 @@ impl Refractr {
fo.remote_callbacks(cb); fo.remote_callbacks(cb);
() ()
}, },
Err(e) => common::error( Err(e) => common::error_quit(
format!("error setting up ssh: {}", e), format!("error setting up ssh: {}", e),
ExitCode::ConfigError, ExitCode::ConfigError,
), ),
@ -386,7 +387,7 @@ impl Refractr {
fo.download_tags(git2::AutotagOption::All); fo.download_tags(git2::AutotagOption::All);
builder.fetch_options(fo); builder.fetch_options(fo);
let repo_dir = format!("{}{}{}", &path_str, MAIN_SEPARATOR_STR, repo_name); let repo_dir = format!("{}{}{}", &work_dir, MAIN_SEPARATOR_STR, repo_name);
common::verbose( common::verbose(
self.verbose, self.verbose,
1, 1,
@ -396,7 +397,7 @@ impl Refractr {
Ok(repo) => repo, Ok(repo) => repo,
Err(e) => { Err(e) => {
if e.code() != ErrorCode::Exists { if e.code() != ErrorCode::Exists {
common::error( common::error_quit(
format!("failed to clone repo to {}: {}", repo_dir, e), format!("failed to clone repo to {}: {}", repo_dir, e),
ExitCode::FilesystemError, ExitCode::FilesystemError,
); );
@ -436,7 +437,7 @@ impl Refractr {
&cfg.config.git.ssh_identity_file, &cfg.config.git.ssh_identity_file,
self.strict.clone(), self.strict.clone(),
) { ) {
common::error( common::error_quit(
format!("failed to fetch repo {}: {}", cfg.config.from, e), format!("failed to fetch repo {}: {}", cfg.config.from, e),
ExitCode::FetchError, ExitCode::FetchError,
); );
@ -463,7 +464,7 @@ impl Refractr {
}; };
if let Err(e) = self.set_up_refs(&repo, &cfg.config.branches) { if let Err(e) = self.set_up_refs(&repo, &cfg.config.branches) {
common::error( common::error_quit(
format!("failed to set up refs: {}", e), format!("failed to set up refs: {}", e),
ExitCode::RepositoryError, ExitCode::RepositoryError,
); );
@ -478,8 +479,9 @@ impl Refractr {
}) })
}, },
}; };
if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) { if let Err(e) = self.push_remotes(&cfg.config, &repo, &remotes) {
common::error(e, ExitCode::PushError); common::error_quit(e, ExitCode::PushError);
} }
if cfg.config.schedule.enabled { if cfg.config.schedule.enabled {
@ -489,9 +491,18 @@ impl Refractr {
cfg: cfg.config, cfg: cfg.config,
ssh, ssh,
}); });
if !work_dirs.contains(&work_dir) {
work_dirs.push(work_dir);
}
} else {
if let Err(e) = self.clean(vec![work_dir]) {
return Err(e);
}
} }
} }
// end for
let mut result = Ok(());
if loop_repos.len() >= 1 { if loop_repos.len() >= 1 {
common::verbose( common::verbose(
self.verbose, self.verbose,
@ -501,7 +512,7 @@ impl Refractr {
loop_repos.len() loop_repos.len()
), ),
); );
return self.looper(loop_repos); result = self.looper(loop_repos);
} else { } else {
common::verbose( common::verbose(
self.verbose, self.verbose,
@ -510,6 +521,13 @@ impl Refractr {
); );
} }
Ok(()) if !self.persist {
match result {
Ok(()) => return self.clean(work_dirs),
Err(_) => result = self.clean(work_dirs),
}
}
result
} }
} }