cascade_cli/cli/commands/
init.rs1use crate::cli::output::Output;
2use crate::config::{initialize_repo, is_repo_initialized};
3use crate::errors::{CascadeError, Result};
4use crate::git::{find_repository_root, is_git_repository};
5use std::env;
6
7pub async fn run(bitbucket_url: Option<String>, force: bool) -> Result<()> {
9 Output::info("Initializing Cascade repository...");
10
11 let current_dir = env::current_dir()
13 .map_err(|e| CascadeError::config(format!("Could not get current directory: {e}")))?;
14
15 if !is_git_repository(¤t_dir) {
17 return Err(CascadeError::not_initialized(
18 "Not in a Git repository. Please run this command from within a Git repository.",
19 ));
20 }
21
22 let repo_root = find_repository_root(¤t_dir)?;
24 tracing::debug!("Found Git repository at: {}", repo_root.display());
25
26 if is_repo_initialized(&repo_root) && !force {
28 return Err(CascadeError::invalid_operation(
29 "Repository is already initialized for Cascade. Use --force to reinitialize.",
30 ));
31 }
32
33 if force && is_repo_initialized(&repo_root) {
34 Output::warning("Force reinitializing repository...");
35 }
36
37 initialize_repo(&repo_root, bitbucket_url.clone())?;
39
40 Output::success("Cascade repository initialized successfully!");
42
43 if let Some(url) = &bitbucket_url {
44 Output::sub_item(format!("Bitbucket Server URL: {url}"));
45 }
46
47 println!();
48 Output::section("Next steps");
49 Output::bullet("Configure Bitbucket Server settings:");
50 if bitbucket_url.is_none() {
51 Output::command_example("ca config set bitbucket.url https://your-bitbucket-server.com");
52 }
53 Output::command_example("ca config set bitbucket.project YOUR_PROJECT_KEY");
54 Output::command_example("ca config set bitbucket.repo your-repo-name");
55 Output::command_example("ca config set bitbucket.token your-personal-access-token");
56 Output::bullet("Verify configuration:");
57 Output::command_example("ca doctor");
58 Output::bullet("Create your first stack:");
59 Output::command_example("ca create \"Add new feature\"");
60
61 Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use git2::{Repository, Signature};
68 use tempfile::TempDir;
69
70 async fn create_test_git_repo() -> (TempDir, std::path::PathBuf) {
71 let temp_dir = TempDir::new().unwrap();
72 let repo_path = temp_dir.path().to_path_buf();
73
74 let repo = Repository::init(&repo_path).unwrap();
76
77 let signature = Signature::now("Test User", "test@example.com").unwrap();
79 let tree_id = {
80 let mut index = repo.index().unwrap();
81 index.write_tree().unwrap()
82 };
83 let tree = repo.find_tree(tree_id).unwrap();
84
85 repo.commit(
86 Some("HEAD"),
87 &signature,
88 &signature,
89 "Initial commit",
90 &tree,
91 &[],
92 )
93 .unwrap();
94
95 (temp_dir, repo_path)
96 }
97
98 #[tokio::test]
99 async fn test_init_in_git_repo() {
100 let (_temp_dir, repo_path) = create_test_git_repo().await;
101
102 assert!(is_git_repository(&repo_path));
105
106 crate::config::initialize_repo(
108 &repo_path,
109 Some("https://bitbucket.example.com".to_string()),
110 )
111 .unwrap();
112
113 assert!(is_repo_initialized(&repo_path));
115
116 Output::success("Cascade initialization in Git repository tested successfully");
117 }
118
119 #[tokio::test]
120 async fn test_init_outside_git_repo() {
121 let temp_dir = TempDir::new().unwrap();
122 let non_git_path = temp_dir.path();
123
124 assert!(!is_git_repository(non_git_path));
126
127 let result = find_repository_root(non_git_path);
129 assert!(result.is_err());
130
131 Output::success("Non-Git directory correctly detected - initialization would be rejected");
132 }
133
134 #[tokio::test]
135 async fn test_init_already_initialized() {
136 let (_temp_dir, repo_path) = create_test_git_repo().await;
137
138 crate::config::initialize_repo(&repo_path, None).unwrap();
140 assert!(is_repo_initialized(&repo_path));
141
142 assert!(is_repo_initialized(&repo_path));
145
146 let repo_root = crate::git::find_repository_root(&repo_path).unwrap();
149 assert!(is_repo_initialized(&repo_root));
150
151 Output::success("Repository correctly detected as already initialized");
154 }
155}