fluidattacks_core_git/
ops.rs1use std::path::Path;
2
3use crate::cli;
4
5pub async fn reset_repo(repo_path: &Path) -> bool {
7 if !repo_path.join(".git").exists() {
8 tracing::error!(repo_path = %repo_path.display(), "not the root of a repository");
9 return false;
10 }
11 if let Err(error) = cli::reset_hard(repo_path).await {
12 tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to reset the repository");
13 return false;
14 }
15 let root = repo_path.to_path_buf();
16 match tokio::task::spawn_blocking(move || super::fs::remove_symlinks(&root)).await {
17 Ok(Ok(())) => true,
18 Ok(Err(error)) => {
19 tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to remove the symlinks");
20 false
21 }
22 Err(error) => {
23 tracing::error!(repo_path = %repo_path.display(), error = %error, "failed to remove the symlinks");
24 false
25 }
26 }
27}
28
29#[cfg(test)]
30mod tests {
31 use super::reset_repo;
32 use std::path::Path;
33
34 fn git(repo: &Path, args: &[&str]) {
35 let status = std::process::Command::new("git")
36 .arg("-C")
37 .arg(repo)
38 .args(args)
39 .status()
40 .unwrap();
41 assert!(status.success());
42 }
43
44 #[tokio::test]
45 async fn only_the_root_of_the_repository_gets_reset() {
46 let dir = tempfile::tempdir().unwrap();
47 std::env::set_var("GIT_CONFIG_GLOBAL", dir.path().join("gitconfig"));
48
49 let repo = dir.path().join("repo");
50 std::fs::create_dir_all(repo.join("sub/deep")).unwrap();
51 git(&repo, &["init", "-q"]);
52 std::fs::write(repo.join("f.txt"), b"committed").unwrap();
53 git(&repo, &["add", "-A"]);
54 git(
55 &repo,
56 &[
57 "-c",
58 "user.email=dev@fluidattacks.com",
59 "-c",
60 "user.name=dev",
61 "commit",
62 "-q",
63 "-m",
64 "files",
65 ],
66 );
67 std::fs::write(repo.join("f.txt"), b"dirty").unwrap();
68
69 let deep = repo.join("sub/deep");
70 assert!(!reset_repo(&deep).await);
71 assert_eq!(
72 std::fs::read_to_string(repo.join("f.txt")).unwrap(),
73 "dirty"
74 );
75
76 assert!(reset_repo(&repo).await);
77 assert_eq!(
78 std::fs::read_to_string(repo.join("f.txt")).unwrap(),
79 "committed"
80 );
81 }
82
83 #[tokio::test]
84 async fn the_reset_runs_no_fsmonitor_of_the_repository() {
85 let dir = tempfile::tempdir().unwrap();
86 let marker = dir.path().join("pwned");
87 let payload = dir.path().join("payload.sh");
88 std::fs::write(&payload, format!("#!/bin/sh\ntouch {}\n", marker.display())).unwrap();
89 std::fs::set_permissions(
90 &payload,
91 std::os::unix::fs::PermissionsExt::from_mode(0o755),
92 )
93 .unwrap();
94
95 let repo = dir.path().join("repo");
96 std::fs::create_dir_all(&repo).unwrap();
97 git(&repo, &["init", "-q"]);
98 std::fs::write(repo.join("f.txt"), b"committed").unwrap();
99 git(&repo, &["add", "-A"]);
100 git(
101 &repo,
102 &[
103 "-c",
104 "user.email=dev@fluidattacks.com",
105 "-c",
106 "user.name=dev",
107 "commit",
108 "-q",
109 "-m",
110 "files",
111 ],
112 );
113 git(
114 &repo,
115 &["config", "core.fsmonitor", &payload.to_string_lossy()],
116 );
117
118 assert!(reset_repo(&repo).await);
119 assert!(!marker.exists());
120 }
121}