rolling_deployer/
git_client.rs1use tracing::info;
2
3pub struct GitClient;
4
5impl GitClient {
6 pub async fn clone_repository_to_versioned_path(
7 &self,
8 repo_url: &str,
9 tag: &str,
10 base_path: &str,
11 ) -> Result<String, Box<dyn std::error::Error>> {
12 let versioned_path = format!("{}/traefik-config-{}", base_path, tag);
13 let symlink_path = format!("{}/current", base_path);
14
15 info!(
16 "Cloning repository {} at tag {} to {}",
17 repo_url, tag, versioned_path
18 );
19
20 if let Some(parent) = std::path::Path::new(&versioned_path).parent() {
22 std::fs::create_dir_all(parent)?;
23 }
24
25 if !std::path::Path::new(&versioned_path).exists() {
27 let output = std::process::Command::new("git")
29 .args(&[
30 "clone",
31 "--depth",
32 "1",
33 "--branch",
34 tag,
35 repo_url,
36 &versioned_path,
37 ])
38 .output()?;
39
40 if !output.status.success() {
41 return Err(format!(
42 "Git clone failed: {}",
43 String::from_utf8_lossy(&output.stderr)
44 )
45 .into());
46 }
47
48 info!(
49 "Successfully cloned {} at tag {} to {}",
50 repo_url, tag, versioned_path
51 );
52 } else {
53 info!("Using existing config at {}", versioned_path);
54 }
55
56 let symlink_path_obj = std::path::Path::new(&symlink_path);
58 if symlink_path_obj.exists() || symlink_path_obj.is_symlink() {
59 std::fs::remove_file(&symlink_path)?;
60 }
61 #[cfg(unix)]
62 std::os::unix::fs::symlink(&versioned_path, &symlink_path)?;
63 #[cfg(windows)]
64 std::os::windows::fs::symlink_dir(&versioned_path, &symlink_path)?;
65
66 Ok(symlink_path)
67 }
68
69 pub async fn fetch_latest(&self, repo_dir: &str) -> Result<(), Box<dyn std::error::Error>> {
70 info!("Fetching latest changes in {}", repo_dir);
71
72 let output = std::process::Command::new("git")
73 .args(&["fetch", "--all"])
74 .current_dir(repo_dir)
75 .output()?;
76
77 if !output.status.success() {
78 return Err(format!(
79 "Git fetch failed: {}",
80 String::from_utf8_lossy(&output.stderr)
81 )
82 .into());
83 }
84
85 Ok(())
86 }
87
88 pub async fn checkout_tag(
89 &self,
90 repo_dir: &str,
91 tag: &str,
92 ) -> Result<(), Box<dyn std::error::Error>> {
93 info!("Checking out tag {} in {}", tag, repo_dir);
94
95 let output = std::process::Command::new("git")
96 .args(&["checkout", tag])
97 .current_dir(repo_dir)
98 .output()?;
99
100 if !output.status.success() {
101 return Err(format!(
102 "Git checkout failed: {}",
103 String::from_utf8_lossy(&output.stderr)
104 )
105 .into());
106 }
107
108 info!("Successfully checked out tag {}", tag);
109 Ok(())
110 }
111}