diff --git a/src/ci_orchestrator/commands/template.py b/src/ci_orchestrator/commands/template.py index 3bc15d4..59c35ae 100644 --- a/src/ci_orchestrator/commands/template.py +++ b/src/ci_orchestrator/commands/template.py @@ -432,10 +432,26 @@ def template_prepare_win( if not build_password: build_password = click.prompt(f"New {build_username} password", hide_input=True) - # --- Backend: start VM and detect IP --- + # --- Pre-flight snapshot conflict check (terminal still clean here) --- + # Must happen before any WinRM/pypsrp calls, which alter tty state and + # prevent readline() from receiving \n on Enter. config = load_config() backend = WorkstationVmrunBackend(vmrun_path=config.vmrun_path) handle = VmHandle(identifier=str(vmx_path), name=vmx_path.stem) + existing_snapshots = backend.list_snapshots(handle) + do_recreate_snapshot = True + if snapshot in existing_snapshots: + click.echo(f"[prepare-win] Snapshot '{snapshot}' already exists.") + if recreate_snapshot: + click.echo(f"[prepare-win] --recreate-snapshot set: will delete and recreate.") + else: + sys.stdout.write(f"[prepare-win] Delete and recreate '{snapshot}'? [y/N] ") + sys.stdout.flush() + raw = sys.stdin.readline() + do_recreate_snapshot = raw.strip().lower() in ("y", "yes") + if not do_recreate_snapshot: + click.echo("[prepare-win] Keeping existing snapshot. Provisioning aborted.") + return if not backend.is_running(handle): click.echo(f"[prepare-win] Starting VM: {vmx_path.name} ...") @@ -664,22 +680,10 @@ Register-ScheduledTask -TaskName 'CI-StaticIp' ` raise click.ClickException("VM did not power off within 120s.") click.echo("[prepare-win] VM powered off.") - # Create snapshot + # Create snapshot — conflict already resolved at pre-flight above. vmrun_exe = backend.vmrun_path - existing = backend.list_snapshots(handle) - if snapshot in existing: - click.echo(f"[prepare-win] Snapshot '{snapshot}' already exists.") - if recreate_snapshot: - do_recreate = True - else: - sys.stdout.write(f"[prepare-win] Delete and recreate '{snapshot}'? [y/N] ") - sys.stdout.flush() - raw = sys.stdin.readline() - do_recreate = raw.strip().lower() in ("y", "yes") - if not do_recreate: - click.echo("[prepare-win] Keeping existing snapshot. Provisioning complete.") - return - click.echo(f"[prepare-win] Deleting snapshot '{snapshot}' ...") + if snapshot in backend.list_snapshots(handle): + click.echo(f"[prepare-win] Deleting existing snapshot '{snapshot}' ...") subprocess.run( [vmrun_exe, "-T", "ws", "deleteSnapshot", str(vmx_path), snapshot], check=True,