Repository navigation
feat(minimal2): auto-spawn minvmd on macOS #354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
norrietaylor
merged 13 commits into
main
from
sdd/331-autospawn-minimal2-eabd47256ffb761d
Jun 5, 2026
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
21eb332
feat(minimal2): auto-spawn minvmd on macOS (R4.5)
gominimal-aw-bot[bot] 11f073a
build(minimal2): add tracing dependency
norrietaylor 8f5c83a
fix(minimal2): gate macOS-only items and satisfy clippy in autospawn
norrietaylor b83d2e8
ci(minvmd): add autospawn-e2e macOS gate for R4.5
norrietaylor a1896bd
ci(minvmd): capture boot diagnostics in autospawn-e2e
norrietaylor 79d280a
fix(minimal2): handle Lifecycle::Stopping explicitly and gate platfor…
gominimal-aw-bot[bot] b7b5950
fix(minvmd): provision before start in run from a clean state
norrietaylor e20fd32
ci(minvmd): start autospawn-e2e from a clean macOS state dir
norrietaylor 5ffb0aa
fix(minimal2): restore catch-all arm in autospawn state match
norrietaylor 4a2b9f4
docs(minimal2): note ls is macOS-only today with a Linux TODO
norrietaylor a17aa7a
fix(minimal2): wait out a stopping minvmd before auto-spawning
norrietaylor 99d86d8
docs(minimal2): add TODO about platform-specific behavior in ls command
gominimal-aw-bot[bot] ac7f15c
docs(minimal2): make the ls platform TODO accurate and issue-linked
norrietaylor File filter
Filter by extension
Conversations
8000
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| //! Auto-spawn logic for minvmd on macOS (R4.5). | ||
| //! | ||
| //! On macOS, checks the minvmd state before connecting to the UDS. If minvmd | ||
| //! is not running, spawns `minvmd run --detach` and waits for the UDS to become | ||
| //! available. | ||
| //! | ||
| //! On Linux, this module is a no-op. | ||
|
|
||
| use std::io; | ||
| #[cfg(target_os = "macos")] | ||
| use std::process::Command; | ||
| #[cfg(target_os = "macos")] | ||
| use std::thread; | ||
| #[cfg(target_os = "macos")] | ||
| use std::time::{Duration, Instant}; | ||
|
|
||
| /// Default timeout in seconds to wait for the UDS when spawning minvmd (R4.5). | ||
| #[cfg(target_os = "macos")] | ||
| const DEFAULT_SPAWN_TIMEOUT_SECS: u64 = 8; | ||
|
|
||
| /// Poll interval while waiting for a shutting-down minvmd to reach a terminal | ||
| /// state before deciding whether to spawn. | ||
| #[cfg(target_os = "macos")] | ||
| const STOPPING_POLL_MS: u64 = 100; | ||
|
|
||
| /// Max time to wait for an in-progress `minvmd stop` (SIGTERM → SIGKILL, ~5 s) | ||
| /// to finish before giving up with a clear error. | ||
| #[cfg(target_os = "macos")] | ||
| const STOPPING_WAIT_SECS: u64 = 6; | ||
|
|
||
| /// Check if minvmd needs to be spawned, and spawn it if necessary. | ||
| /// | ||
| /// On macOS (R4.5): | ||
| /// - Reads the minvmd state from `state.toml` | ||
| /// - If not running, spawns `minvmd run --detach` with a timeout | ||
| /// - Returns an error if spawn fails | ||
| /// | ||
| /// On Linux, this is a no-op since minvmd does not exist on Linux. | ||
| #[cfg(target_os = "macos")] | ||
| pub fn ensure_minvmd_running() -> io::Result<()> { | ||
| let state_dir = minvmd::state::StateDir::new(minvmd::state::StateDir::default_path())?; | ||
|
|
||
| // Read current state (R4.5: check state.toml before connecting) | ||
| let state = state_dir.read_state()?; | ||
|
|
||
| // If already running or starting, no need to spawn | ||
| match state.lifecycle { | ||
| minvmd::lifecycle::Lifecycle::Running | minvmd::lifecycle::Lifecycle::Starting => { | ||
| tracing::debug!("minvmd already running or starting"); | ||
| return Ok(()); | ||
| } | ||
| minvmd::lifecycle::Lifecycle::Stopping => { | ||
| // `minvmd stop` can take up to ~5 s (SIGTERM → SIGKILL escalation), | ||
| // so a fixed short sleep usually leaves the daemon still Stopping. | ||
| // Spawning then would make the new `minvmd run` bail on its own | ||
| // Stopping guard, surfacing an opaque 8 s UDS timeout. Instead poll | ||
| // until it reaches a terminal state, re-reading on each tick. | ||
| tracing::info!("minvmd is stopping; waiting for it to finish"); | ||
| let deadline = Instant::now() + Duration::from_secs(STOPPING_WAIT_SECS); | ||
| loop { | ||
| thread::sleep(Duration::from_millis(STOPPING_POLL_MS)); | ||
| match state_dir.read_state()?.lifecycle { | ||
| // Something else restarted it in the meantime. | ||
| minvmd::lifecycle::Lifecycle::Running | ||
| | minvmd::lifecycle::Lifecycle::Starting => return Ok(()), | ||
| // Shutdown completed; fall through to spawn. | ||
| minvmd::lifecycle::Lifecycle::Stopped | ||
| | minvmd::lifecycle::Lifecycle::NotProvisioned => break, | ||
| _ if Instant::now() >= deadline => { | ||
| return Err(io::Error::other( | ||
| "minvmd is still stopping after waiting; try again shortly", | ||
| )); | ||
| } | ||
| // Still stopping (or a future state); keep polling. | ||
| _ => continue, | ||
| } | ||
| } | ||
| } | ||
| minvmd::lifecycle::Lifecycle::Stopped | minvmd::lifecycle::Lifecycle::NotProvisioned => { | ||
| // Not running; will spawn below | ||
| } | ||
| // Lifecycle is #[non_exhaustive]; treat any future state conservatively | ||
| // as "not running" and fall through to spawn. | ||
| _ => {} | ||
| } | ||
|
|
||
| // Not running; spawn minvmd run --detach with timeout (R4.5) | ||
| tracing::info!( | ||
| "spawning minvmd run --detach with timeout {}", | ||
| DEFAULT_SPAWN_TIMEOUT_SECS | ||
| ); | ||
| let output = Command::new("minvmd") | ||
| .arg("run") | ||
| .arg("--detach") | ||
| .arg("--timeout") | ||
| .arg(DEFAULT_SPAWN_TIMEOUT_SECS.to_string()) | ||
| .output() | ||
| .map_err(|e| io::Error::new(e.kind(), format!("failed to spawn minvmd: {}", e)))?; | ||
|
|
||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| return Err(io::Error::other(format!( | ||
| "minvmd run --detach failed: {}", | ||
| stderr | ||
| ))); | ||
| } | ||
|
|
||
| tracing::info!("minvmd spawned successfully"); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// On Linux, auto-spawn is a no-op since minvmd is not available. | ||
| #[cfg(target_os = "linux")] | ||
| pub fn ensure_minvmd_running() -> io::Result<()> { | ||
| tracing::debug!("ensure_minvmd_running is a no-op on Linux"); | ||
| Ok(()) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| #[test] | #[cfg(target_os = "linux")] | |
| fn test_autospawn_noop_on_linux() { | ||
| // On Linux, ensure_minvmd_running should be a no-op and always succeed | ||
| let result = super::ensure_minvmd_running(); | ||
| assert!( | ||
| result.is_ok(), | ||
| "ensure_minvmd_running should always succeed on Linux" | ||
| ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.