Skip to content

feat: Windows VM phase 3 β€” golden image automation, CI guide, examples#115

Merged
Miyamura80 merged 3 commits intomasterfrom
feat/windows-vm-phase3
Apr 3, 2026
Merged

feat: Windows VM phase 3 β€” golden image automation, CI guide, examples#115
Miyamura80 merged 3 commits intomasterfrom
feat/windows-vm-phase3

Conversation

@Eito-Test-Account
Copy link
Copy Markdown
Contributor

Summary

  • desktest init-windows command: Two-stage golden image builder β€” Stage 1 installs Windows 11 from ISO via Autounattend.xml (unattended, UEFI GPT, VirtIO drivers, TPM bypass, OpenSSH, auto-login), Stage 2 provisions dependencies via SSH (Python 3, PyAutoGUI, uiautomation, WinFsp + VirtIO-FS, agent scripts, scheduled task, system hardening)
  • CI/CD integration guide (dev-docs/windows-ci-guide.md): GitHub Actions workflow YAML with explicit KVM/runner limitations, self-hosted runner setup, cloud nested virtualization matrix, golden image caching strategies, troubleshooting
  • Windows Calculator example (examples/windows-calculator.json): Basic test exercising Calculator app interaction in a QEMU/KVM VM
  • Documentation updates: All markdown files updated to reflect Windows support alongside macOS β€” README, CLAUDE.md, docs/ci.md, docs/macos-support.md, examples/README.md, skills/desktest-skill.md

New files

File Purpose
src/init_windows.rs Two-stage golden image provisioning (QEMU + SSH)
windows/Autounattend.xml Unattended Windows 11 install config
windows/provision.ps1 PowerShell provisioning script
dev-docs/windows-ci-guide.md CI/CD integration guide
examples/windows-calculator.json Calculator test example

Modified files

File Changes
src/cli.rs InitWindows subcommand
src/main.rs mod init_windows + dispatch
src/warnings.rs warn_init_windows_resources()
src/preflight.rs Windows VM status in desktest doctor
dev-docs/windows-support-plan.md Phase 3 marked complete
CLAUDE.md, README.md, docs/ci.md, docs/macos-support.md, examples/README.md, skills/desktest-skill.md Windows documentation

Test plan

  • cargo build compiles cleanly (verified)
  • desktest --help shows init-windows subcommand
  • desktest init-windows --help shows all options (windows-iso, virtio-iso, output, disk-size, ram, cpus)
  • desktest doctor shows Windows VM status line
  • desktest validate examples/windows-calculator.json passes
  • End-to-end init-windows on a Linux host with KVM (requires Windows ISO + VirtIO ISO)

πŸ€– Generated with Claude Code

Edison and others added 2 commits April 3, 2026 11:28
Two-stage `desktest init-windows` command that takes a Windows ISO + VirtIO
driver ISO and produces a ready-to-use QCOW2 golden image:
- Stage 1: Unattended Windows install via Autounattend.xml (UEFI GPT, VirtIO
  drivers, TPM bypass, OpenSSH, auto-login, OOBE skip)
- Stage 2: SSH provisioning via provision.ps1 (Python 3, PyAutoGUI,
  uiautomation, WinFsp + VirtIO-FS mount, agent scripts, scheduled task,
  disabled UAC/Defender/Updates)

Also adds CI/CD integration guide with GitHub Actions workflow YAML (annotated
with KVM/runner limitations), Windows Calculator test example, and updates all
markdown documentation to reflect Windows support alongside macOS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce InitConfig struct to reduce stage1_install parameter count
from 9 to 1, and remove unnecessary borrow on ssh_opts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 3, 2026

Greptile Summary

This PR completes Phase 3 of Windows VM support for desktest: it adds the desktest init-windows command, the unattended Windows 11 installation config (Autounattend.xml), the PowerShell provisioning script (provision.ps1), a CI/CD guide, and a Windows Calculator example. The implementation follows the established two-stage pattern used by init-macos and integrates cleanly with the existing session abstraction and preflight system.

Key changes:

  • src/init_windows.rs β€” 773-line two-stage provisioner: Stage 1 uses QEMU + secondary ISO with Autounattend.xml for unattended OS install; Stage 2 SSHes into the provisioned VM to run provision.ps1 and deploy agent scripts
  • windows/Autounattend.xml β€” UEFI GPT layout, VirtIO driver loading, TPM bypass, OpenSSH Server enablement, auto-login, and Stage 1 shutdown signal
  • windows/provision.ps1 β€” Python 3, PyAutoGUI, WinFsp, vm-agent scheduled task, system hardening
  • src/cli.rs + src/main.rs β€” InitWindows subcommand wired up
  • src/preflight.rs + src/warnings.rs β€” desktest doctor and pre-run warnings for Windows VM
  • Documentation updates across README, CLAUDE.md, docs/, examples/

All three findings are P2 (style/UX) with no correctness or data-integrity impact.

Confidence Score: 5/5

Safe to merge β€” all remaining findings are P2 style/UX improvements with no correctness or data-integrity impact.

The implementation is thorough and well-structured: prerequisite checks run before any side effects, child processes are cleaned up in both success and failure paths, the two-stage flow is clearly separated, and error messages are actionable. All three comments are P2: the example evaluator mismatch is cosmetic, the 120-second post-failure wait is a UX delay not a correctness bug, and the port-2222 conflict surfaces eventually via SSH timeout rather than immediately.

examples/windows-calculator.json (evaluator mismatch), src/init_windows.rs lines 429-439 (unconditional shutdown wait on failure)

Important Files Changed

Filename Overview
src/init_windows.rs Core two-stage provisioner: well-structured, clean error propagation, and proper child-process cleanup. Minor issue: a provisioning failure always incurs a 120-second wait because the shutdown guard is unconditional.
windows/Autounattend.xml Correct UEFI GPT layout, VirtIO driver paths, hardware-check bypasses, and Stage-1 shutdown signal. Hardcoded credentials are intentional for a test VM.
windows/provision.ps1 Comprehensive provisioning script covering Python, pip packages, WinFsp, VirtIO-FS mount config, scheduled task, and system hardening. Hardcoded versions and credential stores are expected for golden-image tooling.
examples/windows-calculator.json The evaluator only checks whether Calculator is running (process ID exists), but the stated goal is to verify that the computation result shows 100 β€” the metric doesn't actually validate the result.
src/cli.rs Clean InitWindows subcommand addition with comprehensive help text and all required arguments.
src/preflight.rs Adds informational Windows VM status line to desktest doctor, gracefully skipped on non-Linux hosts.
src/warnings.rs Adds warn_init_windows_resources() consistent with the existing warn_init_macos_resources() pattern.
dev-docs/windows-ci-guide.md Well-written CI guide with explicit KVM limitations, example workflow, self-hosted runner setup, cloud provider matrix, caching strategies, and troubleshooting table.
src/main.rs Adds mod init_windows and Command::InitWindows dispatch, consistent with how InitMacos is handled.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI as desktest CLI
    participant S1 as Stage 1 (QEMU install)
    participant S2 as Stage 2 (QEMU + SSH)
    participant Win as Windows Guest

    User->>CLI: desktest init-windows --windows-iso ... --virtio-iso ...
    CLI->>CLI: preflight checks (QEMU, OVMF, swtpm, genisoimage, sshpass)
    CLI->>CLI: validate ISO paths, check output doesn't exist
    CLI->>CLI: find windows/ dir (scripts + Autounattend.xml)
    CLI->>CLI: create temp work_dir

    note over CLI,S1: Stage 1 β€” unattended OS install
    CLI->>S1: spawn swtpm (TPM emulator)
    CLI->>S1: genisoimage β†’ autounattend.iso
    CLI->>S1: qemu-img create β†’ output.qcow2
    CLI->>S1: spawn QEMU (Windows ISO + VirtIO ISO + autounattend.iso)
    S1->>Win: boot from CD-ROM
    Win->>Win: Autounattend.xml: partition, install OS, load VirtIO drivers
    Win->>Win: Enable OpenSSH Server, disable Defender
    Win->>Win: FirstLogonCommands: disable UAC, screensaver, set resolution
    Win->>S1: shutdown /s (signals Stage 1 complete)
    S1->>CLI: QEMU exits
    CLI->>CLI: kill swtpm

    note over CLI,S2: Stage 2 β€” SSH provisioning
    CLI->>S2: spawn swtpm (new TPM state)
    CLI->>S2: spawn QEMU (output.qcow2, hostfwd tcp::2222-:22)
    S2->>Win: boot installed Windows
    CLI->>CLI: wait_for_ssh() β€” poll localhost:2222 (3 min timeout)
    CLI->>Win: sshpass/scp β€” copy vm-agent.py, execute-action.py, get-a11y-tree.py, win-screenshot.py
    CLI->>Win: sshpass/scp β€” copy provision.ps1
    CLI->>Win: ssh β€” powershell -File C:\\Temp\\provision.ps1
    Win->>Win: install Python 3, pip packages, WinFsp
    Win->>Win: configure VirtIO-FS (Z:\\), deploy scripts, register scheduled task
    Win->>Win: disable UAC, Update, screensaver, configure auto-login
    Win->>S2: shutdown /s (signals Stage 2 complete, SSH exits 255)
    S2->>CLI: QEMU exits
    CLI->>CLI: kill swtpm, clean up work_dir
    CLI->>User: Golden image ready!
Loading

Reviews (2): Last reviewed commit: "fix: address PR review feedback β€” OVMF_V..." | Re-trigger Greptile

…blocking

- Reuse Stage 1's OVMF_VARS.fd in Stage 2 instead of copying a fresh
  template. The Windows installer writes EFI boot entries into this file;
  discarding it forces a slower UEFI fallback scan or potential boot failure.
- Switch run_sshpass() from .status() to .output() so stderr is captured
  and included in error messages, making provisioning failures diagnosable.
- Wrap check_iso_builder() and check_sshpass_installed() with
  spawn_blocking() to avoid blocking the Tokio worker thread.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Eito-Test-Account
Copy link
Copy Markdown
Contributor Author

@greptileai

1 similar comment
@Eito-Test-Account
Copy link
Copy Markdown
Contributor Author

@greptileai

@Miyamura80 Miyamura80 merged commit 805575c into master Apr 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants