From a21291db58648174f751de011b30c953cab26bc1 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 23 Oct 2023 16:41:23 +0200 Subject: [PATCH 1/8] :package: - Add sys package --- Cargo.lock | 31 +++++++++++++++++++++++++++++++ Cargo.toml | 1 + 2 files changed, 32 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 32bc0de..c7ba5c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + [[package]] name = "criterion" version = "0.4.0" @@ -798,6 +804,15 @@ dependencies = [ "notify", ] +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -987,6 +1002,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "sysinfo", ] [[package]] @@ -1086,6 +1102,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.29.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 0fbab38..db7a53a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ regex = "1.7.1" futures = "0.3.25" futures-timer = "3.0.2" clap = { version = "4.3.17", features = ["derive"] } +sysinfo = "0.29.10" [profile.release] From 0aa900275e691f996fda465c12cf20d3a0dafdc3 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 23 Oct 2023 16:41:59 +0200 Subject: [PATCH 2/8] :art: - Generate a lockfile / keep it in check --- src/lib.rs | 1 + src/lock.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 17 +++++++++++---- 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/lock.rs diff --git a/src/lib.rs b/src/lib.rs index fcfeaf8..e35ee28 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,5 +2,6 @@ pub mod bsconfig; pub mod build; pub mod cmd; pub mod helpers; +pub mod lock; pub mod queue; pub mod watcher; diff --git a/src/lock.rs b/src/lock.rs new file mode 100644 index 0000000..eaf672f --- /dev/null +++ b/src/lock.rs @@ -0,0 +1,62 @@ +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::process; +use sysinfo::{PidExt, System, SystemExt}; + +/* This locking mechanism is meant to never be deleted. Instead, it stores the PID of the process + * that's running, when trying to aquire a lock, it checks wether that process is still running. If + * not, it rewrites the lockfile to have its own PID instead. */ + +pub static LOCKFILE: &str = "rewatch.lock"; + +pub enum Error { + Locked(u32), + ParsingLockfile(std::num::ParseIntError), + ReadingLockfile(std::io::Error), + WritingLockfile(std::io::Error), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let msg = match self { + Error::Locked(pid) => format!("Rewatch is already running with PID {}", pid), + Error::ParsingLockfile(e) => format!("Could not parse lockfile: \n {}", e.to_string()), + Error::ReadingLockfile(e) => format!("Could not read lockfile: \n {}", e.to_string()), + Error::WritingLockfile(e) => format!("Could not write lockfile: \n {}", e.to_string()), + }; + write!(f, "{}", msg) + } +} + +pub enum Lock { + Locked(u32), + Error(Error), +} + +fn exists(to_check_pid: u32) -> bool { + System::new_all() + .processes() + .into_iter() + .any(|(pid, _process)| pid.as_u32() == to_check_pid) +} + +fn create(pid: u32) -> Lock { + File::create(LOCKFILE) + .and_then(|mut file| file.write(pid.to_string().as_bytes()).map(|_| Lock::Locked(pid))) + .unwrap_or_else(|e| Lock::Error(Error::WritingLockfile(e))) +} + +pub fn get() -> Lock { + let pid = process::id(); + + match fs::read_to_string(LOCKFILE) { + Err(e) if (e.kind() == std::io::ErrorKind::NotFound) => create(pid), + Err(e) => Lock::Error(Error::ReadingLockfile(e)), + Ok(s) => match s.parse::() { + Ok(parsed_pid) if !exists(parsed_pid) => create(pid), + Ok(parsed_pid) => Lock::Error(Error::Locked(parsed_pid)), + Err(e) => Lock::Error(Error::ParsingLockfile(e)), + }, + } +} diff --git a/src/main.rs b/src/main.rs index 26b7c2c..1f5c9a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ pub mod bsconfig; pub mod build; pub mod cmd; pub mod helpers; +pub mod lock; pub mod queue; pub mod watcher; @@ -56,9 +57,17 @@ fn main() { .filter .map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex")); - match command { - Command::Clean => build::clean::clean(&folder), - Command::Build => { + let lock = lock::get(); + + match (lock, command) { + (lock::Lock::Error(ref e), _) => { + eprintln!("Error while trying to get lock: {}", e.to_string()); + std::process::exit(1) + } + (lock::Lock::Locked(_), Command::Clean) => { + build::clean::clean(&folder); + } + (lock::Lock::Locked(_), Command::Build) => { match build::build(&filter, &folder, args.no_timing.unwrap_or(false)) { Err(()) => std::process::exit(1), Ok(_) => { @@ -67,7 +76,7 @@ fn main() { } }; } - Command::Watch => { + (lock::Lock::Locked(_), Command::Watch) => { let _initial_build = build::build(&filter, &folder, false); args.after_build.clone().map(|command| cmd::run(command)); watcher::start(&filter, &folder, args.after_build); From 7d9900f639168043da0bb4054dfd36e2d55a2644 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 23 Oct 2023 16:42:14 +0200 Subject: [PATCH 3/8] :rotating_light: - Cargo FMT - extranous changes --- src/build/packages.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/build/packages.rs b/src/build/packages.rs index 9a89d22..eaa3519 100644 --- a/src/build/packages.rs +++ b/src/build/packages.rs @@ -746,23 +746,23 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b ] .iter() .for_each(|(dependency_type, dependencies)| { - if let Some(unallowed_dependency_name) = get_unallowed_dependents(packages, package_name, dependencies) { - let empty_unallowed_deps = UnallowedDependency{ - bs_deps: vec![], - pinned_deps: vec![], - bs_dev_deps: vec![], + if let Some(unallowed_dependency_name) = + get_unallowed_dependents(packages, package_name, dependencies) + { + let empty_unallowed_deps = UnallowedDependency { + bs_deps: vec![], + pinned_deps: vec![], + bs_dev_deps: vec![], }; - + let unallowed_dependency = detected_unallowed_dependencies.entry(String::from(package_name)); - let value = unallowed_dependency - .or_insert_with(||empty_unallowed_deps); + let value = unallowed_dependency.or_insert_with(|| empty_unallowed_deps); match dependency_type { &"bs-dependencies" => value.bs_deps.push(String::from(unallowed_dependency_name)), &"pinned-dependencies" => value.pinned_deps.push(String::from(unallowed_dependency_name)), &"bs-dev-dependencies" => value.bs_dev_deps.push(String::from(unallowed_dependency_name)), _ => (), } - } }); } @@ -772,7 +772,7 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b console::style("Error").red(), console::style(package_name).bold() ); - + vec![ ("bs-dependencies", unallowed_deps.bs_deps.to_owned()), ("pinned-dependencies", unallowed_deps.pinned_deps.to_owned()), @@ -792,9 +792,10 @@ pub fn validate_packages_dependencies(packages: &AHashMap) -> b let has_any_unallowed_dependent = detected_unallowed_dependencies.len() > 0; if has_any_unallowed_dependent { - println!("\nUpdate the {} value in the {} of the unallowed dependencies to solve the issue!", - console::style("unallowed_dependents").bold().dim(), - console::style("bsconfig.json").bold().dim() + println!( + "\nUpdate the {} value in the {} of the unallowed dependencies to solve the issue!", + console::style("unallowed_dependents").bold().dim(), + console::style("bsconfig.json").bold().dim() ) } return !has_any_unallowed_dependent; @@ -805,7 +806,7 @@ mod test { use crate::bsconfig::Source; use ahash::{AHashMap, AHashSet}; - use super::{Package, Namespace}; + use super::{Namespace, Package}; fn create_package( name: String, From f76b592bc74237de1632beabb2004886ae253bcb Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 23 Oct 2023 16:56:17 +0200 Subject: [PATCH 4/8] :white_check_mark: - Test Lock is set correctly --- tests/lock.sh | 50 +++++++++++++++++++++++++++++++++++++++++++++++ tests/suite-ci.sh | 3 +-- 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100755 tests/lock.sh diff --git a/tests/lock.sh b/tests/lock.sh new file mode 100755 index 0000000..2068d20 --- /dev/null +++ b/tests/lock.sh @@ -0,0 +1,50 @@ +source "./utils.sh" +cd ../testrepo + +bold "Test: It should lock - when watching" + +if rewatch clean &> /dev/null; +then + success "Repo Cleaned" +else + error "Error Cleaning Repo" + exit 1 +fi + +exit_watcher() { + # we need to kill the parent process (rewatch) + kill $(pgrep -P $!); +} + +rewatch watch &>/dev/null & +success "Watcher Started" + +sleep 1 + +if rewatch watch 2>&1 | grep 'Error while trying to get lock:' &> /dev/null; +then + success "Lock is correctly set" + exit_watcher +else + error "Not setting lock correctly" + exit_watcher + exit 1 +fi + +touch tmp.txt +rewatch watch &> tmp.txt & +success "Watcher Started" + +sleep 1 + +if cat tmp.txt | grep 'Error while trying to get lock:' &> /dev/null; +then + error "Lock not removed correctly" + exit_watcher + exit 1 +else + success "Lock removed correctly" + exit_watcher +fi + +rm tmp.txt diff --git a/tests/suite-ci.sh b/tests/suite-ci.sh index 3b23532..d0f883f 100755 --- a/tests/suite-ci.sh +++ b/tests/suite-ci.sh @@ -22,5 +22,4 @@ else exit 1 fi -./compile.sh && ./watch.sh - +./compile.sh && ./watch.sh && ./lock.sh From b5e9713ffb5cc63947078d6394b15b5c9d337e59 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Mon, 23 Oct 2023 17:15:06 +0200 Subject: [PATCH 5/8] :recycle: - Lock -> Aquired in positive case --- src/lock.rs | 4 ++-- src/main.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lock.rs b/src/lock.rs index eaf672f..22ef96d 100644 --- a/src/lock.rs +++ b/src/lock.rs @@ -30,7 +30,7 @@ impl std::fmt::Display for Error { } pub enum Lock { - Locked(u32), + Aquired(u32), Error(Error), } @@ -43,7 +43,7 @@ fn exists(to_check_pid: u32) -> bool { fn create(pid: u32) -> Lock { File::create(LOCKFILE) - .and_then(|mut file| file.write(pid.to_string().as_bytes()).map(|_| Lock::Locked(pid))) + .and_then(|mut file| file.write(pid.to_string().as_bytes()).map(|_| Lock::Aquired(pid))) .unwrap_or_else(|e| Lock::Error(Error::WritingLockfile(e))) } diff --git a/src/main.rs b/src/main.rs index 1f5c9a6..db27905 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,10 +64,10 @@ fn main() { eprintln!("Error while trying to get lock: {}", e.to_string()); std::process::exit(1) } - (lock::Lock::Locked(_), Command::Clean) => { + (lock::Lock::Aquired(_), Command::Clean) => { build::clean::clean(&folder); } - (lock::Lock::Locked(_), Command::Build) => { + (lock::Lock::Aquired(_), Command::Build) => { match build::build(&filter, &folder, args.no_timing.unwrap_or(false)) { Err(()) => std::process::exit(1), Ok(_) => { @@ -76,7 +76,7 @@ fn main() { } }; } - (lock::Lock::Locked(_), Command::Watch) => { + (lock::Lock::Aquired(_), Command::Watch) => { let _initial_build = build::build(&filter, &folder, false); args.after_build.clone().map(|command| cmd::run(command)); watcher::start(&filter, &folder, args.after_build); From 976bb2a4b0e8f80f772c2439ff896d806fbd2e30 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Tue, 2 Jan 2024 13:37:14 +0100 Subject: [PATCH 6/8] :art: - Move lockfile into /lib --- src/build.rs | 2 +- src/lock.rs | 24 ++++++++++++++++++------ src/main.rs | 4 +--- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/build.rs b/src/build.rs index 1b0a1ce..b26ec0a 100644 --- a/src/build.rs +++ b/src/build.rs @@ -78,7 +78,7 @@ pub fn build(filter: &Option, path: &str, no_timing: bool) -> Resu if !packages::validate_packages_dependencies(&packages) { return Err(()); } - + let timing_source_files = Instant::now(); print!( diff --git a/src/lock.rs b/src/lock.rs index 22ef96d..f9b00b8 100644 --- a/src/lock.rs +++ b/src/lock.rs @@ -1,6 +1,7 @@ use std::fs; use std::fs::File; use std::io::prelude::*; +use std::path::Path; use std::process; use sysinfo::{PidExt, System, SystemExt}; @@ -41,20 +42,31 @@ fn exists(to_check_pid: u32) -> bool { .any(|(pid, _process)| pid.as_u32() == to_check_pid) } -fn create(pid: u32) -> Lock { - File::create(LOCKFILE) +fn create(lockfile_location: &Path, pid: u32) -> Lock { + // Create /lib if not exists + match lockfile_location + .parent() + .map(|folder| fs::create_dir_all(folder)) + { + Some(Err(e)) => return Lock::Error(Error::WritingLockfile(e)), + _ => (), + }; + + File::create(lockfile_location) .and_then(|mut file| file.write(pid.to_string().as_bytes()).map(|_| Lock::Aquired(pid))) .unwrap_or_else(|e| Lock::Error(Error::WritingLockfile(e))) } -pub fn get() -> Lock { +pub fn get(folder: &str) -> Lock { + let location = format!("{}/lib/{}", folder, LOCKFILE); + let path = Path::new(&location); let pid = process::id(); - match fs::read_to_string(LOCKFILE) { - Err(e) if (e.kind() == std::io::ErrorKind::NotFound) => create(pid), + match fs::read_to_string(&location) { + Err(e) if (e.kind() == std::io::ErrorKind::NotFound) => create(&path, pid), Err(e) => Lock::Error(Error::ReadingLockfile(e)), Ok(s) => match s.parse::() { - Ok(parsed_pid) if !exists(parsed_pid) => create(pid), + Ok(parsed_pid) if !exists(parsed_pid) => create(&path, pid), Ok(parsed_pid) => Lock::Error(Error::Locked(parsed_pid)), Err(e) => Lock::Error(Error::ParsingLockfile(e)), }, diff --git a/src/main.rs b/src/main.rs index db27905..6648165 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,9 +57,7 @@ fn main() { .filter .map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex")); - let lock = lock::get(); - - match (lock, command) { + match (lock::get(&folder), command) { (lock::Lock::Error(ref e), _) => { eprintln!("Error while trying to get lock: {}", e.to_string()); std::process::exit(1) From 2d6dd0aae15dfa3636f9bccdec35ddd8ce27e966 Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Tue, 2 Jan 2024 13:52:05 +0100 Subject: [PATCH 7/8] :recycle: - Nest match instead of repeating --- src/main.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6648165..980c1b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,27 +57,27 @@ fn main() { .filter .map(|filter| Regex::new(filter.as_ref()).expect("Could not parse regex")); - match (lock::get(&folder), command) { - (lock::Lock::Error(ref e), _) => { + match lock::get(&folder) { + lock::Lock::Error(ref e) => { eprintln!("Error while trying to get lock: {}", e.to_string()); std::process::exit(1) } - (lock::Lock::Aquired(_), Command::Clean) => { - build::clean::clean(&folder); - } - (lock::Lock::Aquired(_), Command::Build) => { - match build::build(&filter, &folder, args.no_timing.unwrap_or(false)) { - Err(()) => std::process::exit(1), - Ok(_) => { - args.after_build.map(|command| cmd::run(command)); - std::process::exit(0) - } - }; - } - (lock::Lock::Aquired(_), Command::Watch) => { - let _initial_build = build::build(&filter, &folder, false); - args.after_build.clone().map(|command| cmd::run(command)); - watcher::start(&filter, &folder, args.after_build); - } + lock::Lock::Aquired(_) => match command { + Command::Clean => build::clean::clean(&folder), + Command::Build => { + match build::build(&filter, &folder, args.no_timing.unwrap_or(false)) { + Err(()) => std::process::exit(1), + Ok(_) => { + args.after_build.map(|command| cmd::run(command)); + std::process::exit(0) + } + }; + } + Command::Watch => { + let _initial_build = build::build(&filter, &folder, false); + args.after_build.clone().map(|command| cmd::run(command)); + watcher::start(&filter, &folder, args.after_build); + } + }, } } From 53d2a9bdd756f4bdf041e6069dac093efe78495a Mon Sep 17 00:00:00 2001 From: Roland Peelen Date: Tue, 2 Jan 2024 14:08:48 +0100 Subject: [PATCH 8/8] :white_check_mark: - Test --- tests/lock.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/lock.sh b/tests/lock.sh index 2068d20..65d48cb 100755 --- a/tests/lock.sh +++ b/tests/lock.sh @@ -3,6 +3,8 @@ cd ../testrepo bold "Test: It should lock - when watching" +sleep 1 + if rewatch clean &> /dev/null; then success "Repo Cleaned" @@ -31,6 +33,8 @@ else exit 1 fi +sleep 1 + touch tmp.txt rewatch watch &> tmp.txt & success "Watcher Started"