huskies: merge 784
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
//! Async worktree removal operations.
|
||||
use crate::config::ProjectConfig;
|
||||
use std::path::Path;
|
||||
|
||||
use super::create::run_teardown_commands;
|
||||
use super::git::{branch_name, detect_base_branch, remove_worktree_sync};
|
||||
use super::{WorktreeInfo, worktree_path};
|
||||
|
||||
/// Remove a git worktree and its branch.
|
||||
pub async fn remove_worktree(
|
||||
project_root: &Path,
|
||||
info: &WorktreeInfo,
|
||||
config: &ProjectConfig,
|
||||
) -> Result<(), String> {
|
||||
run_teardown_commands(&info.path, config).await?;
|
||||
|
||||
let root = project_root.to_path_buf();
|
||||
let wt_path = info.path.clone();
|
||||
let branch = info.branch.clone();
|
||||
|
||||
tokio::task::spawn_blocking(move || remove_worktree_sync(&root, &wt_path, &branch))
|
||||
.await
|
||||
.map_err(|e| format!("spawn_blocking: {e}"))?
|
||||
}
|
||||
|
||||
/// Remove a git worktree by story ID, deriving the path and branch deterministically.
|
||||
pub async fn remove_worktree_by_story_id(
|
||||
project_root: &Path,
|
||||
story_id: &str,
|
||||
config: &ProjectConfig,
|
||||
) -> Result<(), String> {
|
||||
let path = worktree_path(project_root, story_id);
|
||||
if !path.exists() {
|
||||
return Err(format!("Worktree not found for story: {story_id}"));
|
||||
}
|
||||
let branch = branch_name(story_id);
|
||||
let base_branch = config
|
||||
.base_branch
|
||||
.clone()
|
||||
.unwrap_or_else(|| detect_base_branch(project_root));
|
||||
let info = WorktreeInfo {
|
||||
path,
|
||||
branch,
|
||||
base_branch,
|
||||
};
|
||||
remove_worktree(project_root, &info, config).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::WatcherConfig;
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
use tempfile::TempDir;
|
||||
|
||||
fn init_git_repo(dir: &std::path::Path) {
|
||||
Command::new("git")
|
||||
.args(["init"])
|
||||
.current_dir(dir)
|
||||
.output()
|
||||
.expect("git init");
|
||||
Command::new("git")
|
||||
.args(["commit", "--allow-empty", "-m", "init"])
|
||||
.current_dir(dir)
|
||||
.output()
|
||||
.expect("git commit");
|
||||
}
|
||||
|
||||
fn empty_config() -> ProjectConfig {
|
||||
ProjectConfig {
|
||||
component: vec![],
|
||||
agent: vec![],
|
||||
watcher: WatcherConfig::default(),
|
||||
default_qa: "server".to_string(),
|
||||
default_coder_model: None,
|
||||
max_coders: None,
|
||||
max_retries: 2,
|
||||
base_branch: None,
|
||||
rate_limit_notifications: true,
|
||||
web_ui_status_consumer: true,
|
||||
matrix_status_consumer: true,
|
||||
slack_status_consumer: true,
|
||||
discord_status_consumer: true,
|
||||
whatsapp_status_consumer: true,
|
||||
timezone: None,
|
||||
rendezvous: None,
|
||||
trusted_keys: Vec::new(),
|
||||
crdt_require_token: false,
|
||||
crdt_tokens: Vec::new(),
|
||||
max_mesh_peers: 3,
|
||||
gateway_url: None,
|
||||
gateway_project: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_by_story_id_returns_err_when_not_found() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
|
||||
let result =
|
||||
remove_worktree_by_story_id(tmp.path(), "99_nonexistent", &empty_config()).await;
|
||||
assert!(result.is_err());
|
||||
assert!(
|
||||
result
|
||||
.unwrap_err()
|
||||
.contains("Worktree not found for story: 99_nonexistent")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_by_story_id_removes_existing_worktree() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
super::super::create::create_worktree(
|
||||
&project_root,
|
||||
"88_remove_by_id",
|
||||
&empty_config(),
|
||||
3001,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let result =
|
||||
remove_worktree_by_story_id(&project_root, "88_remove_by_id", &empty_config()).await;
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Expected removal to succeed: {:?}",
|
||||
result.err()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn remove_worktree_async_removes_directory() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let project_root = tmp.path().join("my-project");
|
||||
fs::create_dir_all(&project_root).unwrap();
|
||||
init_git_repo(&project_root);
|
||||
|
||||
let info = super::super::create::create_worktree(
|
||||
&project_root,
|
||||
"77_remove_async",
|
||||
&empty_config(),
|
||||
3001,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let path = info.path.clone();
|
||||
assert!(path.exists());
|
||||
remove_worktree(&project_root, &info, &empty_config())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!path.exists());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user