clonebox 0.1.6__tar.gz → 0.1.7__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.
- {clonebox-0.1.6 → clonebox-0.1.7}/PKG-INFO +2 -1
- {clonebox-0.1.6 → clonebox-0.1.7}/README.md +1 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/pyproject.toml +1 -1
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox/cli.py +26 -7
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox/cloner.py +20 -1
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox/detector.py +232 -58
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/PKG-INFO +2 -1
- {clonebox-0.1.6 → clonebox-0.1.7}/LICENSE +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/setup.cfg +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox/__init__.py +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox/__main__.py +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/SOURCES.txt +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/dependency_links.txt +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/entry_points.txt +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/requires.txt +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/src/clonebox.egg-info/top_level.txt +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/tests/test_cli.py +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/tests/test_cloner.py +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/tests/test_detector.py +0 -0
- {clonebox-0.1.6 → clonebox-0.1.7}/tests/test_network.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clonebox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -38,6 +38,7 @@ Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
|
38
38
|
Dynamic: license-file
|
|
39
39
|
|
|
40
40
|
# CloneBox 📦
|
|
41
|
+

|
|
41
42
|
|
|
42
43
|
```commandline
|
|
43
44
|
╔═══════════════════════════════════════════════════════╗
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "clonebox"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.7"
|
|
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"}
|
|
@@ -592,6 +592,16 @@ def generate_clonebox_yaml(
|
|
|
592
592
|
paths_mapping[host_path] = f"/mnt/workdir{idx}"
|
|
593
593
|
idx += 1
|
|
594
594
|
|
|
595
|
+
# Add default user folders (Downloads, Documents)
|
|
596
|
+
home_dir = Path.home()
|
|
597
|
+
default_folders = [
|
|
598
|
+
(home_dir / "Downloads", "/home/ubuntu/Downloads"),
|
|
599
|
+
(home_dir / "Documents", "/home/ubuntu/Documents"),
|
|
600
|
+
]
|
|
601
|
+
for host_folder, guest_folder in default_folders:
|
|
602
|
+
if host_folder.exists() and str(host_folder) not in paths_mapping:
|
|
603
|
+
paths_mapping[str(host_folder)] = guest_folder
|
|
604
|
+
|
|
595
605
|
# Determine VM name
|
|
596
606
|
if not vm_name:
|
|
597
607
|
if target_path:
|
|
@@ -604,10 +614,10 @@ def generate_clonebox_yaml(
|
|
|
604
614
|
vcpus = max(2, sys_info["cpu_count"] // 2)
|
|
605
615
|
|
|
606
616
|
# Auto-detect packages from running applications and services
|
|
607
|
-
|
|
608
|
-
|
|
617
|
+
app_packages = detector.suggest_packages_for_apps(snapshot.applications)
|
|
618
|
+
service_packages = detector.suggest_packages_for_services(snapshot.running_services)
|
|
609
619
|
|
|
610
|
-
# Combine with base packages
|
|
620
|
+
# Combine with base packages (apt only)
|
|
611
621
|
base_packages = [
|
|
612
622
|
"build-essential",
|
|
613
623
|
"git",
|
|
@@ -615,10 +625,15 @@ def generate_clonebox_yaml(
|
|
|
615
625
|
"vim",
|
|
616
626
|
]
|
|
617
627
|
|
|
618
|
-
# Merge
|
|
619
|
-
|
|
628
|
+
# Merge apt packages and deduplicate
|
|
629
|
+
all_apt_packages = base_packages + app_packages["apt"] + service_packages["apt"]
|
|
630
|
+
if deduplicate:
|
|
631
|
+
all_apt_packages = deduplicate_list(all_apt_packages)
|
|
632
|
+
|
|
633
|
+
# Merge snap packages and deduplicate
|
|
634
|
+
all_snap_packages = app_packages["snap"] + service_packages["snap"]
|
|
620
635
|
if deduplicate:
|
|
621
|
-
|
|
636
|
+
all_snap_packages = deduplicate_list(all_snap_packages)
|
|
622
637
|
|
|
623
638
|
# Build config
|
|
624
639
|
config = {
|
|
@@ -635,7 +650,9 @@ def generate_clonebox_yaml(
|
|
|
635
650
|
"password": "${VM_PASSWORD}",
|
|
636
651
|
},
|
|
637
652
|
"services": services,
|
|
638
|
-
"packages":
|
|
653
|
+
"packages": all_apt_packages,
|
|
654
|
+
"snap_packages": all_snap_packages,
|
|
655
|
+
"post_commands": [], # User can add custom commands to run after setup
|
|
639
656
|
"paths": paths_mapping,
|
|
640
657
|
"detected": {
|
|
641
658
|
"running_apps": [
|
|
@@ -772,7 +789,9 @@ def create_vm_from_config(
|
|
|
772
789
|
base_image=config["vm"].get("base_image"),
|
|
773
790
|
paths=config.get("paths", {}),
|
|
774
791
|
packages=config.get("packages", []),
|
|
792
|
+
snap_packages=config.get("snap_packages", []),
|
|
775
793
|
services=config.get("services", []),
|
|
794
|
+
post_commands=config.get("post_commands", []),
|
|
776
795
|
user_session=user_session,
|
|
777
796
|
network_mode=config["vm"].get("network_mode", "auto"),
|
|
778
797
|
username=config["vm"].get("username", "ubuntu"),
|
|
@@ -31,7 +31,9 @@ class VMConfig:
|
|
|
31
31
|
base_image: Optional[str] = None
|
|
32
32
|
paths: dict = field(default_factory=dict)
|
|
33
33
|
packages: list = field(default_factory=list)
|
|
34
|
+
snap_packages: list = field(default_factory=list) # Snap packages to install
|
|
34
35
|
services: list = field(default_factory=list)
|
|
36
|
+
post_commands: list = field(default_factory=list) # Commands to run after setup
|
|
35
37
|
user_session: bool = False # Use qemu:///session instead of qemu:///system
|
|
36
38
|
network_mode: str = "auto" # auto|default|user
|
|
37
39
|
username: str = "ubuntu" # VM default username
|
|
@@ -508,7 +510,7 @@ class SelectiveVMCloner:
|
|
|
508
510
|
"\n".join(f" - {pkg}" for pkg in all_packages) if all_packages else ""
|
|
509
511
|
)
|
|
510
512
|
|
|
511
|
-
# Build runcmd - services
|
|
513
|
+
# Build runcmd - services, mounts, snaps, post_commands
|
|
512
514
|
runcmd_lines = []
|
|
513
515
|
|
|
514
516
|
# Add service enablement
|
|
@@ -519,6 +521,12 @@ class SelectiveVMCloner:
|
|
|
519
521
|
for cmd in mount_commands:
|
|
520
522
|
runcmd_lines.append(cmd)
|
|
521
523
|
|
|
524
|
+
# Install snap packages
|
|
525
|
+
if config.snap_packages:
|
|
526
|
+
runcmd_lines.append(" - echo 'Installing snap packages...'")
|
|
527
|
+
for snap_pkg in config.snap_packages:
|
|
528
|
+
runcmd_lines.append(f" - snap install {snap_pkg} --classic || snap install {snap_pkg} || true")
|
|
529
|
+
|
|
522
530
|
# Add GUI setup if enabled - runs AFTER package installation completes
|
|
523
531
|
if config.gui:
|
|
524
532
|
runcmd_lines.extend([
|
|
@@ -526,7 +534,18 @@ class SelectiveVMCloner:
|
|
|
526
534
|
" - systemctl enable gdm3 || systemctl enable gdm || true",
|
|
527
535
|
])
|
|
528
536
|
|
|
537
|
+
# Run user-defined post commands
|
|
538
|
+
if config.post_commands:
|
|
539
|
+
runcmd_lines.append(" - echo 'Running post-setup commands...'")
|
|
540
|
+
for cmd in config.post_commands:
|
|
541
|
+
runcmd_lines.append(f" - {cmd}")
|
|
542
|
+
|
|
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")
|
|
529
547
|
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")
|
|
530
549
|
|
|
531
550
|
# Add reboot command at the end if GUI is enabled
|
|
532
551
|
if config.gui:
|
|
@@ -144,59 +144,209 @@ class SystemDetector:
|
|
|
144
144
|
"esbuild",
|
|
145
145
|
"tmux",
|
|
146
146
|
"screen",
|
|
147
|
+
# IDEs and desktop apps
|
|
148
|
+
"pycharm",
|
|
149
|
+
"idea",
|
|
150
|
+
"webstorm",
|
|
151
|
+
"phpstorm",
|
|
152
|
+
"goland",
|
|
153
|
+
"clion",
|
|
154
|
+
"rider",
|
|
155
|
+
"datagrip",
|
|
156
|
+
"sublime",
|
|
157
|
+
"atom",
|
|
158
|
+
"slack",
|
|
159
|
+
"discord",
|
|
160
|
+
"telegram",
|
|
161
|
+
"spotify",
|
|
162
|
+
"vlc",
|
|
163
|
+
"gimp",
|
|
164
|
+
"inkscape",
|
|
165
|
+
"blender",
|
|
166
|
+
"obs",
|
|
167
|
+
"postman",
|
|
168
|
+
"insomnia",
|
|
169
|
+
"dbeaver",
|
|
147
170
|
]
|
|
148
171
|
|
|
149
|
-
# Map process/service names to Ubuntu packages
|
|
172
|
+
# Map process/service names to Ubuntu packages or snap packages
|
|
173
|
+
# Format: "process_name": ("package_name", "install_type") where install_type is "apt" or "snap"
|
|
150
174
|
APP_TO_PACKAGE_MAP = {
|
|
151
|
-
"python": "python3",
|
|
152
|
-
"python3": "python3",
|
|
153
|
-
"pip": "python3-pip",
|
|
154
|
-
"node": "nodejs",
|
|
155
|
-
"npm": "npm",
|
|
156
|
-
"yarn": "yarnpkg",
|
|
157
|
-
"docker": "docker.io",
|
|
158
|
-
"dockerd": "docker.io",
|
|
159
|
-
"docker-compose": "docker-compose",
|
|
160
|
-
"podman": "podman",
|
|
161
|
-
"nginx": "nginx",
|
|
162
|
-
"apache2": "apache2",
|
|
163
|
-
"httpd": "apache2",
|
|
164
|
-
"postgres": "postgresql",
|
|
165
|
-
"postgresql": "postgresql",
|
|
166
|
-
"mysql": "mysql-server",
|
|
167
|
-
"mysqld": "mysql-server",
|
|
168
|
-
"mongod": "mongodb",
|
|
169
|
-
"mongodb": "mongodb",
|
|
170
|
-
"redis-server": "redis-server",
|
|
171
|
-
"redis": "redis-server",
|
|
172
|
-
"vim": "vim",
|
|
173
|
-
"nvim": "neovim",
|
|
174
|
-
"emacs": "emacs",
|
|
175
|
-
"firefox": "firefox",
|
|
176
|
-
"chromium": "chromium-browser",
|
|
177
|
-
"jupyter": "jupyter-notebook",
|
|
178
|
-
"jupyter-lab": "jupyterlab",
|
|
179
|
-
"gunicorn": "gunicorn",
|
|
180
|
-
"uvicorn": "uvicorn",
|
|
181
|
-
"tmux": "tmux",
|
|
182
|
-
"screen": "screen",
|
|
183
|
-
"git": "git",
|
|
184
|
-
"curl": "curl",
|
|
185
|
-
"wget": "wget",
|
|
186
|
-
"ssh": "openssh-client",
|
|
187
|
-
"sshd": "openssh-server",
|
|
188
|
-
"go": "golang",
|
|
189
|
-
"cargo": "cargo",
|
|
190
|
-
"rustc": "rustc",
|
|
191
|
-
"java": "default-jdk",
|
|
192
|
-
"gradle": "gradle",
|
|
193
|
-
"mvn": "maven",
|
|
175
|
+
"python": ("python3", "apt"),
|
|
176
|
+
"python3": ("python3", "apt"),
|
|
177
|
+
"pip": ("python3-pip", "apt"),
|
|
178
|
+
"node": ("nodejs", "apt"),
|
|
179
|
+
"npm": ("npm", "apt"),
|
|
180
|
+
"yarn": ("yarnpkg", "apt"),
|
|
181
|
+
"docker": ("docker.io", "apt"),
|
|
182
|
+
"dockerd": ("docker.io", "apt"),
|
|
183
|
+
"docker-compose": ("docker-compose", "apt"),
|
|
184
|
+
"podman": ("podman", "apt"),
|
|
185
|
+
"nginx": ("nginx", "apt"),
|
|
186
|
+
"apache2": ("apache2", "apt"),
|
|
187
|
+
"httpd": ("apache2", "apt"),
|
|
188
|
+
"postgres": ("postgresql", "apt"),
|
|
189
|
+
"postgresql": ("postgresql", "apt"),
|
|
190
|
+
"mysql": ("mysql-server", "apt"),
|
|
191
|
+
"mysqld": ("mysql-server", "apt"),
|
|
192
|
+
"mongod": ("mongodb", "apt"),
|
|
193
|
+
"mongodb": ("mongodb", "apt"),
|
|
194
|
+
"redis-server": ("redis-server", "apt"),
|
|
195
|
+
"redis": ("redis-server", "apt"),
|
|
196
|
+
"vim": ("vim", "apt"),
|
|
197
|
+
"nvim": ("neovim", "apt"),
|
|
198
|
+
"emacs": ("emacs", "apt"),
|
|
199
|
+
"firefox": ("firefox", "apt"),
|
|
200
|
+
"chromium": ("chromium-browser", "apt"),
|
|
201
|
+
"jupyter": ("jupyter-notebook", "apt"),
|
|
202
|
+
"jupyter-lab": ("jupyterlab", "apt"),
|
|
203
|
+
"gunicorn": ("gunicorn", "apt"),
|
|
204
|
+
"uvicorn": ("uvicorn", "apt"),
|
|
205
|
+
"tmux": ("tmux", "apt"),
|
|
206
|
+
"screen": ("screen", "apt"),
|
|
207
|
+
"git": ("git", "apt"),
|
|
208
|
+
"curl": ("curl", "apt"),
|
|
209
|
+
"wget": ("wget", "apt"),
|
|
210
|
+
"ssh": ("openssh-client", "apt"),
|
|
211
|
+
"sshd": ("openssh-server", "apt"),
|
|
212
|
+
"go": ("golang", "apt"),
|
|
213
|
+
"cargo": ("cargo", "apt"),
|
|
214
|
+
"rustc": ("rustc", "apt"),
|
|
215
|
+
"java": ("default-jdk", "apt"),
|
|
216
|
+
"gradle": ("gradle", "apt"),
|
|
217
|
+
"mvn": ("maven", "apt"),
|
|
218
|
+
# Popular desktop apps (snap packages)
|
|
219
|
+
"chrome": ("chromium", "snap"),
|
|
220
|
+
"google-chrome": ("chromium", "snap"),
|
|
221
|
+
"pycharm": ("pycharm-community", "snap"),
|
|
222
|
+
"idea": ("intellij-idea-community", "snap"),
|
|
223
|
+
"code": ("code", "snap"),
|
|
224
|
+
"vscode": ("code", "snap"),
|
|
225
|
+
"slack": ("slack", "snap"),
|
|
226
|
+
"discord": ("discord", "snap"),
|
|
227
|
+
"spotify": ("spotify", "snap"),
|
|
228
|
+
"vlc": ("vlc", "apt"),
|
|
229
|
+
"gimp": ("gimp", "apt"),
|
|
230
|
+
"inkscape": ("inkscape", "apt"),
|
|
231
|
+
"blender": ("blender", "apt"),
|
|
232
|
+
"obs": ("obs-studio", "apt"),
|
|
233
|
+
"telegram": ("telegram-desktop", "snap"),
|
|
234
|
+
"postman": ("postman", "snap"),
|
|
235
|
+
"insomnia": ("insomnia", "snap"),
|
|
236
|
+
"dbeaver": ("dbeaver-ce", "snap"),
|
|
237
|
+
"sublime": ("sublime-text", "snap"),
|
|
238
|
+
"atom": ("atom", "snap"),
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Map applications to their config/data directories for complete cloning
|
|
242
|
+
# These directories contain user settings, extensions, profiles, credentials
|
|
243
|
+
APP_DATA_DIRS = {
|
|
244
|
+
# Browsers - profiles, extensions, bookmarks, passwords
|
|
245
|
+
"chrome": [".config/google-chrome", ".config/chromium"],
|
|
246
|
+
"chromium": [".config/chromium"],
|
|
247
|
+
"firefox": [".mozilla/firefox", ".cache/mozilla/firefox"],
|
|
248
|
+
|
|
249
|
+
# IDEs and editors - settings, extensions, projects history
|
|
250
|
+
"code": [".config/Code", ".vscode", ".vscode-server"],
|
|
251
|
+
"vscode": [".config/Code", ".vscode", ".vscode-server"],
|
|
252
|
+
"pycharm": [".config/JetBrains", ".local/share/JetBrains", ".cache/JetBrains"],
|
|
253
|
+
"idea": [".config/JetBrains", ".local/share/JetBrains"],
|
|
254
|
+
"webstorm": [".config/JetBrains", ".local/share/JetBrains"],
|
|
255
|
+
"goland": [".config/JetBrains", ".local/share/JetBrains"],
|
|
256
|
+
"sublime": [".config/sublime-text", ".config/sublime-text-3"],
|
|
257
|
+
"atom": [".atom"],
|
|
258
|
+
"vim": [".vim", ".vimrc", ".config/nvim"],
|
|
259
|
+
"nvim": [".config/nvim", ".local/share/nvim"],
|
|
260
|
+
"emacs": [".emacs.d", ".emacs"],
|
|
261
|
+
"cursor": [".config/Cursor", ".cursor"],
|
|
262
|
+
|
|
263
|
+
# Development tools
|
|
264
|
+
"docker": [".docker"],
|
|
265
|
+
"git": [".gitconfig", ".git-credentials", ".config/git"],
|
|
266
|
+
"npm": [".npm", ".npmrc"],
|
|
267
|
+
"yarn": [".yarn", ".yarnrc"],
|
|
268
|
+
"pip": [".pip", ".config/pip"],
|
|
269
|
+
"cargo": [".cargo"],
|
|
270
|
+
"rustup": [".rustup"],
|
|
271
|
+
"go": [".go", "go"],
|
|
272
|
+
"gradle": [".gradle"],
|
|
273
|
+
"maven": [".m2"],
|
|
274
|
+
|
|
275
|
+
# Python environments
|
|
276
|
+
"python": [".pyenv", ".virtualenvs", ".local/share/virtualenvs"],
|
|
277
|
+
"python3": [".pyenv", ".virtualenvs", ".local/share/virtualenvs"],
|
|
278
|
+
"conda": [".conda", "anaconda3", "miniconda3"],
|
|
279
|
+
|
|
280
|
+
# Node.js
|
|
281
|
+
"node": [".nvm", ".node", ".npm"],
|
|
282
|
+
|
|
283
|
+
# Databases
|
|
284
|
+
"postgres": [".pgpass", ".psqlrc", ".psql_history"],
|
|
285
|
+
"mysql": [".my.cnf", ".mysql_history"],
|
|
286
|
+
"mongodb": [".mongorc.js", ".dbshell"],
|
|
287
|
+
"redis": [".rediscli_history"],
|
|
288
|
+
|
|
289
|
+
# Communication apps
|
|
290
|
+
"slack": [".config/Slack"],
|
|
291
|
+
"discord": [".config/discord"],
|
|
292
|
+
"telegram": [".local/share/TelegramDesktop"],
|
|
293
|
+
"teams": [".config/Microsoft/Microsoft Teams"],
|
|
294
|
+
|
|
295
|
+
# Other tools
|
|
296
|
+
"postman": [".config/Postman"],
|
|
297
|
+
"insomnia": [".config/Insomnia"],
|
|
298
|
+
"dbeaver": [".local/share/DBeaverData"],
|
|
299
|
+
"ssh": [".ssh"],
|
|
300
|
+
"gpg": [".gnupg"],
|
|
301
|
+
"aws": [".aws"],
|
|
302
|
+
"gcloud": [".config/gcloud"],
|
|
303
|
+
"kubectl": [".kube"],
|
|
304
|
+
"terraform": [".terraform.d"],
|
|
305
|
+
"ansible": [".ansible"],
|
|
306
|
+
|
|
307
|
+
# General app data
|
|
308
|
+
"spotify": [".config/spotify"],
|
|
309
|
+
"vlc": [".config/vlc"],
|
|
310
|
+
"gimp": [".config/GIMP", ".gimp-2.10"],
|
|
311
|
+
"obs": [".config/obs-studio"],
|
|
194
312
|
}
|
|
195
313
|
|
|
196
314
|
def __init__(self):
|
|
197
315
|
self.user = pwd.getpwuid(os.getuid()).pw_name
|
|
198
316
|
self.home = Path.home()
|
|
199
317
|
|
|
318
|
+
def detect_app_data_dirs(self, applications: list) -> list:
|
|
319
|
+
"""Detect config/data directories for running applications.
|
|
320
|
+
|
|
321
|
+
Returns list of paths that contain user data needed by running apps.
|
|
322
|
+
"""
|
|
323
|
+
app_data_paths = []
|
|
324
|
+
seen_paths = set()
|
|
325
|
+
|
|
326
|
+
for app in applications:
|
|
327
|
+
app_name = app.name.lower()
|
|
328
|
+
|
|
329
|
+
# Check each known app pattern
|
|
330
|
+
for pattern, dirs in self.APP_DATA_DIRS.items():
|
|
331
|
+
if pattern in app_name:
|
|
332
|
+
for dir_name in dirs:
|
|
333
|
+
full_path = self.home / dir_name
|
|
334
|
+
if full_path.exists() and str(full_path) not in seen_paths:
|
|
335
|
+
seen_paths.add(str(full_path))
|
|
336
|
+
# Calculate size
|
|
337
|
+
try:
|
|
338
|
+
size = self._get_dir_size(full_path, max_depth=2)
|
|
339
|
+
except:
|
|
340
|
+
size = 0
|
|
341
|
+
app_data_paths.append({
|
|
342
|
+
"path": str(full_path),
|
|
343
|
+
"app": app.name,
|
|
344
|
+
"type": "app_data",
|
|
345
|
+
"size_mb": round(size / 1024 / 1024, 1)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
return app_data_paths
|
|
349
|
+
|
|
200
350
|
def detect_all(self) -> SystemSnapshot:
|
|
201
351
|
"""Detect all services, applications and paths."""
|
|
202
352
|
return SystemSnapshot(
|
|
@@ -437,29 +587,53 @@ class SystemDetector:
|
|
|
437
587
|
pass
|
|
438
588
|
return containers
|
|
439
589
|
|
|
440
|
-
def suggest_packages_for_apps(self, applications: list) ->
|
|
441
|
-
"""Suggest
|
|
442
|
-
|
|
590
|
+
def suggest_packages_for_apps(self, applications: list) -> dict:
|
|
591
|
+
"""Suggest packages based on detected applications.
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
dict with 'apt' and 'snap' keys containing lists of packages
|
|
595
|
+
"""
|
|
596
|
+
apt_packages = set()
|
|
597
|
+
snap_packages = set()
|
|
598
|
+
|
|
443
599
|
for app in applications:
|
|
444
600
|
app_name = app.name.lower()
|
|
445
|
-
|
|
446
|
-
for key, package in self.APP_TO_PACKAGE_MAP.items():
|
|
601
|
+
for key, (package, install_type) in self.APP_TO_PACKAGE_MAP.items():
|
|
447
602
|
if key in app_name:
|
|
448
|
-
|
|
603
|
+
if install_type == "snap":
|
|
604
|
+
snap_packages.add(package)
|
|
605
|
+
else:
|
|
606
|
+
apt_packages.add(package)
|
|
449
607
|
break
|
|
450
|
-
|
|
608
|
+
|
|
609
|
+
return {
|
|
610
|
+
"apt": sorted(list(apt_packages)),
|
|
611
|
+
"snap": sorted(list(snap_packages))
|
|
612
|
+
}
|
|
451
613
|
|
|
452
|
-
def suggest_packages_for_services(self, services: list) ->
|
|
453
|
-
"""Suggest
|
|
454
|
-
|
|
614
|
+
def suggest_packages_for_services(self, services: list) -> dict:
|
|
615
|
+
"""Suggest packages based on detected services.
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
dict with 'apt' and 'snap' keys containing lists of packages
|
|
619
|
+
"""
|
|
620
|
+
apt_packages = set()
|
|
621
|
+
snap_packages = set()
|
|
622
|
+
|
|
455
623
|
for service in services:
|
|
456
624
|
service_name = service.name.lower()
|
|
457
|
-
|
|
458
|
-
for key, package in self.APP_TO_PACKAGE_MAP.items():
|
|
625
|
+
for key, (package, install_type) in self.APP_TO_PACKAGE_MAP.items():
|
|
459
626
|
if key in service_name:
|
|
460
|
-
|
|
627
|
+
if install_type == "snap":
|
|
628
|
+
snap_packages.add(package)
|
|
629
|
+
else:
|
|
630
|
+
apt_packages.add(package)
|
|
461
631
|
break
|
|
462
|
-
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
"apt": sorted(list(apt_packages)),
|
|
635
|
+
"snap": sorted(list(snap_packages))
|
|
636
|
+
}
|
|
463
637
|
|
|
464
638
|
def get_system_info(self) -> dict:
|
|
465
639
|
"""Get basic system information."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clonebox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
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
|
|
@@ -38,6 +38,7 @@ Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
|
38
38
|
Dynamic: license-file
|
|
39
39
|
|
|
40
40
|
# CloneBox 📦
|
|
41
|
+

|
|
41
42
|
|
|
42
43
|
```commandline
|
|
43
44
|
╔═══════════════════════════════════════════════════════╗
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|