0.39.0
New commands
avocado vm
On macOS and Windows, the CLI now drives a single user-level QEMU guest that hosts its own dockerd and a 9p mount of the user's workspace. Flows that need Docker (provision, ext build, sdk run) work without Docker Desktop installed and without the developer having to manage QEMU manually:
avocado vm start # launch / resume the helper VM
avocado vm stop # graceful shutdown
avocado vm status # show running state and SSH port
avocado vm shell # interactive shell inside the guest
avocado vm logs # tail guest logs
avocado vm rebuild # rebuild the var.btrfs from scratch
State lives under ~/.avocado/vm/ (pidfile, SSH port, QMP/QGA/control sockets, var.btrfs, generated SSH config and key). Subcommands that need Docker auto-flip DOCKER_HOST to the SSH-forwarded socket inside the guest, so docker run calls land in the VM transparently. Opt out with --no-vm-auto-start, AVOCADO_VM_AUTO_START=0, or --runs-on.
avocado vm update
Polls the configured channel for a newer VM release, downloads any artifacts whose SHA differs from the installed copy, and atomic-swaps them into ~/.avocado/vm/install/. Stops the VM during the swap and restarts it if it was running:
avocado vm update --check # check availability without downloading
avocado vm update -y # skip interactive confirmation
avocado vm update --channel beta
avocado vm update --output json
User state in the persistent var.btrfs survives version bumps. Artifacts marked update_policy: seed_only (currently var) are downloaded only on first install and never overwritten.
avocado vm reset
Wipe the persistent var.btrfs and re-seed from the installed var artifact:
avocado vm reset
This drops Docker volumes, container caches, project work under /data, and /etc/machine-id. Interactive confirmation requires typing reset (not just y) because the operation is irreversible. -y/--yes for scripted use.
avocado completion
Generates shell tab-completion scripts. Candidate sets for --extension, --runtime, --target, and signing-key positionals are computed dynamically at TAB time, reading the current avocado.yaml and the local signing-key registry — so completions stay live as the project changes:
avocado completion bash > /etc/bash_completion.d/avocado
avocado completion zsh > "${fpath[1]}/_avocado"
avocado completion fish > ~/.config/fish/completions/avocado.fish
elvish and powershell are also supported.
avocado provision --list
Discovers the available provisioning profiles for the resolved target by reading the stone manifest inside the SDK volume:
avocado provision --list --output json
The JSON output names each profile, its script, the declared default, and per-profile field schemas (name, type, label, description, required flag, default) for desktop tooling that needs to render dynamic provisioning forms keyed off the manifest. Returns available: false with a reason when the SDK isn't installed yet or the target has no manifest — so callers can distinguish "no profiles yet" from "command failed" without parsing exit codes.
New features
--output json across the lifecycle
Every command the macOS desktop app shells out to now supports --output json, emitting newline-delimited JSON events instead of TUI prose. JSON mode also auto-enables --force semantics so DNF gets -y and Docker drops -it:
avocado init --output json—{event: info, …}per step, terminal{event: complete, …}or{event: error, …}. With--reference X --name myproj, writes into<directory>/<myproj>/rather than<directory>/.avocado config show --output json— narrow projection ofavocado.yaml(distro release/channel, default_target, supported_targets as"*"or list, runtimes, provision_profiles, src_dir, plus aconnect: { org, project }mirror).avocado install / build / provision --output json— NDJSONstepevents per task lifecycle transition (task_registered,stepwithrunning/success/failed,step_error,output).avocado connect auth login --output json—login_url,info,complete,errorevents so a UI can drive the browser handshake.statusandlogoutemit single objects.avocado connect projects create --output json— single-shot{ org, id, name }so callers can persist the new project ID immediately.
post_build and post_install hooks
Extensions and runtimes can now declare a post_build script that runs as the final step of avocado ext build and avocado runtime build:
extensions:
my-ext:
post_build: scripts/post-build.sh
runtimes:
dev:
post_build: scripts/post-build.sh
For extensions the hook fires after the sysroot is populated but before the sysext / confext .raw images are sealed, so the script can mutate $AVOCADO_BUILD_EXT_SYSROOT and have changes baked into the image (e.g. generating /etc/ssl/certs/ca-certificates.crt from the installed ca-certificates package). For runtimes the hook fires after the runtime image is built.
Extension scripts resolve relative to the extension's source directory (/opt/src for local, $AVOCADO_PREFIX/includes/<ext>/ for remote), so remote extensions can ship their own hooks. The post_build field is hashed into the build stamp; adding, removing, or renaming the hook re-triggers a build. Script contents are not hashed — editing in place still needs --no-stamps to force a re-run.
rootfs.post_install and initramfs.post_install accept the same shape and run before the image (e.g. EROFS) is generated, so the script can mutate the sysroot before sealing.
Parallel extension build for avocado build -r <runtime>
avocado build without -r already parallelized extension builds via the task scheduler. The -r <runtime> form was taking an early-return path that ran extensions through a serial for-loop. That early return is gone — extensions for the requested runtime now build concurrently, same as the no-flag path.
Apache-2.0 license + Homebrew tap
The CLI is now Apache-2.0 licensed (was MIT) — aligning with the rest of the avocado-linux org and what Homebrew core requires. Releases now publish:
- Per-tarball
.sha256sidecars alongside each asset. - A consolidated
SHA256SUMSfile on each release. - An automated dispatch to
avocado-linux/homebrew-tapon stable tags.
avocado upgrade and the [UPDATE] notice now detect Homebrew-installed binaries and suggest brew upgrade avocado-cli instead of self-update, so a subsequent brew upgrade doesn't silently revert the binary.
Container tool plumbing
container_tool is now threaded through the volume + image copy paths (copy_volume_path_to_host, VolumeManager, per-command copy_rpm_to_host helpers) instead of hardcoded docker. Podman, nerdctl, and other Docker-compatible runtimes work consistently across ext and sdk image + package flows.
Variable var partition size
The build script now measures the freshly built var.btrfs and forwards the size to stone bundle via --partition-size. Manifests no longer need a hand-picked size for the last expandable partition — stone 2.2.0's omitted-size behavior takes over, rounded up to the partition's size_alignment.
Overlay: cp -a replaces rsync
Overlay merge steps now use cp -a instead of rsync, removing the dependency on rsync being on PATH inside the SDK environment.
Bug fixes
avocado clean --skip-volumesno longer removes.avocado-state. The state file is the only pointer to the Docker volume's name; removing it on unlock-only / stamps-only paths orphaned the volume so the nextavocado clean --volumescould not find it. The two are now paired.- The 9p workspace share is now lazy-mounted from
share::ensure_mounted_in_guestwhen the CLI routes throughavocado-vm. When Avocado.app launches QEMU directly the SSH-mount step would be skipped, leaving/run/workspaceas an empty tmpfs scaffold so every Docker bind under$HOMEsilently mounted nothing — surfacing asCompile script not foundwith an emptyls /opt/srcinside the build container.