diff --git a/run-windows11.py b/run-windows11.py index b67df7e..f15375d 100755 --- a/run-windows11.py +++ b/run-windows11.py @@ -81,13 +81,14 @@ daemonize = False install_from_iso = False install_from_img = False download_only = False +seed_only = False extra_args = [] arch_option = None def usage(stream=sys.stdout): stream.write(f"""\ -Usage: {Path(sys.argv[0]).name} [--daemon] [--download-only] [--install-from-iso | --install-from-img] +Usage: {Path(sys.argv[0]).name} [--daemon] [--download-only] [--seed-only] [--install-from-iso | --install-from-img] [--arch ARCH] [--state-dir DIR] [--rdp-port PORT] [--ssh-port PORT] [-- QEMU_ARGUMENT ...] If the system disk (DIR/disk.qcow2, see --state-dir) is missing, create it from @@ -106,6 +107,8 @@ Options: (for --arch) if they are missing, then exit; needs Node.js and Chromium for the latter, see vm-images/README.md (the base QCOW2 is supplied manually) + --seed-only Build the seed ISO (answer file and setup scripts) and exit, + without starting the VM or fetching the Windows and VirtIO ISOs --install-from-iso Install from ISO when the system disk is missing --install-from-img Like --install-from-iso, but install from the disk image made by vm-images/build-windows-install-disk.sh, which @@ -150,6 +153,8 @@ while args: daemonize = True elif arg == "--download-only": download_only = True + elif arg == "--seed-only": + seed_only = True elif arg == "--install-from-iso": install_from_iso = True elif arg == "--install-from-img": @@ -277,6 +282,39 @@ monitor_socket = state_dir / "qemu-monitor.sock" guest_agent_socket = state_dir / "qemu-guest-agent.sock" qemu_config = state_dir / "qemu.ini" + +def build_seed_iso(image_downloader, force=False): + """Build the seed ISO, unless it is newer than all of its sources and not forced.""" + seed_dir = script_dir / "seediso" / "windows11" + seed_sources = [seed_dir / "Autounattend.xml", seed_dir / "Configure-Windows.ps1", + *sorted(seed_dir.glob("[0-9][0-9]-*.bat"))] + + # build-seed.sh embeds the administrator SSH public key. + ssh_key = os.environ.get("SSH_PUBLIC_KEY_FILE") or next( + (str(k) for n in ("id_ed25519", "id_ecdsa", "id_rsa") + if (k := Path.home() / ".ssh" / f"{n}.pub").is_file()), None) + if ssh_key: + seed_sources.append(Path(ssh_key)) + + # The seed carries the OpenSSH zip for this architecture; build-seed.sh + # downloads it when missing. + seed_sources.append(Path(capture( + [str(image_downloader), "--print-path", "openssh-win"]))) + + def newer_than_seed(source): + # Mirrors bash `-nt`: true if source exists and is newer than seed_iso. + return source.exists() and source.stat().st_mtime > seed_iso.stat().st_mtime + + if force or not seed_iso.is_file() or not seed_sources[-1].is_file() \ + or any(newer_than_seed(s) for s in seed_sources): + run([str(script_dir / "seediso" / "windows11" / "build-seed.sh"), str(seed_iso), host_arch]) + + +if seed_only: + build_seed_iso(find_image_downloader(), force=True) + print(f"Seed ISO is ready: {seed_iso}") + sys.exit(0) + installing = False installing_from_iso = False if not disk_image.is_file(): @@ -326,29 +364,7 @@ if installing_from_iso: if not tools_iso.is_file(): die(f"downloader did not create: {tools_iso}") - seed_dir = script_dir / "seediso" / "windows11" - seed_sources = [seed_dir / "Autounattend.xml", seed_dir / "Configure-Windows.ps1", - *sorted(seed_dir.glob("[0-9][0-9]-*.bat"))] - - # build-seed.sh embeds the administrator SSH public key. - ssh_key = os.environ.get("SSH_PUBLIC_KEY_FILE") or next( - (str(k) for n in ("id_ed25519", "id_ecdsa", "id_rsa") - if (k := Path.home() / ".ssh" / f"{n}.pub").is_file()), None) - if ssh_key: - seed_sources.append(Path(ssh_key)) - - # The seed carries the OpenSSH zip for this architecture; build-seed.sh - # downloads it when missing. - seed_sources.append(Path(capture( - [str(image_downloader), "--print-path", "openssh-win"]))) - - def newer_than_seed(source): - # Mirrors bash `-nt`: true if source exists and is newer than seed_iso. - return source.exists() and source.stat().st_mtime > seed_iso.stat().st_mtime - - if not seed_iso.is_file() or not seed_sources[-1].is_file() \ - or any(newer_than_seed(s) for s in seed_sources): - run([str(script_dir / "seediso" / "windows11" / "build-seed.sh"), str(seed_iso), host_arch]) + build_seed_iso(image_downloader) if pid_file.is_file(): old_pid = pid_file.read_text().strip()