use std::path::{Path, PathBuf};
use trusty_mpm::client::DaemonClient;
use trusty_mpm::core::pr_cleanup::{
ClaimEnder, CleanupRegistry, CleanupRequest, RealGit, RealLanding,
};
use trusty_mpm::session_manager::worktree_safety::inspect_dirt;
use super::{EXIT_BLOCKED, EXIT_OK, RealGhRunner, repo_slug};
use crate::cli::PrCleanupArgs;
struct DaemonClaims {
client: DaemonClient,
}
#[async_trait::async_trait]
impl ClaimEnder for DaemonClaims {
async fn claims_on(&self, path: &Path) -> anyhow::Result<Vec<String>> {
let sessions = self.client.list_managed_sessions().await?;
Ok(sessions
.into_iter()
.filter(|s| {
s.workspace_path
.as_deref()
.is_some_and(|w| Path::new(w) == path)
})
.map(|s| s.id)
.collect())
}
async fn end_claim(&self, id: &str) -> anyhow::Result<()> {
let outcome = self
.client
.decommission_managed_session_record_only(id)
.await?;
anyhow::ensure!(
outcome.workspace_removed == Some(false),
"the daemon did not confirm it only tombstoned session {id}'s record (it reported \
workspace_removed = {:?}) — it may predate the record-only decommission; restart it \
before cleaning up",
outcome.workspace_removed
);
Ok(())
}
}
fn repo_root() -> anyhow::Result<PathBuf> {
let out = trusty_common::git::command()
.args(["rev-parse", "--show-toplevel"])
.output()?;
anyhow::ensure!(
out.status.success(),
"not inside a git checkout: {}",
String::from_utf8_lossy(&out.stderr).trim()
);
let path = String::from_utf8_lossy(&out.stdout).trim().to_string();
anyhow::ensure!(
!path.is_empty(),
"`git rev-parse --show-toplevel` named no directory"
);
Ok(PathBuf::from(path))
}
pub(crate) async fn run(
args: &PrCleanupArgs,
client: &reqwest::Client,
url: &str,
) -> anyhow::Result<i32> {
let gh = RealGhRunner::new()?;
let repo = repo_slug(&gh, args.repo.as_deref())?;
let claims = DaemonClaims {
client: DaemonClient::with_client(client.clone(), url.to_string()),
};
let req = CleanupRequest {
pr: args.pr,
repo: Some(repo.clone()),
repo_root: repo_root()?,
dry_run: args.dry_run,
};
let report = trusty_mpm::core::pr_cleanup::run(
&gh,
&RealGit,
&claims,
&RealLanding,
&inspect_dirt,
&req,
)
.await;
println!("{}", report.render());
if report.failed() {
return Ok(EXIT_BLOCKED);
}
if !req.dry_run {
if let Err(e) =
CleanupRegistry::production().mark_cleaned(&repo, req.pr, chrono::Utc::now())
{
eprintln!("tm pr cleanup: could not stamp the cleanup registry: {e:#}");
}
}
Ok(EXIT_OK)
}
pub(crate) async fn after_merge(
args: &crate::cli::PrMergeArgs,
client: &reqwest::Client,
url: &str,
) -> anyhow::Result<i32> {
let cleanup = PrCleanupArgs {
pr: args.pr,
repo: args.repo.clone(),
dry_run: false,
};
run(&cleanup, client, url).await
}