diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index 7ab4d2ba..45800558 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -731,7 +731,8 @@ async fn exec_send_agent_snapshot_to_fd( } /// Shared body for `CreateContainer` / `UpdateContainer`: validate the -/// name, build the toplevel ourselves, and apply it (streaming line +/// name, build the toplevel ourselves, and run +/// `nixos-container … --system-path ` (streaming line /// events to `writer` when `stream` is set). /// /// **Both verbs build explicitly now — not just `update`.** The first cut @@ -746,15 +747,6 @@ async fn exec_send_agent_snapshot_to_fd( /// something we control. Building here for both verbs removes `buildFlake()` /// from the picture entirely — there's no shared `.tmp` left to race on, /// so there's nothing left to reason about staying in sync with. -/// -/// **`update` no longer calls `nixos-container update` at all.** That -/// action's own version-compat probe runs unconditionally before -/// `--system-path` is ever honored, dying on every agent's update. -/// [`swap_container_profile`] replicates exactly what `update`'s own -/// action does *past* that probe (confirmed against its source): `nix-env -/// --set` the per-container profile, then `systemctl reload` if the -/// container is running. `create` is untouched — it isn't the failing -/// verb. async fn container_flake_action( verb: &str, name: &str, @@ -770,11 +762,12 @@ async fn container_flake_action( // UI that shows build progress: nothing until the longest phase // finishes, then everything at once. let toplevel = nix_build_toplevel(name, stream.then_some(&mut *writer)).await?; - let system_name = container_system_name(name); - if verb == "update" { - return swap_container_profile(&system_name, &toplevel, stream, writer).await; - } - let args = [verb, &system_name, "--system-path", &toplevel]; + let args = [ + verb, + &container_system_name(name), + "--system-path", + &toplevel, + ]; if stream { container_run_streaming(&args, writer).await } else { @@ -782,90 +775,6 @@ async fn container_flake_action( } } -/// Apply a prebuilt toplevel to an existing container directly, without -/// going through `nixos-container update` (see `container_flake_action`'s -/// doc comment for why). Mirrors `nixos-container.pl`'s own `update` -/// action verbatim, past its version probe: point the per-container `nix- -/// env` profile at the new toplevel, then reload the container unit *if* -/// it's currently running — the container's own next start already reads -/// from the profile, so a stopped container needs nothing further. -/// -/// Both steps are near-instant in the happy case, but still stream- -/// forwarded like the rest of this operation: a failure here (e.g. a -/// wedged `systemctl reload`) is exactly when the caller most wants the -/// live line in the build log, not just a summary error afterward. -async fn swap_container_profile( - system_name: &str, - toplevel: &str, - stream: bool, - writer: &mut OwnedWriteHalf, -) -> Result<(String, String)> { - let profile = format!("/nix/var/nix/profiles/per-container/{system_name}/system"); - let set_out = Command::new("nix-env") - .args(["-p", &profile, "--set", toplevel]) - .output() - .await - .context("invoke nix-env --set")?; - let mut stdout = String::from_utf8_lossy(&set_out.stdout).into_owned(); - let mut stderr = String::from_utf8_lossy(&set_out.stderr).into_owned(); - log_and_forward(&stdout, &stderr, stream, writer).await; - if !set_out.status.success() { - bail!( - "nix-env -p {profile} --set failed ({}): {}", - set_out.status, - stderr.trim() - ); - } - - let unit = format!("container@{system_name}"); - let state_out = Command::new("systemctl") - .args(["show", "--property=ActiveState", "--value", &unit]) - .output() - .await - .context("query container ActiveState")?; - let active = String::from_utf8_lossy(&state_out.stdout).trim() == "active"; - if active { - let reload_out = Command::new("systemctl") - .args(["reload", &unit]) - .output() - .await - .context("invoke systemctl reload")?; - let r_stdout = String::from_utf8_lossy(&reload_out.stdout).into_owned(); - let r_stderr = String::from_utf8_lossy(&reload_out.stderr).into_owned(); - log_and_forward(&r_stdout, &r_stderr, stream, writer).await; - if !reload_out.status.success() { - bail!( - "systemctl reload {unit} failed ({}): {}", - reload_out.status, - r_stderr.trim() - ); - } - stdout.push_str(&r_stdout); - stderr.push_str(&r_stderr); - } - Ok((stdout, stderr)) -} - -/// Log a command's captured stdout/stderr the same way every other -/// `nixos-container`-adjacent shellout in this file does, and — when -/// `stream` is set — forward each line to the client as a live -/// [`PrivEvent::Line`] too, so a caller watching the build log sees these -/// lines exactly like any other step's, not just a summary on failure. -async fn log_and_forward(stdout: &str, stderr: &str, stream: bool, writer: &mut OwnedWriteHalf) { - for line in stdout.lines() { - tracing::info!(target: "nixos-container", "{line}"); - if stream { - write_line_event(writer, PrivStream::Stdout, line).await; - } - } - for line in stderr.lines() { - tracing::warn!(target: "nixos-container", "{line}"); - if stream { - write_line_event(writer, PrivStream::Stderr, line).await; - } - } -} - /// The explicit `nixosConfigurations..config.system.build.toplevel` /// flake attr path — same construction `hive-c0re`'s own /// `lifecycle::prebuild_toplevel` uses, kept here as a pure function so