clonebox 0.1.7__py3-none-any.whl → 0.1.8__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.
clonebox/cli.py CHANGED
@@ -602,6 +602,18 @@ def generate_clonebox_yaml(
602
602
  if host_folder.exists() and str(host_folder) not in paths_mapping:
603
603
  paths_mapping[str(host_folder)] = guest_folder
604
604
 
605
+ # Detect and add app-specific data directories for running applications
606
+ # This includes browser profiles, IDE settings, credentials, extensions, etc.
607
+ app_data_dirs = detector.detect_app_data_dirs(snapshot.applications)
608
+ app_data_mapping = {}
609
+ for app_data in app_data_dirs:
610
+ host_path = app_data["path"]
611
+ if host_path not in paths_mapping:
612
+ # Map to same relative path in VM user home
613
+ rel_path = host_path.replace(str(home_dir), "").lstrip("/")
614
+ guest_path = f"/home/ubuntu/{rel_path}"
615
+ app_data_mapping[host_path] = guest_path
616
+
605
617
  # Determine VM name
606
618
  if not vm_name:
607
619
  if target_path:
@@ -654,15 +666,20 @@ def generate_clonebox_yaml(
654
666
  "snap_packages": all_snap_packages,
655
667
  "post_commands": [], # User can add custom commands to run after setup
656
668
  "paths": paths_mapping,
669
+ "app_data_paths": app_data_mapping, # App-specific config/data directories
657
670
  "detected": {
658
671
  "running_apps": [
659
- {"name": a.name, "cwd": a.working_dir, "memory_mb": round(a.memory_mb)}
672
+ {"name": a.name, "cwd": a.working_dir or "", "memory_mb": round(a.memory_mb)}
660
673
  for a in snapshot.applications[:10]
661
674
  ],
675
+ "app_data_dirs": [
676
+ {"path": d["path"], "app": d["app"], "size_mb": d["size_mb"]}
677
+ for d in app_data_dirs[:15]
678
+ ],
662
679
  "all_paths": {
663
- "projects": paths_by_type["project"],
664
- "configs": paths_by_type["config"][:5],
665
- "data": paths_by_type["data"][:5],
680
+ "projects": list(paths_by_type["project"]),
681
+ "configs": list(paths_by_type["config"][:5]),
682
+ "data": list(paths_by_type["data"][:5]),
666
683
  },
667
684
  },
668
685
  }
@@ -781,13 +798,17 @@ def create_vm_from_config(
781
798
  replace: bool = False,
782
799
  ) -> str:
783
800
  """Create VM from YAML config dict."""
801
+ # Merge paths and app_data_paths
802
+ all_paths = config.get("paths", {}).copy()
803
+ all_paths.update(config.get("app_data_paths", {}))
804
+
784
805
  vm_config = VMConfig(
785
806
  name=config["vm"]["name"],
786
807
  ram_mb=config["vm"].get("ram_mb", 4096),
787
808
  vcpus=config["vm"].get("vcpus", 4),
788
809
  gui=config["vm"].get("gui", True),
789
810
  base_image=config["vm"].get("base_image"),
790
- paths=config.get("paths", {}),
811
+ paths=all_paths,
791
812
  packages=config.get("packages", []),
792
813
  snap_packages=config.get("snap_packages", []),
793
814
  services=config.get("services", []),
@@ -915,16 +936,27 @@ def cmd_clone(args):
915
936
  password = config['vm'].get('password', 'ubuntu')
916
937
  console.print("\n[bold yellow]⏰ GUI Setup Process:[/]")
917
938
  console.print(" [yellow]•[/] Installing desktop environment (~5-10 minutes)")
939
+ console.print(" [yellow]•[/] Running health checks on all components")
918
940
  console.print(" [yellow]•[/] Automatic restart after installation")
919
941
  console.print(" [yellow]•[/] GUI login screen will appear")
920
942
  console.print(f" [yellow]•[/] Login: [cyan]{username}[/] / [cyan]{'*' * len(password)}[/] (from .env)")
921
943
  console.print("\n[dim]💡 Progress will be monitored automatically below[/]")
922
944
 
945
+ # Show health check info
946
+ console.print("\n[bold]📊 Health Check (inside VM):[/]")
947
+ console.print(" [cyan]cat /var/log/clonebox-health.log[/] # View full report")
948
+ console.print(" [cyan]cat /var/log/clonebox-health-status[/] # Quick status")
949
+ console.print(" [cyan]clonebox-health[/] # Re-run health check")
950
+
923
951
  # Show mount instructions
924
- if config.get("paths"):
925
- console.print("\n[bold]Inside VM, mount paths with:[/]")
926
- for idx, (host, guest) in enumerate(config["paths"].items()):
927
- console.print(f" [cyan]sudo mount -t 9p -o trans=virtio mount{idx} {guest}[/]")
952
+ all_paths = config.get("paths", {}).copy()
953
+ all_paths.update(config.get("app_data_paths", {}))
954
+ if all_paths:
955
+ console.print("\n[bold]📁 Mounted paths (automatic):[/]")
956
+ for idx, (host, guest) in enumerate(list(all_paths.items())[:5]):
957
+ console.print(f" [dim]{host}[/] → [cyan]{guest}[/]")
958
+ if len(all_paths) > 5:
959
+ console.print(f" [dim]... and {len(all_paths) - 5} more paths[/]")
928
960
  except PermissionError as e:
929
961
  console.print(f"[red]❌ Permission Error:[/]\n{e}")
930
962
  console.print("\n[yellow]💡 Try running with --user flag:[/]")
clonebox/cloner.py CHANGED
@@ -476,6 +476,179 @@ class SelectiveVMCloner:
476
476
 
477
477
  return ET.tostring(root, encoding="unicode")
478
478
 
479
+ def _generate_health_check_script(self, config: VMConfig) -> str:
480
+ """Generate a health check script that validates all installed components."""
481
+ import base64
482
+
483
+ # Build package check commands
484
+ apt_checks = []
485
+ for pkg in config.packages:
486
+ apt_checks.append(f'check_apt_package "{pkg}"')
487
+
488
+ snap_checks = []
489
+ for pkg in config.snap_packages:
490
+ snap_checks.append(f'check_snap_package "{pkg}"')
491
+
492
+ service_checks = []
493
+ for svc in config.services:
494
+ service_checks.append(f'check_service "{svc}"')
495
+
496
+ mount_checks = []
497
+ for idx, (host_path, guest_path) in enumerate(config.paths.items()):
498
+ mount_checks.append(f'check_mount "{guest_path}" "mount{idx}"')
499
+
500
+ apt_checks_str = "\n".join(apt_checks) if apt_checks else "echo 'No apt packages to check'"
501
+ snap_checks_str = "\n".join(snap_checks) if snap_checks else "echo 'No snap packages to check'"
502
+ service_checks_str = "\n".join(service_checks) if service_checks else "echo 'No services to check'"
503
+ mount_checks_str = "\n".join(mount_checks) if mount_checks else "echo 'No mounts to check'"
504
+
505
+ script = f'''#!/bin/bash
506
+ # CloneBox Health Check Script
507
+ # Generated automatically - validates all installed components
508
+
509
+ REPORT_FILE="/var/log/clonebox-health.log"
510
+ PASSED=0
511
+ FAILED=0
512
+ WARNINGS=0
513
+
514
+ # Colors for output
515
+ RED='\\033[0;31m'
516
+ GREEN='\\033[0;32m'
517
+ YELLOW='\\033[1;33m'
518
+ NC='\\033[0m'
519
+
520
+ log() {{
521
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$REPORT_FILE"
522
+ }}
523
+
524
+ check_apt_package() {{
525
+ local pkg="$1"
526
+ if dpkg -l "$pkg" 2>/dev/null | grep -q "^ii"; then
527
+ log "[PASS] APT package '$pkg' is installed"
528
+ ((PASSED++))
529
+ return 0
530
+ else
531
+ log "[FAIL] APT package '$pkg' is NOT installed"
532
+ ((FAILED++))
533
+ return 1
534
+ fi
535
+ }}
536
+
537
+ check_snap_package() {{
538
+ local pkg="$1"
539
+ if snap list "$pkg" &>/dev/null; then
540
+ log "[PASS] Snap package '$pkg' is installed"
541
+ ((PASSED++))
542
+ return 0
543
+ else
544
+ log "[FAIL] Snap package '$pkg' is NOT installed"
545
+ ((FAILED++))
546
+ return 1
547
+ fi
548
+ }}
549
+
550
+ check_service() {{
551
+ local svc="$1"
552
+ if systemctl is-enabled "$svc" &>/dev/null; then
553
+ if systemctl is-active "$svc" &>/dev/null; then
554
+ log "[PASS] Service '$svc' is enabled and running"
555
+ ((PASSED++))
556
+ return 0
557
+ else
558
+ log "[WARN] Service '$svc' is enabled but not running"
559
+ ((WARNINGS++))
560
+ return 1
561
+ fi
562
+ else
563
+ log "[INFO] Service '$svc' is not enabled (may be optional)"
564
+ return 0
565
+ fi
566
+ }}
567
+
568
+ check_mount() {{
569
+ local path="$1"
570
+ local tag="$2"
571
+ if mountpoint -q "$path" 2>/dev/null; then
572
+ log "[PASS] Mount '$path' ($tag) is active"
573
+ ((PASSED++))
574
+ return 0
575
+ elif [ -d "$path" ]; then
576
+ log "[WARN] Directory '$path' exists but not mounted"
577
+ ((WARNINGS++))
578
+ return 1
579
+ else
580
+ log "[INFO] Mount point '$path' does not exist yet"
581
+ return 0
582
+ fi
583
+ }}
584
+
585
+ check_gui() {{
586
+ if systemctl get-default | grep -q graphical; then
587
+ log "[PASS] System configured for graphical target"
588
+ ((PASSED++))
589
+ if systemctl is-active gdm3 &>/dev/null || systemctl is-active gdm &>/dev/null; then
590
+ log "[PASS] Display manager (GDM) is running"
591
+ ((PASSED++))
592
+ else
593
+ log "[WARN] Display manager not yet running (may start after reboot)"
594
+ ((WARNINGS++))
595
+ fi
596
+ else
597
+ log "[INFO] System not configured for GUI"
598
+ fi
599
+ }}
600
+
601
+ # Start health check
602
+ log "=========================================="
603
+ log "CloneBox Health Check Report"
604
+ log "VM Name: {config.name}"
605
+ log "Date: $(date)"
606
+ log "=========================================="
607
+
608
+ log ""
609
+ log "--- APT Packages ---"
610
+ {apt_checks_str}
611
+
612
+ log ""
613
+ log "--- Snap Packages ---"
614
+ {snap_checks_str}
615
+
616
+ log ""
617
+ log "--- Services ---"
618
+ {service_checks_str}
619
+
620
+ log ""
621
+ log "--- Mounts ---"
622
+ {mount_checks_str}
623
+
624
+ log ""
625
+ log "--- GUI Status ---"
626
+ check_gui
627
+
628
+ log ""
629
+ log "=========================================="
630
+ log "Health Check Summary"
631
+ log "=========================================="
632
+ log "Passed: $PASSED"
633
+ log "Failed: $FAILED"
634
+ log "Warnings: $WARNINGS"
635
+
636
+ if [ $FAILED -eq 0 ]; then
637
+ log ""
638
+ log "[SUCCESS] All critical checks passed!"
639
+ echo "HEALTH_STATUS=OK" > /var/log/clonebox-health-status
640
+ exit 0
641
+ else
642
+ log ""
643
+ log "[ERROR] Some checks failed. Review log for details."
644
+ echo "HEALTH_STATUS=FAILED" > /var/log/clonebox-health-status
645
+ exit 1
646
+ fi
647
+ '''
648
+ # Encode script to base64 for safe embedding in cloud-init
649
+ encoded = base64.b64encode(script.encode()).decode()
650
+ return encoded
651
+
479
652
  def _create_cloudinit_iso(self, vm_dir: Path, config: VMConfig) -> Path:
480
653
  """Create cloud-init ISO with user-data and meta-data."""
481
654
 
@@ -540,12 +713,12 @@ class SelectiveVMCloner:
540
713
  for cmd in config.post_commands:
541
714
  runcmd_lines.append(f" - {cmd}")
542
715
 
543
- # Validation - check installed packages and log results
544
- runcmd_lines.append(" - echo '=== CloneBox Setup Validation ===' >> /var/log/clonebox-setup.log")
545
- runcmd_lines.append(" - dpkg -l | grep -E 'ii' | wc -l >> /var/log/clonebox-setup.log")
546
- runcmd_lines.append(" - snap list >> /var/log/clonebox-setup.log 2>/dev/null || true")
716
+ # Generate health check script
717
+ health_script = self._generate_health_check_script(config)
718
+ runcmd_lines.append(f" - echo '{health_script}' | base64 -d > /usr/local/bin/clonebox-health")
719
+ runcmd_lines.append(" - chmod +x /usr/local/bin/clonebox-health")
720
+ runcmd_lines.append(" - /usr/local/bin/clonebox-health >> /var/log/clonebox-health.log 2>&1")
547
721
  runcmd_lines.append(" - echo 'CloneBox VM ready!' > /var/log/clonebox-ready")
548
- runcmd_lines.append(" - echo 'Setup completed at:' $(date) >> /var/log/clonebox-setup.log")
549
722
 
550
723
  # Add reboot command at the end if GUI is enabled
551
724
  if config.gui:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 0.1.7
3
+ Version: 0.1.8
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
@@ -0,0 +1,11 @@
1
+ clonebox/__init__.py,sha256=IOk7G0DiSQ33EGbFC0xbnnFB9aou_6yuyFxvycQEvA0,407
2
+ clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
+ clonebox/cli.py,sha256=Kp-1C9Be39ZK9fVe-4bQLyb25W2otXk8597MvOOee7M,43133
4
+ clonebox/cloner.py,sha256=CMy0NWxOiUMULaQuDHY0_LDUaELW0_h4ewj_dZ_5WHw,31171
5
+ clonebox/detector.py,sha256=4fu04Ty6KC82WkcJZ5UL5TqXpWYE7Kb7R0uJ-9dtbCk,21635
6
+ clonebox-0.1.8.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
+ clonebox-0.1.8.dist-info/METADATA,sha256=6a8ry0WjVyY21D3r1teMvSEL7zwVaoweH9GRtFxnZJY,15582
8
+ clonebox-0.1.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ clonebox-0.1.8.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
10
+ clonebox-0.1.8.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
11
+ clonebox-0.1.8.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- clonebox/__init__.py,sha256=IOk7G0DiSQ33EGbFC0xbnnFB9aou_6yuyFxvycQEvA0,407
2
- clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
- clonebox/cli.py,sha256=ngC6Pwbfr1brFkf_0ODlFzDikojTOkA3Yw-3iY4vrmY,41441
4
- clonebox/cloner.py,sha256=eDIxORCtnqG9mFKJl4OmW9F6tkEJp7dENYX-2x1Favg,26453
5
- clonebox/detector.py,sha256=4fu04Ty6KC82WkcJZ5UL5TqXpWYE7Kb7R0uJ-9dtbCk,21635
6
- clonebox-0.1.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
- clonebox-0.1.7.dist-info/METADATA,sha256=wsaV7GyZ6zfLCxLuNoSopr4XUHdyTlrgxxJMiHuFnKU,15582
8
- clonebox-0.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
- clonebox-0.1.7.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
10
- clonebox-0.1.7.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
11
- clonebox-0.1.7.dist-info/RECORD,,