use eyre::{Result, bail};
use std::path::Path;
#[cfg(any(windows, test))]
use crate::system::scheduled_tasks::ServiceLaunch;
#[cfg(windows)]
pub(crate) fn run(name: &str, launch: &Path, digest: &str) -> Result<i32> {
let launch = read_launch(name, launch, digest)?;
use std::os::windows::process::CommandExt;
let mut cmd = std::process::Command::new(&launch.program);
if !launch.args.is_empty() {
cmd.raw_arg(&launch.args);
}
cmd.envs(&launch.environment);
let (mut child, job) = crate::windows_job::spawn(&mut cmd, 0)?;
let code = match child.wait() {
Ok(status) => status.code().unwrap_or(1),
Err(err) => {
job.kill();
return Err(err.into());
}
};
drop(job);
std::process::exit(code)
}
#[cfg(not(windows))]
pub(crate) fn run(_name: &str, _launch: &Path, _digest: &str) -> Result<i32> {
bail!("user services run through mise only on windows")
}
#[cfg(any(windows, test))]
fn read_launch(name: &str, path: &Path, digest: &str) -> Result<ServiceLaunch> {
let Ok(stored) = std::fs::read_to_string(path) else {
bail!(
"user service '{name}' has no stored launch at {}; run `mise bootstrap services apply`",
crate::file::display_path(path)
);
};
if crate::hash::hash_blake3_to_str(&stored) != digest {
bail!(
"user service '{name}' is registered for a different environment than {} holds; run `mise bootstrap services apply`",
crate::file::display_path(path)
);
}
Ok(serde_json::from_str(&stored)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_launch_must_be_the_one_the_action_names() {
let name = "service-exec-digest";
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("mise-history.launch.json");
let launch = ServiceLaunch {
program: "agent.exe".to_string(),
args: "--serve".to_string(),
environment: [("RUST_LOG".to_string(), "info".to_string())]
.into_iter()
.collect(),
};
let stored = serde_json::to_string(&launch).unwrap();
std::fs::write(&path, &stored).unwrap();
let digest = crate::hash::hash_blake3_to_str(&stored);
assert_eq!(read_launch(name, &path, &digest).unwrap(), launch);
let err = read_launch(name, &path, "0").unwrap_err().to_string();
assert!(
err.contains("registered for a different environment"),
"{err}"
);
std::fs::remove_file(&path).unwrap();
let err = read_launch(name, &path, &digest).unwrap_err().to_string();
assert!(err.contains("no stored launch"), "{err}");
}
}