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 println!("ā
Cascade repository initialized successfully!");
42
43 if let Some(url) = &bitbucket_url {
44 println!("š Bitbucket Server URL: {url}");
45 }
46
47 println!("\nš Next steps:");
48 println!(" 1. Configure Bitbucket Server settings:");
49 if bitbucket_url.is_none() {
50 println!(" ca config set bitbucket.url https://your-bitbucket-server.com");
51 }
52 println!(" ca config set bitbucket.project YOUR_PROJECT_KEY");
53 println!(" ca config set bitbucket.repo your-repo-name");
54 println!(" ca config set bitbucket.token your-personal-access-token");
55 println!(" 2. Verify configuration:");
56 println!(" ca doctor");
57 println!(" 3. Create your first stack:");
58 println!(" ca create \"Add new feature\"");
59
60 Ok(())
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use git2::{Repository, Signature};
67 use tempfile::TempDir;
68
69 async fn create_test_git_repo() -> (TempDir, std::path::PathBuf) {
70 let temp_dir = TempDir::new().unwrap();
71 let repo_path = temp_dir.path().to_path_buf();
72
73 let repo = Repository::init(&repo_path).unwrap();
75
76 let signature = Signature::now("Test User", "test@example.com").unwrap();
78 let tree_id = {
79 let mut index = repo.index().unwrap();
80 index.write_tree().unwrap()
81 };
82 let tree = repo.find_tree(tree_id).unwrap();
83
84 repo.commit(
85 Some("HEAD"),
86 &signature,
87 &signature,
88 "Initial commit",
89 &tree,
90 &[],
91 )
92 .unwrap();
93
94 (temp_dir, repo_path)
95 }
96
97 #[tokio::test]
98 async fn test_init_in_git_repo() {
99 let (_temp_dir, repo_path) = create_test_git_repo().await;
100
101 assert!(is_git_repository(&repo_path));
104
105 crate::config::initialize_repo(
107 &repo_path,
108 Some("https://bitbucket.example.com".to_string()),
109 )
110 .unwrap();
111
112 assert!(is_repo_initialized(&repo_path));
114
115 println!("ā
Cascade initialization in Git repository tested successfully");
116 }
117
118 #[tokio::test]
119 async fn test_init_outside_git_repo() {
120 let temp_dir = TempDir::new().unwrap();
121 let non_git_path = temp_dir.path();
122
123 assert!(!is_git_repository(non_git_path));
125
126 let result = find_repository_root(non_git_path);
128 assert!(result.is_err());
129
130 println!("ā
Non-Git directory correctly detected - initialization would be rejected");
131 }
132
133 #[tokio::test]
134 async fn test_init_already_initialized() {
135 let (_temp_dir, repo_path) = create_test_git_repo().await;
136
137 crate::config::initialize_repo(&repo_path, None).unwrap();
139 assert!(is_repo_initialized(&repo_path));
140
141 assert!(is_repo_initialized(&repo_path));
144
145 let repo_root = crate::git::find_repository_root(&repo_path).unwrap();
148 assert!(is_repo_initialized(&repo_root));
149
150 println!("ā
Repository correctly detected as already initialized");
153 }
154}