From cb247b9cc3dddd8590d61b1028c7e20337d60577 Mon Sep 17 00:00:00 2001 From: rickpeters Date: Tue, 29 Sep 2026 15:40:03 +0200 Subject: [PATCH] docs: wsl-build-host skill, vminstall.sh without udisks (WSL), AGENTS.md/TODO WSL notes corrected The WSL box has a desktop after all (WSLg windows on Windows), closing the Arch window shuts it down, and vminstall.sh loop-mounts the ISO with losetup as root when udisksctl is missing (R1c). --- .claude/skills/wsl-build-host/SKILL.md | 101 +++++++++++++++++++++++++ AGENTS.md | 8 +- TODO.md | 8 +- scripts/vminstall.sh | 27 ++++--- 4 files changed, 127 insertions(+), 17 deletions(-) create mode 100644 .claude/skills/wsl-build-host/SKILL.md diff --git a/.claude/skills/wsl-build-host/SKILL.md b/.claude/skills/wsl-build-host/SKILL.md new file mode 100644 index 0000000..c620274 --- /dev/null +++ b/.claude/skills/wsl-build-host/SKILL.md @@ -0,0 +1,101 @@ +--- +name: wsl-build-host +description: Use when building or testing the Steam Machine ISO, or running the VM tests (vmtest.sh), on the user's Windows PC - Arch Linux in WSL2 (`ssh wsl`), with podman, QEMU + KVM and WSLg windows on the Windows desktop. Covers access, syncing working trees from the laptop, the podman ISO build as root, VM windows vs headless, showing output in the user's WSL terminal, and the WSL pitfalls (closing the window kills it, no udisks, no GPU passthrough). +--- + +# WSL build host (Arch on WSL2, the Windows PC) + +The laptop is an arm64 Mac: x86_64 QEMU or containers there run emulated, +far too slow for Plasma/Calamares or an ISO build. The PC is the x86 host: +same workflows as `steam-machine-iso`, `vm-install` and `vmtest.sh`, with +the differences below. Found and tried on 2026-09-28 (ISO build, ISO VM with +a window, the loop-mount fallback); `vmtest.sh` itself not yet run here. + +## Access + +- `ssh wsl` = `root@10.0.0.36`, port 22 (alias in the laptop's + `~/.ssh/config`). That's Arch's own sshd inside WSL, not Windows' OpenSSH. +- `%UserProfile%\.wslconfig`: `networkingMode=mirrored` (WSL shares the PC's + LAN IP), `nestedVirtualization=true` (`/dev/kvm`), `memory=` (WSL2 defaults + to half the RAM: 30 GB seen). `/etc/wsl.conf`: `[boot] systemd=true`. +- Installed: podman, qemu-full, edk2-ovmf (`/usr/share/edk2/x64/OVMF*.4m.fd`, + run.sh finds it), git, rsync, python, python-pillow (`qmpshot.py` writes a + .ppm without it). vmtest.sh also wants `screen`. +- **The Arch window must stay open.** Closing the last WSL terminal shuts + the distro down a little later, with every build, VM and sshd in it: SSH + times out while the PC still pings. Ask the user to keep it open during + runs and to reopen it after a Windows reboot. After such a restart, delete + half-written VM disks before rerunning (`vminstall.sh` refuses an + existing `disk.qcow2`, and `--force` asks). + +## Differences from a normal Linux host + +- User is **root**: no `sudo`, `systemd-run` without `--user`, paths under + `/root/projects/` and `/root/vms/`. Units have no `$HOME`: set `HOME=/root` + for scripts started with `systemd-run` (vminstall.sh needs it). +- No udisks: `vminstall.sh` falls back to `losetup -P` + `mount` when + `udisksctl` is missing and it runs as root. Don't install udisks2 here. +- `pkill -x qemu-system-x86_64` never matches (names over 15 characters): + `pkill -f '^[q]emu-system'`. + +## Getting the code there + +The WSL clones have no GitHub key: sync the laptop's working trees +(uncommitted changes included) instead of pulling. `vmtest-remote.sh` +pulls the pushed branch on the remote, so it needs a GitHub key there first +(and `REMOTE_REPO=/root/projects/steamify-cachyos-dev`). + +```bash +cd ~/Projects && rsync -az --exclude out --exclude build \ + steammachine-cachyos-live-iso steamify-cachyos steamify-cachyos-dev wsl:projects/ +ssh wsl 'find ~/projects -name __pycache__ -prune -exec rm -rf {} +; chown -R root: ~/projects' +``` + +Both fixups are needed: rsync keeps the laptop's uid (git then refuses with +"dubious ownership"), and a synced `patches/__pycache__` breaks +`steamify-prepare.sh` ("Is a directory"). + +## Building the ISO (~8 minutes) + +```bash +ssh wsl 'cd ~/projects/steammachine-cachyos-live-iso && ./steamify-prepare.sh ~/projects/steamify-cachyos' +ssh wsl 'cd ~/projects/steammachine-cachyos-live-iso && rm -rf build out && systemd-run --collect -q -u isobuild-$(date +%s) --working-directory=$PWD bash -c "podman run --rm -t --pids-limit=-1 --ulimit nofile=65536:65536 --privileged --network=host -v /root/projects/iso-cache:/var/cache/pacman/pkg -v $PWD:/iso -w /iso docker.io/cachyos/cachyos:latest bash -c '\''pacman-key --init && pacman-key --populate && pacman -Syu --noconfirm --needed archiso mkinitcpio-archiso git squashfs-tools grub sudo && ./build-live-modules.sh && ./build-calamares-modules.sh && { ./buildiso.sh -p desktop -w || ./buildiso.sh -p desktop -c -w; }'\'' > /root/projects/iso-build.log 2>&1"' +``` + +Wait in one background command, not a polling loop: +`ssh wsl 'until ! podman ps -q | grep -q .; do sleep 20; done; tail -c 1500 ~/projects/iso-build.log; ls -la ~/projects/steammachine-cachyos-live-iso/out/desktop/'`. +Never start a build while `podman ps` shows one. Output: +`/root/projects/steammachine-cachyos-live-iso/out/desktop/*.iso` (from +Windows Explorer: `\\wsl$\\root\projects\...`). The trailing +`chown: missing operand` / "unknown error" is harmless. mksquashfs shows no +progress in the log; the growing `build/iso/arch/x86_64/airootfs.sfs` +(~3.1 GB when done) is the measure. + +## VM windows on the Windows desktop (WSLg) + +There is a desktop after all: WSLg (`/tmp/.X11-unix/X0`, +`/mnt/wslg/runtime-dir/wayland-0`) puts QEMU's GTK window on the user's +Windows desktop. SSH sessions and units lack the variables, so set them: +`DISPLAY=:0 WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/mnt/wslg/runtime-dir`. + +- A VM for the user to watch or drive: that env plus e.g. + `VM_SERIAL=/root/vms/iso-vm/serial ISO_VM_DIR=/root/vms/iso-vm scripts/vmisoboot.sh --fresh --iso `. + The user may click through it themselves: check the disk size / serial + log before assuming it still sits at the live session. +- Automated runs stay headless (`--headless` / `VM_HEADLESS=1`, vmtest.sh's + default); `qmpshot.py` works then. +- GPU: no passthrough of the NVIDIA card. WSL2 only has the paravirtual + `/dev/dxg`, no PCI device for VFIO, and Windows keeps driving the card. + run.sh's default virgl (`virtio-vga-gl`, `gl=on`) renders through WSLg's + d3d12 Mesa on it, but QMP screenshots then give "no surface". + +## Showing output in the user's WSL terminal + +Their visible terminal is the `-bash` whose parent is WSL's `/init` +(`ps -eo pid,ppid,tty,args --forest`), usually `/dev/pts/0`. Not the +`login -- root` one: a hidden console getty. Stream a log into it as a unit +so it can be stopped: +`systemd-run --collect -q -u showlog bash -c "tail -n 20 -F > /dev/pts/0 2>&1"`; +stop with `systemctl stop showlog` (Ctrl+C there doesn't). Tell the user +not to type there while it runs. A header printed before a busy log scrolls +away at once. diff --git a/AGENTS.md b/AGENTS.md index 4d35f1b..093c5a3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,14 +32,14 @@ skills in `.claude/skills/` (vm-install, cachyos-vm-testing, steam-machine-testi - Written but not proven as a whole: the speed-ups (base image + qcow2 overlays, own ssh port per suite, package cache, `VM_MEM=4G`, `MAX_PARALLEL=3`); a complete parallel run has never finished. First job on a new machine: one full `scripts/vmtest.sh --screen`, read `~/vms/last-test.txt`, fix what fails. -- Open work: `TODO.md` (WSL2 setup R1b/R1c, the GitHub Actions workflow, speed P1-P3). +- Open work: `TODO.md` (WSL2 setup R1b, the GitHub Actions workflow, speed P1-P3). ## Where the tests run The tests run on the user's PC (Ryzen 9800X3D, 64 GB, **WSL2**), started from a laptop over ssh (the user's own skill, or `scripts/vmtest-remote.sh [args]`: pulls the pushed branch there, starts `vmtest.sh --screen`, waits for -`~/vms/last-test.txt`). On WSL check `/dev/kvm`, `.wslconfig` memory (WSL2 defaults to half the RAM), qemu + ovmf + screen, -`~/vms` on WSL's own ext4; `vminstall.sh` needs `udisksctl` (absent): use bsdtar/7z or copy the VM folders -(`~/vms/steamify-vm`, `bl-*`, ~35 GB). No desktop there: headless, no Konsole, `screen -r vmtest` to watch. +`~/vms/last-test.txt`). WSL specifics (access `ssh wsl` = root@10.0.0.36, keep the Arch window open or WSL shuts +down, syncing from the laptop, the podman ISO build, VM windows through WSLg, output in the user's WSL terminal, no GPU +passthrough): `.claude/skills/wsl-build-host/SKILL.md`. `vminstall.sh` works there without udisks (losetup as root). ## Problems found in the Steamify ISO (fix in `steammachine-cachyos-live-iso`, PRs into `feat/steamify`) - cachyos-installer leaves systemd-boot with `#timeout 3` and no default entry: a real machine waits in the menu for ever diff --git a/TODO.md b/TODO.md index 64a2fe1..b79986c 100644 --- a/TODO.md +++ b/TODO.md @@ -66,10 +66,10 @@ Where the time goes: boot loader VMs ~3 min each, every suite block restores a s - [ ] R1b. The PC is WSL2 (Windows). To check there: `/dev/kvm` exists (Windows 11, virtualization on in the BIOS, nested virtualization on); `%UserProfile%\.wslconfig` `memory=` is raised (WSL2 defaults to half the RAM = 32 GB; 3-4 VMs at 4-8 GB want ~48 GB) and `processors=16`; keep `~/vms` and the repos on WSL's own ext4, not under `/mnt/c`; - `sudo apt install qemu-system-x86 ovmf screen`; no desktop there, so runs are headless with no Konsole (`view_start` - returns early; watch with `screen -r vmtest` or `tail -f ~/vms/test.log`) -- [ ] R1c. `vminstall.sh` reads the ISO's kernel with `udisksctl`, which WSL doesn't have: use `bsdtar`/`7z` when there is no - udisks (same as CI step 2), or copy the VM directories (`~/vms/steamify-vm`, `bl-*`, ~35 GB) from the laptop with rsync + the distro is Arch (root): `pacman -S qemu-full edk2-ovmf screen`; automated runs are headless with no Konsole + (`view_start` returns early; watch with `screen -r vmtest` or `tail -f ~/vms/test.log`), a VM for the user gets a + window through WSLg (see the wsl-build-host skill). Still to do: a first full `vmtest.sh` run there +- [x] R1c. `vminstall.sh` without udisks (WSL): `losetup -P` + `mount` when `udisksctl` is missing and it runs as root - [ ] R2. With 64 GB and 8c/16t: try `MAX_PARALLEL=4` and 8 GB VMs (`VM_MEM=8G`) ## Boot loader test (local) diff --git a/scripts/vminstall.sh b/scripts/vminstall.sh index e421e9a..c508fe9 100755 --- a/scripts/vminstall.sh +++ b/scripts/vminstall.sh @@ -84,15 +84,24 @@ echo "ISO: $iso key: $VM_SSH_KEY" # --- The ISO's kernel and initramfs, to boot it with our parameters. boot="$VM_DIR/iso-boot" mkdir -p "$boot" -loop="$(udisksctl loop-setup --no-user-interaction -r -f "$iso" | grep -o '/dev/loop[0-9]*')" -cleanup_loop() { udisksctl unmount --no-user-interaction -b "${loop}p1" >/dev/null 2>&1 || true; udisksctl loop-delete --no-user-interaction -b "$loop" >/dev/null 2>&1 || true; } -trap cleanup_loop EXIT -mnt="" -for _ in $(seq 10); do - mnt="$(findmnt -nro TARGET "${loop}p1" 2>/dev/null || true)" - [[ -n "$mnt" ]] && break - udisksctl mount --no-user-interaction -b "${loop}p1" >/dev/null 2>&1 || true; sleep 1 -done +if ! command -v udisksctl >/dev/null && [[ $EUID -eq 0 ]]; then + # No udisks (the WSL box, as root): a plain loop device and mount. + loop="$(losetup -f -r -P --show "$iso")" + mnt="$(mktemp -d)" + cleanup_loop() { umount "$mnt" 2>/dev/null || true; rmdir "$mnt" 2>/dev/null || true; losetup -d "$loop" 2>/dev/null || true; } + trap cleanup_loop EXIT + mount -r "${loop}p1" "$mnt" 2>/dev/null || mnt="" +else + loop="$(udisksctl loop-setup --no-user-interaction -r -f "$iso" | grep -o '/dev/loop[0-9]*')" + cleanup_loop() { udisksctl unmount --no-user-interaction -b "${loop}p1" >/dev/null 2>&1 || true; udisksctl loop-delete --no-user-interaction -b "$loop" >/dev/null 2>&1 || true; } + trap cleanup_loop EXIT + mnt="" + for _ in $(seq 10); do + mnt="$(findmnt -nro TARGET "${loop}p1" 2>/dev/null || true)" + [[ -n "$mnt" ]] && break + udisksctl mount --no-user-interaction -b "${loop}p1" >/dev/null 2>&1 || true; sleep 1 + done +fi [[ -n "$mnt" ]] || { echo "Couldn't mount the ISO." >&2; exit 1; } rm -f "$boot"/* # copied read-only from the ISO install -m 644 "$mnt/arch/boot/x86_64/vmlinuz-linux-cachyos" "$mnt/arch/boot/x86_64/initramfs-linux-cachyos.img" "$boot/"