0.40.2
avocado vm UX
Streaming downloads with progress
avocado vm update previously buffered each artifact's entire response body in memory before writing to disk, with a 30‑second client-wide timeout that the 458 MB var.btrfs could not meet. Replaced with connect_timeout only plus a streaming download loop that writes each chunk directly to disk:
- Human mode renders an
indicatifMultiProgressqueue matchingavocado connect upload's style — one cyan/blue bar per artifact, finishing with(done). - JSON mode emits throttled
download_started,download_progress, anddownload_completedNDJSON events at roughly 10 Hz.
Smarter avocado vm start defaults
After avocado vm update populates ~/.avocado/vm/install/, vm start now finds it without needing --vm-source or AVOCADO_VM_DIR. Resolution order is now:
--vm-sourceflagAVOCADO_VM_DIRenv var~/.avocado/vm/install/(managed install)~/.avocado/vm/artifact-dir(dev workflow)- Helpful error pointing at
avocado vm update
End-users on the managed path no longer have to think about environment variables.
Persistent CPU and memory overrides
avocado vm start --memory-mib and --cpus are now optional. They resolve to runtime.cpus and runtime.memory_mib in ~/.avocado/vm/config.yaml (also written by Avocado.app's settings UI). Passing a flag writes it back so the next flag-less vm start and the desktop app converge. vm reset / vm update and route-on-demand callers now pass None instead of hardcoded 4096 MiB / 4 CPUs, picking up persisted settings.
avocado vm config subcommand
~/.avocado/vm/config.yaml is the shared host/guest configuration file Avocado.app reads and writes. The CLI gains the matching surface:
avocado vm config get <key>
avocado vm config set <key> <value>
avocado vm config unset <key>
avocado vm config list
The first consumer is network.dns (and an optional one-shot --dns on vm start). The configured resolver is pushed into the guest via resolvectl once SSH is up, resolving the macOS-VPN case where scoped resolvers on the host are invisible to SLIRP's DNS proxy.
avocado vm lifecycle
CLI owns QEMU lifecycle on macOS
vm start and vm stop previously delegated to Avocado.app over a synchronous JSON-line IPC client, which made the CLI unusable when the app was not installed and stalled vm stop whenever the app's main actor was busy. The CLI now spawns and signals QEMU directly on every platform; Avocado.app, when present, adopts the PID via its existing pidfile reconciler.
Remaining IPC (vm.notify.{starting,running,stopping,stopped}) is now best-effort dashboard hinting only: 100 ms timeouts, silent no-op when unreachable, self-heals via the reconciler within roughly two seconds.
A second virtio-serial port (avocado.control → control.sock) lets Avocado.app's USB host bridge and control plane talk to avocado-vm-agent without spawning QEMU itself.
Desktop integration (JSON output)
avocado runtime deploy --output json
Skips TUI rendering and emits NDJSON task_registered, step, and step_error events per phase (stamps, hash-collection, metadata-sign, deploy). Phase names mirror the human labels so the desktop app can render them directly without translation.
avocado config show --detail
Adds a detail block alongside the existing narrow projection:
- Per-runtime extension references with
definedandenabledflags plusnode_pathsfor cross-reference navigation. - Per-extension
types,packages,services, andused_by_runtimes. - An SDK image and packages summary.
Default output is byte-stable so existing consumers (the desktop app's project-list scan) keep working unchanged.
Cross-platform groundwork
Windows MSVC compile check
A new windows-check PR job runs cargo check --target x86_64-pc-windows-msvc. The qga (AF_UNIX QEMU guest agent) and qmp (QEMU monitor over AF_UNIX) modules are now #[cfg(unix)] at the module declaration; call sites in lifecycle::stop and boot_sync::wait_for_guest_ready get matching gates with a non-Unix fallback that waits for SSH only. libc::SIG{TERM,KILL} is replaced with local POSIX constants at the two referencing sites. No behavior change on macOS or Linux.
Runtime extension map syntax everywhere
runtimes.<name>.extensions accepts the map form:
runtimes:
dev:
extensions:
- foo: { enabled: false }
- bar
utils::runtime_extension::RuntimeExtensionSpec::parse_entry is the single source of truth — every list-walker (Config::extension_deps, Config::find_active_extensions, runtime/build, runtime/deps, build, install) now funnels through it. Two more call sites that were silently skipping map-form entries — collect_extension_dependencies in build.rs and the runtime input-hash compute in stamps.rs — are fixed in this release. runtime build propagates enabled: false into the manifest's AVOCADO_EXT_DISABLED so avocadoctl skips activation at refresh time.
Extension build
AVOCADO_ON_MERGE=systemd-tmpfiles --create for tmpfiles.d/
Mirrors the existing sysusers.d and ld.so.conf.d detection. Extensions shipping configuration under usr/(local/)lib/tmpfiles.d/ or etc/tmpfiles.d/ get the merge hook automatically.
Reliability
Streaming hashing for multi-GB images
compute_file_hash previously read the entire file into memory before feeding it to SHA-256 / BLAKE3. Image artifacts can be tens of gigabytes; on a 16 GB Mac that pushed the process into swap thrash. Replaced with a fixed 1 MiB chunk streaming loop. The resulting digest is identical.
Bug fixes
avocado clean --unlockactually unlocks now. The previous code calledlock_file.save()after clearing entries, which merges with on-disk state and re-adds them. The desktop app's Unlock button hit this and silently no-op'd. Switched tosave_replacing; regression test included.avocado clean --unlocknow clears pinned kernel versions.--unlockcleared the SDK, rootfs, initramfs, target-sysroot, extension, and runtime locks, but left the pinnedKERNEL_VERSIONand per-kernel sysroot package tables intact. After unlocking and reinstalling, dnf re-resolved sysroot content from the latest feed while the CLI still demanded the old pinned kernel by exact name, failing withUnable to find a match: kernel-image-…once the rolling feed dropped that version. Unlock now also drops the kernel pins so the next install re-picks the latest.- Update banner no longer corrupts
--output json. When a newer release is available,avocadowrites a yellow[UPDATE] avocado X is available …banner to stderr. Callers that merge stdout and stderr before decoding (the desktop app's CLI runner) saw the banner break every JSON payload the moment a new version shipped on the CDN. The banner is now suppressed whenever--output jsonis present in the arguments; native CLI runs still get the human-readable nudge. avocado vm updateno longer fails the post-update boot check on preservedvarimages.verify_allran a SHA-256 check against every artifact in the new release manifest, includingSeedOnlyentries likevar. Once you mutate your preservedvarimage (the entire point ofSeedOnly), its on-disk hash legitimately diverges from the release's seed hash, and every post-update boot failed with asha256 mismatch for role 'var'error.verify_allnow skipsSeedOnlyartifacts; replace artifacts (kernel, initramfs, rootfs) are still verified.avocado connect initproduces valid YAML on compact-style scaffolds. The connect-extension inserter hard-coded a 6-space indent when adding the three connect extensions to a runtime'sextensions:sequence. Reference scaffolds that emit the sequence in YAML compact style (items at the same indent as theextensions:key) ended up with mixed-indent output that no parser would accept —avocado install -ffailed immediately withdid not find expected keyon any freshly scaffolded, connect-init'd project. All four runtime-list scans now recognize-items at the parent indent as still inside the block.