feat(service): AgentSpec and launchd install/uninstall
This commit is contained in:
Generated
+274
-7
@@ -84,6 +84,12 @@ version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.1"
|
||||
@@ -148,12 +154,73 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "codepage"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48f68d061bc2828ae826206326e61251aca94c1e4a5305cf52d9138639c918b4"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
|
||||
|
||||
[[package]]
|
||||
name = "encoding-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87b881ab2524b96a5ce932056c7482ba6152e2226fed3936b3e592adeb95ca6d"
|
||||
dependencies = [
|
||||
"codepage",
|
||||
"encoding_rs",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -199,6 +266,17 @@ version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
@@ -317,6 +395,21 @@ version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
@@ -419,6 +512,12 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
@@ -477,6 +576,25 @@ version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"indexmap",
|
||||
"quick-xml",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
@@ -496,6 +614,15 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
@@ -511,6 +638,17 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.17",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
@@ -528,6 +666,19 @@ version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
@@ -537,7 +688,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"linux-raw-sys 0.12.1",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
@@ -590,6 +741,22 @@ dependencies = [
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "service-manager"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ff6975a4ea07dda326cf122fcc1b5cf6266daad42d1442a666a99fe50f0de9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs",
|
||||
"encoding-utils",
|
||||
"encoding_rs",
|
||||
"log",
|
||||
"plist",
|
||||
"which",
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
@@ -649,19 +816,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"getrandom 0.4.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"rustix 1.1.4",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -684,6 +871,36 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.3"
|
||||
@@ -882,12 +1099,55 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -1073,6 +1333,12 @@ dependencies = [
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
|
||||
|
||||
[[package]]
|
||||
name = "xy"
|
||||
version = "0.1.0"
|
||||
@@ -1084,6 +1350,7 @@ dependencies = [
|
||||
"nix",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"service-manager",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -1100,7 +1367,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"xy-protocol",
|
||||
@@ -1116,7 +1383,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1126,7 +1393,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"nix",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"tracing",
|
||||
|
||||
@@ -33,3 +33,4 @@ humantime-serde = "1"
|
||||
async-trait = "0.1"
|
||||
tempfile = "3"
|
||||
tokio-test = "0.4"
|
||||
service-manager = "0.11"
|
||||
|
||||
@@ -30,6 +30,7 @@ anyhow.workspace = true
|
||||
etcetera.workspace = true
|
||||
nix.workspace = true
|
||||
humantime.workspace = true
|
||||
service-manager = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
|
||||
@@ -5,6 +5,7 @@ mod daemon;
|
||||
mod logging;
|
||||
mod paths;
|
||||
mod pidfile;
|
||||
mod service;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "xy", version, about = "HTTP MCP server supervisor")]
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use service_manager::{
|
||||
LaunchdServiceManager, RestartPolicy, ServiceInstallCtx, ServiceLabel, ServiceManager,
|
||||
ServiceUninstallCtx,
|
||||
};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
pub(crate) const DEFAULT_LABEL: &str = "se.aceofba.xy";
|
||||
|
||||
pub(crate) struct AgentSpec {
|
||||
pub label: String,
|
||||
pub program: PathBuf,
|
||||
pub args: Vec<String>,
|
||||
pub path_env: String,
|
||||
pub working_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl AgentSpec {
|
||||
pub fn for_current_exe() -> Result<Self> {
|
||||
let program = std::env::current_exe()
|
||||
.context("resolve current executable")?
|
||||
.canonicalize()
|
||||
.context("canonicalize current executable")?;
|
||||
|
||||
let path_env = std::env::var("PATH").context("read PATH")?;
|
||||
|
||||
let working_dir = etcetera::home_dir().context("locate home directory")?;
|
||||
|
||||
Ok(Self {
|
||||
label: DEFAULT_LABEL.to_string(),
|
||||
program,
|
||||
args: vec!["daemon".to_string()],
|
||||
path_env,
|
||||
working_dir,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn install_ctx(&self) -> Result<ServiceInstallCtx> {
|
||||
let label: ServiceLabel = self.label.parse().context("parse service label")?;
|
||||
|
||||
Ok(ServiceInstallCtx {
|
||||
label,
|
||||
program: self.program.clone(),
|
||||
args: self.args.iter().map(std::ffi::OsString::from).collect(),
|
||||
contents: None,
|
||||
username: None,
|
||||
working_directory: Some(self.working_dir.clone()),
|
||||
environment: Some(vec![("PATH".to_string(), self.path_env.clone())]),
|
||||
autostart: true,
|
||||
restart_policy: RestartPolicy::Always { delay_secs: None },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn plist_path(&self) -> Result<PathBuf> {
|
||||
plist_path_for(&self.label)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn plist_path_for(label: &str) -> Result<PathBuf> {
|
||||
let home = etcetera::home_dir().context("locate home directory")?;
|
||||
|
||||
Ok(home
|
||||
.join("Library")
|
||||
.join("LaunchAgents")
|
||||
.join(format!("{label}.plist")))
|
||||
}
|
||||
|
||||
pub(crate) fn is_build_tree_path(path: &Path) -> bool {
|
||||
let mut components = path.components().peekable();
|
||||
|
||||
while let Some(component) = components.next() {
|
||||
if component != Component::Normal("target".as_ref()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if matches!(
|
||||
components.peek(),
|
||||
Some(Component::Normal(next))
|
||||
if *next == std::ffi::OsStr::new("debug") || *next == std::ffi::OsStr::new("release")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(crate) fn ensure_supported() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
pub(crate) fn ensure_supported() -> Result<()> {
|
||||
anyhow::bail!("start-on-login is macOS-only for now")
|
||||
}
|
||||
|
||||
fn manager() -> LaunchdServiceManager {
|
||||
LaunchdServiceManager::user()
|
||||
}
|
||||
|
||||
pub(crate) fn install(spec: &AgentSpec) -> Result<()> {
|
||||
manager()
|
||||
.install(spec.install_ctx()?)
|
||||
.context("install launchd agent")
|
||||
}
|
||||
|
||||
pub(crate) fn uninstall(label: &str) -> Result<()> {
|
||||
let label: ServiceLabel = label.parse().context("parse service label")?;
|
||||
|
||||
manager()
|
||||
.uninstall(ServiceUninstallCtx { label })
|
||||
.context("uninstall launchd agent")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn sample_spec() -> AgentSpec {
|
||||
AgentSpec {
|
||||
label: "se.aceofba.xy-test".to_string(),
|
||||
program: PathBuf::from("/usr/local/bin/xy"),
|
||||
args: vec!["daemon".to_string()],
|
||||
path_env: "/usr/local/bin:/usr/bin".to_string(),
|
||||
working_dir: PathBuf::from("/Users/someone"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn install_ctx_maps_every_field() {
|
||||
let ctx = sample_spec().install_ctx().unwrap();
|
||||
|
||||
assert_eq!(ctx.label.to_qualified_name(), "se.aceofba.xy-test");
|
||||
assert_eq!(ctx.program, PathBuf::from("/usr/local/bin/xy"));
|
||||
assert_eq!(ctx.args, vec![std::ffi::OsString::from("daemon")]);
|
||||
assert_eq!(ctx.working_directory, Some(PathBuf::from("/Users/someone")));
|
||||
assert!(ctx.autostart);
|
||||
assert_eq!(
|
||||
ctx.environment,
|
||||
Some(vec![(
|
||||
"PATH".to_string(),
|
||||
"/usr/local/bin:/usr/bin".to_string()
|
||||
)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn install_ctx_uses_always_restart_without_delay() {
|
||||
let ctx = sample_spec().install_ctx().unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
ctx.restart_policy,
|
||||
RestartPolicy::Always { delay_secs: None }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn install_ctx_supplies_no_raw_contents() {
|
||||
let ctx = sample_spec().install_ctx().unwrap();
|
||||
|
||||
assert!(ctx.contents.is_none());
|
||||
assert!(ctx.username.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_tree_paths_are_detected() {
|
||||
assert!(is_build_tree_path(Path::new("/home/me/xy/target/debug/xy")));
|
||||
assert!(is_build_tree_path(Path::new(
|
||||
"/home/me/xy/target/release/xy"
|
||||
)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn installed_paths_are_not_build_tree_paths() {
|
||||
assert!(!is_build_tree_path(Path::new("/Users/me/.cargo/bin/xy")));
|
||||
assert!(!is_build_tree_path(Path::new("/usr/local/bin/xy")));
|
||||
assert!(!is_build_tree_path(Path::new("/opt/targeted/bin/xy")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plist_path_sits_in_user_launch_agents() {
|
||||
let path = sample_spec().plist_path().unwrap();
|
||||
|
||||
assert!(path.ends_with("Library/LaunchAgents/se.aceofba.xy-test.plist"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos_is_supported() {
|
||||
assert!(ensure_supported().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn other_platforms_are_rejected() {
|
||||
let err = ensure_supported().unwrap_err().to_string();
|
||||
|
||||
assert!(err.contains("macOS-only"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user