Skip to content

Commit 268996b

Browse files
authored
Prevent lockfile creation when project folder is missing (#7927)
* Prevent lockfile creation when project folder is missing * CHANGELOG
1 parent 24b02f1 commit 268996b

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
#### :bug: Bug fix
2222

23+
- Prevent lockfile creation when project folder is missing. https://github.com/rescript-lang/rescript/pull/7927
24+
2325
#### :memo: Documentation
2426

2527
#### :nail_care: Polish

rewatch/src/lock.rs

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub enum Error {
1616
ParsingLockfile(std::num::ParseIntError),
1717
ReadingLockfile(std::io::Error),
1818
WritingLockfile(std::io::Error),
19+
ProjectFolderMissing(std::path::PathBuf),
1920
}
2021

2122
impl std::fmt::Display for Error {
@@ -31,6 +32,10 @@ impl std::fmt::Display for Error {
3132
format!("Could not read lockfile: \n {e} \n (try removing it and running the command again)")
3233
}
3334
Error::WritingLockfile(e) => format!("Could not write lockfile: \n {e}"),
35+
Error::ProjectFolderMissing(path) => format!(
36+
"Could not write lockfile because the specified project folder does not exist: {}",
37+
path.to_string_lossy()
38+
),
3439
};
3540
write!(f, "{msg}")
3641
}
@@ -41,35 +46,91 @@ pub enum Lock {
4146
Error(Error),
4247
}
4348

44-
fn exists(to_check_pid: u32) -> bool {
49+
fn pid_exists(to_check_pid: u32) -> bool {
4550
System::new_all()
4651
.processes()
4752
.iter()
4853
.any(|(pid, _process)| pid.as_u32() == to_check_pid)
4954
}
5055

51-
fn create(lockfile_location: &Path, pid: u32) -> Lock {
52-
// Create /lib if not exists
53-
if let Some(Err(e)) = lockfile_location.parent().map(fs::create_dir_all) {
54-
return Lock::Error(Error::WritingLockfile(e));
55-
};
56-
57-
File::create(lockfile_location)
58-
.and_then(|mut file| file.write(pid.to_string().as_bytes()).map(|_| Lock::Aquired(pid)))
59-
.unwrap_or_else(|e| Lock::Error(Error::WritingLockfile(e)))
60-
}
61-
6256
pub fn get(folder: &str) -> Lock {
63-
let location = Path::new(folder).join("lib").join(LOCKFILE);
57+
let project_folder = Path::new(folder);
58+
if !project_folder.exists() {
59+
return Lock::Error(Error::ProjectFolderMissing(project_folder.to_path_buf()));
60+
}
61+
62+
let lib_dir = project_folder.join("lib");
63+
let location = lib_dir.join(LOCKFILE);
6464
let pid = process::id();
6565

66+
// When a lockfile already exists we parse its PID: if the process is still alive we refuse to
67+
// proceed, otherwise we will overwrite the stale lock with our own PID.
6668
match fs::read_to_string(&location) {
67-
Err(e) if (e.kind() == std::io::ErrorKind::NotFound) => create(&location, pid),
68-
Err(e) => Lock::Error(Error::ReadingLockfile(e)),
69-
Ok(s) => match s.parse::<u32>() {
70-
Ok(parsed_pid) if !exists(parsed_pid) => create(&location, pid),
71-
Ok(parsed_pid) => Lock::Error(Error::Locked(parsed_pid)),
72-
Err(e) => Lock::Error(Error::ParsingLockfile(e)),
69+
Ok(contents) => match contents.parse::<u32>() {
70+
Ok(parsed_pid) if pid_exists(parsed_pid) => return Lock::Error(Error::Locked(parsed_pid)),
71+
Ok(_) => (),
72+
Err(e) => return Lock::Error(Error::ParsingLockfile(e)),
73+
},
74+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => (),
75+
Err(e) => return Lock::Error(Error::ReadingLockfile(e)),
76+
}
77+
78+
if let Err(e) = fs::create_dir_all(&lib_dir) {
79+
return Lock::Error(Error::WritingLockfile(e));
80+
}
81+
82+
// Rewrite the lockfile with our own PID.
83+
match File::create(&location) {
84+
Ok(mut file) => match file.write(pid.to_string().as_bytes()) {
85+
Ok(_) => Lock::Aquired(pid),
86+
Err(e) => Lock::Error(Error::WritingLockfile(e)),
7387
},
88+
Err(e) => Lock::Error(Error::WritingLockfile(e)),
89+
}
90+
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use super::*;
95+
use std::fs;
96+
use tempfile::TempDir;
97+
98+
#[test]
99+
fn returns_error_when_project_folder_missing() {
100+
let temp_dir = TempDir::new().expect("temp dir should be created");
101+
let missing_folder = temp_dir.path().join("missing_project");
102+
103+
match get(missing_folder.to_str().expect("path should be valid")) {
104+
Lock::Error(Error::ProjectFolderMissing(path)) => {
105+
assert_eq!(path, missing_folder);
106+
}
107+
_ => panic!("expected ProjectFolderMissing error"),
108+
}
109+
110+
assert!(
111+
!missing_folder.exists(),
112+
"missing project folder should not be created"
113+
);
114+
}
115+
116+
#[test]
117+
fn creates_lock_when_project_folder_exists() {
118+
let temp_dir = TempDir::new().expect("temp dir should be created");
119+
let project_folder = temp_dir.path().join("project");
120+
fs::create_dir(&project_folder).expect("project folder should be created");
121+
122+
match get(project_folder.to_str().expect("path should be valid")) {
123+
Lock::Aquired(_) => {}
124+
_ => panic!("expected lock to be acquired"),
125+
}
126+
127+
assert!(
128+
project_folder.join("lib").exists(),
129+
"lib directory should be created"
130+
);
131+
assert!(
132+
project_folder.join("lib").join(LOCKFILE).exists(),
133+
"lockfile should be created"
134+
);
74135
}
75136
}

0 commit comments

Comments
 (0)