#!/usr/bin/env python3## Copyright (C) 2026 Red Hat, Inc.# SPDX-License-Identifier: LGPL-2.1-or-later"""Fast iteration of src/cockpit/ against existing running VMs.Instead of the slow image-prepare way (building packages), this script:1. Uploads the current src/cockpit code to /var/tmp on the VM2. Detects the package manager (dpkg/rpm/pacman) or container3. Bind-mounts the new code over the installed package path4. For containers, rebuilds the cockpit/ws container imageUsage:test/vm-copy-bridge PORT"""import argparseimport osimport subprocessimport sys# Add bots to pathBOTS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bots")sys.path.insert(0, BOTS_DIR)from lib.constants import DEFAULT_IDENTITY_FILE # ruff: ignore[module-import-not-at-top-of-file]from machine.machine_core.ssh_connection import SSHConnection # ruff: ignore[module-import-not-at-top-of-file]def find_cockpit_path(file_list: str) -> str:"""Find the cockpit module path from a list of file paths.Handles package manager output formats:- dpkg/rpm: one filepath per line- pacman: "package-name filepath" per line"""for line in file_list.splitlines():line = line.strip()if line.endswith('/cockpit/__init__.py'):# Extract the path (for pacman, take last field to skip package name)path = line.split()[-1]return os.path.dirname(path)sys.exit("Could not find cockpit module path in package file list: " + file_list)def setup_bind_mount(conn: SSHConnection, cockpit_path: str, port: int) -> None:"""Set up bind mount from /var/tmp/cockpit to the package path."""conn.execute(f"if mountpoint -q {cockpit_path}; then umount {cockpit_path}; fi")conn.execute(f"mount --bind /var/tmp/cockpit {cockpit_path}")print(f"Mounted /var/tmp/cockpit over {cockpit_path}")def rebuild_container(conn: SSHConnection) -> None:"""Rebuild the cockpit/ws container with the new bridge code."""print("Rebuilding cockpit/ws container...")# Find the cockpit module path inside the containercockpit_path_in_container = conn.execute("podman run --rm localhost/cockpit/ws python3 -c ""'import cockpit, os; print(os.path.dirname(cockpit.__file__))'").strip()# Build Containerfile with stdin - copy uploaded code into container# Note: COPY source is relative to build context (.)containerfile = f"""FROM localhost/cockpit/ws:latestCOPY cockpit {cockpit_path_in_container}"""# Build the new container using stdin inputconn.execute("cd /var/tmp && podman build -t localhost/cockpit/ws:latest -f - .",input=containerfile,timeout=30)print("cockpit/ws container rebuilt successfully!")## main#parser = argparse.ArgumentParser(description="Copy bridge code to a running VM")parser.add_argument("port", type=int, help="SSH port of the running VM")parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")args = parser.parse_args()# Connection parameters matching ~/.ssh/config "Host c" paragraphconn = SSHConnection(user="root",address="127.0.0.2",ssh_port=args.port,identity_file=DEFAULT_IDENTITY_FILE,verbose=args.verbose)# Check if we can connectif not conn.wait_execute(timeout_sec=5):sys.exit(f"Cannot connect to VM on port {args.port}")print(f"Connected to VM on port {args.port}")# Get the local src/cockpit directorysrc_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))cockpit_src = os.path.join(src_dir, "src", "cockpit")if not os.path.isdir(cockpit_src):sys.exit(f"{cockpit_src} does not exist")# Upload the cockpit module to /var/tmpprint(f"Uploading {cockpit_src} to VM...")conn.execute("rm -rf /var/tmp/cockpit")conn.upload(["src/cockpit"], "/var/tmp/", relative_dir=src_dir)# Known package managerspkg_managers = [("dpkg -L cockpit-bridge", "Debian/Ubuntu"),("rpm -ql cockpit-bridge", "RPM-based"),("pacman -Ql cockpit", "Arch Linux"),]for cmd, system_type in pkg_managers:try:output = conn.execute(cmd)except subprocess.CalledProcessError:continueelse:cockpit_path = find_cockpit_path(output)print(f"Detected {system_type} system, cockpit at: {cockpit_path}")setup_bind_mount(conn, cockpit_path, args.port)breakelse:# No package manager found, try containertry:conn.execute("podman image exists localhost/cockpit/ws")print("Detected container-based system (localhost/cockpit/ws)")rebuild_container(conn)except subprocess.CalledProcessError:sys.exit("Could not detect package type (tried dpkg, rpm, pacman, podman)")conn.disconnect()