1use std::{path::Path, process::ExitStatus, sync::mpsc::Sender};
2
3use crate::{exec, pkgx::Pkgx, Extension};
4use anyhow::Error;
5use fluentci_types::Output;
6
7#[derive(Default)]
8pub struct Git {}
9
10impl Git {
11 pub fn validate_url(&self, url: &str) -> Result<(), Error> {
12 if url.is_empty() {
13 return Err(Error::msg("URL is empty"));
14 }
15 if !regex::Regex::new(
16 r"^(?:https:\/\/([^\/]+)\/([^\/]+)\/([^\/]+)|git@([^:]+):([^\/]+)\/([^\/]+))$",
17 )
18 .unwrap()
19 .is_match(url)
20 {
21 return Err(Error::msg("Invalid URL"));
22 }
23 Ok(())
24 }
25}
26
27impl Extension for Git {
28 fn exec(
29 &mut self,
30 url: &str,
31 tx: Sender<String>,
32 out: Output,
33 last_cmd: bool,
34 work_dir: &str,
35 ) -> Result<ExitStatus, Error> {
36 self.setup()?;
37
38 if self.validate_url(url).is_err() {
39 return Err(Error::msg("Invalid URL"));
40 }
41
42 let repo = url.split('/').last().unwrap().replace(".git", "");
43 let git_dir = format!("{}/{}/.git", work_dir, repo);
44 if Path::new(&git_dir).exists() {
45 let cmd = format!("git pull {}", url);
46 let work_dir = format!("{}/{}", work_dir, repo);
47 return exec(&cmd, tx, out, last_cmd, &work_dir);
48 }
49
50 let cmd = format!("git clone {}", url);
51 exec(&cmd, tx, out, last_cmd, work_dir)
52 }
53
54 fn setup(&self) -> Result<(), Error> {
55 Pkgx::default().install(vec!["git"])?;
56 Ok(())
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn test_validate_url() {
66 let git = Git::default();
67 assert!(git.validate_url("https://github.com/tsirysndr/me").is_ok());
68 assert!(git.validate_url("git@github.com:tsirysndr/me").is_ok());
69 assert!(git.validate_url("github.com:tsirysndr/me").is_err());
70 }
71}