clonebox 0.1.20__tar.gz → 0.1.21__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.
Files changed (31) hide show
  1. {clonebox-0.1.20/src/clonebox.egg-info → clonebox-0.1.21}/PKG-INFO +195 -6
  2. {clonebox-0.1.20 → clonebox-0.1.21}/README.md +194 -5
  3. {clonebox-0.1.20 → clonebox-0.1.21}/pyproject.toml +1 -1
  4. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/cli.py +22 -0
  5. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/cloner.py +28 -2
  6. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/models.py +6 -2
  7. {clonebox-0.1.20 → clonebox-0.1.21/src/clonebox.egg-info}/PKG-INFO +195 -6
  8. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_cli.py +48 -0
  9. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_cloner.py +1 -1
  10. clonebox-0.1.21/tests/test_profiles.py +56 -0
  11. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_validator.py +50 -0
  12. clonebox-0.1.20/tests/test_profiles.py +0 -26
  13. {clonebox-0.1.20 → clonebox-0.1.21}/LICENSE +0 -0
  14. {clonebox-0.1.20 → clonebox-0.1.21}/setup.cfg +0 -0
  15. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/__init__.py +0 -0
  16. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/__main__.py +0 -0
  17. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/container.py +0 -0
  18. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/dashboard.py +0 -0
  19. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/detector.py +0 -0
  20. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/profiles.py +0 -0
  21. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/templates/profiles/ml-dev.yaml +0 -0
  22. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox/validator.py +0 -0
  23. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox.egg-info/SOURCES.txt +0 -0
  24. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox.egg-info/dependency_links.txt +0 -0
  25. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox.egg-info/entry_points.txt +0 -0
  26. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox.egg-info/requires.txt +0 -0
  27. {clonebox-0.1.20 → clonebox-0.1.21}/src/clonebox.egg-info/top_level.txt +0 -0
  28. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_container.py +0 -0
  29. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_detector.py +0 -0
  30. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_models.py +0 -0
  31. {clonebox-0.1.20 → clonebox-0.1.21}/tests/test_network.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 0.1.20
3
+ Version: 0.1.21
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
@@ -85,6 +85,22 @@ CloneBox lets you create isolated virtual machines with only the applications, d
85
85
  - 🧪 **Configuration testing** - Validate VM settings and functionality
86
86
  - 📁 **App data sync** - Include browser profiles, IDE settings, and app configs
87
87
 
88
+ ## Use Cases
89
+
90
+ CloneBox excels in scenarios where developers need:
91
+ - Isolated sandbox environments for testing AI agents, edge computing simulations, or integration workflows without risking host system stability
92
+ - Reproducible development setups that can be quickly spun up with identical configurations across different machines
93
+ - Safe experimentation with system-level changes that can be discarded by simply deleting the VM
94
+ - Quick onboarding for new team members who need a fully configured development environment
95
+
96
+ ## What's Next
97
+
98
+ Project roadmap includes:
99
+ - Container runtime integration (Podman/Docker lightweight mode)
100
+ - Local dashboard for VM and container management
101
+ - Profile system for reusable configuration presets
102
+ - Proxmox export capabilities for production migration
103
+
88
104
 
89
105
 
90
106
 
@@ -95,6 +111,8 @@ Kluczowe komendy:
95
111
  - `clonebox` – interaktywny wizard (detect + create + start)
96
112
  - `clonebox detect` – skanuje usługi/apps/ścieżki
97
113
  - `clonebox clone . --user --run` – szybki klon bieżącego katalogu z użytkownikiem i autostartem
114
+ - `clonebox container up|ps|stop|rm` – lekki runtime kontenerowy (podman/docker)
115
+ - `clonebox dashboard` – lokalny dashboard (VM + containers)
98
116
 
99
117
  ### Dlaczego wirtualne klony workstation mają sens?
100
118
 
@@ -167,6 +185,11 @@ pip install -e .
167
185
  # Or directly
168
186
  pip install clonebox
169
187
  ```
188
+
189
+ Dashboard ma opcjonalne zależności:
190
+ ```bash
191
+ pip install "clonebox[dashboard]"
192
+ ```
170
193
  lub
171
194
  ```bash
172
195
  # Aktywuj venv
@@ -247,7 +270,34 @@ Simply run `clonebox` to start the interactive wizard:
247
270
 
248
271
  ```bash
249
272
  clonebox
250
- clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2
273
+
274
+ clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2 --disk-size-gb 30
275
+
276
+ clonebox test . --user --validate
277
+ ```
278
+
279
+ ### Profiles (Reusable presets)
280
+
281
+ Profiles pozwalają trzymać gotowe presety dla VM/container (np. `ml-dev`, `web-dev`) i nakładać je na bazową konfigurację.
282
+
283
+ ```bash
284
+ # Przykład: uruchom kontener z profilem
285
+ clonebox container up . --profile ml-dev --engine podman
286
+
287
+ # Przykład: generuj VM config z profilem
288
+ clonebox clone . --profile ml-dev --user --run
289
+ ```
290
+
291
+ Domyślne lokalizacje profili:
292
+ - `~/.clonebox.d/<name>.yaml`
293
+ - `./.clonebox.d/<name>.yaml`
294
+ - wbudowane: `src/clonebox/templates/profiles/<name>.yaml`
295
+
296
+ ### Dashboard
297
+
298
+ ```bash
299
+ clonebox dashboard --port 8080
300
+ # http://127.0.0.1:8080
251
301
  ```
252
302
 
253
303
  The wizard will:
@@ -268,7 +318,10 @@ clonebox create --name my-dev-vm --config '{
268
318
  },
269
319
  "packages": ["python3", "nodejs", "docker.io"],
270
320
  "services": ["docker"]
271
- }' --ram 4096 --vcpus 4 --start
321
+ }' --ram 4096 --vcpus 4 --disk-size-gb 20 --start
322
+
323
+ # Create VM with larger root disk
324
+ clonebox create --name my-dev-vm --disk-size-gb 30 --config '{"paths": {}, "packages": [], "services": []}'
272
325
 
273
326
  # List VMs
274
327
  clonebox list
@@ -325,10 +378,45 @@ clonebox clone . --user --run
325
378
 
326
379
  # Access in VM:
327
380
  ls ~/.config/google-chrome # Chrome profile
328
- ls ~/.mozilla/firefox # Firefox profile
329
- ls ~/.config/JetBrains # PyCharm settings
381
+
382
+ # Firefox profile (Ubuntu często używa snap):
383
+ ls ~/snap/firefox/common/.mozilla/firefox
384
+ ls ~/.mozilla/firefox
385
+
386
+ # PyCharm profile (snap):
387
+ ls ~/snap/pycharm-community/common/.config/JetBrains
388
+ ls ~/.config/JetBrains
389
+ ```
390
+
391
+ ### Container workflow (podman/docker)
392
+
393
+ ```bash
394
+ # Start a dev container (auto-detect engine if not specified)
395
+ clonebox container up . --engine podman --detach
396
+
397
+ # List running containers
398
+ clonebox container ps
399
+
400
+ # Stop/remove
401
+ clonebox container stop <name>
402
+ clonebox container rm <name>
403
+ ```
404
+
405
+ ### Full validation (VM)
406
+
407
+ `clonebox test` weryfikuje, że VM faktycznie ma zamontowane ścieżki i spełnia wymagania z `.clonebox.yaml`.
408
+
409
+ ```bash
410
+ clonebox test . --user --validate
330
411
  ```
331
412
 
413
+ Walidowane kategorie:
414
+ - **Mounts** (9p)
415
+ - **Packages** (apt)
416
+ - **Snap packages**
417
+ - **Services** (enabled + running)
418
+ - **Apps** (instalacja + dostępność profilu: Firefox/PyCharm/Chrome)
419
+
332
420
  ### Testing and Validating VM Configuration
333
421
 
334
422
  ```bash
@@ -557,6 +645,9 @@ The fastest way to clone your current working directory:
557
645
  # Base OS image is automatically downloaded to ~/Downloads on first run
558
646
  clonebox clone .
559
647
 
648
+ # Increase VM disk size (recommended for GUI + large tooling)
649
+ clonebox clone . --user --disk-size-gb 30
650
+
560
651
  # Clone specific path
561
652
  clonebox clone ~/projects/my-app
562
653
 
@@ -687,8 +778,10 @@ clonebox clone . --network auto
687
778
  | `clonebox clone . --replace` | Replace existing VM (stop, delete, recreate) |
688
779
  | `clonebox clone . --user` | Clone in user session (no root) |
689
780
  | `clonebox clone . --base-image <path>` | Use custom base image |
781
+ | `clonebox clone . --disk-size-gb <gb>` | Set root disk size in GB (generated configs default to 20GB) |
690
782
  | `clonebox clone . --network user` | Use user-mode networking (slirp) |
691
783
  | `clonebox clone . --network auto` | Auto-detect network mode (default) |
784
+ | `clonebox create --config <json> --disk-size-gb <gb>` | Create VM from JSON config with specified disk size |
692
785
  | `clonebox start .` | Start VM from `.clonebox.yaml` in current dir |
693
786
  | `clonebox start . --viewer` | Start VM and open GUI window |
694
787
  | `clonebox start <name>` | Start existing VM by name |
@@ -701,6 +794,11 @@ clonebox clone . --network auto
701
794
  | `clonebox detect --yaml` | Output as YAML config |
702
795
  | `clonebox detect --yaml --dedupe` | YAML with duplicates removed |
703
796
  | `clonebox detect --json` | Output as JSON |
797
+ | `clonebox container up .` | Start a dev container for given path |
798
+ | `clonebox container ps` | List containers |
799
+ | `clonebox container stop <name>` | Stop a container |
800
+ | `clonebox container rm <name>` | Remove a container |
801
+ | `clonebox dashboard` | Run local dashboard (VM + containers) |
704
802
  | `clonebox status . --user` | Check VM health, cloud-init, IP, and mount status |
705
803
  | `clonebox status . --user --health` | Check VM status and run full health check |
706
804
  | `clonebox test . --user` | Test VM configuration (basic checks) |
@@ -721,6 +819,97 @@ clonebox clone . --network auto
721
819
 
722
820
  ## Troubleshooting
723
821
 
822
+ ### Critical: Insufficient Disk Space
823
+
824
+ If you install a full desktop environment and large development tools (e.g. `ubuntu-desktop-minimal`, `docker.io`, large snaps like `pycharm-community`/`chromium`), you may hit low disk space warnings inside the VM.
825
+
826
+ Recommended fix:
827
+ - Set a larger root disk in `.clonebox.yaml`:
828
+
829
+ ```yaml
830
+ vm:
831
+ disk_size_gb: 30
832
+ ```
833
+
834
+ You can also set it during config generation:
835
+ ```bash
836
+ clonebox clone . --user --disk-size-gb 30
837
+ ```
838
+
839
+ Notes:
840
+ - New configs generated by `clonebox clone` default to `disk_size_gb: 20`.
841
+ - You can override this by setting `vm.disk_size_gb` in `.clonebox.yaml`.
842
+
843
+ Workaround for an existing VM (host-side resize + guest filesystem grow):
844
+ ```bash
845
+ clonebox stop . --user
846
+ qemu-img resize ~/.local/share/libvirt/images/<vm-name>/root.qcow2 +10G
847
+ clonebox start . --user
848
+ ```
849
+
850
+ Inside the VM:
851
+ ```bash
852
+ sudo growpart /dev/vda 1
853
+ sudo resize2fs /dev/vda1
854
+ df -h /
855
+ ```
856
+
857
+ ### Known Issue: IBus Preferences crash
858
+
859
+ During validation you may occasionally see a crash dialog from **IBus Preferences** in the Ubuntu desktop environment.
860
+ This is an upstream issue related to the input method daemon (`ibus-daemon`) and obsolete system packages (e.g. `libglib2.0`, `libssl3`, `libxml2`, `openssl`).
861
+ It does **not** affect CloneBox functionality and the VM operates normally.
862
+
863
+ Workaround:
864
+ - Dismiss the crash dialog
865
+ - Or run `sudo apt upgrade` inside the VM to update system packages
866
+
867
+ ### Snap Apps Not Launching (PyCharm, Chromium, Firefox)
868
+
869
+ If snap-installed applications (e.g., PyCharm, Chromium) are installed but don't launch when clicked, the issue is usually **disconnected snap interfaces**. This happens because snap interfaces are not auto-connected when installing via cloud-init.
870
+
871
+ **New VMs created with updated CloneBox automatically connect snap interfaces**, but for older VMs or manual installs:
872
+
873
+ ```bash
874
+ # Check snap interface connections
875
+ snap connections pycharm-community
876
+
877
+ # If you see "-" instead of ":desktop", interfaces are NOT connected
878
+
879
+ # Connect required interfaces
880
+ sudo snap connect pycharm-community:desktop :desktop
881
+ sudo snap connect pycharm-community:desktop-legacy :desktop-legacy
882
+ sudo snap connect pycharm-community:x11 :x11
883
+ sudo snap connect pycharm-community:wayland :wayland
884
+ sudo snap connect pycharm-community:home :home
885
+ sudo snap connect pycharm-community:network :network
886
+
887
+ # Restart snap daemon and try again
888
+ sudo systemctl restart snapd
889
+ snap run pycharm-community
890
+ ```
891
+
892
+ **For Chromium/Firefox:**
893
+ ```bash
894
+ sudo snap connect chromium:desktop :desktop
895
+ sudo snap connect chromium:x11 :x11
896
+ sudo snap connect firefox:desktop :desktop
897
+ sudo snap connect firefox:x11 :x11
898
+ ```
899
+
900
+ **Debug launch:**
901
+ ```bash
902
+ PYCHARM_DEBUG=true snap run pycharm-community 2>&1 | tee /tmp/pycharm-debug.log
903
+ ```
904
+
905
+ **Nuclear option (reinstall):**
906
+ ```bash
907
+ snap remove pycharm-community
908
+ rm -rf ~/snap/pycharm-community
909
+ sudo snap install pycharm-community --classic
910
+ sudo snap connect pycharm-community:desktop :desktop
911
+ ```
912
+
724
913
  ### Network Issues
725
914
 
726
915
  If you encounter "Network not found" or "network 'default' is not active" errors:
@@ -1069,4 +1258,4 @@ qm set 9000 --boot c --bootdisk scsi0
1069
1258
 
1070
1259
  ## License
1071
1260
 
1072
- MIT License - see [LICENSE](LICENSE) file.
1261
+ Apache License - see [LICENSE](LICENSE) file.
@@ -37,6 +37,22 @@ CloneBox lets you create isolated virtual machines with only the applications, d
37
37
  - 🧪 **Configuration testing** - Validate VM settings and functionality
38
38
  - 📁 **App data sync** - Include browser profiles, IDE settings, and app configs
39
39
 
40
+ ## Use Cases
41
+
42
+ CloneBox excels in scenarios where developers need:
43
+ - Isolated sandbox environments for testing AI agents, edge computing simulations, or integration workflows without risking host system stability
44
+ - Reproducible development setups that can be quickly spun up with identical configurations across different machines
45
+ - Safe experimentation with system-level changes that can be discarded by simply deleting the VM
46
+ - Quick onboarding for new team members who need a fully configured development environment
47
+
48
+ ## What's Next
49
+
50
+ Project roadmap includes:
51
+ - Container runtime integration (Podman/Docker lightweight mode)
52
+ - Local dashboard for VM and container management
53
+ - Profile system for reusable configuration presets
54
+ - Proxmox export capabilities for production migration
55
+
40
56
 
41
57
 
42
58
 
@@ -47,6 +63,8 @@ Kluczowe komendy:
47
63
  - `clonebox` – interaktywny wizard (detect + create + start)
48
64
  - `clonebox detect` – skanuje usługi/apps/ścieżki
49
65
  - `clonebox clone . --user --run` – szybki klon bieżącego katalogu z użytkownikiem i autostartem
66
+ - `clonebox container up|ps|stop|rm` – lekki runtime kontenerowy (podman/docker)
67
+ - `clonebox dashboard` – lokalny dashboard (VM + containers)
50
68
 
51
69
  ### Dlaczego wirtualne klony workstation mają sens?
52
70
 
@@ -119,6 +137,11 @@ pip install -e .
119
137
  # Or directly
120
138
  pip install clonebox
121
139
  ```
140
+
141
+ Dashboard ma opcjonalne zależności:
142
+ ```bash
143
+ pip install "clonebox[dashboard]"
144
+ ```
122
145
  lub
123
146
  ```bash
124
147
  # Aktywuj venv
@@ -199,7 +222,34 @@ Simply run `clonebox` to start the interactive wizard:
199
222
 
200
223
  ```bash
201
224
  clonebox
202
- clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2
225
+
226
+ clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2 --disk-size-gb 30
227
+
228
+ clonebox test . --user --validate
229
+ ```
230
+
231
+ ### Profiles (Reusable presets)
232
+
233
+ Profiles pozwalają trzymać gotowe presety dla VM/container (np. `ml-dev`, `web-dev`) i nakładać je na bazową konfigurację.
234
+
235
+ ```bash
236
+ # Przykład: uruchom kontener z profilem
237
+ clonebox container up . --profile ml-dev --engine podman
238
+
239
+ # Przykład: generuj VM config z profilem
240
+ clonebox clone . --profile ml-dev --user --run
241
+ ```
242
+
243
+ Domyślne lokalizacje profili:
244
+ - `~/.clonebox.d/<name>.yaml`
245
+ - `./.clonebox.d/<name>.yaml`
246
+ - wbudowane: `src/clonebox/templates/profiles/<name>.yaml`
247
+
248
+ ### Dashboard
249
+
250
+ ```bash
251
+ clonebox dashboard --port 8080
252
+ # http://127.0.0.1:8080
203
253
  ```
204
254
 
205
255
  The wizard will:
@@ -220,7 +270,10 @@ clonebox create --name my-dev-vm --config '{
220
270
  },
221
271
  "packages": ["python3", "nodejs", "docker.io"],
222
272
  "services": ["docker"]
223
- }' --ram 4096 --vcpus 4 --start
273
+ }' --ram 4096 --vcpus 4 --disk-size-gb 20 --start
274
+
275
+ # Create VM with larger root disk
276
+ clonebox create --name my-dev-vm --disk-size-gb 30 --config '{"paths": {}, "packages": [], "services": []}'
224
277
 
225
278
  # List VMs
226
279
  clonebox list
@@ -277,10 +330,45 @@ clonebox clone . --user --run
277
330
 
278
331
  # Access in VM:
279
332
  ls ~/.config/google-chrome # Chrome profile
280
- ls ~/.mozilla/firefox # Firefox profile
281
- ls ~/.config/JetBrains # PyCharm settings
333
+
334
+ # Firefox profile (Ubuntu często używa snap):
335
+ ls ~/snap/firefox/common/.mozilla/firefox
336
+ ls ~/.mozilla/firefox
337
+
338
+ # PyCharm profile (snap):
339
+ ls ~/snap/pycharm-community/common/.config/JetBrains
340
+ ls ~/.config/JetBrains
341
+ ```
342
+
343
+ ### Container workflow (podman/docker)
344
+
345
+ ```bash
346
+ # Start a dev container (auto-detect engine if not specified)
347
+ clonebox container up . --engine podman --detach
348
+
349
+ # List running containers
350
+ clonebox container ps
351
+
352
+ # Stop/remove
353
+ clonebox container stop <name>
354
+ clonebox container rm <name>
355
+ ```
356
+
357
+ ### Full validation (VM)
358
+
359
+ `clonebox test` weryfikuje, że VM faktycznie ma zamontowane ścieżki i spełnia wymagania z `.clonebox.yaml`.
360
+
361
+ ```bash
362
+ clonebox test . --user --validate
282
363
  ```
283
364
 
365
+ Walidowane kategorie:
366
+ - **Mounts** (9p)
367
+ - **Packages** (apt)
368
+ - **Snap packages**
369
+ - **Services** (enabled + running)
370
+ - **Apps** (instalacja + dostępność profilu: Firefox/PyCharm/Chrome)
371
+
284
372
  ### Testing and Validating VM Configuration
285
373
 
286
374
  ```bash
@@ -509,6 +597,9 @@ The fastest way to clone your current working directory:
509
597
  # Base OS image is automatically downloaded to ~/Downloads on first run
510
598
  clonebox clone .
511
599
 
600
+ # Increase VM disk size (recommended for GUI + large tooling)
601
+ clonebox clone . --user --disk-size-gb 30
602
+
512
603
  # Clone specific path
513
604
  clonebox clone ~/projects/my-app
514
605
 
@@ -639,8 +730,10 @@ clonebox clone . --network auto
639
730
  | `clonebox clone . --replace` | Replace existing VM (stop, delete, recreate) |
640
731
  | `clonebox clone . --user` | Clone in user session (no root) |
641
732
  | `clonebox clone . --base-image <path>` | Use custom base image |
733
+ | `clonebox clone . --disk-size-gb <gb>` | Set root disk size in GB (generated configs default to 20GB) |
642
734
  | `clonebox clone . --network user` | Use user-mode networking (slirp) |
643
735
  | `clonebox clone . --network auto` | Auto-detect network mode (default) |
736
+ | `clonebox create --config <json> --disk-size-gb <gb>` | Create VM from JSON config with specified disk size |
644
737
  | `clonebox start .` | Start VM from `.clonebox.yaml` in current dir |
645
738
  | `clonebox start . --viewer` | Start VM and open GUI window |
646
739
  | `clonebox start <name>` | Start existing VM by name |
@@ -653,6 +746,11 @@ clonebox clone . --network auto
653
746
  | `clonebox detect --yaml` | Output as YAML config |
654
747
  | `clonebox detect --yaml --dedupe` | YAML with duplicates removed |
655
748
  | `clonebox detect --json` | Output as JSON |
749
+ | `clonebox container up .` | Start a dev container for given path |
750
+ | `clonebox container ps` | List containers |
751
+ | `clonebox container stop <name>` | Stop a container |
752
+ | `clonebox container rm <name>` | Remove a container |
753
+ | `clonebox dashboard` | Run local dashboard (VM + containers) |
656
754
  | `clonebox status . --user` | Check VM health, cloud-init, IP, and mount status |
657
755
  | `clonebox status . --user --health` | Check VM status and run full health check |
658
756
  | `clonebox test . --user` | Test VM configuration (basic checks) |
@@ -673,6 +771,97 @@ clonebox clone . --network auto
673
771
 
674
772
  ## Troubleshooting
675
773
 
774
+ ### Critical: Insufficient Disk Space
775
+
776
+ If you install a full desktop environment and large development tools (e.g. `ubuntu-desktop-minimal`, `docker.io`, large snaps like `pycharm-community`/`chromium`), you may hit low disk space warnings inside the VM.
777
+
778
+ Recommended fix:
779
+ - Set a larger root disk in `.clonebox.yaml`:
780
+
781
+ ```yaml
782
+ vm:
783
+ disk_size_gb: 30
784
+ ```
785
+
786
+ You can also set it during config generation:
787
+ ```bash
788
+ clonebox clone . --user --disk-size-gb 30
789
+ ```
790
+
791
+ Notes:
792
+ - New configs generated by `clonebox clone` default to `disk_size_gb: 20`.
793
+ - You can override this by setting `vm.disk_size_gb` in `.clonebox.yaml`.
794
+
795
+ Workaround for an existing VM (host-side resize + guest filesystem grow):
796
+ ```bash
797
+ clonebox stop . --user
798
+ qemu-img resize ~/.local/share/libvirt/images/<vm-name>/root.qcow2 +10G
799
+ clonebox start . --user
800
+ ```
801
+
802
+ Inside the VM:
803
+ ```bash
804
+ sudo growpart /dev/vda 1
805
+ sudo resize2fs /dev/vda1
806
+ df -h /
807
+ ```
808
+
809
+ ### Known Issue: IBus Preferences crash
810
+
811
+ During validation you may occasionally see a crash dialog from **IBus Preferences** in the Ubuntu desktop environment.
812
+ This is an upstream issue related to the input method daemon (`ibus-daemon`) and obsolete system packages (e.g. `libglib2.0`, `libssl3`, `libxml2`, `openssl`).
813
+ It does **not** affect CloneBox functionality and the VM operates normally.
814
+
815
+ Workaround:
816
+ - Dismiss the crash dialog
817
+ - Or run `sudo apt upgrade` inside the VM to update system packages
818
+
819
+ ### Snap Apps Not Launching (PyCharm, Chromium, Firefox)
820
+
821
+ If snap-installed applications (e.g., PyCharm, Chromium) are installed but don't launch when clicked, the issue is usually **disconnected snap interfaces**. This happens because snap interfaces are not auto-connected when installing via cloud-init.
822
+
823
+ **New VMs created with updated CloneBox automatically connect snap interfaces**, but for older VMs or manual installs:
824
+
825
+ ```bash
826
+ # Check snap interface connections
827
+ snap connections pycharm-community
828
+
829
+ # If you see "-" instead of ":desktop", interfaces are NOT connected
830
+
831
+ # Connect required interfaces
832
+ sudo snap connect pycharm-community:desktop :desktop
833
+ sudo snap connect pycharm-community:desktop-legacy :desktop-legacy
834
+ sudo snap connect pycharm-community:x11 :x11
835
+ sudo snap connect pycharm-community:wayland :wayland
836
+ sudo snap connect pycharm-community:home :home
837
+ sudo snap connect pycharm-community:network :network
838
+
839
+ # Restart snap daemon and try again
840
+ sudo systemctl restart snapd
841
+ snap run pycharm-community
842
+ ```
843
+
844
+ **For Chromium/Firefox:**
845
+ ```bash
846
+ sudo snap connect chromium:desktop :desktop
847
+ sudo snap connect chromium:x11 :x11
848
+ sudo snap connect firefox:desktop :desktop
849
+ sudo snap connect firefox:x11 :x11
850
+ ```
851
+
852
+ **Debug launch:**
853
+ ```bash
854
+ PYCHARM_DEBUG=true snap run pycharm-community 2>&1 | tee /tmp/pycharm-debug.log
855
+ ```
856
+
857
+ **Nuclear option (reinstall):**
858
+ ```bash
859
+ snap remove pycharm-community
860
+ rm -rf ~/snap/pycharm-community
861
+ sudo snap install pycharm-community --classic
862
+ sudo snap connect pycharm-community:desktop :desktop
863
+ ```
864
+
676
865
  ### Network Issues
677
866
 
678
867
  If you encounter "Network not found" or "network 'default' is not active" errors:
@@ -1021,4 +1210,4 @@ qm set 9000 --boot c --bootdisk scsi0
1021
1210
 
1022
1211
  ## License
1023
1212
 
1024
- MIT License - see [LICENSE](LICENSE) file.
1213
+ Apache License - see [LICENSE](LICENSE) file.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "clonebox"
7
- version = "0.1.20"
7
+ version = "0.1.21"
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"}
@@ -653,6 +653,7 @@ def interactive_mode():
653
653
  summary_table.add_row("Name", vm_name)
654
654
  summary_table.add_row("RAM", f"{ram_mb} MB")
655
655
  summary_table.add_row("vCPUs", str(vcpus))
656
+ summary_table.add_row("Disk", f"{20 if enable_gui else 10} GB")
656
657
  summary_table.add_row("Services", ", ".join(selected_services) or "None")
657
658
  summary_table.add_row(
658
659
  "Packages",
@@ -684,6 +685,7 @@ def interactive_mode():
684
685
  name=vm_name,
685
686
  ram_mb=ram_mb,
686
687
  vcpus=vcpus,
688
+ disk_size_gb=20 if enable_gui else 10,
687
689
  gui=enable_gui,
688
690
  base_image=base_image if base_image else None,
689
691
  paths=paths_mapping,
@@ -732,6 +734,7 @@ def cmd_create(args):
732
734
  name=args.name,
733
735
  ram_mb=args.ram,
734
736
  vcpus=args.vcpus,
737
+ disk_size_gb=getattr(args, "disk_size_gb", 10),
735
738
  gui=not args.no_gui,
736
739
  base_image=args.base_image,
737
740
  paths=config_data.get("paths", {}),
@@ -1708,6 +1711,7 @@ def generate_clonebox_yaml(
1708
1711
  vm_name: str = None,
1709
1712
  network_mode: str = "auto",
1710
1713
  base_image: Optional[str] = None,
1714
+ disk_size_gb: Optional[int] = None,
1711
1715
  ) -> str:
1712
1716
  """Generate YAML config from system snapshot."""
1713
1717
  sys_info = detector.get_system_info()
@@ -1813,6 +1817,9 @@ def generate_clonebox_yaml(
1813
1817
  ram_mb = min(4096, int(sys_info["memory_available_gb"] * 1024 * 0.5))
1814
1818
  vcpus = max(2, sys_info["cpu_count"] // 2)
1815
1819
 
1820
+ if disk_size_gb is None:
1821
+ disk_size_gb = 20
1822
+
1816
1823
  # Auto-detect packages from running applications and services
1817
1824
  app_packages = detector.suggest_packages_for_apps(snapshot.applications)
1818
1825
  service_packages = detector.suggest_packages_for_services(snapshot.running_services)
@@ -1871,6 +1878,7 @@ def generate_clonebox_yaml(
1871
1878
  "name": vm_name,
1872
1879
  "ram_mb": ram_mb,
1873
1880
  "vcpus": vcpus,
1881
+ "disk_size_gb": disk_size_gb,
1874
1882
  "gui": True,
1875
1883
  "base_image": base_image,
1876
1884
  "network_mode": network_mode,
@@ -2024,6 +2032,7 @@ def create_vm_from_config(
2024
2032
  name=config["vm"]["name"],
2025
2033
  ram_mb=config["vm"].get("ram_mb", 4096),
2026
2034
  vcpus=config["vm"].get("vcpus", 4),
2035
+ disk_size_gb=config["vm"].get("disk_size_gb", 10),
2027
2036
  gui=config["vm"].get("gui", True),
2028
2037
  base_image=config["vm"].get("base_image"),
2029
2038
  paths=all_paths,
@@ -2103,6 +2112,7 @@ def cmd_clone(args):
2103
2112
  vm_name=vm_name,
2104
2113
  network_mode=args.network,
2105
2114
  base_image=getattr(args, "base_image", None),
2115
+ disk_size_gb=getattr(args, "disk_size_gb", None),
2106
2116
  )
2107
2117
 
2108
2118
  profile_name = getattr(args, "profile", None)
@@ -2345,6 +2355,12 @@ def main():
2345
2355
  )
2346
2356
  create_parser.add_argument("--ram", type=int, default=4096, help="RAM in MB")
2347
2357
  create_parser.add_argument("--vcpus", type=int, default=4, help="Number of vCPUs")
2358
+ create_parser.add_argument(
2359
+ "--disk-size-gb",
2360
+ type=int,
2361
+ default=10,
2362
+ help="Root disk size in GB (default: 10)",
2363
+ )
2348
2364
  create_parser.add_argument("--base-image", help="Path to base qcow2 image")
2349
2365
  create_parser.add_argument("--no-gui", action="store_true", help="Disable SPICE graphics")
2350
2366
  create_parser.add_argument("--start", "-s", action="store_true", help="Start VM after creation")
@@ -2551,6 +2567,12 @@ def main():
2551
2567
  "--base-image",
2552
2568
  help="Path to a bootable qcow2 image to use as a base disk",
2553
2569
  )
2570
+ clone_parser.add_argument(
2571
+ "--disk-size-gb",
2572
+ type=int,
2573
+ default=None,
2574
+ help="Root disk size in GB (default: 20 for generated configs)",
2575
+ )
2554
2576
  clone_parser.add_argument(
2555
2577
  "--profile",
2556
2578
  help="Profile name (loads ~/.clonebox.d/<name>.yaml, .clonebox.d/<name>.yaml, or built-in templates)",
@@ -18,6 +18,16 @@ try:
18
18
  except ImportError:
19
19
  libvirt = None
20
20
 
21
+ SNAP_INTERFACES = {
22
+ 'pycharm-community': ['desktop', 'desktop-legacy', 'x11', 'wayland', 'home', 'network', 'network-bind', 'cups-control', 'removable-media'],
23
+ 'chromium': ['desktop', 'desktop-legacy', 'x11', 'wayland', 'home', 'network', 'audio-playback', 'camera'],
24
+ 'firefox': ['desktop', 'desktop-legacy', 'x11', 'wayland', 'home', 'network', 'audio-playback', 'removable-media'],
25
+ 'code': ['desktop', 'desktop-legacy', 'x11', 'wayland', 'home', 'network', 'ssh-keys'],
26
+ 'slack': ['desktop', 'desktop-legacy', 'x11', 'wayland', 'home', 'network', 'audio-playback'],
27
+ 'spotify': ['desktop', 'x11', 'wayland', 'home', 'network', 'audio-playback'],
28
+ }
29
+ DEFAULT_SNAP_INTERFACES = ['desktop', 'desktop-legacy', 'x11', 'home', 'network']
30
+
21
31
 
22
32
  @dataclass
23
33
  class VMConfig:
@@ -26,7 +36,7 @@ class VMConfig:
26
36
  name: str = "clonebox-vm"
27
37
  ram_mb: int = 4096
28
38
  vcpus: int = 4
29
- disk_size_gb: int = 10
39
+ disk_size_gb: int = 20
30
40
  gui: bool = True
31
41
  base_image: Optional[str] = None
32
42
  paths: dict = field(default_factory=dict)
@@ -680,7 +690,7 @@ fi
680
690
 
681
691
  # User-data
682
692
  # Add desktop environment if GUI is enabled
683
- base_packages = ["qemu-guest-agent"]
693
+ base_packages = ["qemu-guest-agent", "cloud-guest-utils"]
684
694
  if config.gui:
685
695
  base_packages.extend([
686
696
  "ubuntu-desktop-minimal",
@@ -719,6 +729,15 @@ fi
719
729
  runcmd_lines.append(" - echo 'Installing snap packages...'")
720
730
  for snap_pkg in config.snap_packages:
721
731
  runcmd_lines.append(f" - snap install {snap_pkg} --classic || snap install {snap_pkg} || true")
732
+
733
+ # Connect snap interfaces for GUI apps (not auto-connected via cloud-init)
734
+ runcmd_lines.append(" - echo 'Connecting snap interfaces...'")
735
+ for snap_pkg in config.snap_packages:
736
+ interfaces = SNAP_INTERFACES.get(snap_pkg, DEFAULT_SNAP_INTERFACES)
737
+ for iface in interfaces:
738
+ runcmd_lines.append(f" - snap connect {snap_pkg}:{iface} :{iface} 2>/dev/null || true")
739
+
740
+ runcmd_lines.append(" - systemctl restart snapd || true")
722
741
 
723
742
  # Add GUI setup if enabled - runs AFTER package installation completes
724
743
  if config.gui:
@@ -770,6 +789,13 @@ ssh_pwauth: true
770
789
  chpasswd:
771
790
  expire: false
772
791
 
792
+ # Make sure root partition + filesystem grows to fill the qcow2 disk size
793
+ growpart:
794
+ mode: auto
795
+ devices: ["/"]
796
+ ignore_growroot_disabled: false
797
+ resize_rootfs: true
798
+
773
799
  # Update package cache and upgrade
774
800
  package_update: true
775
801
  package_upgrade: false
@@ -16,7 +16,7 @@ class VMSettings(BaseModel):
16
16
  name: str = Field(default="clonebox-vm", description="VM name")
17
17
  ram_mb: int = Field(default=4096, ge=512, le=131072, description="RAM in MB")
18
18
  vcpus: int = Field(default=4, ge=1, le=128, description="Number of vCPUs")
19
- disk_size_gb: int = Field(default=10, ge=1, le=2048, description="Disk size in GB")
19
+ disk_size_gb: int = Field(default=20, ge=1, le=2048, description="Disk size in GB")
20
20
  gui: bool = Field(default=True, description="Enable SPICE graphics")
21
21
  base_image: Optional[str] = Field(default=None, description="Path to base qcow2 image")
22
22
  network_mode: str = Field(default="auto", description="Network mode: auto|default|user")
@@ -107,6 +107,10 @@ class CloneBoxConfig(BaseModel):
107
107
  """Convert to legacy VMConfig dataclass for compatibility."""
108
108
  from clonebox.cloner import VMConfig as VMConfigDataclass
109
109
 
110
+ # Merge paths and app_data_paths
111
+ all_paths = dict(self.paths)
112
+ all_paths.update(self.app_data_paths)
113
+
110
114
  return VMConfigDataclass(
111
115
  name=self.vm.name,
112
116
  ram_mb=self.vm.ram_mb,
@@ -114,7 +118,7 @@ class CloneBoxConfig(BaseModel):
114
118
  disk_size_gb=self.vm.disk_size_gb,
115
119
  gui=self.vm.gui,
116
120
  base_image=self.vm.base_image,
117
- paths=self.paths,
121
+ paths=all_paths,
118
122
  packages=self.packages,
119
123
  snap_packages=self.snap_packages,
120
124
  services=self.services,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 0.1.20
3
+ Version: 0.1.21
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
@@ -85,6 +85,22 @@ CloneBox lets you create isolated virtual machines with only the applications, d
85
85
  - 🧪 **Configuration testing** - Validate VM settings and functionality
86
86
  - 📁 **App data sync** - Include browser profiles, IDE settings, and app configs
87
87
 
88
+ ## Use Cases
89
+
90
+ CloneBox excels in scenarios where developers need:
91
+ - Isolated sandbox environments for testing AI agents, edge computing simulations, or integration workflows without risking host system stability
92
+ - Reproducible development setups that can be quickly spun up with identical configurations across different machines
93
+ - Safe experimentation with system-level changes that can be discarded by simply deleting the VM
94
+ - Quick onboarding for new team members who need a fully configured development environment
95
+
96
+ ## What's Next
97
+
98
+ Project roadmap includes:
99
+ - Container runtime integration (Podman/Docker lightweight mode)
100
+ - Local dashboard for VM and container management
101
+ - Profile system for reusable configuration presets
102
+ - Proxmox export capabilities for production migration
103
+
88
104
 
89
105
 
90
106
 
@@ -95,6 +111,8 @@ Kluczowe komendy:
95
111
  - `clonebox` – interaktywny wizard (detect + create + start)
96
112
  - `clonebox detect` – skanuje usługi/apps/ścieżki
97
113
  - `clonebox clone . --user --run` – szybki klon bieżącego katalogu z użytkownikiem i autostartem
114
+ - `clonebox container up|ps|stop|rm` – lekki runtime kontenerowy (podman/docker)
115
+ - `clonebox dashboard` – lokalny dashboard (VM + containers)
98
116
 
99
117
  ### Dlaczego wirtualne klony workstation mają sens?
100
118
 
@@ -167,6 +185,11 @@ pip install -e .
167
185
  # Or directly
168
186
  pip install clonebox
169
187
  ```
188
+
189
+ Dashboard ma opcjonalne zależności:
190
+ ```bash
191
+ pip install "clonebox[dashboard]"
192
+ ```
170
193
  lub
171
194
  ```bash
172
195
  # Aktywuj venv
@@ -247,7 +270,34 @@ Simply run `clonebox` to start the interactive wizard:
247
270
 
248
271
  ```bash
249
272
  clonebox
250
- clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2
273
+
274
+ clonebox clone . --user --run --replace --base-image ~/ubuntu-22.04-cloud.qcow2 --disk-size-gb 30
275
+
276
+ clonebox test . --user --validate
277
+ ```
278
+
279
+ ### Profiles (Reusable presets)
280
+
281
+ Profiles pozwalają trzymać gotowe presety dla VM/container (np. `ml-dev`, `web-dev`) i nakładać je na bazową konfigurację.
282
+
283
+ ```bash
284
+ # Przykład: uruchom kontener z profilem
285
+ clonebox container up . --profile ml-dev --engine podman
286
+
287
+ # Przykład: generuj VM config z profilem
288
+ clonebox clone . --profile ml-dev --user --run
289
+ ```
290
+
291
+ Domyślne lokalizacje profili:
292
+ - `~/.clonebox.d/<name>.yaml`
293
+ - `./.clonebox.d/<name>.yaml`
294
+ - wbudowane: `src/clonebox/templates/profiles/<name>.yaml`
295
+
296
+ ### Dashboard
297
+
298
+ ```bash
299
+ clonebox dashboard --port 8080
300
+ # http://127.0.0.1:8080
251
301
  ```
252
302
 
253
303
  The wizard will:
@@ -268,7 +318,10 @@ clonebox create --name my-dev-vm --config '{
268
318
  },
269
319
  "packages": ["python3", "nodejs", "docker.io"],
270
320
  "services": ["docker"]
271
- }' --ram 4096 --vcpus 4 --start
321
+ }' --ram 4096 --vcpus 4 --disk-size-gb 20 --start
322
+
323
+ # Create VM with larger root disk
324
+ clonebox create --name my-dev-vm --disk-size-gb 30 --config '{"paths": {}, "packages": [], "services": []}'
272
325
 
273
326
  # List VMs
274
327
  clonebox list
@@ -325,10 +378,45 @@ clonebox clone . --user --run
325
378
 
326
379
  # Access in VM:
327
380
  ls ~/.config/google-chrome # Chrome profile
328
- ls ~/.mozilla/firefox # Firefox profile
329
- ls ~/.config/JetBrains # PyCharm settings
381
+
382
+ # Firefox profile (Ubuntu często używa snap):
383
+ ls ~/snap/firefox/common/.mozilla/firefox
384
+ ls ~/.mozilla/firefox
385
+
386
+ # PyCharm profile (snap):
387
+ ls ~/snap/pycharm-community/common/.config/JetBrains
388
+ ls ~/.config/JetBrains
389
+ ```
390
+
391
+ ### Container workflow (podman/docker)
392
+
393
+ ```bash
394
+ # Start a dev container (auto-detect engine if not specified)
395
+ clonebox container up . --engine podman --detach
396
+
397
+ # List running containers
398
+ clonebox container ps
399
+
400
+ # Stop/remove
401
+ clonebox container stop <name>
402
+ clonebox container rm <name>
403
+ ```
404
+
405
+ ### Full validation (VM)
406
+
407
+ `clonebox test` weryfikuje, że VM faktycznie ma zamontowane ścieżki i spełnia wymagania z `.clonebox.yaml`.
408
+
409
+ ```bash
410
+ clonebox test . --user --validate
330
411
  ```
331
412
 
413
+ Walidowane kategorie:
414
+ - **Mounts** (9p)
415
+ - **Packages** (apt)
416
+ - **Snap packages**
417
+ - **Services** (enabled + running)
418
+ - **Apps** (instalacja + dostępność profilu: Firefox/PyCharm/Chrome)
419
+
332
420
  ### Testing and Validating VM Configuration
333
421
 
334
422
  ```bash
@@ -557,6 +645,9 @@ The fastest way to clone your current working directory:
557
645
  # Base OS image is automatically downloaded to ~/Downloads on first run
558
646
  clonebox clone .
559
647
 
648
+ # Increase VM disk size (recommended for GUI + large tooling)
649
+ clonebox clone . --user --disk-size-gb 30
650
+
560
651
  # Clone specific path
561
652
  clonebox clone ~/projects/my-app
562
653
 
@@ -687,8 +778,10 @@ clonebox clone . --network auto
687
778
  | `clonebox clone . --replace` | Replace existing VM (stop, delete, recreate) |
688
779
  | `clonebox clone . --user` | Clone in user session (no root) |
689
780
  | `clonebox clone . --base-image <path>` | Use custom base image |
781
+ | `clonebox clone . --disk-size-gb <gb>` | Set root disk size in GB (generated configs default to 20GB) |
690
782
  | `clonebox clone . --network user` | Use user-mode networking (slirp) |
691
783
  | `clonebox clone . --network auto` | Auto-detect network mode (default) |
784
+ | `clonebox create --config <json> --disk-size-gb <gb>` | Create VM from JSON config with specified disk size |
692
785
  | `clonebox start .` | Start VM from `.clonebox.yaml` in current dir |
693
786
  | `clonebox start . --viewer` | Start VM and open GUI window |
694
787
  | `clonebox start <name>` | Start existing VM by name |
@@ -701,6 +794,11 @@ clonebox clone . --network auto
701
794
  | `clonebox detect --yaml` | Output as YAML config |
702
795
  | `clonebox detect --yaml --dedupe` | YAML with duplicates removed |
703
796
  | `clonebox detect --json` | Output as JSON |
797
+ | `clonebox container up .` | Start a dev container for given path |
798
+ | `clonebox container ps` | List containers |
799
+ | `clonebox container stop <name>` | Stop a container |
800
+ | `clonebox container rm <name>` | Remove a container |
801
+ | `clonebox dashboard` | Run local dashboard (VM + containers) |
704
802
  | `clonebox status . --user` | Check VM health, cloud-init, IP, and mount status |
705
803
  | `clonebox status . --user --health` | Check VM status and run full health check |
706
804
  | `clonebox test . --user` | Test VM configuration (basic checks) |
@@ -721,6 +819,97 @@ clonebox clone . --network auto
721
819
 
722
820
  ## Troubleshooting
723
821
 
822
+ ### Critical: Insufficient Disk Space
823
+
824
+ If you install a full desktop environment and large development tools (e.g. `ubuntu-desktop-minimal`, `docker.io`, large snaps like `pycharm-community`/`chromium`), you may hit low disk space warnings inside the VM.
825
+
826
+ Recommended fix:
827
+ - Set a larger root disk in `.clonebox.yaml`:
828
+
829
+ ```yaml
830
+ vm:
831
+ disk_size_gb: 30
832
+ ```
833
+
834
+ You can also set it during config generation:
835
+ ```bash
836
+ clonebox clone . --user --disk-size-gb 30
837
+ ```
838
+
839
+ Notes:
840
+ - New configs generated by `clonebox clone` default to `disk_size_gb: 20`.
841
+ - You can override this by setting `vm.disk_size_gb` in `.clonebox.yaml`.
842
+
843
+ Workaround for an existing VM (host-side resize + guest filesystem grow):
844
+ ```bash
845
+ clonebox stop . --user
846
+ qemu-img resize ~/.local/share/libvirt/images/<vm-name>/root.qcow2 +10G
847
+ clonebox start . --user
848
+ ```
849
+
850
+ Inside the VM:
851
+ ```bash
852
+ sudo growpart /dev/vda 1
853
+ sudo resize2fs /dev/vda1
854
+ df -h /
855
+ ```
856
+
857
+ ### Known Issue: IBus Preferences crash
858
+
859
+ During validation you may occasionally see a crash dialog from **IBus Preferences** in the Ubuntu desktop environment.
860
+ This is an upstream issue related to the input method daemon (`ibus-daemon`) and obsolete system packages (e.g. `libglib2.0`, `libssl3`, `libxml2`, `openssl`).
861
+ It does **not** affect CloneBox functionality and the VM operates normally.
862
+
863
+ Workaround:
864
+ - Dismiss the crash dialog
865
+ - Or run `sudo apt upgrade` inside the VM to update system packages
866
+
867
+ ### Snap Apps Not Launching (PyCharm, Chromium, Firefox)
868
+
869
+ If snap-installed applications (e.g., PyCharm, Chromium) are installed but don't launch when clicked, the issue is usually **disconnected snap interfaces**. This happens because snap interfaces are not auto-connected when installing via cloud-init.
870
+
871
+ **New VMs created with updated CloneBox automatically connect snap interfaces**, but for older VMs or manual installs:
872
+
873
+ ```bash
874
+ # Check snap interface connections
875
+ snap connections pycharm-community
876
+
877
+ # If you see "-" instead of ":desktop", interfaces are NOT connected
878
+
879
+ # Connect required interfaces
880
+ sudo snap connect pycharm-community:desktop :desktop
881
+ sudo snap connect pycharm-community:desktop-legacy :desktop-legacy
882
+ sudo snap connect pycharm-community:x11 :x11
883
+ sudo snap connect pycharm-community:wayland :wayland
884
+ sudo snap connect pycharm-community:home :home
885
+ sudo snap connect pycharm-community:network :network
886
+
887
+ # Restart snap daemon and try again
888
+ sudo systemctl restart snapd
889
+ snap run pycharm-community
890
+ ```
891
+
892
+ **For Chromium/Firefox:**
893
+ ```bash
894
+ sudo snap connect chromium:desktop :desktop
895
+ sudo snap connect chromium:x11 :x11
896
+ sudo snap connect firefox:desktop :desktop
897
+ sudo snap connect firefox:x11 :x11
898
+ ```
899
+
900
+ **Debug launch:**
901
+ ```bash
902
+ PYCHARM_DEBUG=true snap run pycharm-community 2>&1 | tee /tmp/pycharm-debug.log
903
+ ```
904
+
905
+ **Nuclear option (reinstall):**
906
+ ```bash
907
+ snap remove pycharm-community
908
+ rm -rf ~/snap/pycharm-community
909
+ sudo snap install pycharm-community --classic
910
+ sudo snap connect pycharm-community:desktop :desktop
911
+ ```
912
+
724
913
  ### Network Issues
725
914
 
726
915
  If you encounter "Network not found" or "network 'default' is not active" errors:
@@ -1069,4 +1258,4 @@ qm set 9000 --boot c --bootdisk scsi0
1069
1258
 
1070
1259
  ## License
1071
1260
 
1072
- MIT License - see [LICENSE](LICENSE) file.
1261
+ Apache License - see [LICENSE](LICENSE) file.
@@ -170,6 +170,24 @@ class TestGenerateCloneboxYaml:
170
170
 
171
171
  assert config["vm"]["base_image"] == "/images/base.qcow2"
172
172
 
173
+ def test_generate_yaml_includes_disk_size_default(self):
174
+ snapshot = self.create_mock_snapshot()
175
+ detector = self.create_mock_detector()
176
+
177
+ yaml_str = generate_clonebox_yaml(snapshot, detector)
178
+ config = yaml.safe_load(yaml_str)
179
+
180
+ assert config["vm"]["disk_size_gb"] == 20
181
+
182
+ def test_generate_yaml_disk_size_override(self):
183
+ snapshot = self.create_mock_snapshot()
184
+ detector = self.create_mock_detector()
185
+
186
+ yaml_str = generate_clonebox_yaml(snapshot, detector, disk_size_gb=42)
187
+ config = yaml.safe_load(yaml_str)
188
+
189
+ assert config["vm"]["disk_size_gb"] == 42
190
+
173
191
 
174
192
  class TestLoadCloneboxConfig:
175
193
  """Test loading .clonebox.yaml configs."""
@@ -334,6 +352,7 @@ class TestCLIIntegration:
334
352
  user=True,
335
353
  network="auto",
336
354
  base_image=None,
355
+ disk_size_gb=None,
337
356
  replace=False,
338
357
  )
339
358
  cmd_clone(args)
@@ -343,6 +362,35 @@ class TestCLIIntegration:
343
362
  assert "vm" in config
344
363
  assert "version" in config
345
364
 
365
+ @patch("clonebox.cli.SelectiveVMCloner")
366
+ def test_create_vm_from_config_propagates_disk_size(self, mock_cloner_class, tmp_path):
367
+ from clonebox.cli import create_vm_from_config
368
+
369
+ mock_cloner = MagicMock()
370
+ mock_cloner.create_vm.return_value = "uuid-123"
371
+ mock_cloner.check_prerequisites.return_value = {
372
+ "images_dir_writable": True,
373
+ "images_dir": "/tmp",
374
+ "session_type": "user",
375
+ }
376
+ mock_cloner_class.return_value = mock_cloner
377
+
378
+ cfg = {
379
+ "version": "1",
380
+ "vm": {"name": "test-vm", "disk_size_gb": 50},
381
+ "paths": {},
382
+ "packages": [],
383
+ "snap_packages": [],
384
+ "services": [],
385
+ "post_commands": [],
386
+ }
387
+
388
+ create_vm_from_config(cfg, start=False, user_session=True, replace=False)
389
+
390
+ assert mock_cloner.create_vm.called
391
+ passed_vm_config = mock_cloner.create_vm.call_args[0][0]
392
+ assert getattr(passed_vm_config, "disk_size_gb") == 50
393
+
346
394
 
347
395
  class TestCLIParametrized:
348
396
  """Parametrized CLI tests."""
@@ -17,7 +17,7 @@ class TestVMConfig:
17
17
  assert config.name == "clonebox-vm"
18
18
  assert config.ram_mb == 4096
19
19
  assert config.vcpus == 4
20
- assert config.disk_size_gb == 10
20
+ assert config.disk_size_gb == 20
21
21
  assert config.gui is True
22
22
  assert config.base_image is None
23
23
  assert config.paths == {}
@@ -0,0 +1,56 @@
1
+ from pathlib import Path
2
+
3
+ import pytest
4
+
5
+ from clonebox.profiles import load_profile, merge_with_profile
6
+
7
+
8
+ def test_profile_loader_finds_default_profiles(tmp_path: Path):
9
+ (tmp_path / "templates" / "profiles").mkdir(parents=True)
10
+ (tmp_path / "templates" / "profiles" / "ml-dev.yaml").write_text(
11
+ """
12
+ container:
13
+ image: python:3.11-slim
14
+ """.lstrip()
15
+ )
16
+
17
+ profile = load_profile("ml-dev", [tmp_path])
18
+ assert profile["container"]["image"] == "python:3.11-slim"
19
+
20
+
21
+ def test_profile_merges_over_base():
22
+ base = {"container": {"image": "ubuntu:22.04"}}
23
+ prof = {"container": {"image": "python:3.11-slim"}}
24
+
25
+ merged = merge_with_profile(base, profile=prof)
26
+ assert merged["container"]["image"] == "python:3.11-slim"
27
+
28
+
29
+ def test_profile_deep_merge_nested_dicts():
30
+ base = {"container": {"env": {"A": "1", "B": "2"}, "image": "ubuntu:22.04"}}
31
+ prof = {"container": {"env": {"B": "override", "C": "3"}}}
32
+
33
+ merged = merge_with_profile(base, profile=prof)
34
+ assert merged["container"]["image"] == "ubuntu:22.04"
35
+ assert merged["container"]["env"] == {"A": "1", "B": "override", "C": "3"}
36
+
37
+
38
+ def test_merge_with_profile_invalid_profile_is_noop():
39
+ base = {"container": {"image": "ubuntu:22.04"}}
40
+ merged = merge_with_profile(base, profile=123) # type: ignore[arg-type]
41
+ assert merged == base
42
+
43
+
44
+ def test_load_profile_prefers_search_paths_over_builtins(tmp_path: Path):
45
+ # Put profile in a search path
46
+ (tmp_path / "templates" / "profiles").mkdir(parents=True)
47
+ (tmp_path / "templates" / "profiles" / "ml-dev.yaml").write_text(
48
+ """
49
+ container:
50
+ image: python:3.11-slim
51
+ """.lstrip()
52
+ )
53
+
54
+ profile = load_profile("ml-dev", [tmp_path])
55
+ assert profile is not None
56
+ assert profile["container"]["image"] == "python:3.11-slim"
@@ -168,6 +168,56 @@ class TestVMValidatorApps:
168
168
  assert results["total"] == 2
169
169
  assert results["failed"] == 1
170
170
 
171
+
172
+ class TestVMValidatorServices:
173
+ @pytest.fixture
174
+ def sample_config(self):
175
+ return {
176
+ "vm": {"name": "test-vm"},
177
+ "paths": {
178
+ "/home/user/projects": "/mnt/projects",
179
+ "/home/user/data": "/mnt/data",
180
+ },
181
+ "app_data_paths": {
182
+ "/home/user/.config/test": "/home/ubuntu/.config/test",
183
+ },
184
+ "packages": ["curl", "git", "vim"],
185
+ "snap_packages": ["code"],
186
+ "services": ["docker", "ssh"],
187
+ }
188
+
189
+ @pytest.fixture
190
+ def mock_console(self):
191
+ console = MagicMock()
192
+ console.print = MagicMock()
193
+ return console
194
+
195
+ def test_validate_services_skips_host_only(self, mock_console):
196
+ config = {
197
+ "vm": {"name": "test-vm"},
198
+ "paths": {},
199
+ "app_data_paths": {},
200
+ "packages": [],
201
+ "snap_packages": [],
202
+ "services": ["libvirtd", "docker"],
203
+ }
204
+
205
+ validator = VMValidator(config=config, vm_name="test-vm", conn_uri="qemu:///session", console=mock_console)
206
+
207
+ def fake_exec(cmd: str, timeout: int = 10):
208
+ if "systemctl is-enabled docker" in cmd:
209
+ return "enabled"
210
+ if "systemctl is-active docker" in cmd:
211
+ return "active"
212
+ return ""
213
+
214
+ validator._exec_in_vm = fake_exec
215
+
216
+ results = validator.validate_services()
217
+ assert results["total"] == 1
218
+ assert results["passed"] == 1
219
+ assert results.get("skipped") == 1
220
+
171
221
  @patch('subprocess.run')
172
222
  def test_exec_in_vm_success(self, mock_run, sample_config, mock_console):
173
223
  """Test successful command execution in VM."""
@@ -1,26 +0,0 @@
1
- from pathlib import Path
2
-
3
- import pytest
4
-
5
- from clonebox.profiles import load_profile, merge_with_profile
6
-
7
-
8
- def test_profile_loader_finds_default_profiles(tmp_path: Path):
9
- (tmp_path / "templates" / "profiles").mkdir(parents=True)
10
- (tmp_path / "templates" / "profiles" / "ml-dev.yaml").write_text(
11
- """
12
- container:
13
- image: python:3.11-slim
14
- """.lstrip()
15
- )
16
-
17
- profile = load_profile("ml-dev", [tmp_path])
18
- assert profile["container"]["image"] == "python:3.11-slim"
19
-
20
-
21
- def test_profile_merges_over_base():
22
- base = {"container": {"image": "ubuntu:22.04"}}
23
- prof = {"container": {"image": "python:3.11-slim"}}
24
-
25
- merged = merge_with_profile(base, profile=prof)
26
- assert merged["container"]["image"] == "python:3.11-slim"
File without changes
File without changes