1use std::fs;
2use std::path::Path;
3use std::process::{Command, Output};
4
5use tempfile::TempDir;
6
7#[derive(Debug, Clone)]
8pub struct InitRepoOptions {
9 pub branch: Option<String>,
10 pub initial_commit: bool,
11 pub initial_commit_name: String,
12 pub initial_commit_contents: String,
13 pub initial_commit_message: String,
14}
15
16impl InitRepoOptions {
17 pub fn new() -> Self {
18 Self::default()
19 }
20
21 pub fn with_branch(mut self, branch: impl Into<String>) -> Self {
22 self.branch = Some(branch.into());
23 self
24 }
25
26 pub fn without_branch(mut self) -> Self {
27 self.branch = None;
28 self
29 }
30
31 pub fn with_initial_commit(mut self) -> Self {
32 self.initial_commit = true;
33 self
34 }
35}
36
37impl Default for InitRepoOptions {
38 fn default() -> Self {
39 Self {
40 branch: Some("main".to_string()),
41 initial_commit: false,
42 initial_commit_name: "README.md".to_string(),
43 initial_commit_contents: "init".to_string(),
44 initial_commit_message: "init".to_string(),
45 }
46 }
47}
48
49pub fn git_output(dir: &Path, args: &[&str]) -> Output {
50 Command::new("git")
51 .args(args)
52 .current_dir(dir)
53 .output()
54 .expect("git command failed to spawn")
55}
56
57pub fn git(dir: &Path, args: &[&str]) -> String {
58 let output = git_output(dir, args);
59 if !output.status.success() {
60 panic!(
61 "git {:?} failed: {}{}",
62 args,
63 String::from_utf8_lossy(&output.stderr),
64 String::from_utf8_lossy(&output.stdout)
65 );
66 }
67 String::from_utf8_lossy(&output.stdout).to_string()
68}
69
70pub fn git_with_env(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> String {
71 let output = git_output_with_env(dir, args, envs);
72 if !output.status.success() {
73 panic!(
74 "git {:?} failed: {}{}",
75 args,
76 String::from_utf8_lossy(&output.stderr),
77 String::from_utf8_lossy(&output.stdout)
78 );
79 }
80 String::from_utf8_lossy(&output.stdout).to_string()
81}
82
83fn git_output_with_env(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> Output {
84 let mut cmd = Command::new("git");
85 cmd.args(args).current_dir(dir);
86 for (key, value) in envs {
87 cmd.env(key, value);
88 }
89 cmd.output().expect("git command failed to spawn")
90}
91
92pub fn init_repo_with(options: InitRepoOptions) -> TempDir {
93 let dir = TempDir::new().expect("tempdir");
94 git(dir.path(), &["init", "-q"]);
95
96 if let Some(branch) = options.branch.as_deref() {
97 git(dir.path(), &["checkout", "-q", "-B", branch]);
99 }
100
101 git(dir.path(), &["config", "user.email", "test@example.com"]);
102 git(dir.path(), &["config", "user.name", "Test User"]);
103 git(dir.path(), &["config", "commit.gpgsign", "false"]);
104 git(dir.path(), &["config", "tag.gpgSign", "false"]);
105
106 if options.initial_commit {
107 let file_path = dir.path().join(&options.initial_commit_name);
108 fs::write(&file_path, &options.initial_commit_contents).expect("write initial commit");
109 git(dir.path(), &["add", &options.initial_commit_name]);
110 git(
111 dir.path(),
112 &["commit", "-m", &options.initial_commit_message],
113 );
114 }
115
116 dir
117}
118
119pub fn commit_file(dir: &Path, name: &str, contents: &str, message: &str) -> String {
120 let path = dir.join(name);
121 fs::write(&path, contents).expect("write file");
122 git(dir, &["add", name]);
123 git(dir, &["commit", "-m", message]);
124 git(dir, &["rev-parse", "HEAD"]).trim().to_string()
125}
126
127pub fn repo_id(dir: &Path) -> String {
128 dir.file_name()
129 .and_then(|value| value.to_str())
130 .unwrap_or("")
131 .to_string()
132}