From 8669deb07859404772de3a7ccba58527e5737287 Mon Sep 17 00:00:00 2001 From: robcholz <84130577+robcholz@users.noreply.github.com> Date: Mon, 9 Feb 2026 02:18:22 -0500 Subject: [PATCH] fix: try to fix provision.sh --- src/provision.sh | 4 +-- src/vm.rs | 67 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/src/provision.sh b/src/provision.sh index 2e12a58..842676a 100644 --- a/src/provision.sh +++ b/src/provision.sh @@ -1,7 +1,7 @@ #!/bin/bash set -eEux -trap 'rc=$?; echo "[vibebox][error] provisioning failed at: ${BASH_COMMAND} (exit ${rc})"; echo "VIBEBOX_PROVISION_FAILED"; systemctl poweroff || true; exit 1' ERR +trap 'rc=$?; echo "[vibebox][error] provisioning failed at: ${BASH_COMMAND} (exit ${rc})"; printf "%s%s\n" VIBEBOX_PROVISION_ FAILED; systemctl poweroff || true; exit 1' ERR # Wait for network + DNS before apt-get to avoid early boot flakiness. wait_for_network() { @@ -98,5 +98,5 @@ sleep 100 # sleep here so that we don't see the login screen flash up before the EOF # Done provisioning, power off the VM -echo "VIBEBOX_PROVISION_OK" +printf "%s%s\n" VIBEBOX_PROVISION_ OK systemctl poweroff diff --git a/src/vm.rs b/src/vm.rs index 37189a9..e425a69 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -209,8 +209,10 @@ where let guest_mise_cache = cache_dir.join(".guest-mise-cache"); let instance_dir = project_root.join(INSTANCE_DIR_NAME); + fs::create_dir_all(&instance_dir)?; let status_file = StatusFile::new(instance_dir.join(STATUS_FILE_NAME)); status_file.update("preparing VM image..."); + let provision_log = instance_dir.join("provision.log"); let basename_compressed = DEBIAN_COMPRESSED_DISK_URL.rsplit('/').next().unwrap(); let base_compressed = cache_dir.join(basename_compressed); @@ -235,6 +237,7 @@ where &default_raw, std::slice::from_ref(&mise_directory_share), Some(&status_file), + Some(&provision_log), )?; let _ = ensure_instance_disk( &instance_raw, @@ -583,6 +586,7 @@ fn ensure_default_image( default_raw: &Path, directory_shares: &[DirectoryShare], status: Option<&StatusFile>, + provision_log: Option<&Path>, ) -> Result<(), Box> { if default_raw.exists() { return Ok(()); @@ -605,14 +609,31 @@ fn ensure_default_image( timeout: PROVISION_EXPECT_TIMEOUT, }, ]; - if let Err(err) = run_vm( - default_raw, - &provision_actions, - directory_shares, - DEFAULT_CPU_COUNT, - DEFAULT_RAM_BYTES, - None, - ) { + let provision_result = if let Some(log_path) = provision_log { + let log_path = log_path.to_path_buf(); + run_vm_with_io( + default_raw, + &provision_actions, + directory_shares, + DEFAULT_CPU_COUNT, + DEFAULT_RAM_BYTES, + None, + move |output_monitor, vm_output_fd, vm_input_fd| { + spawn_vm_io_with_log(output_monitor, vm_output_fd, vm_input_fd, log_path) + }, + ) + } else { + run_vm( + default_raw, + &provision_actions, + directory_shares, + DEFAULT_CPU_COUNT, + DEFAULT_RAM_BYTES, + None, + ) + }; + + if let Err(err) = provision_result { let _ = fs::remove_file(default_raw); return Err(err); } @@ -915,6 +936,36 @@ pub fn spawn_vm_io( spawn_vm_io_with_line_handler(output_monitor, vm_output_fd, vm_input_fd, |_| false) } +fn spawn_vm_io_with_log( + output_monitor: Arc, + vm_output_fd: OwnedFd, + vm_input_fd: OwnedFd, + log_path: PathBuf, +) -> IoContext { + let log_file = fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&log_path) + .ok() + .map(|file| Arc::new(Mutex::new(file))); + + spawn_vm_io_with_hooks( + output_monitor, + vm_output_fd, + vm_input_fd, + IoControl::new(), + |_| false, + move |bytes| { + if let Some(log) = &log_file + && let Ok(mut file) = log.lock() + { + let _ = file.write_all(bytes); + } + }, + ) +} + impl IoContext { pub fn shutdown(self) { let _ = self.input_tx.send(VmInput::Shutdown);