refactor(cli): rename vmgui command to vmware

Rename the GUI launcher command from `vmgui` to `vmware` (`ci vmware ...`).
Updates the click command name, function, log-file prefix, __main__
registration, tests and runbook references. Behaviour unchanged.
This commit is contained in:
2026-06-08 00:32:13 +02:00
parent b74e854d48
commit de5348c36c
4 changed files with 45 additions and 45 deletions
+9 -9
View File
@@ -69,7 +69,7 @@ The script defines two helpers:
| `ci artifacts collect ...` | copy artifacts out of a guest | §4 |
| `ci vm remove --vmx ...` | stop + delete one clone | §4 |
| `ci vm cleanup` | sweep orphaned clones + stale locks | §4 / §6 |
| `ci vmgui --vmx ...` | open a VMX in the VMware Workstation GUI | §4 |
| `ci vmware --vmx ...` | open a VMX in the VMware Workstation GUI | §4 |
| `ci creds set --user ...` | store guest credentials in keyring | §5 |
| `ci template deploy-linux` | build the Linux template VM | §5 |
| `ci template prepare-linux` | provision Linux template over SSH | §5 |
@@ -217,19 +217,19 @@ Launches the **interactive** VMware Workstation GUI (not the headless vmrun path
so you can watch/poke a clone by hand.
On a normal desktop session it just works — `sudo` already forwards `DISPLAY`,
and `vmgui` behaves exactly like `sudo -u ci-runner vmware`:
and `vmware` behaves exactly like `sudo -u ci-runner vmware`:
```bash
ci vmgui # bare GUI, no VM
ci vmgui --vmx /var/lib/ci/build-vms/Clone_dbg-1_*/Clone_dbg-1_*.vmx
ci vmgui --vmx <path> --power-on # power it on (-x)
ci vmgui --vmx <path> --fullscreen # power on + fullscreen (-X)
ci vmware # bare GUI, no VM
ci vmware --vmx /var/lib/ci/build-vms/Clone_dbg-1_*/Clone_dbg-1_*.vmx
ci vmware --vmx <path> --power-on # power it on (-x)
ci vmware --vmx <path> --fullscreen # power on + fullscreen (-X)
```
Other flags: `--new-window` (`-n`), `--display :0` (force a display if `$DISPLAY`
isn't forwarded), `--vmware-path`. Spawns detached; prints PID + log path.
**Why it works**: VMware connects via the X **host ACL**, not the cookie. The
inherited `XAUTHORITY` (the desktop user's `~/.Xauthority`) is unreadable by
ci-runner, so `vmgui` drops it and relies on the xhost grant — which is normally
ci-runner, so `vmware` drops it and relies on the xhost grant — which is normally
already present (`xhost` lists `SI:localuser:ci-runner`).
**Only if you get `cannot open X display`**: the grant is missing. Add it from
@@ -237,8 +237,8 @@ the logged-in desktop user (not ci-runner):
```bash
xhost +SI:localuser:ci-runner
```
`vmgui` pre-flights the display (`xdpyinfo`) and prints this exact command on
failure instead of dying silently; the GUI's output is captured in `/tmp/vmgui-*.log`.
`vmware` pre-flights the display (`xdpyinfo`) and prints this exact command on
failure instead of dying silently; the GUI's output is captured in `/tmp/vmware-*.log`.
---
+2 -2
View File
@@ -23,7 +23,7 @@ from ci_orchestrator.commands.retention import retention
from ci_orchestrator.commands.smoke import smoke
from ci_orchestrator.commands.template import template
from ci_orchestrator.commands.validate import validate
from ci_orchestrator.commands.vm import vm, vmgui
from ci_orchestrator.commands.vm import vm, vmware
# Re-export so legacy A1 tests that patched ``cli_module.<name>`` keep working.
from ci_orchestrator.commands.wait import ( # noqa: F401 (intentional re-exports)
@@ -54,7 +54,7 @@ cli.add_command(bench)
cli.add_command(smoke)
cli.add_command(validate)
cli.add_command(creds)
cli.add_command(vmgui)
cli.add_command(vmware)
if __name__ == "__main__": # pragma: no cover
+8 -8
View File
@@ -534,7 +534,7 @@ def _launch_gui(argv: list[str], env: dict[str, str], log_path: Path) -> subproc
)
@click.command("vmgui")
@click.command("vmware")
@click.option(
"--vmx",
"--vm-path",
@@ -575,7 +575,7 @@ def _launch_gui(argv: list[str], env: dict[str, str], log_path: Path) -> subproc
default=None,
help="X display to use (default: inherit $DISPLAY). e.g. ':0'.",
)
def vmgui(
def vmware(
vmx: str | None,
vmware_path: str,
power_on: bool,
@@ -619,7 +619,7 @@ def vmgui(
if xauth and not os.access(xauth, os.R_OK):
env.pop("XAUTHORITY", None)
click.echo(
f"[vmgui] dropping unreadable XAUTHORITY ({xauth}); relying on xhost.",
f"[vmware] dropping unreadable XAUTHORITY ({xauth}); relying on xhost.",
err=True,
)
@@ -644,14 +644,14 @@ def vmgui(
argv.append(str(vmx_path))
elif power_on or fullscreen:
click.echo(
"[vmgui] note: --power-on/--fullscreen ignored without --vmx.",
"[vmware] note: --power-on/--fullscreen ignored without --vmx.",
err=True,
)
ts = datetime.now(tz=UTC).strftime("%Y%m%d-%H%M%S")
log_path = Path(tempfile.gettempdir()) / f"vmgui-{os.getpid()}-{ts}.log"
log_path = Path(tempfile.gettempdir()) / f"vmware-{os.getpid()}-{ts}.log"
click.echo(f"[vmgui] launching VMware GUI: {' '.join(argv)}")
click.echo(f"[vmware] launching VMware GUI: {' '.join(argv)}")
try:
proc = _launch_gui(argv, env, log_path)
except (OSError, ValueError) as exc:
@@ -672,8 +672,8 @@ def vmgui(
)
click.echo(
f"[vmgui] VMware GUI started (pid {proc.pid}); display={disp}; log={log_path}"
f"[vmware] VMware GUI started (pid {proc.pid}); display={disp}; log={log_path}"
)
__all__ = ["cleanup_orphans", "vm", "vmgui"]
__all__ = ["cleanup_orphans", "vm", "vmware"]
+26 -26
View File
@@ -499,11 +499,11 @@ def test_vm_cleanup_backend_unavailable(monkeypatch: pytest.MonkeyPatch, tmp_pat
assert "vmrun not available" in result.output
# ── vmgui ───────────────────────────────────────────────────────────────────
# ── vmware ───────────────────────────────────────────────────────────────────
class _FakeProc:
"""Minimal subprocess.Popen stand-in for vmgui tests."""
"""Minimal subprocess.Popen stand-in for vmware GUI tests."""
def __init__(self, pid: int = 4242, rc: int | None = None) -> None:
self.pid = pid
@@ -533,68 +533,68 @@ def _capture_launch(
return captured
def test_vmgui_missing_vmx_errors(tmp_path: Path) -> None:
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(tmp_path / "nope.vmx")])
def test_vmware_missing_vmx_errors(tmp_path: Path) -> None:
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(tmp_path / "nope.vmx")])
assert result.exit_code != 0
assert "VMX file not found" in result.output
def test_vmgui_happy_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_happy_path(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code == 0, result.output
assert cap["argv"][-1] == str(vmx)
assert "-x" not in cap["argv"] and "-X" not in cap["argv"]
assert "pid 4242" in result.output
def test_vmgui_power_on_adds_x(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_power_on_adds_x(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx), "--power-on"])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx), "--power-on"])
assert result.exit_code == 0, result.output
assert "-x" in cap["argv"]
def test_vmgui_fullscreen_adds_capital_x(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_fullscreen_adds_capital_x(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(
cli, ["vmgui", "--vmx", str(vmx), "--fullscreen", "--new-window"]
cli, ["vmware", "--vmx", str(vmx), "--fullscreen", "--new-window"]
)
assert result.exit_code == 0, result.output
assert "-X" in cap["argv"] and "-n" in cap["argv"]
def test_vmgui_display_option_overrides_env(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_display_option_overrides_env(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
cap = _capture_launch(monkeypatch)
monkeypatch.delenv("DISPLAY", raising=False)
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx), "--display", ":1"])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx), "--display", ":1"])
assert result.exit_code == 0, result.output
assert cap["env"]["DISPLAY"] == ":1"
assert "display=:1" in result.output
def test_vmgui_errors_without_display(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_errors_without_display(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
_capture_launch(monkeypatch)
monkeypatch.delenv("DISPLAY", raising=False)
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code != 0
assert "no X display" in result.output
def test_vmgui_preflight_failure_reports_xhost(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_preflight_failure_reports_xhost(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
_capture_launch(monkeypatch)
@@ -603,35 +603,35 @@ def test_vmgui_preflight_failure_reports_xhost(monkeypatch: pytest.MonkeyPatch,
vm_module, "_x_display_reachable", lambda _env: (False, "cannot open display :0")
)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code != 0
assert "xhost +SI:localuser:ci-runner" in result.output
def test_vmgui_drops_unreadable_xauthority(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_drops_unreadable_xauthority(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
monkeypatch.setenv("XAUTHORITY", str(tmp_path / "missing.Xauthority"))
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code == 0, result.output
assert "XAUTHORITY" not in cap["env"]
assert "dropping unreadable XAUTHORITY" in result.output
def test_vmgui_immediate_exit_surfaces_log(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_immediate_exit_surfaces_log(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
_capture_launch(monkeypatch, rc=1)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code != 0
assert "exited immediately" in result.output
assert "cannot open display" in result.output
def test_vmgui_launch_oserror_is_reported(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
def test_vmware_launch_oserror_is_reported(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
vmx = tmp_path / "clone.vmx"
vmx.write_text("config")
@@ -642,25 +642,25 @@ def test_vmgui_launch_oserror_is_reported(monkeypatch: pytest.MonkeyPatch, tmp_p
monkeypatch.setattr(vm_module, "_x_display_reachable", lambda _env: (True, ""))
monkeypatch.setattr(vm_module.time, "sleep", lambda _s: None)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--vmx", str(vmx)])
result = CliRunner().invoke(cli, ["vmware", "--vmx", str(vmx)])
assert result.exit_code != 0
assert "failed to launch VMware GUI" in result.output
def test_vmgui_without_vmx_launches_bare_gui(monkeypatch: pytest.MonkeyPatch) -> None:
def test_vmware_without_vmx_launches_bare_gui(monkeypatch: pytest.MonkeyPatch) -> None:
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui"])
result = CliRunner().invoke(cli, ["vmware"])
assert result.exit_code == 0, result.output
assert cap["argv"][1:] == [] or cap["argv"][1:] == ["-n"]
assert "-x" not in cap["argv"] and "-X" not in cap["argv"]
assert "pid 4242" in result.output
def test_vmgui_power_on_ignored_without_vmx(monkeypatch: pytest.MonkeyPatch) -> None:
def test_vmware_power_on_ignored_without_vmx(monkeypatch: pytest.MonkeyPatch) -> None:
cap = _capture_launch(monkeypatch)
monkeypatch.setenv("DISPLAY", ":0")
result = CliRunner().invoke(cli, ["vmgui", "--power-on"])
result = CliRunner().invoke(cli, ["vmware", "--power-on"])
assert result.exit_code == 0, result.output
assert "-x" not in cap["argv"]
assert "ignored without --vmx" in result.output