vmix.nix/lib/images/windows/helpers/makeImage.nix
Git Sagar 97fdbce6d0 windows images: stop a non-matching display glob from poisoning the build env
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
2026-09-10 00:06:07 -03:00

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; }