machineconfig 5.20__py3-none-any.whl → 5.22__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 machineconfig might be problematic. Click here for more details.

Files changed (26) hide show
  1. machineconfig/cluster/sessions_managers/zellij_local.py +1 -3
  2. machineconfig/jobs/installer/custom_dev/brave.py +0 -6
  3. machineconfig/jobs/installer/package_groups.py +12 -12
  4. machineconfig/profile/shell.py +1 -1
  5. machineconfig/scripts/python/cloud_repo_sync.py +21 -22
  6. machineconfig/scripts/python/croshell.py +2 -4
  7. machineconfig/scripts/python/devops.py +18 -9
  8. machineconfig/scripts/python/devops_status.py +521 -0
  9. machineconfig/scripts/python/devops_update_repos.py +1 -3
  10. machineconfig/scripts/python/fire_jobs.py +15 -50
  11. machineconfig/scripts/python/fire_jobs_args_helper.py +4 -1
  12. machineconfig/scripts/python/fire_jobs_route_helper.py +46 -0
  13. machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -40
  14. machineconfig/scripts/python/onetimeshare.py +0 -1
  15. machineconfig/scripts/python/sessions.py +7 -10
  16. machineconfig/scripts/python/sessions_multiprocess.py +56 -0
  17. machineconfig/setup_linux/repos.sh +1 -29
  18. machineconfig/setup_windows/repos.ps1 +0 -12
  19. machineconfig/utils/files/read.py +4 -6
  20. machineconfig/utils/notifications.py +1 -1
  21. machineconfig/utils/ssh.py +2 -13
  22. {machineconfig-5.20.dist-info → machineconfig-5.22.dist-info}/METADATA +1 -1
  23. {machineconfig-5.20.dist-info → machineconfig-5.22.dist-info}/RECORD +26 -24
  24. {machineconfig-5.20.dist-info → machineconfig-5.22.dist-info}/WHEEL +0 -0
  25. {machineconfig-5.20.dist-info → machineconfig-5.22.dist-info}/entry_points.txt +0 -0
  26. {machineconfig-5.20.dist-info → machineconfig-5.22.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,521 @@
1
+ """Machine Status Display - Comprehensive system and configuration overview"""
2
+
3
+ import platform
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from rich.console import Console
9
+ from rich.panel import Panel
10
+ from rich.table import Table
11
+ from rich.text import Text
12
+
13
+ from machineconfig.utils.path_extended import PathExtended
14
+ from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH, LIBRARY_ROOT
15
+
16
+
17
+ console = Console()
18
+
19
+
20
+ def _check_system_info() -> dict[str, str]:
21
+ """Gather basic system information."""
22
+ import socket
23
+ import os
24
+
25
+ return {
26
+ "hostname": socket.gethostname(),
27
+ "system": platform.system(),
28
+ "release": platform.release(),
29
+ "version": platform.version(),
30
+ "machine": platform.machine(),
31
+ "processor": platform.processor() or "Unknown",
32
+ "python_version": platform.python_version(),
33
+ "user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
34
+ }
35
+
36
+
37
+ def _check_shell_profile_status() -> dict[str, Any]:
38
+ """Check shell profile configuration status."""
39
+ from machineconfig.profile.shell import get_shell_profile_path
40
+
41
+ try:
42
+ profile_path = get_shell_profile_path()
43
+ profile_exists = profile_path.exists()
44
+ profile_content = profile_path.read_text(encoding="utf-8") if profile_exists else ""
45
+
46
+ system_name = platform.system()
47
+ if system_name == "Windows":
48
+ init_script = PathExtended(LIBRARY_ROOT).joinpath("settings/shells/pwsh/init.ps1")
49
+ init_script_copy = PathExtended(CONFIG_PATH).joinpath("profile/init.ps1").collapseuser()
50
+ source_reference = f". {str(init_script.collapseuser()).replace('~', '$HOME')}"
51
+ source_copy = f". {str(init_script_copy).replace('~', '$HOME')}"
52
+ else:
53
+ init_script = PathExtended(LIBRARY_ROOT).joinpath("settings/shells/bash/init.sh")
54
+ init_script_copy = PathExtended(CONFIG_PATH).joinpath("profile/init.sh").collapseuser()
55
+ source_reference = f"source {str(init_script.collapseuser()).replace('~', '$HOME')}"
56
+ source_copy = f"source {str(init_script_copy).replace('~', '$HOME')}"
57
+
58
+ configured = source_reference in profile_content or source_copy in profile_content
59
+ method = "reference" if source_reference in profile_content else ("copy" if source_copy in profile_content else "none")
60
+
61
+ return {
62
+ "profile_path": str(profile_path),
63
+ "exists": profile_exists,
64
+ "configured": configured,
65
+ "method": method,
66
+ "init_script_exists": init_script.exists(),
67
+ "init_script_copy_exists": init_script_copy.exists(),
68
+ }
69
+ except Exception as ex:
70
+ return {"profile_path": "Error", "exists": False, "configured": False, "method": "error", "error": str(ex), "init_script_exists": False, "init_script_copy_exists": False}
71
+
72
+
73
+ def _check_machineconfig_repo() -> dict[str, Any]:
74
+ """Check machineconfig repository status."""
75
+ repo_path = Path.home().joinpath("code/machineconfig")
76
+ if not repo_path.exists():
77
+ return {"exists": False, "is_repo": False, "clean": False, "branch": "N/A", "commit": "N/A", "remotes": []}
78
+
79
+ try:
80
+ import git
81
+
82
+ repo = git.Repo(str(repo_path))
83
+ is_dirty = repo.is_dirty(untracked_files=True)
84
+ current_branch = repo.active_branch.name if not repo.head.is_detached else "DETACHED"
85
+ current_commit = repo.head.commit.hexsha[:8]
86
+ remotes = [remote.name for remote in repo.remotes]
87
+
88
+ return {
89
+ "exists": True,
90
+ "is_repo": True,
91
+ "clean": not is_dirty,
92
+ "branch": current_branch,
93
+ "commit": current_commit,
94
+ "remotes": remotes,
95
+ "path": str(repo_path),
96
+ }
97
+ except Exception as ex:
98
+ return {"exists": True, "is_repo": False, "clean": False, "branch": "Error", "commit": "N/A", "remotes": [], "error": str(ex)}
99
+
100
+
101
+ def _check_repos_status() -> dict[str, Any]:
102
+ """Check configured repositories status."""
103
+ from machineconfig.utils.io import read_ini
104
+
105
+ try:
106
+ repos_str = read_ini(DEFAULTS_PATH)["general"]["repos"]
107
+ repo_paths = [Path(p.strip()).expanduser() for p in repos_str.split(",") if p.strip()]
108
+
109
+ repos_info = []
110
+ for repo_path in repo_paths:
111
+ if not repo_path.exists():
112
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": False, "is_repo": False})
113
+ continue
114
+
115
+ try:
116
+ import git
117
+
118
+ repo = git.Repo(str(repo_path))
119
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": True, "is_repo": True, "clean": not repo.is_dirty(untracked_files=True), "branch": repo.active_branch.name if not repo.head.is_detached else "DETACHED"})
120
+ except Exception:
121
+ repos_info.append({"path": str(repo_path), "name": repo_path.name, "exists": True, "is_repo": False})
122
+
123
+ return {"configured": True, "count": len(repos_info), "repos": repos_info}
124
+ except (FileNotFoundError, KeyError, IndexError):
125
+ return {"configured": False, "count": 0, "repos": []}
126
+
127
+
128
+ def _check_ssh_status() -> dict[str, Any]:
129
+ """Check SSH configuration status."""
130
+ ssh_dir = PathExtended.home().joinpath(".ssh")
131
+ if not ssh_dir.exists():
132
+ return {"ssh_dir_exists": False, "keys": [], "config_exists": False, "authorized_keys_exists": False, "known_hosts_exists": False}
133
+
134
+ keys = []
135
+ for pub_key in ssh_dir.glob("*.pub"):
136
+ private_key = pub_key.with_suffix("")
137
+ keys.append({"name": pub_key.stem, "public_exists": True, "private_exists": private_key.exists(), "public_path": str(pub_key), "private_path": str(private_key)})
138
+
139
+ config_file = ssh_dir.joinpath("config")
140
+ authorized_keys = ssh_dir.joinpath("authorized_keys")
141
+ known_hosts = ssh_dir.joinpath("known_hosts")
142
+
143
+ return {
144
+ "ssh_dir_exists": True,
145
+ "keys": keys,
146
+ "config_exists": config_file.exists(),
147
+ "authorized_keys_exists": authorized_keys.exists(),
148
+ "known_hosts_exists": known_hosts.exists(),
149
+ "ssh_dir_path": str(ssh_dir),
150
+ }
151
+
152
+
153
+ def _check_config_files_status() -> dict[str, Any]:
154
+ """Check public and private configuration files status."""
155
+ from machineconfig.profile.create import read_mapper
156
+
157
+ try:
158
+ mapper = read_mapper()
159
+ public_configs = list(mapper.get("public", {}).keys())
160
+ private_configs = list(mapper.get("private", {}).keys())
161
+
162
+ public_count = len(public_configs)
163
+ private_count = len(private_configs)
164
+
165
+ public_linked = 0
166
+ for config_name in public_configs:
167
+ for config_item in mapper["public"][config_name]:
168
+ target_path = PathExtended(config_item["config_file_default_path"]).expanduser()
169
+ if target_path.exists():
170
+ public_linked += 1
171
+ break
172
+
173
+ private_linked = 0
174
+ for config_name in private_configs:
175
+ for config_item in mapper["private"][config_name]:
176
+ target_path = PathExtended(config_item["config_file_default_path"]).expanduser()
177
+ if target_path.exists():
178
+ private_linked += 1
179
+ break
180
+
181
+ return {
182
+ "public_count": public_count,
183
+ "public_linked": public_linked,
184
+ "private_count": private_count,
185
+ "private_linked": private_linked,
186
+ "public_configs": public_configs,
187
+ "private_configs": private_configs,
188
+ }
189
+ except Exception as ex:
190
+ return {"public_count": 0, "public_linked": 0, "private_count": 0, "private_linked": 0, "error": str(ex), "public_configs": [], "private_configs": []}
191
+
192
+
193
+ def _check_important_tools() -> dict[str, dict[str, bool]]:
194
+ """Check if important CLI tools are installed, organized by groups."""
195
+ from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
196
+
197
+ group_status = {}
198
+ for group_name, tools in PACKAGE_GROUP2NAMES.items():
199
+ tool_status = {}
200
+ for tool in tools:
201
+ tool_status[tool] = shutil.which(tool) is not None
202
+ group_status[group_name] = tool_status
203
+
204
+ return group_status
205
+
206
+
207
+ def _check_backup_config() -> dict[str, Any]:
208
+ """Check backup configuration status."""
209
+ from machineconfig.utils.io import read_ini
210
+ import tomllib
211
+
212
+ try:
213
+ cloud_config = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
214
+ except (FileNotFoundError, KeyError, IndexError):
215
+ cloud_config = "Not configured"
216
+
217
+ try:
218
+ backup_file = LIBRARY_ROOT.joinpath("profile/backup.toml")
219
+ if backup_file.exists():
220
+ backup_data = tomllib.loads(backup_file.read_text(encoding="utf-8"))
221
+ backup_items = list(backup_data.keys())
222
+ backup_items_count = len(backup_items)
223
+ else:
224
+ backup_items = []
225
+ backup_items_count = 0
226
+ except Exception:
227
+ backup_items = []
228
+ backup_items_count = 0
229
+
230
+ return {"cloud_config": cloud_config, "backup_items_count": backup_items_count, "backup_items": backup_items}
231
+
232
+
233
+ def _display_system_info(info: dict[str, str]) -> None:
234
+ """Display system information panel."""
235
+ console.rule("[bold blue]💻 System Information[/bold blue]")
236
+
237
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
238
+ table.add_column("Property", style="cyan", no_wrap=True)
239
+ table.add_column("Value", style="white")
240
+
241
+ table.add_row("🏠 Hostname", info["hostname"])
242
+ table.add_row("💿 System", f"{info['system']} {info['release']}")
243
+ table.add_row("🖥️ Machine", info["machine"])
244
+ table.add_row("⚙️ Processor", info["processor"])
245
+ table.add_row("🐍 Python", info["python_version"])
246
+ table.add_row("👤 User", info["user"])
247
+
248
+ console.print(Panel(table, title="System", border_style="blue", padding=(1, 2), expand=False))
249
+
250
+
251
+ def _display_shell_status(status: dict[str, Any]) -> None:
252
+ """Display shell profile status panel."""
253
+ console.rule("[bold green]🐚 Shell Profile[/bold green]")
254
+
255
+ if "error" in status:
256
+ console.print(Panel(f"❌ Error: {status['error']}", title="Shell Profile", border_style="red", padding=(1, 2), expand=False))
257
+ return
258
+
259
+ from rich.columns import Columns
260
+
261
+ left_table = Table(show_header=False, box=None, padding=(0, 1))
262
+ left_table.add_column("Item", style="cyan", no_wrap=True)
263
+ left_table.add_column("Status")
264
+
265
+ left_table.add_row("📄 Profile", status['profile_path'])
266
+ left_table.add_row(f"{'✅' if status['exists'] else '❌'} Exists", str(status['exists']))
267
+ left_table.add_row(f"{'✅' if status['configured'] else '❌'} Configured", str(status['configured']))
268
+
269
+ right_table = Table(show_header=False, box=None, padding=(0, 1))
270
+ right_table.add_column("Item", style="cyan", no_wrap=True)
271
+ right_table.add_column("Status")
272
+
273
+ right_table.add_row("🔧 Method", status['method'])
274
+ right_table.add_row(f"{'✅' if status['init_script_exists'] else '❌'} Init (source)", str(status['init_script_exists']))
275
+ right_table.add_row(f"{'✅' if status['init_script_copy_exists'] else '❌'} Init (copy)", str(status['init_script_copy_exists']))
276
+
277
+ border_style = "green" if status["configured"] else "yellow"
278
+ console.print(Panel(Columns([left_table, right_table], equal=True, expand=True), title="Shell Profile", border_style=border_style, padding=(1, 2), expand=False))
279
+
280
+
281
+ def _display_machineconfig_repo(info: dict[str, Any]) -> None:
282
+ """Display machineconfig repository status."""
283
+ console.rule("[bold magenta]📦 Machineconfig Repository[/bold magenta]")
284
+
285
+ if not info["exists"]:
286
+ console.print(Panel("❌ Machineconfig repository not found at ~/code/machineconfig", title="Repository Status", border_style="red", padding=(1, 2), expand=False))
287
+ return
288
+
289
+ if not info["is_repo"]:
290
+ console.print(Panel(f"❌ Directory exists but is not a git repository\n{info.get('error', 'Unknown error')}", title="Repository Status", border_style="red", padding=(1, 2), expand=False))
291
+ return
292
+
293
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
294
+ table.add_column("Property", style="cyan", no_wrap=True)
295
+ table.add_column("Value", style="white")
296
+
297
+ table.add_row("📁 Path", info['path'])
298
+ table.add_row("🌿 Branch", info['branch'])
299
+ table.add_row("🔖 Commit", info['commit'])
300
+ table.add_row(f"{'✅' if info['clean'] else '⚠️'} Status", 'Clean' if info['clean'] else 'Uncommitted changes')
301
+ table.add_row("📡 Remotes", ', '.join(info['remotes']) if info['remotes'] else 'None')
302
+
303
+ border_style = "green" if info["clean"] else "yellow"
304
+ console.print(Panel(table, title="Machineconfig Repository", border_style=border_style, padding=(1, 2), expand=False))
305
+
306
+
307
+ def _display_repos_status(status: dict[str, Any]) -> None:
308
+ """Display configured repositories status."""
309
+ console.rule("[bold cyan]📚 Configured Repositories[/bold cyan]")
310
+
311
+ if not status["configured"]:
312
+ console.print(Panel(f"⚠️ No repositories configured in {DEFAULTS_PATH}", title="Repositories", border_style="yellow", padding=(1, 2)))
313
+ return
314
+
315
+ if status["count"] == 0:
316
+ console.print(Panel("ℹ️ No repositories configured", title="Repositories", border_style="blue", padding=(1, 2)))
317
+ return
318
+
319
+ table = Table(show_lines=True, header_style="bold cyan")
320
+ table.add_column("Repository", style="bold")
321
+ table.add_column("Status")
322
+ table.add_column("Details")
323
+
324
+ for repo in status["repos"]:
325
+ name = repo["name"]
326
+ if not repo["exists"]:
327
+ table.add_row(f"❌ {name}", "Missing", f"Path: {repo['path']}")
328
+ elif not repo["is_repo"]:
329
+ table.add_row(f"⚠️ {name}", "Not a repo", f"Path: {repo['path']}")
330
+ else:
331
+ status_icon = "✅" if repo["clean"] else "⚠️"
332
+ status_text = "Clean" if repo["clean"] else "Uncommitted changes"
333
+ table.add_row(f"{status_icon} {name}", status_text, f"Branch: {repo['branch']}")
334
+
335
+ console.print(Panel(table, title=f"Repositories ({status['count']})", border_style="cyan", padding=(1, 2)))
336
+
337
+
338
+ def _display_ssh_status(status: dict[str, Any]) -> None:
339
+ """Display SSH configuration status."""
340
+ console.rule("[bold yellow]🔐 SSH Configuration[/bold yellow]")
341
+
342
+ if not status["ssh_dir_exists"]:
343
+ console.print(Panel("❌ SSH directory (~/.ssh) does not exist", title="SSH Status", border_style="red", padding=(1, 2), expand=False))
344
+ return
345
+
346
+ from rich.columns import Columns
347
+
348
+ config_table = Table(show_header=False, box=None, padding=(0, 1))
349
+ config_table.add_column("Item", style="cyan", no_wrap=True)
350
+ config_table.add_column("Status")
351
+
352
+ config_table.add_row("📁 Directory", status['ssh_dir_path'])
353
+ config_table.add_row(f"{'✅' if status['config_exists'] else '❌'} Config", str(status['config_exists']))
354
+ config_table.add_row(f"{'✅' if status['authorized_keys_exists'] else '❌'} Auth Keys", str(status['authorized_keys_exists']))
355
+ config_table.add_row(f"{'✅' if status['known_hosts_exists'] else '❌'} Known Hosts", str(status['known_hosts_exists']))
356
+
357
+ config_panel = Panel(config_table, title="SSH Config", border_style="yellow", padding=(1, 2), expand=False)
358
+
359
+ if status["keys"]:
360
+ keys_table = Table(show_header=True, box=None, padding=(0, 1), show_lines=False, expand=False)
361
+ keys_table.add_column("Key Name", style="bold cyan")
362
+ keys_table.add_column("Pub", justify="center")
363
+ keys_table.add_column("Priv", justify="center")
364
+
365
+ for key in status["keys"]:
366
+ pub_status = "✅" if key["public_exists"] else "❌"
367
+ priv_status = "✅" if key["private_exists"] else "❌"
368
+ keys_table.add_row(key["name"], pub_status, priv_status)
369
+
370
+ keys_panel = Panel(keys_table, title=f"SSH Keys ({len(status['keys'])})", border_style="yellow", padding=(1, 2), expand=False)
371
+
372
+ console.print(Columns([config_panel, keys_panel], equal=False, expand=True))
373
+ else:
374
+ console.print(config_panel)
375
+
376
+
377
+ def _display_config_files_status(status: dict[str, Any]) -> None:
378
+ """Display configuration files status."""
379
+ console.rule("[bold bright_blue]⚙️ Configuration Files[/bold bright_blue]")
380
+
381
+ if "error" in status:
382
+ console.print(Panel(f"❌ Error reading configuration: {status['error']}", title="Configuration Files", border_style="red", padding=(1, 2), expand=False))
383
+ return
384
+
385
+ public_percentage = (status["public_linked"] / status["public_count"] * 100) if status["public_count"] > 0 else 0
386
+ private_percentage = (status["private_linked"] / status["private_count"] * 100) if status["private_count"] > 0 else 0
387
+
388
+ table = Table(show_header=True, box=None, padding=(0, 2), expand=False)
389
+ table.add_column("Type", style="cyan", no_wrap=True)
390
+ table.add_column("Linked", justify="right")
391
+ table.add_column("Total", justify="right")
392
+ table.add_column("Progress", justify="right")
393
+
394
+ table.add_row("📂 Public", str(status['public_linked']), str(status['public_count']), f"{public_percentage:.0f}%")
395
+ table.add_row("🔒 Private", str(status['private_linked']), str(status['private_count']), f"{private_percentage:.0f}%")
396
+
397
+ overall_linked = status["public_linked"] + status["private_linked"]
398
+ overall_total = status["public_count"] + status["private_count"]
399
+ overall_percentage = (overall_linked / overall_total * 100) if overall_total > 0 else 0
400
+
401
+ border_style = "green" if overall_percentage > 80 else ("yellow" if overall_percentage > 50 else "red")
402
+
403
+ console.print(Panel(table, title=f"Configuration Files ({overall_percentage:.0f}% configured)", border_style=border_style, padding=(1, 2), expand=False))
404
+
405
+
406
+ def _display_tools_status(grouped_tools: dict[str, dict[str, bool]]) -> None:
407
+ """Display important tools installation status organized by groups."""
408
+ console.rule("[bold bright_magenta]🛠️ Important Tools[/bold bright_magenta]")
409
+
410
+ from rich.columns import Columns
411
+
412
+ all_group_panels = []
413
+ total_installed = 0
414
+ total_tools = 0
415
+
416
+ for group_name, tools in grouped_tools.items():
417
+ sorted_tools = sorted(tools.keys())
418
+ installed = [tool for tool, status in tools.items() if status]
419
+ total_installed += len(installed)
420
+ total_tools += len(tools)
421
+
422
+ num_columns = 8
423
+ tools_per_column = (len(sorted_tools) + num_columns - 1) // num_columns
424
+
425
+ tables = []
426
+ for col_idx in range(num_columns):
427
+ table = Table(show_header=False, box=None, padding=(0, 0), collapse_padding=True)
428
+ table.add_column("Tool", style="cyan", no_wrap=True, width=None)
429
+ table.add_column("", justify="center", width=2, no_wrap=True)
430
+
431
+ start_idx = col_idx * tools_per_column
432
+ end_idx = min(start_idx + tools_per_column, len(sorted_tools))
433
+
434
+ for i in range(start_idx, end_idx):
435
+ tool = sorted_tools[i]
436
+ status_icon = "✅" if tools[tool] else "❌"
437
+ table.add_row(tool, status_icon)
438
+
439
+ if start_idx < len(sorted_tools):
440
+ tables.append(table)
441
+
442
+ installed_percentage = (len(installed) / len(tools) * 100) if tools else 0
443
+ border_style = "green" if installed_percentage > 80 else ("yellow" if installed_percentage > 50 else "red")
444
+
445
+ group_display_name = group_name.replace("_", " ").title()
446
+ group_panel = Panel(
447
+ Columns(tables, equal=False, expand=False, padding=(0, 1)),
448
+ title=f"{group_display_name} ({len(installed)}/{len(tools)})",
449
+ border_style=border_style,
450
+ padding=(0, 1),
451
+ expand=False
452
+ )
453
+ all_group_panels.append(group_panel)
454
+
455
+ overall_percentage = (total_installed / total_tools * 100) if total_tools else 0
456
+ master_border_style = "green" if overall_percentage > 80 else ("yellow" if overall_percentage > 50 else "red")
457
+
458
+ from rich.console import Group
459
+ master_panel = Panel(
460
+ Group(*all_group_panels),
461
+ title=f"🛠️ Tools Overview ({total_installed}/{total_tools} installed - {overall_percentage:.0f}%)",
462
+ border_style=master_border_style,
463
+ padding=(1, 2),
464
+ expand=False
465
+ )
466
+ console.print(master_panel)
467
+
468
+
469
+ def _display_backup_status(status: dict[str, Any]) -> None:
470
+ """Display backup configuration status."""
471
+ console.rule("[bold bright_cyan]💾 Backup Configuration[/bold bright_cyan]")
472
+
473
+ table = Table(show_header=False, box=None, padding=(0, 1), expand=False)
474
+ table.add_column("Property", style="cyan", no_wrap=True)
475
+ table.add_column("Value", style="white")
476
+
477
+ table.add_row("🌥️ Cloud Config", status['cloud_config'])
478
+ table.add_row("📦 Backup Items", str(status['backup_items_count']))
479
+
480
+ border_style = "green" if status["cloud_config"] != "Not configured" else "yellow"
481
+
482
+ console.print(Panel(table, title="Backup Configuration", border_style=border_style, padding=(1, 2), expand=False))
483
+
484
+
485
+ def main() -> None:
486
+ """Main function to display comprehensive machine status."""
487
+ console.print("\n")
488
+ console.print(Panel(Text("📊 Machine Status Report", justify="center", style="bold white"), style="bold blue", padding=(1, 2)))
489
+ console.print("\n")
490
+
491
+ system_info = _check_system_info()
492
+ _display_system_info(system_info)
493
+
494
+ shell_status = _check_shell_profile_status()
495
+ _display_shell_status(shell_status)
496
+
497
+ machineconfig_repo = _check_machineconfig_repo()
498
+ _display_machineconfig_repo(machineconfig_repo)
499
+
500
+ repos_status = _check_repos_status()
501
+ _display_repos_status(repos_status)
502
+
503
+ ssh_status = _check_ssh_status()
504
+ _display_ssh_status(ssh_status)
505
+
506
+ config_status = _check_config_files_status()
507
+ _display_config_files_status(config_status)
508
+
509
+ tools_status = _check_important_tools()
510
+ _display_tools_status(tools_status)
511
+
512
+ backup_status = _check_backup_config()
513
+ _display_backup_status(backup_status)
514
+
515
+ console.print("\n")
516
+ console.print(Panel(Text("✨ Status report complete!", justify="center", style="bold green"), style="green", padding=(1, 2)))
517
+ console.print("\n")
518
+
519
+
520
+ if __name__ == "__main__":
521
+ main()
@@ -148,7 +148,7 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
148
148
  def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
149
149
  """Main function to update all configured repositories."""
150
150
  _ = verbose
151
- repos: list[Path] = [Path.home() / "code/machineconfig", Path.home() / "code/crocodile"]
151
+ repos: list[Path] = [Path.home() / "code/machineconfig"]
152
152
  try:
153
153
  tmp = read_ini(DEFAULTS_PATH)["general"]["repos"].split(",")
154
154
  if tmp[-1] == "":
@@ -237,11 +237,9 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
237
237
  padding=(1, 2),
238
238
  )
239
239
  )
240
-
241
240
  # Run uv sync for repositories where pyproject.toml changed but sync wasn't run yet
242
241
  for repo_path in repos_with_changes:
243
242
  run_uv_sync(repo_path)
244
-
245
243
  # Generate and display summary
246
244
  _display_summary(results)
247
245