clonebox 1.1.16__py3-none-any.whl → 1.1.18__py3-none-any.whl

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/cli.py CHANGED
@@ -2498,6 +2498,18 @@ def monitor_cloud_init_status(vm_name: str, user_session: bool = False, timeout:
2498
2498
  "grep -E '\\[[0-9]/[0-9]\\]|→' /var/log/cloud-init-output.log 2>/dev/null | tail -n 5"
2499
2499
  )
2500
2500
 
2501
+ # Check disk space in real-time
2502
+ disk_info = _exec_in_vm_qga(
2503
+ vm_name,
2504
+ conn_uri,
2505
+ "df / --output=pcent | tail -n 1 | tr -dc '0-9'"
2506
+ )
2507
+ if disk_info and disk_info.isdigit():
2508
+ usage = int(disk_info)
2509
+ if usage > 90:
2510
+ console.print(f"[bold red]⚠️ WARNING: VM Disk is nearly full ({usage}%)![/]")
2511
+ console.print("[red] Installation may fail. Consider increasing --disk-size-gb.[/]")
2512
+
2501
2513
  if raw_info:
2502
2514
  lines = [l.strip() for l in raw_info.strip().split('\n') if l.strip()]
2503
2515
  for line in lines:
clonebox/cloner.py CHANGED
@@ -1124,7 +1124,7 @@ fi
1124
1124
  mount_checks.append(f'check_mount "{guest_path}" "mount{idx}"')
1125
1125
 
1126
1126
  # Add copied paths checks
1127
- copy_paths = config.copy_paths or config.app_data_paths
1127
+ copy_paths = config.copy_paths or getattr(config, "app_data_paths", {})
1128
1128
  if copy_paths:
1129
1129
  for idx, (host_path, guest_path) in enumerate(copy_paths.items()):
1130
1130
  mount_checks.append(f'check_copy_path "{guest_path}"')
@@ -1161,6 +1161,27 @@ log() {{
1161
1161
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$REPORT_FILE"
1162
1162
  }}
1163
1163
 
1164
+ check_disk_space() {{
1165
+ local usage
1166
+ usage=$(df / --output=pcent | tail -n 1 | tr -dc '0-9')
1167
+ local avail
1168
+ avail=$(df -h / --output=avail | tail -n 1 | tr -d ' ')
1169
+
1170
+ if [ "$usage" -gt 95 ]; then
1171
+ log "[FAIL] Disk space nearly full: ${{usage}}% used ($avail available)"
1172
+ ((FAILED++))
1173
+ return 1
1174
+ elif [ "$usage" -gt 85 ]; then
1175
+ log "[WARN] Disk usage high: ${{usage}}% used ($avail available)"
1176
+ ((WARNINGS++))
1177
+ return 0
1178
+ else
1179
+ log "[PASS] Disk space OK: ${{usage}}% used ($avail available)"
1180
+ ((PASSED++))
1181
+ return 0
1182
+ fi
1183
+ }}
1184
+
1164
1185
  check_apt_package() {{
1165
1186
  local pkg="$1"
1166
1187
  if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
@@ -1234,9 +1255,9 @@ check_mount() {{
1234
1255
  log "[INFO] Mount point '$path' does not exist yet"
1235
1256
  return 0
1236
1257
  fi
1237
- }
1258
+ }}
1238
1259
 
1239
- check_copy_path() {
1260
+ check_copy_path() {{
1240
1261
  local path="$1"
1241
1262
  if [ -d "$path" ]; then
1242
1263
  if [ "$(ls -A "$path" 2>/dev/null | wc -l)" -gt 0 ]; then
@@ -1283,6 +1304,11 @@ log "VM Name: {config.name}"
1283
1304
  log "Date: $(date)"
1284
1305
  log "=========================================="
1285
1306
 
1307
+ log ""
1308
+ log "--- System Health ---"
1309
+ check_disk_space
1310
+ check_gui
1311
+
1286
1312
  log ""
1287
1313
  log "--- APT Packages ---"
1288
1314
  {apt_checks_str}
@@ -1403,7 +1429,7 @@ fi
1403
1429
  (cloudinit_dir / "meta-data").write_text(meta_data)
1404
1430
 
1405
1431
  # Generate mount commands and fstab entries for 9p filesystems
1406
- mount_commands = []
1432
+ bind_mount_commands = []
1407
1433
  fstab_entries = []
1408
1434
  all_paths = dict(config.paths) if config.paths else {}
1409
1435
  pre_chown_dirs: set[str] = set()
@@ -1421,8 +1447,8 @@ fi
1421
1447
  d_str = str(current)
1422
1448
  if d_str not in pre_chown_dirs:
1423
1449
  pre_chown_dirs.add(d_str)
1424
- mount_commands.append(f" - mkdir -p {d_str}")
1425
- mount_commands.append(f" - chown 1000:1000 {d_str}")
1450
+ bind_mount_commands.append(f" - mkdir -p {d_str}")
1451
+ bind_mount_commands.append(f" - chown 1000:1000 {d_str}")
1426
1452
  except ValueError:
1427
1453
  pass
1428
1454
 
@@ -1433,14 +1459,15 @@ fi
1433
1459
 
1434
1460
  # Ensure target exists and is owned by user (if not already handled)
1435
1461
  if str(guest_path) not in pre_chown_dirs:
1436
- mount_commands.append(f" - mkdir -p {guest_path}")
1437
- mount_commands.append(f" - chown 1000:1000 {guest_path}")
1462
+ bind_mount_commands.append(f" - mkdir -p {guest_path}")
1463
+ bind_mount_commands.append(f" - chown 1000:1000 {guest_path}")
1438
1464
 
1439
- mount_commands.append(f" - mount -t 9p -o {mount_opts} {tag} {guest_path} || true")
1465
+ bind_mount_commands.append(f" - mount -t 9p -o {mount_opts} {tag} {guest_path} || true")
1440
1466
  # Add fstab entry for persistence after reboot
1441
1467
  fstab_entries.append(f"{tag} {guest_path} 9p {mount_opts},nofail 0 0")
1442
1468
 
1443
1469
  # Handle copy_paths (import then copy)
1470
+ import_mount_commands = []
1444
1471
  all_copy_paths = dict(config.copy_paths) if config.copy_paths else {}
1445
1472
  existing_copy_paths = {h: g for h, g in all_copy_paths.items() if Path(h).exists()}
1446
1473
 
@@ -1451,29 +1478,29 @@ fi
1451
1478
  mount_opts = "trans=virtio,version=9p2000.L,mmap,uid=1000,gid=1000"
1452
1479
 
1453
1480
  # 1. Create temp mount point
1454
- mount_commands.append(f" - mkdir -p {temp_mount_point}")
1481
+ import_mount_commands.append(f" - mkdir -p {temp_mount_point}")
1455
1482
 
1456
1483
  # 2. Mount the 9p share
1457
- mount_commands.append(f" - mount -t 9p -o {mount_opts} {tag} {temp_mount_point} || true")
1484
+ import_mount_commands.append(f" - mount -t 9p -o {mount_opts} {tag} {temp_mount_point} || true")
1458
1485
 
1459
1486
  # 3. Ensure target directory exists and permissions are prepared
1460
1487
  if str(guest_path).startswith("/home/ubuntu/"):
1461
- mount_commands.append(f" - mkdir -p {guest_path}")
1462
- mount_commands.append(f" - chown 1000:1000 {guest_path}")
1488
+ import_mount_commands.append(f" - mkdir -p {guest_path}")
1489
+ import_mount_commands.append(f" - chown 1000:1000 {guest_path}")
1463
1490
  else:
1464
- mount_commands.append(f" - mkdir -p {guest_path}")
1491
+ import_mount_commands.append(f" - mkdir -p {guest_path}")
1465
1492
 
1466
1493
  # 4. Copy contents (cp -rT to copy contents of source to target)
1467
1494
  # We use || true to ensure boot continues even if copy fails
1468
- mount_commands.append(f" - echo 'Importing {host_path} to {guest_path}...'")
1469
- mount_commands.append(f" - cp -rT {temp_mount_point} {guest_path} || true")
1495
+ import_mount_commands.append(f" - echo 'Importing {host_path} to {guest_path}...'")
1496
+ import_mount_commands.append(f" - cp -rT {temp_mount_point} {guest_path} || true")
1470
1497
 
1471
1498
  # 5. Fix ownership recursively
1472
- mount_commands.append(f" - chown -R 1000:1000 {guest_path}")
1499
+ import_mount_commands.append(f" - chown -R 1000:1000 {guest_path}")
1473
1500
 
1474
1501
  # 6. Unmount and cleanup
1475
- mount_commands.append(f" - umount {temp_mount_point} || true")
1476
- mount_commands.append(f" - rmdir {temp_mount_point} || true")
1502
+ import_mount_commands.append(f" - umount {temp_mount_point} || true")
1503
+ import_mount_commands.append(f" - rmdir {temp_mount_point} || true")
1477
1504
 
1478
1505
  # User-data
1479
1506
  # Add desktop environment if GUI is enabled
@@ -1506,7 +1533,8 @@ fi
1506
1533
  for i, pkg in enumerate(all_packages, 1):
1507
1534
  runcmd_lines.append(f" - echo ' → [{i}/{len(all_packages)}] Installing {pkg}...'")
1508
1535
  runcmd_lines.append(f" - apt-get install -y {pkg} || echo ' ⚠️ Failed to install {pkg}'")
1509
- runcmd_lines.append(" - echo ' ✓ APT packages installed'")
1536
+ runcmd_lines.append(" - apt-get clean")
1537
+ runcmd_lines.append(" - echo ' ✓ APT packages installed and cache cleaned'")
1510
1538
  runcmd_lines.append(" - echo ''")
1511
1539
  else:
1512
1540
  runcmd_lines.append(" - echo '[1/9] 📦 No APT packages to install'")
@@ -1532,14 +1560,21 @@ fi
1532
1560
  runcmd_lines.append(" - echo ''")
1533
1561
 
1534
1562
  # Phase 4: Filesystem mounts
1535
- runcmd_lines.append(f" - echo '[4/9] 📁 Mounting shared directories ({len(fstab_entries)} mounts)...'")
1563
+ runcmd_lines.append(f" - echo '[4/9] 📁 Mounting shared directories ({len(config.paths)} mounts)...'")
1564
+ if bind_mount_commands:
1565
+ for cmd in bind_mount_commands:
1566
+ if "mount -t 9p" in cmd:
1567
+ # Extract mount point for logging
1568
+ parts = cmd.split()
1569
+ mp = parts[-2] if len(parts) > 2 else "path"
1570
+ runcmd_lines.append(f" - echo ' → Mounting {mp}...'")
1571
+ runcmd_lines.append(cmd)
1572
+
1536
1573
  if fstab_entries:
1537
1574
  runcmd_lines.append(
1538
1575
  " - grep -q '^# CloneBox 9p mounts' /etc/fstab || echo '# CloneBox 9p mounts' >> /etc/fstab"
1539
1576
  )
1540
1577
  for i, entry in enumerate(fstab_entries, 1):
1541
- mount_point = entry.split()[1] if len(entry.split()) > 1 else entry
1542
- runcmd_lines.append(f" - echo ' → [{i}/{len(fstab_entries)}] {mount_point}'")
1543
1578
  runcmd_lines.append(
1544
1579
  f" - grep -qF \"{entry}\" /etc/fstab || echo '{entry}' >> /etc/fstab"
1545
1580
  )
@@ -1550,9 +1585,9 @@ fi
1550
1585
  # Phase 5: Data Import (copied paths)
1551
1586
  if existing_copy_paths:
1552
1587
  runcmd_lines.append(f" - echo '[5/9] 📥 Importing data ({len(existing_copy_paths)} paths)...'")
1553
- # Add mounts (immediate, before reboot)
1588
+ # Add import commands with progress
1554
1589
  import_count = 0
1555
- for cmd in mount_commands:
1590
+ for cmd in import_mount_commands:
1556
1591
  if "Importing" in cmd:
1557
1592
  import_count += 1
1558
1593
  runcmd_lines.append(cmd.replace("Importing", f" → [{import_count}/{len(existing_copy_paths)}] Importing"))
@@ -2446,25 +2481,21 @@ if __name__ == "__main__":
2446
2481
  # Note: The bash monitor is already installed above, no need to install Python monitor
2447
2482
 
2448
2483
  # Create logs disk for host access
2449
- # Use different paths based on session type
2450
- if user_session:
2451
- logs_disk_path = str(Path.home() / ".local/share/libvirt/images/clonebox-logs.qcow2")
2452
- else:
2453
- logs_disk_path = "/var/lib/libvirt/images/clonebox-logs.qcow2"
2484
+ # Inside the VM, we use a fixed path for the image file
2485
+ vm_logs_img_path = "/var/lib/clonebox/logs.img"
2454
2486
 
2455
2487
  runcmd_lines.extend(
2456
2488
  [
2457
- " - mkdir -p /mnt/logs",
2458
- f" - truncate -s 1G {logs_disk_path}",
2459
- f" - mkfs.ext4 -F {logs_disk_path}",
2460
- f" - echo '{logs_disk_path} /mnt/logs ext4 loop,defaults 0 0' >> /etc/fstab",
2489
+ " - mkdir -p /var/lib/clonebox /mnt/logs",
2490
+ f" - truncate -s 1G {vm_logs_img_path}",
2491
+ f" - mkfs.ext4 -F {vm_logs_img_path}",
2492
+ f" - echo '{vm_logs_img_path} /mnt/logs ext4 loop,defaults 0 0' >> /etc/fstab",
2461
2493
  " - mount -a",
2462
2494
  " - mkdir -p /mnt/logs/var/log",
2463
2495
  " - mkdir -p /mnt/logs/tmp",
2464
2496
  " - cp -r /var/log/clonebox*.log /mnt/logs/var/log/ 2>/dev/null || true",
2465
2497
  " - cp -r /tmp/*-error.log /mnt/logs/tmp/ 2>/dev/null || true",
2466
- f" - echo 'Logs disk mounted at /mnt/logs - accessible from host as {logs_disk_path}'",
2467
- f" - \"echo 'To view logs on host: sudo mount -o loop {logs_disk_path} /mnt/clonebox-logs'\"",
2498
+ f" - echo 'Logs disk mounted at /mnt/logs - backing file: {vm_logs_img_path}'",
2468
2499
  ]
2469
2500
  )
2470
2501
 
clonebox/validator.py CHANGED
@@ -76,39 +76,38 @@ class VMValidator:
76
76
 
77
77
  pid = response["return"]["pid"]
78
78
 
79
- # Wait a bit for command to complete
80
- time.sleep(0.3)
81
-
82
- # Get result
83
- status_result = subprocess.run(
84
- [
85
- "virsh",
86
- "--connect",
87
- self.conn_uri,
88
- "qemu-agent-command",
89
- self.vm_name,
90
- f'{{"execute":"guest-exec-status","arguments":{{"pid":{pid}}}}}',
91
- ],
92
- capture_output=True,
93
- text=True,
94
- timeout=5,
95
- )
79
+ deadline = time.time() + timeout
80
+ while time.time() < deadline:
81
+ status_result = subprocess.run(
82
+ [
83
+ "virsh",
84
+ "--connect",
85
+ self.conn_uri,
86
+ "qemu-agent-command",
87
+ self.vm_name,
88
+ f'{{"execute":"guest-exec-status","arguments":{{"pid":{pid}}}}}',
89
+ ],
90
+ capture_output=True,
91
+ text=True,
92
+ timeout=5,
93
+ )
96
94
 
97
- if status_result.returncode != 0:
98
- return None
95
+ if status_result.returncode != 0:
96
+ return None
99
97
 
100
- status_resp = json.loads(status_result.stdout)
101
- if "return" not in status_resp:
102
- return None
98
+ status_resp = json.loads(status_result.stdout)
99
+ if "return" not in status_resp:
100
+ return None
103
101
 
104
- ret = status_resp["return"]
105
- if not ret.get("exited", False):
106
- return None
102
+ ret = status_resp["return"]
103
+ if ret.get("exited", False):
104
+ if "out-data" in ret:
105
+ return base64.b64decode(ret["out-data"]).decode().strip()
106
+ return ""
107
107
 
108
- if "out-data" in ret:
109
- return base64.b64decode(ret["out-data"]).decode().strip()
108
+ time.sleep(0.2)
110
109
 
111
- return ""
110
+ return None
112
111
 
113
112
  except Exception:
114
113
  return None
@@ -397,6 +396,8 @@ class VMValidator:
397
396
  svc_table.add_column("PID", justify="right", style="dim")
398
397
  svc_table.add_column("Note", style="dim")
399
398
 
399
+ setup_in_progress = self._setup_in_progress() is True
400
+
400
401
  for service in services:
401
402
  if service in self.VM_EXCLUDED_SERVICES:
402
403
  svc_table.add_row(service, "[dim]—[/]", "[dim]—[/]", "[dim]—[/]", "host-only")
@@ -413,6 +414,7 @@ class VMValidator:
413
414
  continue
414
415
 
415
416
  self.results["services"]["total"] += 1
417
+ setup_in_progress = self._setup_in_progress() is True
416
418
 
417
419
  enabled_cmd = f"systemctl is-enabled {service} 2>/dev/null"
418
420
  enabled_status = self._exec_in_vm(enabled_cmd)
@@ -970,6 +972,138 @@ class VMValidator:
970
972
  except Exception:
971
973
  return False
972
974
 
975
+ def validate_disk_space(self) -> Dict:
976
+ """Validate disk space on root filesystem."""
977
+ self.console.print("\n[bold]💾 Validating Disk Space...[/]")
978
+
979
+ df_output = self._exec_in_vm("df -h / --output=pcent,avail,size | tail -n 1", timeout=20)
980
+ if not df_output:
981
+ self.console.print("[red]❌ Could not check disk space[/]")
982
+ return {"status": "error"}
983
+
984
+ try:
985
+ # Format: pcent avail size
986
+ # Example: 98% 100M 30G
987
+ parts = df_output.split()
988
+ usage_pct = int(parts[0].replace('%', ''))
989
+ avail = parts[1]
990
+ total = parts[2]
991
+
992
+ self.results["disk"] = {
993
+ "usage_pct": usage_pct,
994
+ "avail": avail,
995
+ "total": total
996
+ }
997
+
998
+ if usage_pct > 95:
999
+ self.console.print(f"[red]❌ Disk nearly full: {usage_pct}% used ({avail} available of {total})[/]")
1000
+ status = "fail"
1001
+ elif usage_pct > 80:
1002
+ self.console.print(f"[yellow]⚠️ Disk usage high: {usage_pct}% used ({avail} available of {total})[/]")
1003
+ status = "warning"
1004
+ else:
1005
+ self.console.print(f"[green]✅ Disk space OK: {usage_pct}% used ({avail} available of {total})[/]")
1006
+ status = "pass"
1007
+
1008
+ if usage_pct > 80:
1009
+ self._print_disk_usage_breakdown()
1010
+
1011
+ return self.results["disk"]
1012
+ except Exception as e:
1013
+ self.console.print(f"[red]❌ Error parsing df output: {e}[/]")
1014
+ return {"status": "error"}
1015
+
1016
+ def _print_disk_usage_breakdown(self) -> None:
1017
+ def _parse_du_lines(out: Optional[str]) -> List[Tuple[str, str]]:
1018
+ if not out:
1019
+ return []
1020
+ rows: List[Tuple[str, str]] = []
1021
+ for line in out.splitlines():
1022
+ line = line.strip()
1023
+ if not line:
1024
+ continue
1025
+ parts = line.split(maxsplit=1)
1026
+ if len(parts) != 2:
1027
+ continue
1028
+ size, path = parts
1029
+ rows.append((path, size))
1030
+ return rows
1031
+
1032
+ def _dir_size(path: str, timeout: int = 30) -> Optional[str]:
1033
+ out = self._exec_in_vm(f"du -x -s -h {path} 2>/dev/null | head -n 1 | cut -f1", timeout=timeout)
1034
+ return out.strip() if out else None
1035
+
1036
+ self.console.print("\n[bold]📁 Disk usage breakdown (largest directories)[/]")
1037
+
1038
+ top_level = self._exec_in_vm(
1039
+ "du -x -h --max-depth=1 / 2>/dev/null | sort -hr | head -n 15",
1040
+ timeout=60,
1041
+ )
1042
+ top_rows = _parse_du_lines(top_level)
1043
+
1044
+ if top_rows:
1045
+ table = Table(title="Disk Usage: / (Top 15)", border_style="cyan")
1046
+ table.add_column("Path", style="bold")
1047
+ table.add_column("Size", justify="right")
1048
+ for path, size in top_rows:
1049
+ table.add_row(path, size)
1050
+ self.console.print(table)
1051
+ else:
1052
+ self.console.print("[dim]Could not compute top-level directory sizes (du may be busy)[/]")
1053
+
1054
+ var_sz = _dir_size("/var")
1055
+ home_sz = _dir_size("/home")
1056
+ if var_sz or home_sz:
1057
+ sum_table = Table(title="Disk Usage: Key Directories", border_style="cyan")
1058
+ sum_table.add_column("Path", style="bold")
1059
+ sum_table.add_column("Size", justify="right")
1060
+ for p in ["/var", "/var/lib", "/var/log", "/var/cache", "/var/lib/snapd", "/home", "/home/ubuntu", "/tmp"]:
1061
+ sz = _dir_size(p, timeout=30)
1062
+ if sz:
1063
+ sum_table.add_row(p, sz)
1064
+ self.console.print(sum_table)
1065
+
1066
+ var_breakdown = self._exec_in_vm(
1067
+ "du -x -h --max-depth=1 /var 2>/dev/null | sort -hr | head -n 12",
1068
+ timeout=60,
1069
+ )
1070
+ var_rows = _parse_du_lines(var_breakdown)
1071
+ if var_rows:
1072
+ vtable = Table(title="Disk Usage: /var (Top 12)", border_style="cyan")
1073
+ vtable.add_column("Path", style="bold")
1074
+ vtable.add_column("Size", justify="right")
1075
+ for path, size in var_rows:
1076
+ vtable.add_row(path, size)
1077
+ self.console.print(vtable)
1078
+
1079
+ home_breakdown = self._exec_in_vm(
1080
+ "du -x -h --max-depth=2 /home/ubuntu 2>/dev/null | sort -hr | head -n 12",
1081
+ timeout=60,
1082
+ )
1083
+ home_rows = _parse_du_lines(home_breakdown)
1084
+ if home_rows:
1085
+ htable = Table(title="Disk Usage: /home/ubuntu (Top 12)", border_style="cyan")
1086
+ htable.add_column("Path", style="bold")
1087
+ htable.add_column("Size", justify="right")
1088
+ for path, size in home_rows:
1089
+ htable.add_row(path, size)
1090
+ self.console.print(htable)
1091
+
1092
+ copy_paths = self.config.get("copy_paths", None)
1093
+ if not isinstance(copy_paths, dict) or not copy_paths:
1094
+ copy_paths = self.config.get("app_data_paths", {})
1095
+ if copy_paths:
1096
+ ctable = Table(title="Disk Usage: Configured Imported Paths", border_style="cyan")
1097
+ ctable.add_column("Guest Path", style="bold")
1098
+ ctable.add_column("Size", justify="right")
1099
+ for _, guest_path in copy_paths.items():
1100
+ sz = _dir_size(guest_path, timeout=30)
1101
+ if sz:
1102
+ ctable.add_row(str(guest_path), sz)
1103
+ else:
1104
+ ctable.add_row(str(guest_path), "—")
1105
+ self.console.print(ctable)
1106
+
973
1107
  def validate_all(self) -> Dict:
974
1108
  """Run all validations and return comprehensive results."""
975
1109
  self.console.print("[bold cyan]🔍 Running Full Validation...[/]")
@@ -1026,6 +1160,7 @@ class VMValidator:
1026
1160
  )
1027
1161
 
1028
1162
  # Run all validations
1163
+ self.validate_disk_space()
1029
1164
  self.validate_mounts()
1030
1165
  self.validate_packages()
1031
1166
  self.validate_snap_packages()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 1.1.16
3
+ Version: 1.1.18
4
4
  Summary: Clone your workstation environment to an isolated VM with selective apps, paths and services
5
5
  Author: CloneBox Team
6
6
  License: Apache-2.0
@@ -1,8 +1,8 @@
1
1
  clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
2
  clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
3
  clonebox/audit.py,sha256=1W9vaIjB0A--_p7CgE3cIP5RNckJG1RxJrL-tOb-QmU,14298
4
- clonebox/cli.py,sha256=k3q-JrzsiDcGPjhzHbm1jPKajxD2chYJxSGGftCbA20,178481
5
- clonebox/cloner.py,sha256=ZMeIpEuzT8ZO9fav99JdE3krWT6e06K8pBR7XxDKbos,106958
4
+ clonebox/cli.py,sha256=x0VQ9WI1KZrNQGAh7FDf_036jDypdT5Q7vuTlr-dgi0,179087
5
+ clonebox/cloner.py,sha256=WEMNjhHVdXwiA2zCq14fr5eVD6vhLuX4vnJ47p3SiNI,107839
6
6
  clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
7
7
  clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
8
8
  clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
@@ -20,7 +20,7 @@ clonebox/resource_monitor.py,sha256=lDR9KyPbVtImeeOkOBPPVP-5yCgoL5hsVFPZ_UqsY0w,
20
20
  clonebox/resources.py,sha256=IkuM4OdSDV4qhyc0eIynwbAHBTv0aVSxxW-gghsnCAs,6815
21
21
  clonebox/rollback.py,sha256=hpwO-8Ehe1pW0wHuZvJkC_qxZ6yEo9otCJRhGIUArCo,5711
22
22
  clonebox/secrets.py,sha256=l1jwJcEPB1qMoGNLPjyrkKKr1khh9VmftFJI9BWhgK0,10628
23
- clonebox/validator.py,sha256=uXBkdrWSk8oBhRnD1y4mwxRdxOPduRraroSIs14bAo8,45342
23
+ clonebox/validator.py,sha256=PWPgkR9cjewTh6-uW_2YyjxuEj2wI9O7h7hk7NK8nAI,51181
24
24
  clonebox/backends/libvirt_backend.py,sha256=sIHFIvFO1hIOXEFR_foSkOGBgIzaJVQs-njOU8GdafA,7170
25
25
  clonebox/backends/qemu_disk.py,sha256=YsGjYX5sbEf35Y4yjTpNkZat73a4RGBxY-KTVzJhqIs,1687
26
26
  clonebox/backends/subprocess_runner.py,sha256=c-IyaMxM1cmUu64h654oAvulm83K5Mu-VQxXJ_0BOds,1506
@@ -40,9 +40,9 @@ clonebox/snapshots/manager.py,sha256=hGzM8V6ZJPXjTqj47c4Kr8idlE-c1Q3gPUvuw1HvS1A
40
40
  clonebox/snapshots/models.py,sha256=sRnn3OZE8JG9FZJlRuA3ihO-JXoPCQ3nD3SQytflAao,6206
41
41
  clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
42
42
  clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
43
- clonebox-1.1.16.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
44
- clonebox-1.1.16.dist-info/METADATA,sha256=mPn_uTUwAXlnakSJc8DLWyg1KlNERP5-oIEyWABxuwA,49052
45
- clonebox-1.1.16.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
- clonebox-1.1.16.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
47
- clonebox-1.1.16.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
48
- clonebox-1.1.16.dist-info/RECORD,,
43
+ clonebox-1.1.18.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
44
+ clonebox-1.1.18.dist-info/METADATA,sha256=n1VJE8-Mk-zf9QgHBbvt1Z5fbJMdpD1vjZVVjEBeaxE,49052
45
+ clonebox-1.1.18.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
46
+ clonebox-1.1.18.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
47
+ clonebox-1.1.18.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
48
+ clonebox-1.1.18.dist-info/RECORD,,