Skip to main content

ethrex_sdk_contract_utils/
deps.rs

1use 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            // Look for default branch name (could be main, master or other)
33            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        // Fetch
65        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        // Checkout to branch
81        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        // Reset branch to origin
100        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        // Update submodules
119        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}