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