use crate::Result;
use crate::daemon_id::DaemonId;
use crate::ipc::client::IpcClient;
use crate::pitchfork_toml::PitchforkToml;
use miette::ensure;
use std::sync::Arc;
#[derive(Debug, clap::Args)]
#[clap(
visible_alias = "kill",
verbatim_doc_comment,
long_about = "\
Sends a stop signal to a daemon
Uses a graceful shutdown strategy:
1. Send SIGTERM and wait up to ~3 seconds for the process to exit (fast 10ms checks initially, then 50ms)
2. If still running, send SIGKILL to force termination
Most processes will exit immediately after the first SIGTERM. The escalation
ensures stubborn processes are eventually terminated while giving well-behaved
processes time to clean up resources.
When using --all, daemons are stopped in reverse dependency order:
dependents are stopped before the daemons they depend on.
Examples:
pitchfork stop api Stop a single daemon
pitchfork stop api worker Stop multiple daemons
pitchfork stop --all Stop all running daemons in dependency order
pitchfork kill api Same as 'stop' (alias)"
)]
pub struct Stop {
id: Vec<String>,
#[clap(long, short)]
all: bool,
}
impl Stop {
pub async fn run(&self) -> Result<()> {
ensure!(
!self.all || self.id.is_empty(),
"--all and daemon IDs cannot be used together"
);
ensure!(
self.all || !self.id.is_empty(),
"At least one daemon ID must be provided (or use --all)"
);
let ipc = Arc::new(IpcClient::connect(false).await?);
let ids: Vec<DaemonId> = if self.all {
ipc.get_running_daemons().await?
} else {
PitchforkToml::resolve_ids(&self.id)?
};
let result = ipc.stop_daemons(&ids).await?;
if result.any_failed {
std::process::exit(1);
}
Ok(())
}
}