cuddle_please_misc/
git_client.rs

1use std::path::{Path, PathBuf};
2
3#[derive(Clone, Debug, PartialEq)]
4pub enum VcsClient {
5    Noop {},
6    Git {
7        source: PathBuf,
8        username: String,
9        email: String,
10    },
11}
12
13impl VcsClient {
14    pub fn new_noop() -> VcsClient {
15        Self::Noop {}
16    }
17
18    pub fn new_git(
19        path: &Path,
20        git_username: Option<impl Into<String>>,
21        git_email: Option<impl Into<String>>,
22    ) -> anyhow::Result<VcsClient> {
23        if !path.to_path_buf().join(".git").exists() {
24            anyhow::bail!("git directory not found in: {}", path.display().to_string())
25        }
26
27        Ok(Self::Git {
28            source: path.to_path_buf(),
29            username: git_username
30                .map(|u| u.into())
31                .unwrap_or("cuddle-please".to_string()),
32            email: git_email
33                .map(|e| e.into())
34                .unwrap_or("bot@cuddle.sh".to_string()),
35        })
36    }
37
38    pub fn checkout_branch(&self) -> anyhow::Result<()> {
39        match self {
40            VcsClient::Noop {} => {}
41            VcsClient::Git { .. } => {
42                if let Err(_e) = self.exec_git(&["branch", "-D", "cuddle-please/release"]) {
43                    tracing::debug!("failed to cleaned up local branch for force-push, this may be because it didn't exist before running this command");
44                }
45                self.exec_git(&["checkout", "-b", "cuddle-please/release"])?;
46            }
47        }
48
49        Ok(())
50    }
51
52    fn exec_git(&self, args: &[&str]) -> anyhow::Result<()> {
53        match self {
54            VcsClient::Noop {} => {}
55            VcsClient::Git {
56                source,
57                username,
58                email,
59            } => {
60                let checkout_branch = std::process::Command::new("git")
61                    .current_dir(source.as_path())
62                    .args(&[
63                        "-c",
64                        &format!("user.name={}", username),
65                        "-c",
66                        &format!("user.email={}", email),
67                    ])
68                    .args(args)
69                    .output()?;
70
71                let stdout = std::str::from_utf8(&checkout_branch.stdout)?;
72                let stderr = std::str::from_utf8(&checkout_branch.stderr)?;
73                tracing::debug!(stdout = stdout, stderr = stderr, "git {}", args.join(" "));
74            }
75        }
76
77        Ok(())
78    }
79
80    pub fn commit_and_push(&self, version: impl Into<String>, dry_run: bool) -> anyhow::Result<()> {
81        match self {
82            VcsClient::Noop {} => {}
83            VcsClient::Git { .. } => {
84                self.exec_git(&["add", "."])?;
85                self.exec_git(&[
86                    "commit",
87                    "-m",
88                    &format!("chore(release): {}", version.into()),
89                ])?;
90
91                tracing::trace!("git push -u -f origin cuddle-please/release");
92                if !dry_run {
93                    self.exec_git(&["push", "-u", "-f", "origin", "cuddle-please/release"])?;
94                }
95            }
96        }
97
98        Ok(())
99    }
100}