dev: add pod runtime command override
This commit is contained in:
@@ -3,6 +3,8 @@ use std::fmt;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const POD_RUNTIME_COMMAND_ENV: &str = "INSOMNIA_POD_RUNTIME_COMMAND";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PodRuntimeCommand {
|
||||
pub program: PathBuf,
|
||||
@@ -28,9 +30,29 @@ impl PodRuntimeCommand {
|
||||
/// Resolve the Pod runtime command used for subprocess launches.
|
||||
///
|
||||
/// The default launch path is always the current `insomnia` executable plus
|
||||
/// the unified `pod` prefix argument.
|
||||
/// the unified `pod` prefix argument. During development, a non-empty
|
||||
/// `INSOMNIA_POD_RUNTIME_COMMAND` value replaces only the executable path;
|
||||
/// the `pod` prefix is still added here and the env value is not parsed as a
|
||||
/// shell command.
|
||||
pub fn resolve() -> io::Result<Self> {
|
||||
Self::for_current_exe()
|
||||
Self::resolve_from_env_value(
|
||||
std::env::var_os(POD_RUNTIME_COMMAND_ENV),
|
||||
std::env::current_exe,
|
||||
)
|
||||
}
|
||||
|
||||
fn resolve_from_env_value<F>(
|
||||
override_program: Option<OsString>,
|
||||
current_exe: F,
|
||||
) -> io::Result<Self>
|
||||
where
|
||||
F: FnOnce() -> io::Result<PathBuf>,
|
||||
{
|
||||
if let Some(program) = override_program.filter(|program| !program.as_os_str().is_empty()) {
|
||||
return Ok(Self::for_executable(program));
|
||||
}
|
||||
|
||||
Ok(Self::for_executable(current_exe()?))
|
||||
}
|
||||
|
||||
pub fn program(&self) -> &Path {
|
||||
@@ -98,4 +120,49 @@ mod tests {
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_uses_current_exe_when_override_is_unset() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(None, || {
|
||||
Ok(PathBuf::from("/opt/insomnia/bin/insomnia"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_uses_current_exe_when_override_is_empty() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(Some(OsString::new()), || {
|
||||
Ok(PathBuf::from("/opt/insomnia/bin/insomnia"))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
PodRuntimeCommand::for_executable("/opt/insomnia/bin/insomnia")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_override_replaces_only_program_and_keeps_pod_prefix() {
|
||||
let command = PodRuntimeCommand::resolve_from_env_value(
|
||||
Some(OsString::from("/tmp/rebuilt insomnia")),
|
||||
|| panic!("override must not inspect current_exe"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(command.program(), Path::new("/tmp/rebuilt insomnia"));
|
||||
assert_eq!(command.prefix_args(), [OsString::from("pod")]);
|
||||
assert_eq!(
|
||||
command.argv_with(["--pod", "agent"]),
|
||||
vec!["pod", "--pod", "agent"]
|
||||
.into_iter()
|
||||
.map(OsString::from)
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,10 @@ pub enum SpawnError {
|
||||
Io(io::Error),
|
||||
/// runtime ディレクトリが解決できなかった (環境変数未設定等)。
|
||||
RuntimeDirUnavailable,
|
||||
PodLaunchFailed(io::Error),
|
||||
PodLaunchFailed {
|
||||
command: PodRuntimeCommand,
|
||||
source: io::Error,
|
||||
},
|
||||
PodExitedEarly {
|
||||
stderr_tail: String,
|
||||
},
|
||||
@@ -68,7 +71,10 @@ impl std::fmt::Display for SpawnError {
|
||||
f,
|
||||
"could not resolve runtime directory (set INSOMNIA_HOME, INSOMNIA_RUNTIME_DIR, XDG_RUNTIME_DIR, or HOME)"
|
||||
),
|
||||
Self::PodLaunchFailed(e) => write!(f, "failed to launch pod: {e}"),
|
||||
Self::PodLaunchFailed { command, source } => write!(
|
||||
f,
|
||||
"failed to launch pod runtime command `{command}`: {source}"
|
||||
),
|
||||
Self::PodExitedEarly { stderr_tail } => {
|
||||
if stderr_tail.is_empty() {
|
||||
write!(f, "pod exited before becoming ready")
|
||||
@@ -85,7 +91,14 @@ impl std::fmt::Display for SpawnError {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SpawnError {}
|
||||
impl std::error::Error for SpawnError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::Io(error) | Self::PodLaunchFailed { source: error, .. } => Some(error),
|
||||
Self::RuntimeDirUnavailable | Self::PodExitedEarly { .. } | Self::Timeout => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SpawnError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
@@ -132,7 +145,12 @@ where
|
||||
.arg("--session-pod-name")
|
||||
.arg(&config.pod_name);
|
||||
}
|
||||
let mut child = command.spawn().map_err(SpawnError::PodLaunchFailed)?;
|
||||
let mut child = command
|
||||
.spawn()
|
||||
.map_err(|source| SpawnError::PodLaunchFailed {
|
||||
command: config.runtime_command.clone(),
|
||||
source,
|
||||
})?;
|
||||
|
||||
// Default `kill_on_drop = false` plus `process_group(0)` makes this
|
||||
// a detached Pod once startup succeeds: dropping the handle does not
|
||||
|
||||
Reference in New Issue
Block a user