clonebox 1.1.6__tar.gz → 1.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clonebox might be problematic. Click here for more details.
- {clonebox-1.1.6/src/clonebox.egg-info → clonebox-1.1.8}/PKG-INFO +1 -1
- {clonebox-1.1.6 → clonebox-1.1.8}/pyproject.toml +1 -1
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/cli.py +83 -93
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/cloner.py +22 -4
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/validator.py +14 -1
- {clonebox-1.1.6 → clonebox-1.1.8/src/clonebox.egg-info}/PKG-INFO +1 -1
- {clonebox-1.1.6 → clonebox-1.1.8}/LICENSE +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/README.md +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/setup.cfg +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/__init__.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/__main__.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/backends/libvirt_backend.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/backends/qemu_disk.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/backends/subprocess_runner.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/container.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/dashboard.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/detector.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/di.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/exporter.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/health/__init__.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/health/manager.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/health/models.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/health/probes.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/importer.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/interfaces/disk.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/interfaces/hypervisor.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/interfaces/network.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/interfaces/process.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/logging.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/models.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/monitor.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/p2p.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/profiles.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/resource_monitor.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/resources.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/rollback.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/secrets.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/snapshots/__init__.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/snapshots/manager.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/snapshots/models.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/templates/profiles/ml-dev.yaml +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox/templates/profiles/web-stack.yaml +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox.egg-info/SOURCES.txt +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox.egg-info/dependency_links.txt +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox.egg-info/entry_points.txt +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox.egg-info/requires.txt +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/src/clonebox.egg-info/top_level.txt +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_cli.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_cloner.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_cloner_simple.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_container.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_coverage_additional.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_coverage_boost_final.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_dashboard_coverage.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_detector.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_models.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_network.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_profiles.py +0 -0
- {clonebox-1.1.6 → clonebox-1.1.8}/tests/test_validator.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "clonebox"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.8"
|
|
8
8
|
description = "Clone your workstation environment to an isolated VM with selective apps, paths and services"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
@@ -92,22 +92,31 @@ def _resolve_vm_name_and_config_file(name: Optional[str]) -> Tuple[str, Optional
|
|
|
92
92
|
|
|
93
93
|
def _qga_ping(vm_name: str, conn_uri: str) -> bool:
|
|
94
94
|
import subprocess
|
|
95
|
+
import json
|
|
96
|
+
import time
|
|
95
97
|
|
|
96
98
|
try:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
for _ in range(5):
|
|
100
|
+
try:
|
|
101
|
+
result = subprocess.run(
|
|
102
|
+
[
|
|
103
|
+
"virsh",
|
|
104
|
+
"--connect",
|
|
105
|
+
conn_uri,
|
|
106
|
+
"qemu-agent-command",
|
|
107
|
+
vm_name,
|
|
108
|
+
json.dumps({"execute": "guest-ping"}),
|
|
109
|
+
],
|
|
110
|
+
capture_output=True,
|
|
111
|
+
text=True,
|
|
112
|
+
timeout=5,
|
|
113
|
+
)
|
|
114
|
+
if result.returncode == 0:
|
|
115
|
+
return True
|
|
116
|
+
except subprocess.TimeoutExpired:
|
|
117
|
+
pass
|
|
118
|
+
time.sleep(1)
|
|
119
|
+
return False
|
|
111
120
|
except Exception:
|
|
112
121
|
return False
|
|
113
122
|
|
|
@@ -116,6 +125,7 @@ def _qga_exec(vm_name: str, conn_uri: str, command: str, timeout: int = 10) -> O
|
|
|
116
125
|
import subprocess
|
|
117
126
|
import base64
|
|
118
127
|
import time
|
|
128
|
+
import json
|
|
119
129
|
|
|
120
130
|
try:
|
|
121
131
|
payload = {
|
|
@@ -1654,6 +1664,7 @@ def cmd_test(args):
|
|
|
1654
1664
|
"""Test VM configuration and health."""
|
|
1655
1665
|
import subprocess
|
|
1656
1666
|
import json
|
|
1667
|
+
import time
|
|
1657
1668
|
from clonebox.validator import VMValidator
|
|
1658
1669
|
|
|
1659
1670
|
name = args.name
|
|
@@ -1733,6 +1744,15 @@ def cmd_test(args):
|
|
|
1733
1744
|
if state == "running":
|
|
1734
1745
|
console.print("[green]✅ VM is running[/]")
|
|
1735
1746
|
|
|
1747
|
+
# Give QEMU Guest Agent some time to come up (common during early boot)
|
|
1748
|
+
qga_ready = _qga_ping(vm_name, conn_uri)
|
|
1749
|
+
if not qga_ready:
|
|
1750
|
+
for _ in range(12): # ~60s
|
|
1751
|
+
time.sleep(5)
|
|
1752
|
+
qga_ready = _qga_ping(vm_name, conn_uri)
|
|
1753
|
+
if qga_ready:
|
|
1754
|
+
break
|
|
1755
|
+
|
|
1736
1756
|
# Test network if running
|
|
1737
1757
|
console.print("\n Checking network...")
|
|
1738
1758
|
try:
|
|
@@ -1751,11 +1771,7 @@ def cmd_test(args):
|
|
|
1751
1771
|
else:
|
|
1752
1772
|
console.print("[yellow]⚠️ No IP address detected via virsh domifaddr[/]")
|
|
1753
1773
|
# Fallback: try to get IP via QEMU Guest Agent (useful for slirp/user networking)
|
|
1754
|
-
|
|
1755
|
-
from .cli import _qga_ping, _qga_exec
|
|
1756
|
-
except ImportError:
|
|
1757
|
-
from clonebox.cli import _qga_ping, _qga_exec
|
|
1758
|
-
if _qga_ping(vm_name, conn_uri):
|
|
1774
|
+
if qga_ready:
|
|
1759
1775
|
try:
|
|
1760
1776
|
ip_out = _qga_exec(
|
|
1761
1777
|
vm_name,
|
|
@@ -1787,61 +1803,22 @@ def cmd_test(args):
|
|
|
1787
1803
|
if not quick and state == "running":
|
|
1788
1804
|
console.print("[bold]3. Cloud-init Status[/]")
|
|
1789
1805
|
try:
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
response = json.loads(result.stdout)
|
|
1807
|
-
if "return" in response:
|
|
1808
|
-
pid = response["return"]["pid"]
|
|
1809
|
-
# Get output
|
|
1810
|
-
result2 = subprocess.run(
|
|
1811
|
-
[
|
|
1812
|
-
"virsh",
|
|
1813
|
-
"--connect",
|
|
1814
|
-
conn_uri,
|
|
1815
|
-
"qemu-agent-command",
|
|
1816
|
-
vm_name,
|
|
1817
|
-
f'{{"execute":"guest-exec-status","arguments":{"pid":{pid}}}}',
|
|
1818
|
-
],
|
|
1819
|
-
capture_output=True,
|
|
1820
|
-
text=True,
|
|
1821
|
-
timeout=15,
|
|
1822
|
-
)
|
|
1823
|
-
if result2.returncode == 0:
|
|
1824
|
-
resp2 = json.loads(result2.stdout)
|
|
1825
|
-
if "return" in resp2 and resp2["return"]["exited"]:
|
|
1826
|
-
output = resp2["return"]["out-data"]
|
|
1827
|
-
if output:
|
|
1828
|
-
import base64
|
|
1829
|
-
|
|
1830
|
-
status = base64.b64decode(output).decode()
|
|
1831
|
-
if "done" in status.lower():
|
|
1832
|
-
console.print("[green]✅ Cloud-init completed[/]")
|
|
1833
|
-
elif "running" in status.lower():
|
|
1834
|
-
console.print("[yellow]⚠️ Cloud-init still running[/]")
|
|
1835
|
-
else:
|
|
1836
|
-
console.print(
|
|
1837
|
-
f"[yellow]⚠️ Cloud-init status: {status.strip()}[/]"
|
|
1838
|
-
)
|
|
1839
|
-
except:
|
|
1840
|
-
pass
|
|
1841
|
-
except:
|
|
1842
|
-
console.print(
|
|
1843
|
-
"[yellow]⚠️ Could not check cloud-init (QEMU agent may not be running)[/]"
|
|
1844
|
-
)
|
|
1806
|
+
if not qga_ready:
|
|
1807
|
+
console.print("[yellow]⚠️ Cloud-init status unknown (QEMU Guest Agent not connected)[/]")
|
|
1808
|
+
else:
|
|
1809
|
+
status = _qga_exec(vm_name, conn_uri, "cloud-init status 2>/dev/null || true", timeout=15)
|
|
1810
|
+
if status is None:
|
|
1811
|
+
console.print("[yellow]⚠️ Could not check cloud-init (QGA command failed)[/]")
|
|
1812
|
+
elif "done" in status.lower():
|
|
1813
|
+
console.print("[green]✅ Cloud-init completed[/]")
|
|
1814
|
+
elif "running" in status.lower():
|
|
1815
|
+
console.print("[yellow]⚠️ Cloud-init still running[/]")
|
|
1816
|
+
elif status.strip():
|
|
1817
|
+
console.print(f"[yellow]⚠️ Cloud-init status: {status.strip()}[/]")
|
|
1818
|
+
else:
|
|
1819
|
+
console.print("[yellow]⚠️ Cloud-init status: unknown[/]")
|
|
1820
|
+
except Exception:
|
|
1821
|
+
console.print("[yellow]⚠️ Could not check cloud-init (QEMU agent may not be running)[/]")
|
|
1845
1822
|
|
|
1846
1823
|
console.print()
|
|
1847
1824
|
|
|
@@ -1876,25 +1853,31 @@ def cmd_test(args):
|
|
|
1876
1853
|
if not quick and state == "running":
|
|
1877
1854
|
console.print("[bold]5. Health Check[/]")
|
|
1878
1855
|
try:
|
|
1879
|
-
|
|
1880
|
-
[
|
|
1881
|
-
"virsh",
|
|
1882
|
-
"--connect",
|
|
1883
|
-
conn_uri,
|
|
1884
|
-
"qemu-agent-command",
|
|
1885
|
-
vm_name,
|
|
1886
|
-
'{"execute":"guest-exec","arguments":{"path":"/usr/local/bin/clonebox-health","capture-output":true}}',
|
|
1887
|
-
],
|
|
1888
|
-
capture_output=True,
|
|
1889
|
-
text=True,
|
|
1890
|
-
timeout=60,
|
|
1891
|
-
)
|
|
1892
|
-
if result.returncode == 0:
|
|
1893
|
-
console.print("[green]✅ Health check triggered[/]")
|
|
1894
|
-
console.print(" View results in VM: cat /var/log/clonebox-health.log")
|
|
1856
|
+
if not qga_ready:
|
|
1857
|
+
console.print("[yellow]⚠️ QEMU Guest Agent not connected - cannot run health check[/]")
|
|
1895
1858
|
else:
|
|
1896
|
-
|
|
1897
|
-
|
|
1859
|
+
exists = _qga_exec(
|
|
1860
|
+
vm_name,
|
|
1861
|
+
conn_uri,
|
|
1862
|
+
"test -x /usr/local/bin/clonebox-health && echo yes || echo no",
|
|
1863
|
+
timeout=10,
|
|
1864
|
+
)
|
|
1865
|
+
if exists and exists.strip() == "yes":
|
|
1866
|
+
out = _qga_exec(
|
|
1867
|
+
vm_name,
|
|
1868
|
+
conn_uri,
|
|
1869
|
+
"/usr/local/bin/clonebox-health >/dev/null 2>&1 && echo yes || echo no",
|
|
1870
|
+
timeout=60,
|
|
1871
|
+
)
|
|
1872
|
+
if out and out.strip() == "yes":
|
|
1873
|
+
console.print("[green]✅ Health check ran successfully[/]")
|
|
1874
|
+
console.print(" View results in VM: cat /var/log/clonebox-health.log")
|
|
1875
|
+
else:
|
|
1876
|
+
console.print("[yellow]⚠️ Health check did not report success[/]")
|
|
1877
|
+
console.print(" View logs in VM: cat /var/log/clonebox-health.log")
|
|
1878
|
+
else:
|
|
1879
|
+
console.print("[yellow]⚠️ Health check script not found[/]")
|
|
1880
|
+
console.print(" This is expected until cloud-init completes")
|
|
1898
1881
|
except Exception as e:
|
|
1899
1882
|
console.print(f"[yellow]⚠️ Could not run health check: {e}[/]")
|
|
1900
1883
|
|
|
@@ -2743,6 +2726,11 @@ def cmd_exec(args) -> None:
|
|
|
2743
2726
|
vm_name, config_file = _resolve_vm_name_and_config_file(args.name)
|
|
2744
2727
|
conn_uri = "qemu:///session" if getattr(args, "user", False) else "qemu:///system"
|
|
2745
2728
|
command = args.command
|
|
2729
|
+
if isinstance(command, list):
|
|
2730
|
+
command = " ".join(command).strip()
|
|
2731
|
+
if not command:
|
|
2732
|
+
console.print("[red]❌ No command specified[/]")
|
|
2733
|
+
return
|
|
2746
2734
|
timeout = getattr(args, "timeout", 30)
|
|
2747
2735
|
|
|
2748
2736
|
if not _qga_ping(vm_name, conn_uri):
|
|
@@ -3486,7 +3474,9 @@ def main():
|
|
|
3486
3474
|
exec_parser.add_argument(
|
|
3487
3475
|
"name", nargs="?", default=None, help="VM name or '.' to use .clonebox.yaml"
|
|
3488
3476
|
)
|
|
3489
|
-
exec_parser.add_argument(
|
|
3477
|
+
exec_parser.add_argument(
|
|
3478
|
+
"command", nargs=argparse.REMAINDER, help="Command to execute in VM"
|
|
3479
|
+
)
|
|
3490
3480
|
exec_parser.add_argument(
|
|
3491
3481
|
"-u", "--user", action="store_true", help="Use user session (qemu:///session)"
|
|
3492
3482
|
)
|
|
@@ -623,8 +623,11 @@ class SelectiveVMCloner:
|
|
|
623
623
|
ET.SubElement(devices, "input", type="tablet", bus="usb")
|
|
624
624
|
ET.SubElement(devices, "input", type="keyboard", bus="usb")
|
|
625
625
|
|
|
626
|
+
ET.SubElement(devices, "controller", type="virtio-serial", index="0")
|
|
627
|
+
|
|
626
628
|
# Channel for guest agent
|
|
627
629
|
channel = ET.SubElement(devices, "channel", type="unix")
|
|
630
|
+
ET.SubElement(channel, "source", mode="bind")
|
|
628
631
|
ET.SubElement(channel, "target", type="virtio", name="org.qemu.guest_agent.0")
|
|
629
632
|
|
|
630
633
|
# Memory balloon
|
|
@@ -1254,8 +1257,22 @@ fi
|
|
|
1254
1257
|
mount_commands = []
|
|
1255
1258
|
fstab_entries = []
|
|
1256
1259
|
all_paths = dict(config.paths) if config.paths else {}
|
|
1260
|
+
pre_chown_dirs: set[str] = set()
|
|
1257
1261
|
for idx, (host_path, guest_path) in enumerate(all_paths.items()):
|
|
1258
1262
|
if Path(host_path).exists():
|
|
1263
|
+
if str(guest_path).startswith("/home/ubuntu/snap/"):
|
|
1264
|
+
guest_parts = Path(guest_path).parts
|
|
1265
|
+
if len(guest_parts) > 4:
|
|
1266
|
+
snap_name = guest_parts[4]
|
|
1267
|
+
for d in (
|
|
1268
|
+
"/home/ubuntu",
|
|
1269
|
+
"/home/ubuntu/snap",
|
|
1270
|
+
f"/home/ubuntu/snap/{snap_name}",
|
|
1271
|
+
):
|
|
1272
|
+
if d not in pre_chown_dirs:
|
|
1273
|
+
pre_chown_dirs.add(d)
|
|
1274
|
+
mount_commands.append(f" - mkdir -p {d}")
|
|
1275
|
+
mount_commands.append(f" - chown 1000:1000 {d}")
|
|
1259
1276
|
tag = f"mount{idx}"
|
|
1260
1277
|
# Use uid=1000,gid=1000 to give ubuntu user access to mounts
|
|
1261
1278
|
# mmap allows proper file mapping
|
|
@@ -1306,6 +1323,9 @@ fi
|
|
|
1306
1323
|
for cmd in mount_commands:
|
|
1307
1324
|
runcmd_lines.append(cmd)
|
|
1308
1325
|
|
|
1326
|
+
runcmd_lines.append(" - chown -R 1000:1000 /home/ubuntu || true")
|
|
1327
|
+
runcmd_lines.append(" - chown -R 1000:1000 /home/ubuntu/snap || true")
|
|
1328
|
+
|
|
1309
1329
|
# Install snap packages
|
|
1310
1330
|
if config.snap_packages:
|
|
1311
1331
|
runcmd_lines.append(" - echo 'Installing snap packages...'")
|
|
@@ -2146,7 +2166,7 @@ if __name__ == "__main__":
|
|
|
2146
2166
|
" - cp -r /var/log/clonebox*.log /mnt/logs/var/log/ 2>/dev/null || true",
|
|
2147
2167
|
" - cp -r /tmp/*-error.log /mnt/logs/tmp/ 2>/dev/null || true",
|
|
2148
2168
|
" - echo 'Logs disk mounted at /mnt/logs - accessible from host as /var/lib/libvirt/images/clonebox-logs.qcow2'",
|
|
2149
|
-
" - echo 'To view logs on host: sudo mount -o loop /var/lib/libvirt/images/clonebox-logs.qcow2 /mnt/clonebox-logs'",
|
|
2169
|
+
" - \"echo 'To view logs on host: sudo mount -o loop /var/lib/libvirt/images/clonebox-logs.qcow2 /mnt/clonebox-logs'\"",
|
|
2150
2170
|
]
|
|
2151
2171
|
)
|
|
2152
2172
|
|
|
@@ -2158,9 +2178,7 @@ if __name__ == "__main__":
|
|
|
2158
2178
|
runcmd_yaml = "\n".join(runcmd_lines) if runcmd_lines else ""
|
|
2159
2179
|
|
|
2160
2180
|
# Build bootcmd combining mount commands and extra security bootcmds
|
|
2161
|
-
bootcmd_lines = list(
|
|
2162
|
-
if bootcmd_extra:
|
|
2163
|
-
bootcmd_lines.extend(bootcmd_extra)
|
|
2181
|
+
bootcmd_lines = list(bootcmd_extra) if bootcmd_extra else []
|
|
2164
2182
|
|
|
2165
2183
|
bootcmd_block = ""
|
|
2166
2184
|
if bootcmd_lines:
|
|
@@ -728,7 +728,20 @@ class VMValidator:
|
|
|
728
728
|
return None if out is None else out.strip() == "yes"
|
|
729
729
|
|
|
730
730
|
def _run_test(app: str) -> Optional[bool]:
|
|
731
|
-
|
|
731
|
+
uid_out = self._exec_in_vm(f"id -u {vm_user} 2>/dev/null || true", timeout=10)
|
|
732
|
+
vm_uid = (uid_out or "").strip()
|
|
733
|
+
if not vm_uid.isdigit():
|
|
734
|
+
vm_uid = "1000"
|
|
735
|
+
|
|
736
|
+
runtime_dir = f"/run/user/{vm_uid}"
|
|
737
|
+
self._exec_in_vm(
|
|
738
|
+
f"mkdir -p {runtime_dir} && chown {vm_uid}:{vm_uid} {runtime_dir} && chmod 700 {runtime_dir}",
|
|
739
|
+
timeout=10,
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
user_env = (
|
|
743
|
+
f"sudo -u {vm_user} env HOME=/home/{vm_user} USER={vm_user} LOGNAME={vm_user} XDG_RUNTIME_DIR={runtime_dir}"
|
|
744
|
+
)
|
|
732
745
|
|
|
733
746
|
if app == "pycharm-community":
|
|
734
747
|
out = self._exec_in_vm(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|