1use git2::{Repository, RepositoryInitOptions};
6use std::path::{Path, PathBuf};
7
8use crate::git_config::DEFAULT_GIT_BRANCH;
9
10#[derive(thiserror::Error, Debug)]
11pub(crate) enum PrepareLocalRepoError {
12 #[error("libgit2 Error {0}")]
13 Libgit2(#[from] git2::Error),
14}
15
16#[derive(Clone, Debug)]
23pub struct LocalRepositoryBuilder {
24 pub base_dir: PathBuf,
26 pub head: String,
28 pub description: Option<String>,
30}
31
32impl LocalRepositoryBuilder {
34 pub fn new(base_dir: &Path) -> Self {
36 LocalRepositoryBuilder {
37 base_dir: base_dir.to_path_buf(),
38 head: DEFAULT_GIT_BRANCH.to_string(),
39 description: None,
40 }
41 }
42
43 pub fn git_directory_is_initialized(&self, dir: &Path) -> bool {
46 let test_paths = [
47 dir.join("git-repository/HEAD"),
48 dir.join("git-repository/config"),
49 dir.join("git-repository/objects"),
50 ];
51 test_paths
52 .iter()
53 .all(|file_path| std::fs::metadata(file_path.as_path()).is_ok())
54 }
55
56 pub fn head(mut self, head: Option<String>) -> Self {
58 self.head = if let Some(head) = head {
59 head
60 } else {
61 DEFAULT_GIT_BRANCH.to_string()
62 };
63 self
64 }
65
66 pub fn description(mut self, description: Option<String>) -> Self {
68 self.description = description;
69 self
70 }
71
72 #[tracing::instrument(name = "local::build", skip_all)]
77 pub fn build(self, repo_path: &str) -> Result<(), git2::Error> {
78 let dir = PathBuf::from(format!(
79 "{}/{}/git-repository",
80 self.base_dir.display(),
81 repo_path
82 ));
83 if !self.git_directory_is_initialized(&dir) {
84 tracing::debug!("Creating repository at {:?}...", &dir);
85
86 let mut opts = RepositoryInitOptions::new();
87 opts.bare(true);
88 opts.workdir_path(&dir);
89 opts.initial_head(&self.head);
90 if let Some(description) = self.description {
91 opts.description(&description);
92 }
93
94 let repo = Repository::init_opts(dir, &opts)?;
95 repo.config()?.set_bool("lorry.managed", true)?;
96 }
97 Ok(())
98 }
99}
100
101#[cfg(test)]
102mod test {
103
104 use tempfile::tempdir;
105
106 use crate::local::LocalRepositoryBuilder;
107
108 #[tokio::test]
109 async fn test_initialize_local_repository() {
110 let temp_dir = tempdir().unwrap();
111 let path = temp_dir.path().join("a/asdf/mirrors");
112 let repo_path = "foo/bar";
113 LocalRepositoryBuilder::new(&path).build(repo_path).unwrap();
114
115 assert!(path.join("foo/bar/git-repository/HEAD").exists());
116 assert!(path.join("foo/bar/git-repository/description").exists());
117 assert!(path.join("foo/bar/git-repository/refs/heads").exists())
118 }
119}