use std::path::Path;
use std::process::Command;
use std::time::Duration;
use anyhow::{Context, bail};
use super::launch::PaneState;
use super::session_name::SessionNames;
use super::{Probe, Step};
pub(crate) const START_SCRIPT: &str = "scripts/start-fleet-poll.sh";
const SETTLE: Duration = Duration::from_secs(1);
pub(crate) fn step(dir: &Path, launch: bool, probe: Probe, names: &SessionNames) -> Step {
let poll = names.poll();
if !launch {
return Step::Skipped(format!(
"poller start (--no-launch); start it with `tm fleet init --dir {}`",
dir.display()
));
}
match (probe.pane)(poll) {
PaneState::Live(other) | PaneState::Dead(other) if other != dir => Step::Failed(format!(
"poller: tmux session {poll} already runs in {}, not in this Architect; \
stop it with `tmux kill-session -t ={poll}` and run `tm fleet init` \
again",
other.display()
)),
PaneState::Live(_) => Step::Unchanged(format!("poller session {poll} is running")),
PaneState::Dead(_) => Step::Failed(dead_pane(poll)),
PaneState::Unknown(err) => {
Step::Failed(format!("poller: cannot read tmux session {poll}: {err}"))
}
PaneState::Absent => match start(dir, probe, names) {
Ok(()) => Step::Changed(format!("started poller session {poll} ({START_SCRIPT})")),
Err(err) => Step::Failed(format!("poller start: {err:#}")),
},
}
}
fn dead_pane(poll: &str) -> String {
format!(
"poller: tmux session {poll} exists but its pane is dead (the poller exited); \
read its last output with `tmux capture-pane -p -t ={poll}:`, stop it with \
`tmux kill-session -t ={poll}` and run `tm fleet init` again"
)
}
pub(crate) fn start(dir: &Path, probe: Probe, names: &SessionNames) -> anyhow::Result<()> {
let poll = names.poll();
let script = dir.join(START_SCRIPT);
let out = start_command(dir, names)
.output()
.with_context(|| format!("cannot run {}", script.display()))?;
if !out.status.success() {
let text = format!(
"{}{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
bail!("{START_SCRIPT} failed ({}): {}", out.status, text.trim());
}
std::thread::sleep(SETTLE);
match (probe.pane)(poll) {
PaneState::Live(_) => Ok(()),
PaneState::Dead(_) => bail!("{START_SCRIPT} exited 0, but {}", dead_pane(poll)),
PaneState::Unknown(err) => {
bail!("{START_SCRIPT} exited 0, but tmux session {poll} cannot be read: {err}")
}
PaneState::Absent => bail!(
"{START_SCRIPT} exited 0 but tmux session {poll} is not running; \
check that `python3` runs `scripts/fleet-poll.py`"
),
}
}
pub(crate) fn start_command(dir: &Path, names: &SessionNames) -> Command {
let mut cmd = Command::new("bash");
cmd.arg(dir.join(START_SCRIPT))
.current_dir(dir)
.env("ARCHITECT_SESSION", names.architect())
.env("ARCHITECT_POLL_SESSION", names.poll())
.env("ARCHITECT_PROJECT_DIR", dir)
.env_remove("TMUX_SOCKET");
cmd
}