Skip to main content
May 2026

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 of avocado.yaml (distro release/channel, default_target, supported_targets as "*" or list, runtimes, provision_profiles, src_dir, plus a connect: { org, project } mirror).
  • avocado install / build / provision --output json — NDJSON step events per task lifecycle transition (task_registered, step with running / success / failed, step_error, output).
  • avocado connect auth login --output jsonlogin_url, info, complete, error events so a UI can drive the browser handshake. status and logout emit 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 .sha256 sidecars alongside each asset.
  • A consolidated SHA256SUMS file on each release.
  • An automated dispatch to avocado-linux/homebrew-tap on 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-volumes no 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 next avocado clean --volumes could not find it. The two are now paired.
  • The 9p workspace share is now lazy-mounted from share::ensure_mounted_in_guest when the CLI routes through avocado-vm. When Avocado.app launches QEMU directly the SSH-mount step would be skipped, leaving /run/workspace as an empty tmpfs scaffold so every Docker bind under $HOME silently mounted nothing — surfacing as Compile script not found with an empty ls /opt/src inside the build container.