vmix.nix/lib/images/macos/templates/software/default.nix
Git Sagar 0f9373263d macOS: guest agent, online templates, virtio-fs shares, persistent home volume
Apple's built-in QEMU guest agent (AppleQEMUGuestAgent, launched by launchd
when a virtio console port org.qemu.guest_agent.0 appears; guest-exec as root)
is attached by vmix run --macos and the NixOS module. AppleVirtIO.kext on x86
Tahoe drives virtio-fs, block, console, input, net — verified in QEMU.

- customizeImage: `bootScript` — online step through the guest agent (driver
  mode qga): boot the image, run the script as root with the VMIX volume, shut
  down through the agent. `as_user` runs commands in the logged-in session.
- templates.software: pkg/app (offline in the PE), script/homebrew (online).
- templates.profile.settings: widgets, wallpaper (pinned desktoppr — Apple
  Events need TCC consent that a headless session cannot give), dock apps,
  autohide, dark mode, hidden files.
- generalize: persistHome (fstab LABEL=vmix-home /Users), hideWidgets offline.
- formatVolume: formats a blank disk image as APFS by booting the PE (~35 s);
  idempotent.
- NixOS module: macos.guestAgent (/run/vmix/qga-<name>.sock), shares via
  virtiofsd + vhost-user-fs (Apple automount tag for the first share, others
  mounted through the agent), macos.homeDisk (created + formatted on first
  start, virtio-blk), SPICE keeps -vga vmware for macOS.
- CLI: vmix run --macos --share DIR --home FILE --qga PATH.
- qemu.nix helpers; README section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsESshRCoBoUVWV9qKURUF
2026-09-10 13:34:28 -03:00

51 lines
2.3 KiB
Nix

# Software installation templates.
# pkg — install a flat/distribution .pkg offline from the PE (`installer -target`)
# app — copy an .app bundle (from a directory or zip) into /Applications offline
# script — run a shell script as root on the booted image (network available)
{ pkgs, lib, ... }:
rec {
pkg = { name, src, choices ? null }: {
name = "pkg-${name}";
files = [ { source = src; name = "${name}.pkg"; } ]
++ lib.optional (choices != null) { source = choices; name = "${name}.choices.xml"; };
script = ''
echo "vmix: installing ${name}.pkg into $SYS"
installer -verboseR -pkg "$V/${name}.pkg" -target "$SYS" \
${lib.optionalString (choices != null) ''-applyChoiceChangesXML "$V/${name}.choices.xml"''} \
|| pe_fail "installer ${name}.pkg"
'';
};
app = { name, src }: {
name = "app-${name}";
files = [ { source = src; name = "${name}.app"; } ];
script = ''
echo "vmix: copying ${name}.app to $DATA/Applications"
mkdir -p "$DATA/Applications"
rm -rf "$DATA/Applications/${name}.app"
ditto "$V/${name}.app" "$DATA/Applications/${name}.app" || pe_fail "ditto ${name}.app"
chown -R 0:80 "$DATA/Applications/${name}.app"
xattr -dr com.apple.quarantine "$DATA/Applications/${name}.app" 2>/dev/null || true
'';
};
script = { name, script, files ? [], network ? true }: {
name = "script-${name}";
inherit files network;
bootScript = script;
};
# Homebrew (needs network; installs for the console user or the given user)
homebrew = { user ? null, formulae ? [], casks ? [] }: {
name = "homebrew";
bootScript = ''
U=${if user == null then "$CONSOLE_USER" else user}
[ -n "$U" ] || { echo "vmix: no user to install Homebrew for"; exit 1; }
launchctl asuser "$(id -u "$U")" sudo -u "$U" env NONINTERACTIVE=1 \
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || exit 1
B=/usr/local/bin/brew
${lib.optionalString (formulae != []) ''launchctl asuser "$(id -u "$U")" sudo -u "$U" $B install ${lib.escapeShellArgs formulae} || exit 1''}
${lib.optionalString (casks != []) ''launchctl asuser "$(id -u "$U")" sudo -u "$U" $B install --cask ${lib.escapeShellArgs casks} || exit 1''}
'';
};
}