Image builds have been dying with "Argument list too long" from sed, mktemp and timeout alike -- commands whose argv is trivial, which is the tell that it was the environment that had grown, not the arguments. The X11 forwarding hint is looked up with `ls -t /tmp/.vmix-display-* | head -1`. Under nullglob a non-matching pattern is removed from the command line rather than passed through literally, so `ls -t` runs with no arguments at all and lists the working directory instead. In a nix build that directory is the build tree, whose newest file is nix's own env-vars dump. The result is that VMIX_DF becomes "env-vars", the SDL branch is taken on a machine with no X at all, and DISPLAY is exported with a slice of the env dump inside it. From that line onward every exec in the build fails with E2BIG. find does the same lookup without depending on how the shell treats an unmatched pattern, and -type f keeps a stray directory out of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117qMyjpuXsjpVAcpJbFD8g
93 lines
3.6 KiB
Nix
93 lines
3.6 KiB
Nix
# Build a pre-installed Windows qcow2 image using QEMU unattended install.
|
|
# Boots into Audit Mode after install and shuts down.
|
|
# Apply templates via customizeImageFold to install software (auditScript)
|
|
# and customize registry (windowsRegistry), then generalize when done.
|
|
{ pkgs, lib, drivers, makeWinISO, makeAuditModeAutounattend, ... }:
|
|
{
|
|
name ? "windows",
|
|
upstreamISO,
|
|
productKey ? "",
|
|
imageIndex ? 1,
|
|
diskSize ? "64G",
|
|
bypassRequirements ? false,
|
|
locale ? "en-US",
|
|
efi ? true,
|
|
smp ? 4,
|
|
memSize ? 4096,
|
|
vncDisplay ? null, # e.g. ":10" to enable VNC on port 5910 for monitoring
|
|
useAHCI ? false, # use AHCI storage instead of VirtIO (for physical hardware deployment)
|
|
windowsVersionForVirtioDrivers ? "w10", # "w10", "w11", "2k22", "2k19", etc.
|
|
}:
|
|
let
|
|
AutounattendedXml = makeAuditModeAutounattend {
|
|
inherit locale productKey imageIndex
|
|
efi bypassRequirements windowsVersionForVirtioDrivers;
|
|
diskIndex = 0;
|
|
virtioDriverLetter = "E";
|
|
};
|
|
|
|
iso = makeWinISO {
|
|
iso = upstreamISO;
|
|
inherit AutounattendedXml;
|
|
};
|
|
|
|
displayArg = if vncDisplay != null then "-vnc ${vncDisplay}" else null;
|
|
|
|
drv = pkgs.runCommand "${name}-vmix.qcow2" {
|
|
__noChroot = true;
|
|
requiredSystemFeatures = [ "kvm" ];
|
|
nativeBuildInputs = with pkgs; [ qemu ];
|
|
} ''
|
|
echo "=== vmix: creating ${diskSize} disk ==="
|
|
qemu-img create -f qcow2 disk.qcow2 ${diskSize}
|
|
|
|
cp ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd vars.fd
|
|
chmod +w vars.fd
|
|
|
|
echo "=== vmix: installing ${name} (unattended, 5-10 min) ==="
|
|
|
|
VMIX_DISPLAY="-nographic"
|
|
${lib.optionalString (displayArg != null) ''VMIX_DISPLAY="${displayArg}"''}
|
|
${lib.optionalString (displayArg == null) ''
|
|
# find, not a glob: under nullglob a non-matching /tmp/.vmix-display-*
|
|
# disappears entirely, leaving `ls -t` to list the build directory and
|
|
# hand back nix's own env-vars dump. Exporting that as DISPLAY bloats the
|
|
# environment until every exec dies with E2BIG.
|
|
VMIX_DF=$(find /tmp -maxdepth 1 -type f -name '.vmix-display-*' -printf '%T@ %p\n' 2>/dev/null | sort -rn | head -1 | cut -d' ' -f2-)
|
|
if [ -n "$VMIX_DF" ]; then
|
|
export DISPLAY=$(sed -n '1p' "$VMIX_DF")
|
|
export XAUTHORITY=$(sed -n '2p' "$VMIX_DF")
|
|
export HOME=$(mktemp -d)
|
|
export XDG_RUNTIME_DIR=$HOME
|
|
export SDL_VIDEODRIVER=x11
|
|
VMIX_DISPLAY="-display sdl"
|
|
fi
|
|
''}
|
|
|
|
# Windows installs unattended, reboots into Audit Mode,
|
|
# deletes cached Autounattend, shuts down → QEMU exits.
|
|
QEMU_ARGS="-accel kvm -m ${toString memSize} -smp ${toString smp} -cpu host -machine type=q35 \
|
|
-drive if=pflash,format=raw,readonly=on,file=${pkgs.OVMF.fd}/FV/OVMF_CODE.fd \
|
|
-drive if=pflash,format=raw,file=vars.fd \
|
|
-rtc base=localtime,clock=host \
|
|
-device qemu-xhci -device usb-tablet \
|
|
${if useAHCI
|
|
then "-drive file=disk.qcow2,format=qcow2,if=none,id=disk0 -device ide-hd,drive=disk0"
|
|
else "-drive file=disk.qcow2,format=qcow2,if=virtio"} \
|
|
-drive file=${iso},media=cdrom,readonly=on \
|
|
-drive file=${drivers.virtio-iso},media=cdrom,readonly=on \
|
|
-nic user,model=${if useAHCI then "e1000" else "virtio-net-pci"}"
|
|
|
|
timeout 3600 qemu-system-x86_64 $VMIX_DISPLAY $QEMU_ARGS || \
|
|
if [[ "$VMIX_DISPLAY" == "-display sdl" ]]; then
|
|
echo "=== vmix: SDL failed, retrying headless ==="
|
|
cp ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd vars.fd && chmod +w vars.fd
|
|
timeout 3600 qemu-system-x86_64 -nographic $QEMU_ARGS
|
|
else
|
|
exit 1
|
|
fi
|
|
|
|
echo "=== vmix: ${name} install complete ==="
|
|
mv disk.qcow2 $out
|
|
'';
|
|
in drv // { _vmixOsType = "windows"; inherit useAHCI; }
|