The snapshot commands could only name a snapshot and could not say anything
about it. A script could not tell when a snapshot was taken, how much it
holds, whether restoring it resumes the guest or starts it up, or whether
its data has gone missing, and there was no way to rename one or to throw
away a suspended state, both of which the window offers.
Make a snapshot an object instead. A virtual machine now has snapshots as
elements, each carrying those details, so a script can pick one out and
read it:
tell application "UTM"
set vm to virtual machine named "Debian"
set candidates to (snapshots of vm whose includes running state)
restore (item 1 of candidates)
end tell
Renaming is setting the name, and the commands that act on a snapshot take
the snapshot itself. Creating one returns it. Two more commands cover what
the window can do and scripting could not: scanning the images for
snapshots made by other tools, and discarding a saved state so the guest
starts up instead of resuming.
This replaces the snapshot commands of the 5.0.5 beta, which addressed a
snapshot by name through its virtual machine, so scripts and utmctl calls
written against them have to be updated.
Snapshots are addressed by name, so names are now kept apart the way
virtual machine names are, by appending a number.
Deleting a snapshot is its own command rather than the standard delete,
which coerces a specifier for anything inside a virtual machine into the
virtual machine itself; deleting that would throw away far more than was
asked for. The delete command refuses such a specifier for the same reason.
UTMScripting.swift is regenerated from the sdef; only the hunks that
changed are taken.
Assisted-by: Claude:claude-opus-5
Snapshots were read straight out of the QEMU images by the scripting
commands, which only worked while the VM was off and knew nothing beyond
the tags in the image.
Put a backend behind the operations instead. Which one is chosen depends on
whether the VM is running, and each says what it can do in that state:
- A running VM goes through the QEMU monitor, which saves and loads the
whole machine along with the disks.
- A VM that is off goes through qemu-img, which can only save and revert
disk contents. Nothing may touch the images while QEMU has them open, so
a snapshot of disks alone is left alone until the VM stops.
Either way a snapshot spans every writable image, the UEFI variable store
included, because QEMU refuses to load one that any of them is missing. A
snapshot that some of them lack, such as one taken before a drive was added,
is kept but cannot be restored, which is not the same as its state being
gone: reverting only some of the drives would leave them at different points
in time. Reclaiming space rebuilds an image without its snapshots, so it is
refused while there are any.
UTMSnapshotService sits above the backend and keeps the manifest and the
stored state consistent: it marks entries whose state has disappeared when
an operation fails, and the first time a VM is opened it adopts whatever
snapshots are already in the images so that snapshots taken by other tools
are not lost. Both wait until the VM is stopped, because a running QEMU
lists the snapshots of every drive together, removable ones included.
Suspending is folded into the same mechanism. A VM now suspends to a
snapshot of its own rather than always the same name, so a suspended state
can be kept as a snapshot without copying it, and restoring a snapshot
makes the VM resume from it. The state is deleted once nothing refers to
it, where it used to be left behind in the image. One that cannot be deleted
right away stays listed in the manifest and is tried again the next time.
Assisted-by: Claude:claude-fable-5-1
Assisted-by: Claude:claude-opus-5
macOS 27 can create the guest's user account, turn on automatic login and
Remote Login without going through Setup Assistant, which lets CI and other
automation get a ready-to-use macOS VM. The guest only reads these options
on the first boot after macOS is installed and they include a password, so
they are passed as a parameter to a single start and are never saved to the
configuration or the registry.
This is only exposed to AppleScript (`start ... provisioning {...}`) and
utmctl (`start --provision-*`) because most users will never need it. utmctl
can read the password from standard input to keep it out of the process
list. Requests that cannot be honoured (not a macOS guest, host older than
macOS 27, install still pending, saved state, recovery mode, invalid account)
fail before the VM starts so the first boot is not used up.
UTMScripting.swift has only the regenerated `start` declaration. The rest of
the current bridge-gen.sh output is unrelated to this change.
Assisted-by: Claude:claude-fable-5-1
A macOS install takes several minutes, well past the default two minute
Apple Event timeout. A plain `install` therefore failed with -1712 while
the installation kept running, and a script reacting to that error by
retrying or starting the VM would race the install.
`install` now returns as soon as the installation starts, and the new
`query install` verb reports whether it is still running and how far it
has progressed, or raises the error if it failed. The installation only
counts as finished once the VM has stopped by itself, so the VM can be
started right away.
Other verbs fail with a clear error while an installation started from
a script is in progress. This includes `start`, which would otherwise
open a window that asks to install macOS again. As in the GUI, `stop`
cancels the installation.
Assisted-by: Claude:claude-opus-5
VMDisplayAppleWindowController.windowDidLoad shows a confirmation sheet
before calling requestInstallVM, so a macOS install cannot be driven from
a script without someone clicking OK.
This does not remove the sheet. It guards a genuinely destructive action
(it erases the primary drive) and a GUI user who opens a VM that happens
to carry an IPSW should keep being asked. It is also structurally
unavailable to a script: showConfirmAlert calls
beginSheetModal(for: window!), so it needs a window, and the scripted
`start` verb calls data.run(...), which loads the window controller and
raises the sheet in the first place.
So this adds a separate `install` verb that goes straight to
installVM(with:) and never opens a window. The confirmation moves rather
than disappearing: asking for `install` is the confirmation, which is why
the sdef entry says DESTRUCTIVE in as many words. This matches the
CONTRIBUTING.md guidance about putting things most users do not need into
the scripting interface rather than the UI.
It also clears the IPSW on success, as the GUI does in
didCompleteInstallation. That clearing lives in the window controller's
delegate callback, and a scripted install has no window, so without it
the IPSW outlives the install and the next `start` that does open a
display window asks "Would you like to install macOS?" over a guest that
is already installed. Measured before the fix: the registry still held
the IPSW after a completed scripted install.
Two guards, because installVM returns silently when the VM is not
stopped and calling it unguarded would report success having done
nothing: a VM with no IPSW configured, and a VM that is not stopped, each
fail with an error naming the cause.
Verified against main on Xcode 26.6 / Apple Silicon: no confirmation
sheet at any point, a macOS 26.6.2 install run to completion (21 GB,
framework returned success), the IPSW cleared from the registry
afterwards, and a subsequent `start` opening a display window with no
reinstall prompt and the guest booting to Setup Assistant.
UTMScripting.swift gains the install() method that bridge-gen.sh
generates for this sdef change.
Refs #6092
Assisted-by: Claude:claude-opus-5
Expose named create, list, restore, and delete operations through AppleScript and utmctl so automation can manage QEMU full-VM snapshots. QEMU image metadata remains the source of truth, the internal suspend snapshot stays hidden and reserved, and Apple virtualization reports the feature as unsupported until it can preserve equivalent VM state reliably.
Assisted-by: Codex:gpt-5
The registry suite is used to read and update vm registry.
All configs saved to disk is exposed by Configuration suite,
similarly those that are not saved to disk are stored in RegistryEntry
will now be exposed by Registry suite.
- UTMScriptingRegistryEntryImpl will control what will be exposed from RegistryEntry to scripting bridge, currently only shared directory is exposed in the form of [URL]
- expose UTMQemuVirtualMachine.system as public to be used in Scripting
- Add UTMRegistryEntry.appendSharedDirectory to support multiple directory shares.