ethrex_sdk_contract_utils/
deps.rs1use std::{
2 path::Path,
3 process::{Command, ExitStatus},
4};
5
6use tracing::{info, trace};
7
8#[derive(Debug, thiserror::Error)]
9pub enum GitError {
10 #[error("Failed to clone: {0}")]
11 DependencyError(String),
12 #[error("Internal error: {0}")]
13 InternalError(String),
14 #[error("Failed to get string from path")]
15 FailedToGetStringFromPath,
16}
17
18pub fn git_clone(
19 repository_url: &str,
20 outdir: &str,
21 branch: Option<&str>,
22 submodules: bool,
23) -> Result<ExitStatus, GitError> {
24 info!(repository_url = %repository_url, outdir = %outdir, branch = ?branch, "Cloning or updating git repository");
25
26 if Path::new(outdir).join(".git").exists() {
27 info!(outdir = %outdir, "Found existing git repository, updating...");
28
29 let branch_name = if let Some(b) = branch {
30 b.to_string()
31 } else {
32 let output = Command::new("git")
34 .current_dir(outdir)
35 .arg("symbolic-ref")
36 .arg("refs/remotes/origin/HEAD")
37 .output()
38 .map_err(|e| {
39 GitError::DependencyError(format!(
40 "Failed to get default branch for {outdir}: {e}"
41 ))
42 })?;
43
44 if !output.status.success() {
45 let stderr = String::from_utf8_lossy(&output.stderr);
46 return Err(GitError::DependencyError(format!(
47 "Failed to get default branch for {outdir}: {stderr}"
48 )));
49 }
50
51 String::from_utf8(output.stdout)
52 .map_err(|_| GitError::InternalError("Failed to parse git output".to_string()))?
53 .trim()
54 .split('/')
55 .next_back()
56 .ok_or(GitError::InternalError(
57 "Failed to parse default branch".to_string(),
58 ))?
59 .to_string()
60 };
61
62 trace!(branch = %branch_name, "Updating to branch");
63
64 let fetch_status = Command::new("git")
66 .current_dir(outdir)
67 .args(["fetch", "origin"])
68 .spawn()
69 .map_err(|err| GitError::DependencyError(format!("Failed to spawn git fetch: {err}")))?
70 .wait()
71 .map_err(|err| {
72 GitError::DependencyError(format!("Failed to wait for git fetch: {err}"))
73 })?;
74 if !fetch_status.success() {
75 return Err(GitError::DependencyError(format!(
76 "git fetch failed for {outdir}"
77 )));
78 }
79
80 let checkout_status = Command::new("git")
82 .current_dir(outdir)
83 .arg("checkout")
84 .arg(&branch_name)
85 .spawn()
86 .map_err(|err| {
87 GitError::DependencyError(format!("Failed to spawn git checkout: {err}"))
88 })?
89 .wait()
90 .map_err(|err| {
91 GitError::DependencyError(format!("Failed to wait for git checkout: {err}"))
92 })?;
93 if !checkout_status.success() {
94 return Err(GitError::DependencyError(format!(
95 "git checkout of branch {branch_name} failed for {outdir}, try deleting the repo folder"
96 )));
97 }
98
99 let reset_status = Command::new("git")
101 .current_dir(outdir)
102 .arg("reset")
103 .arg("--hard")
104 .arg(format!("origin/{branch_name}"))
105 .spawn()
106 .map_err(|err| GitError::DependencyError(format!("Failed to spawn git reset: {err}")))?
107 .wait()
108 .map_err(|err| {
109 GitError::DependencyError(format!("Failed to wait for git reset: {err}"))
110 })?;
111
112 if !reset_status.success() {
113 return Err(GitError::DependencyError(format!(
114 "git reset failed for {outdir}"
115 )));
116 }
117
118 if submodules {
120 let submodule_status = Command::new("git")
121 .current_dir(outdir)
122 .arg("submodule")
123 .arg("update")
124 .arg("--init")
125 .arg("--recursive")
126 .spawn()
127 .map_err(|err| {
128 GitError::DependencyError(format!(
129 "Failed to spawn git submodule update: {err}"
130 ))
131 })?
132 .wait()
133 .map_err(|err| {
134 GitError::DependencyError(format!(
135 "Failed to wait for git submodule update: {err}"
136 ))
137 })?;
138 if !submodule_status.success() {
139 return Err(GitError::DependencyError(format!(
140 "git submodule update failed for {outdir}"
141 )));
142 }
143 }
144
145 Ok(reset_status)
146 } else {
147 trace!(repository_url = %repository_url, outdir = %outdir, branch = ?branch, "Cloning git repository");
148 let mut git_cmd = Command::new("git");
149
150 let git_clone_cmd = git_cmd.arg("clone").arg(repository_url);
151
152 if let Some(branch) = branch {
153 git_clone_cmd.arg("--branch").arg(branch);
154 }
155
156 if submodules {
157 git_clone_cmd.arg("--recurse-submodules");
158 }
159
160 git_clone_cmd
161 .arg(outdir)
162 .spawn()
163 .map_err(|err| GitError::DependencyError(format!("Failed to spawn git: {err}")))?
164 .wait()
165 .map_err(|err| GitError::DependencyError(format!("Failed to wait for git: {err}")))
166 }
167}