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
51 lines
2.3 KiB
Nix
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''}
|
|
'';
|
|
};
|
|
}
|