clonebox 0.1.29__py3-none-any.whl → 1.1.1__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/cloner.py CHANGED
@@ -699,12 +699,18 @@ test_launch() {{
699
699
  fi
700
700
  ;;
701
701
  firefox)
702
- if timeout 10 firefox --headless --screenshot /tmp/ff-test.png about:blank &>/dev/null; then
703
- rm -f /tmp/ff-test.png
702
+ if timeout 15 firefox --headless --version &>/dev/null; then
704
703
  return 0
705
704
  else
706
- echo "Firefox headless test failed" >> "$error_detail"
707
- timeout 5 firefox --version >> "$error_detail" 2>&1 || true
705
+ echo "Firefox test failed" >> "$error_detail"
706
+ return 1
707
+ fi
708
+ ;;
709
+ google-chrome|google-chrome-stable)
710
+ if timeout 15 google-chrome-stable --headless --version &>/dev/null; then
711
+ return 0
712
+ else
713
+ echo "Chrome test failed" >> "$error_detail"
708
714
  return 1
709
715
  fi
710
716
  ;;
@@ -1290,17 +1296,15 @@ Description=CloneBox Boot Diagnostic
1290
1296
  After=network-online.target snapd.service
1291
1297
  Before=gdm.service display-manager.service
1292
1298
  Wants=network-online.target
1299
+ DefaultDependencies=no
1293
1300
 
1294
1301
  [Service]
1295
1302
  Type=oneshot
1296
1303
  ExecStart=/usr/local/bin/clonebox-boot-diagnostic
1297
1304
  StandardOutput=journal+console
1298
1305
  StandardError=journal+console
1299
- TTYPath=/dev/tty1
1300
- TTYReset=yes
1301
- TTYVHangup=yes
1306
+ TimeoutStartSec=300
1302
1307
  RemainAfterExit=yes
1303
- TimeoutStartSec=600
1304
1308
 
1305
1309
  [Install]
1306
1310
  WantedBy=multi-user.target"""
@@ -1722,11 +1726,13 @@ esac
1722
1726
  service_content = f"""[Unit]
1723
1727
  Description={app["display_name"]} Autostart
1724
1728
  After={app["after"]}
1729
+ PartOf=graphical-session.target
1725
1730
 
1726
1731
  [Service]
1727
1732
  Type=simple
1728
1733
  Environment=DISPLAY=:0
1729
1734
  Environment=XDG_RUNTIME_DIR=/run/user/1000
1735
+ Environment=XDG_SESSION_TYPE=x11
1730
1736
  ExecStart={app["exec"]}
1731
1737
  Restart=on-failure
1732
1738
  RestartSec=5
@@ -1738,6 +1744,29 @@ WantedBy=default.target
1738
1744
  service_path = f"/home/{config.username}/.config/systemd/user/{app['name']}.service"
1739
1745
  runcmd_lines.append(f" - echo '{service_b64}' | base64 -d > {service_path}")
1740
1746
 
1747
+ # Fix snap interfaces reconnection script to be more robust
1748
+ snap_fix_script = r'''#!/bin/bash
1749
+ # Fix snap interfaces for GUI apps
1750
+ set -euo pipefail
1751
+ SNAP_LIST=$(snap list | awk 'NR>1 {print $1}')
1752
+ for snap in $SNAP_LIST; do
1753
+ case "$snap" in
1754
+ pycharm-community|chromium|firefox|code|slack|spotify)
1755
+ echo "Connecting interfaces for $snap..."
1756
+ IFACES="desktop desktop-legacy x11 wayland home network network-bind audio-playback"
1757
+ for iface in $IFACES; do
1758
+ snap connect "$snap:$iface" ":$iface" 2>/dev/null || true
1759
+ done
1760
+ ;;
1761
+ esac
1762
+ done
1763
+ systemctl restart snapd 2>/dev/null || true
1764
+ '''
1765
+ snap_fix_b64 = base64.b64encode(snap_fix_script.encode()).decode()
1766
+ runcmd_lines.append(f" - echo '{snap_fix_b64}' | base64 -d > /usr/local/bin/clonebox-fix-snaps")
1767
+ runcmd_lines.append(" - chmod +x /usr/local/bin/clonebox-fix-snaps")
1768
+ runcmd_lines.append(" - /usr/local/bin/clonebox-fix-snaps || true")
1769
+
1741
1770
  # Generate desktop autostart files for GUI apps (alternative to systemd user services)
1742
1771
  for app in autostart_apps:
1743
1772
  desktop_content = f"""[Desktop Entry]
clonebox/dashboard.py CHANGED
@@ -18,15 +18,25 @@ def _run_clonebox(args: List[str]) -> subprocess.CompletedProcess:
18
18
 
19
19
 
20
20
  def _render_table(title: str, headers: List[str], rows: List[List[str]]) -> str:
21
- head_html = "".join(f"<th>{h}</th>" for h in headers)
22
- body_html = "".join("<tr>" + "".join(f"<td>{c}</td>" for c in row) + "</tr>" for row in rows)
21
+ head_html = "".join(
22
+ f'<th class="px-4 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">{h}</th>'
23
+ for h in headers
24
+ )
25
+ body_html = "".join(
26
+ '<tr class="hover:bg-gray-700 transition-colors">'
27
+ + "".join(f'<td class="px-4 py-3 whitespace-nowrap">{c}</td>' for c in row)
28
+ + "</tr>"
29
+ for row in rows
30
+ )
23
31
 
24
32
  return (
25
- f"<h2>{title}</h2>"
26
- "<table>"
27
- f"<thead><tr>{head_html}</tr></thead>"
28
- f"<tbody>{body_html}</tbody>"
29
- "</table>"
33
+ f'<h2 class="text-xl font-semibold text-cyan-400 mb-4 flex items-center gap-2">'
34
+ f'{"🖥️" if "VM" in title else "🐳"} {title}</h2>'
35
+ '<div class="overflow-x-auto">'
36
+ '<table class="min-w-full divide-y divide-gray-700">'
37
+ f'<thead class="bg-gray-900"><tr>{head_html}</tr></thead>'
38
+ f'<tbody class="divide-y divide-gray-700">{body_html}</tbody>'
39
+ "</table></div>"
30
40
  )
31
41
 
32
42
 
@@ -34,24 +44,48 @@ def _render_table(title: str, headers: List[str], rows: List[List[str]]) -> str:
34
44
  async def dashboard() -> str:
35
45
  return """
36
46
  <!DOCTYPE html>
37
- <html>
47
+ <html lang="en">
38
48
  <head>
49
+ <meta charset="UTF-8">
50
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
39
51
  <title>CloneBox Dashboard</title>
40
52
  <script src="https://unpkg.com/htmx.org@1.9.10"></script>
53
+ <script src="https://cdn.tailwindcss.com"></script>
41
54
  <style>
42
- body { font-family: system-ui, -apple-system, sans-serif; margin: 20px; }
43
- table { border-collapse: collapse; width: 100%; margin-bottom: 24px; }
44
- th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
45
- th { background: #f6f6f6; }
46
- code { background: #f6f6f6; padding: 2px 4px; border-radius: 4px; }
55
+ .htmx-request { opacity: 0.5; transition: opacity 200ms ease-in; }
47
56
  </style>
48
57
  </head>
49
- <body>
50
- <h1>CloneBox Dashboard</h1>
51
- <p>Auto-refresh every 5s. JSON endpoints: <code>/api/vms.json</code>, <code>/api/containers.json</code></p>
52
-
53
- <div id="vms" hx-get="/api/vms" hx-trigger="load, every 5s">Loading VMs...</div>
54
- <div id="containers" hx-get="/api/containers" hx-trigger="load, every 5s">Loading containers...</div>
58
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
59
+ <div class="max-w-6xl mx-auto px-4 py-8">
60
+ <header class="mb-8">
61
+ <h1 class="text-3xl font-bold text-cyan-400 flex items-center gap-3">
62
+ <span class="text-4xl">📦</span> CloneBox Dashboard
63
+ </h1>
64
+ <p class="text-gray-400 mt-2">
65
+ Auto-refresh every 3s &bull;
66
+ <code class="bg-gray-800 px-2 py-1 rounded text-sm">/api/vms.json</code>
67
+ <code class="bg-gray-800 px-2 py-1 rounded text-sm ml-1">/api/containers.json</code>
68
+ </p>
69
+ </header>
70
+
71
+ <div class="grid gap-6">
72
+ <section class="bg-gray-800 rounded-lg p-6 shadow-lg">
73
+ <div id="vms" hx-get="/api/vms" hx-trigger="load, every 3s">
74
+ <div class="animate-pulse text-gray-500">Loading VMs...</div>
75
+ </div>
76
+ </section>
77
+
78
+ <section class="bg-gray-800 rounded-lg p-6 shadow-lg">
79
+ <div id="containers" hx-get="/api/containers" hx-trigger="load, every 3s">
80
+ <div class="animate-pulse text-gray-500">Loading containers...</div>
81
+ </div>
82
+ </section>
83
+ </div>
84
+
85
+ <footer class="mt-8 text-center text-gray-500 text-sm">
86
+ CloneBox v1.1 &bull; <a href="https://github.com/wronai/clonebox" class="text-cyan-400 hover:underline">GitHub</a>
87
+ </footer>
88
+ </div>
55
89
  </body>
56
90
  </html>
57
91
  """
@@ -69,7 +103,7 @@ async def api_vms() -> str:
69
103
  return f"<pre>Invalid JSON from clonebox list:\n{proc.stdout}</pre>"
70
104
 
71
105
  if not items:
72
- return "<h2>VMs</h2><p><em>No VMs found.</em></p>"
106
+ return '<h2 class="text-xl font-semibold text-cyan-400 mb-4">🖥️ VMs</h2><p class="text-gray-500 italic">No VMs found.</p>'
73
107
 
74
108
  rows = [
75
109
  [str(i.get("name", "")), str(i.get("state", "")), str(i.get("uuid", ""))] for i in items
@@ -89,7 +123,7 @@ async def api_containers() -> str:
89
123
  return f"<pre>Invalid JSON from clonebox container ps:\n{proc.stdout}</pre>"
90
124
 
91
125
  if not items:
92
- return "<h2>Containers</h2><p><em>No containers found.</em></p>"
126
+ return '<h2 class="text-xl font-semibold text-cyan-400 mb-4">🐳 Containers</h2><p class="text-gray-500 italic">No containers found.</p>'
93
127
 
94
128
  rows = [
95
129
  [
@@ -1,6 +1,26 @@
1
+ # Machine Learning Development Profile
2
+ # Usage: clonebox container up . --profile ml-dev
3
+ # clonebox clone . --profile ml-dev --user --run
4
+
1
5
  container:
2
6
  image: python:3.11-slim
3
- packages: ["pip", "jupyterlab"]
4
- ports: ["8888:8888"]
7
+ packages:
8
+ - pip
9
+ - jupyter
10
+ - build-essential
11
+ ports:
12
+ - "8888:8888"
13
+ env_from_dotenv: true
14
+
5
15
  vm:
6
- packages: ["python3-pip", "jupyterlab"]
16
+ packages:
17
+ - python3-pip
18
+ - python3-venv
19
+ - jupyterlab
20
+ - build-essential
21
+ - git
22
+ - curl
23
+ services:
24
+ - postgresql
25
+ ram_mb: 8192
26
+ vcpus: 4
@@ -0,0 +1,30 @@
1
+ # Web Development Stack Profile
2
+ # Usage: clonebox container up . --profile web-stack
3
+ # clonebox clone . --profile web-stack --user --run
4
+
5
+ container:
6
+ image: node:20-slim
7
+ packages:
8
+ - nginx
9
+ - curl
10
+ - git
11
+ ports:
12
+ - "3000:3000"
13
+ - "8080:80"
14
+ env_from_dotenv: true
15
+
16
+ vm:
17
+ packages:
18
+ - nginx
19
+ - nodejs
20
+ - npm
21
+ - postgresql
22
+ - redis-server
23
+ - git
24
+ - curl
25
+ services:
26
+ - nginx
27
+ - postgresql
28
+ - redis-server
29
+ ram_mb: 4096
30
+ vcpus: 4
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 0.1.29
3
+ Version: 1.1.1
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
@@ -42,6 +42,8 @@ Provides-Extra: test
42
42
  Requires-Dist: pytest>=7.0.0; extra == "test"
43
43
  Requires-Dist: pytest-cov>=4.0.0; extra == "test"
44
44
  Requires-Dist: pytest-timeout>=2.0.0; extra == "test"
45
+ Requires-Dist: pytest-asyncio; extra == "test"
46
+ Requires-Dist: fastapi>=0.100.0; extra == "test"
45
47
  Provides-Extra: dashboard
46
48
  Requires-Dist: fastapi>=0.100.0; extra == "dashboard"
47
49
  Requires-Dist: uvicorn>=0.22.0; extra == "dashboard"
@@ -100,13 +102,24 @@ CloneBox excels in scenarios where developers need:
100
102
  - Safe experimentation with system-level changes that can be discarded by simply deleting the VM
101
103
  - Quick onboarding for new team members who need a fully configured development environment
102
104
 
103
- ## What's Next
105
+ ## What's New in v1.1
104
106
 
105
- Project roadmap includes:
106
- - **v0.2.0**: `clonebox exec` command, VM snapshots, web dashboard MVP
107
- - **v0.3.0**: Container runtime integration (Podman/Docker), multi-VM orchestration
108
- - **v0.4.0**: Cloud provider support (AWS, GCP, Azure), Windows WSL2 support
109
- - **v1.0.0**: Production-ready with full monitoring, backup/restore, enterprise features
107
+ **v1.1.0** is production-ready with two full runtimes:
108
+
109
+ | Feature | Status |
110
+ |---------|--------|
111
+ | 🖥️ VM Runtime (libvirt/QEMU) | Stable |
112
+ | 🐳 Container Runtime (Podman/Docker) | ✅ Stable |
113
+ | 📊 Web Dashboard (FastAPI + HTMX + Tailwind) | ✅ Stable |
114
+ | 🎛️ Profiles System (`ml-dev`, `web-stack`) | ✅ Stable |
115
+ | 🔍 Auto-detection (services, apps, paths) | ✅ Stable |
116
+ | 🧪 95%+ Test Coverage | ✅ |
117
+
118
+ ### Roadmap
119
+
120
+ - **v1.2.0**: `clonebox exec` command, VM snapshots, snapshot restore
121
+ - **v1.3.0**: Multi-VM orchestration, cluster mode
122
+ - **v2.0.0**: Cloud provider support (AWS, GCP, Azure), Windows WSL2 support
110
123
 
111
124
  See [TODO.md](TODO.md) for detailed roadmap and [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
112
125
 
@@ -283,9 +296,12 @@ Simply run `clonebox` to start the interactive wizard:
283
296
  clonebox
284
297
 
285
298
  clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2 --disk-size-gb 30
299
+ # Sprawdź diagnostykę na żywo
300
+ clonebox watch . --user
286
301
 
287
- clonebox test . --user --validate
288
302
  clonebox test . --user --validate --require-running-apps
303
+ # Uruchom pełną walidację (wykorzystuje QGA do sprawdzenia serwisów wewnątrz)
304
+ clonebox test . --user --validate --smoke-test
289
305
  ```
290
306
 
291
307
  ### Profiles (Reusable presets)
@@ -0,0 +1,18 @@
1
+ clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
+ clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
+ clonebox/cli.py,sha256=0ZOE66WVbIDdmIBf0_VyWw4U-jzPo_T4pCTXkWect5o,114804
4
+ clonebox/cloner.py,sha256=2YQO4SHCv0xOsU1hL9IqdgmxxJN-2j75X9pe-LpTpJE,82696
5
+ clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
6
+ clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
7
+ clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
8
+ clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
9
+ clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
10
+ clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
11
+ clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
12
+ clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
13
+ clonebox-1.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
14
+ clonebox-1.1.1.dist-info/METADATA,sha256=M1EvUdyNOrCz_U17oG1BbQ-JyD-t5kByHKSNDsHeBPU,47164
15
+ clonebox-1.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
16
+ clonebox-1.1.1.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
17
+ clonebox-1.1.1.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
18
+ clonebox-1.1.1.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
- clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
- clonebox/cli.py,sha256=0ZOE66WVbIDdmIBf0_VyWw4U-jzPo_T4pCTXkWect5o,114804
4
- clonebox/cloner.py,sha256=NTwXX_HqATK36BOrGdNH05_mzISIGdKAgLylCPxlDuA,81509
5
- clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
6
- clonebox/dashboard.py,sha256=_3cckyfW4RIMuQa0zqHw0tjhYugx2LLqBT5jf8VeYOA,4268
7
- clonebox/detector.py,sha256=vS65cvFNPmUBCX1Y_TMTnSRljw6r1Ae9dlVtACs5XFc,23075
8
- clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
9
- clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
10
- clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
11
- clonebox/templates/profiles/ml-dev.yaml,sha256=MT7Wu3xGBnYIsO5mzZ2GDI4AAEFGOroIx0eU3XjNARg,140
12
- clonebox-0.1.29.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- clonebox-0.1.29.dist-info/METADATA,sha256=jO5NvtxzsrX5FUutODZ5yEjW_zOgaW1Br1auxyJzQf8,46615
14
- clonebox-0.1.29.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
- clonebox-0.1.29.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
16
- clonebox-0.1.29.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
17
- clonebox-0.1.29.dist-info/RECORD,,