machineconfig 2.0__py3-none-any.whl → 2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (235) hide show
  1. machineconfig/cluster/cloud_manager.py +0 -3
  2. machineconfig/cluster/data_transfer.py +0 -1
  3. machineconfig/cluster/file_manager.py +0 -1
  4. machineconfig/cluster/job_params.py +0 -3
  5. machineconfig/cluster/loader_runner.py +0 -3
  6. machineconfig/cluster/remote_machine.py +0 -1
  7. machineconfig/cluster/script_notify_upon_completion.py +0 -1
  8. machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +3 -5
  9. machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
  10. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +17 -57
  11. machineconfig/cluster/sessions_managers/wt_local.py +36 -110
  12. machineconfig/cluster/sessions_managers/wt_local_manager.py +42 -112
  13. machineconfig/cluster/sessions_managers/wt_remote.py +23 -30
  14. machineconfig/cluster/sessions_managers/wt_remote_manager.py +20 -62
  15. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +10 -15
  16. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +27 -127
  17. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +10 -43
  18. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +22 -101
  19. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +11 -39
  20. machineconfig/cluster/sessions_managers/zellij_local.py +49 -102
  21. machineconfig/cluster/sessions_managers/zellij_local_manager.py +34 -78
  22. machineconfig/cluster/sessions_managers/zellij_remote.py +17 -24
  23. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +7 -13
  24. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -2
  25. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +6 -6
  26. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +18 -88
  27. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +2 -6
  28. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +12 -40
  29. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -2
  30. machineconfig/cluster/templates/cli_click.py +0 -1
  31. machineconfig/cluster/templates/cli_gooey.py +0 -2
  32. machineconfig/cluster/templates/cli_trogon.py +0 -1
  33. machineconfig/cluster/templates/run_cloud.py +0 -1
  34. machineconfig/cluster/templates/run_cluster.py +0 -1
  35. machineconfig/cluster/templates/run_remote.py +0 -1
  36. machineconfig/cluster/templates/utils.py +26 -10
  37. machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
  38. machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
  39. machineconfig/jobs/python/check_installations.py +1 -0
  40. machineconfig/jobs/python/create_bootable_media.py +0 -2
  41. machineconfig/jobs/python/python_ve_symlink.py +9 -11
  42. machineconfig/jobs/python/tasks.py +0 -1
  43. machineconfig/jobs/python/vscode/api.py +5 -5
  44. machineconfig/jobs/python/vscode/link_ve.py +13 -14
  45. machineconfig/jobs/python/vscode/select_interpreter.py +21 -22
  46. machineconfig/jobs/python/vscode/sync_code.py +9 -13
  47. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  48. machineconfig/jobs/python_custom_installers/archive/ngrok.py +13 -13
  49. machineconfig/jobs/python_custom_installers/dev/aider.py +7 -15
  50. machineconfig/jobs/python_custom_installers/dev/alacritty.py +9 -18
  51. machineconfig/jobs/python_custom_installers/dev/brave.py +10 -19
  52. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +8 -15
  53. machineconfig/jobs/python_custom_installers/dev/code.py +14 -21
  54. machineconfig/jobs/python_custom_installers/dev/cursor.py +3 -14
  55. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +8 -7
  56. machineconfig/jobs/python_custom_installers/dev/espanso.py +15 -19
  57. machineconfig/jobs/python_custom_installers/dev/goes.py +5 -12
  58. machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -17
  59. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +12 -19
  60. machineconfig/jobs/python_custom_installers/dev/redis.py +12 -20
  61. machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -19
  62. machineconfig/jobs/python_custom_installers/dev/winget.py +5 -23
  63. machineconfig/jobs/python_custom_installers/docker.py +12 -21
  64. machineconfig/jobs/python_custom_installers/gh.py +11 -19
  65. machineconfig/jobs/python_custom_installers/hx.py +32 -16
  66. machineconfig/jobs/python_custom_installers/warp-cli.py +12 -20
  67. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  68. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
  69. machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
  70. machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
  71. machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
  72. machineconfig/jobs/windows/start_terminal.ps1 +1 -1
  73. machineconfig/profile/create.py +29 -22
  74. machineconfig/profile/create_hardlinks.py +26 -19
  75. machineconfig/profile/shell.py +51 -28
  76. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  77. machineconfig/scripts/cloud/init.sh +2 -2
  78. machineconfig/scripts/linux/checkout_versions +1 -1
  79. machineconfig/scripts/linux/choose_wezterm_theme +1 -1
  80. machineconfig/scripts/linux/cloud_copy +1 -1
  81. machineconfig/scripts/linux/cloud_manager +1 -1
  82. machineconfig/scripts/linux/cloud_mount +1 -1
  83. machineconfig/scripts/linux/cloud_repo_sync +1 -1
  84. machineconfig/scripts/linux/cloud_sync +1 -1
  85. machineconfig/scripts/linux/croshell +1 -1
  86. machineconfig/scripts/linux/devops +4 -6
  87. machineconfig/scripts/linux/fire +1 -1
  88. machineconfig/scripts/linux/fire_agents +3 -2
  89. machineconfig/scripts/linux/ftpx +1 -1
  90. machineconfig/scripts/linux/gh_models +1 -1
  91. machineconfig/scripts/linux/kill_process +1 -1
  92. machineconfig/scripts/linux/mcinit +1 -1
  93. machineconfig/scripts/linux/repos +1 -1
  94. machineconfig/scripts/linux/scheduler +1 -1
  95. machineconfig/scripts/linux/start_slidev +1 -1
  96. machineconfig/scripts/linux/start_terminals +1 -1
  97. machineconfig/scripts/linux/url2md +1 -1
  98. machineconfig/scripts/linux/warp-cli.sh +122 -0
  99. machineconfig/scripts/linux/wifi_conn +1 -1
  100. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  101. machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  106. machineconfig/scripts/python/ai/__init__.py +0 -0
  107. machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
  108. machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
  109. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
  110. machineconfig/scripts/python/ai/generate_files.py +84 -0
  111. machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +2 -2
  112. machineconfig/scripts/python/ai/mcinit.py +7 -3
  113. machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +10 -5
  114. machineconfig/scripts/python/cloud_copy.py +1 -1
  115. machineconfig/scripts/python/cloud_mount.py +1 -1
  116. machineconfig/scripts/python/cloud_repo_sync.py +4 -4
  117. machineconfig/scripts/python/croshell.py +5 -3
  118. machineconfig/scripts/python/devops_add_identity.py +1 -1
  119. machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
  120. machineconfig/scripts/python/devops_backup_retrieve.py +1 -1
  121. machineconfig/scripts/python/devops_update_repos.py +140 -52
  122. machineconfig/scripts/python/dotfile.py +1 -1
  123. machineconfig/scripts/python/fire_agents.py +28 -9
  124. machineconfig/scripts/python/fire_jobs.py +3 -4
  125. machineconfig/scripts/python/ftpx.py +2 -1
  126. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  127. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  128. machineconfig/scripts/python/helpers/helpers2.py +2 -2
  129. machineconfig/scripts/python/helpers/helpers4.py +1 -2
  130. machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
  131. machineconfig/scripts/python/mount_nfs.py +1 -1
  132. machineconfig/scripts/python/mount_ssh.py +1 -1
  133. machineconfig/scripts/python/repos.py +1 -1
  134. machineconfig/scripts/python/start_slidev.py +1 -1
  135. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  136. machineconfig/scripts/windows/checkout_version.ps1 +1 -3
  137. machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
  138. machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
  139. machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
  140. machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
  141. machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
  142. machineconfig/scripts/windows/croshell.ps1 +2 -2
  143. machineconfig/scripts/windows/devops.ps1 +1 -4
  144. machineconfig/scripts/windows/dotfile.ps1 +1 -3
  145. machineconfig/scripts/windows/fire.ps1 +1 -1
  146. machineconfig/scripts/windows/ftpx.ps1 +2 -2
  147. machineconfig/scripts/windows/gpt.ps1 +1 -1
  148. machineconfig/scripts/windows/kill_process.ps1 +1 -2
  149. machineconfig/scripts/windows/mcinit.ps1 +1 -1
  150. machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
  151. machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
  152. machineconfig/scripts/windows/pomodoro.ps1 +1 -1
  153. machineconfig/scripts/windows/py2exe.ps1 +1 -3
  154. machineconfig/scripts/windows/repos.ps1 +1 -1
  155. machineconfig/scripts/windows/scheduler.ps1 +1 -1
  156. machineconfig/scripts/windows/snapshot.ps1 +2 -2
  157. machineconfig/scripts/windows/start_slidev.ps1 +1 -1
  158. machineconfig/scripts/windows/start_terminals.ps1 +1 -1
  159. machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
  160. machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
  161. machineconfig/settings/lf/linux/lfrc +1 -1
  162. machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
  163. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
  164. machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
  165. machineconfig/settings/svim/linux/init.toml +1 -1
  166. machineconfig/settings/svim/windows/init.toml +1 -1
  167. machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
  168. machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
  169. machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
  170. machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
  171. machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
  172. machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
  173. machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
  174. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +66 -12
  175. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +44 -36
  176. machineconfig/utils/ai/generate_file_checklist.py +8 -10
  177. machineconfig/utils/ai/url2md.py +4 -2
  178. machineconfig/utils/cloud/onedrive/setup_oauth.py +1 -0
  179. machineconfig/utils/cloud/onedrive/transaction.py +63 -98
  180. machineconfig/utils/code.py +60 -39
  181. machineconfig/utils/installer.py +27 -33
  182. machineconfig/utils/installer_utils/installer_abc.py +8 -7
  183. machineconfig/utils/installer_utils/installer_class.py +149 -70
  184. machineconfig/utils/links.py +22 -11
  185. machineconfig/utils/notifications.py +197 -0
  186. machineconfig/utils/options.py +29 -23
  187. machineconfig/utils/path.py +13 -6
  188. machineconfig/utils/path_reduced.py +485 -216
  189. machineconfig/utils/procs.py +47 -41
  190. machineconfig/utils/scheduling.py +0 -1
  191. machineconfig/utils/ssh.py +157 -76
  192. machineconfig/utils/terminal.py +82 -37
  193. machineconfig/utils/utils.py +12 -10
  194. machineconfig/utils/utils2.py +38 -48
  195. machineconfig/utils/utils5.py +183 -116
  196. machineconfig/utils/ve.py +9 -4
  197. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/METADATA +3 -2
  198. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/RECORD +200 -217
  199. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  200. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  201. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  202. machineconfig/jobs/python/archive/python_tools.txt +0 -12
  203. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  204. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  205. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  206. machineconfig/jobs/python_generic_installers/update.py +0 -3
  207. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  208. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  209. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  210. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  211. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  212. machineconfig/scripts/linux/activate_ve +0 -87
  213. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  214. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  215. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  216. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  217. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  218. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  219. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  220. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  221. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  222. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  223. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  224. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  225. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  226. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  227. machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-311.pyc +0 -0
  228. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  229. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  230. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  231. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  232. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  233. machineconfig/scripts/windows/activate_ve.ps1 +0 -54
  234. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
  235. {machineconfig-2.0.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
- """package manager
2
- """
1
+ """package manager"""
2
+
3
3
  from machineconfig.utils.installer_utils.installer_abc import LINUX_INSTALL_PATH, CATEGORY
4
4
  from machineconfig.utils.installer_utils.installer_class import Installer
5
5
  from rich.console import Console
6
- from rich.panel import Panel # Added import
6
+ from rich.panel import Panel # Added import
7
7
 
8
- from machineconfig.utils.path_reduced import P as PathExtended
8
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
9
9
  from machineconfig.utils.utils import INSTALL_VERSION_ROOT
10
10
  from machineconfig.utils.utils2 import read_json
11
11
 
@@ -16,8 +16,8 @@ from joblib import Parallel, delayed
16
16
 
17
17
 
18
18
  def check_latest():
19
- console = Console() # Added console initialization
20
- console.print(Panel("šŸ” CHECKING FOR LATEST VERSIONS", title="Status", expand=False)) # Replaced print with Panel
19
+ console = Console() # Added console initialization
20
+ console.print(Panel("šŸ” CHECKING FOR LATEST VERSIONS", title="Status", expand=False)) # Replaced print with Panel
21
21
  installers = get_installers(system=platform.system(), dev=False)
22
22
  # installers += get_installers(system=platform.system(), dev=True)
23
23
  installers_github = []
@@ -46,12 +46,7 @@ def check_latest():
46
46
  # Convert to list of dictionaries and group by status
47
47
  result_data = []
48
48
  for tool, status, current_ver, new_ver in res:
49
- result_data.append({
50
- "Tool": tool,
51
- "Status": status,
52
- "Current Version": current_ver,
53
- "New Version": new_ver
54
- })
49
+ result_data.append({"Tool": tool, "Status": status, "Current Version": current_ver, "New Version": new_ver})
55
50
 
56
51
  # Group by status
57
52
  grouped_data: dict[str, list[dict[str, Any]]] = {}
@@ -70,11 +65,11 @@ def check_latest():
70
65
  for item in items:
71
66
  print(f" {item['Tool']:<20} | Current: {item['Current Version']:<15} | New: {item['New Version']}")
72
67
  print("-" * 60)
73
- print(f"{'═'*80}")
68
+ print(f"{'═' * 80}")
74
69
 
75
70
 
76
71
  def get_installed_cli_apps():
77
- print(f"\n{'='*80}\nšŸ” LISTING INSTALLED CLI APPS šŸ”\n{'='*80}")
72
+ print(f"\n{'=' * 80}\nšŸ” LISTING INSTALLED CLI APPS šŸ”\n{'=' * 80}")
78
73
  if platform.system() == "Windows":
79
74
  print("🪟 Searching for Windows executables...")
80
75
  apps = PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps").search("*.exe", not_in=["notepad"])
@@ -89,12 +84,12 @@ def get_installed_cli_apps():
89
84
  print(error_msg)
90
85
  raise NotImplementedError(error_msg)
91
86
  apps = [app for app in apps if app.size("kb") > 0.1 and not app.is_symlink()] # no symlinks like paint and wsl and bash
92
- print(f"āœ… Found {len(apps)} installed applications\n{'='*80}")
87
+ print(f"āœ… Found {len(apps)} installed applications\n{'=' * 80}")
93
88
  return apps
94
89
 
95
90
 
96
91
  def get_installers(system: str, dev: bool) -> list[Installer]:
97
- print(f"\n{'='*80}\nšŸ” LOADING INSTALLER CONFIGURATIONS šŸ”\n{'='*80}")
92
+ print(f"\n{'=' * 80}\nšŸ” LOADING INSTALLER CONFIGURATIONS šŸ”\n{'=' * 80}")
98
93
  res_all = get_all_dicts(system=system)
99
94
  if not dev:
100
95
  print("ā„¹ļø Excluding development installers...")
@@ -104,12 +99,12 @@ def get_installers(system: str, dev: bool) -> list[Installer]:
104
99
  res_final = {}
105
100
  for _k, v in res_all.items():
106
101
  res_final.update(v)
107
- print(f"āœ… Loaded {len(res_final)} installer configurations\n{'='*80}")
102
+ print(f"āœ… Loaded {len(res_final)} installer configurations\n{'=' * 80}")
108
103
  return [Installer.from_dict(d=vd, name=k) for k, vd in res_final.items()]
109
104
 
110
105
 
111
106
  def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
112
- print(f"\n{'='*80}\nšŸ“‚ LOADING CONFIGURATION FILES šŸ“‚\n{'='*80}")
107
+ print(f"\n{'=' * 80}\nšŸ“‚ LOADING CONFIGURATION FILES šŸ“‚\n{'=' * 80}")
113
108
 
114
109
  print(f"šŸ” Importing OS-specific installers for {system}...")
115
110
  if system == "Windows":
@@ -119,6 +114,7 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
119
114
 
120
115
  print("šŸ” Importing generic installers...")
121
116
  import machineconfig.jobs.python_generic_installers as generic_installer
117
+
122
118
  path_os_specific = PathExtended(os_specific_installer.__file__).parent
123
119
  path_os_generic = PathExtended(generic_installer.__file__).parent
124
120
 
@@ -127,7 +123,7 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
127
123
 
128
124
  print("šŸ“‚ Loading configuration files...")
129
125
  res_final: dict[CATEGORY, dict[str, dict[str, Any]]] = {}
130
- print(f"""šŸ“„ Loading OS-specific config from: {path_os_specific.joinpath('config.json')}""")
126
+ print(f"""šŸ“„ Loading OS-specific config from: {path_os_specific.joinpath("config.json")}""")
131
127
  res_final["OS_SPECIFIC"] = read_json(path=path_os_specific.joinpath("config.json"))
132
128
 
133
129
  print(f"""šŸ“„ Loading OS-generic config from: {path_os_generic.joinpath("config.json")}""")
@@ -144,11 +140,12 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
144
140
 
145
141
  print(f"šŸ” Loading custom installers from: {path_custom_installer}")
146
142
  import runpy
143
+
147
144
  res_custom: dict[str, dict[str, Any]] = {}
148
145
  for item in path_custom_installer.search("*.py", r=False, not_in=["__init__"]):
149
146
  try:
150
147
  print(f"šŸ“„ Loading custom installer: {item.name}")
151
- config_dict = runpy.run_path(str(item), run_name=None)['config_dict']
148
+ config_dict = runpy.run_path(str(item), run_name=None)["config_dict"]
152
149
  res_custom[item.stem] = config_dict
153
150
  except Exception as ex:
154
151
  print(f"āŒ Failed to load {item}: {ex}")
@@ -158,7 +155,7 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
158
155
  for item in path_custom_installer_dev.search("*.py", r=False, not_in=["__init__"]):
159
156
  try:
160
157
  print(f"šŸ“„ Loading custom dev installer: {item.name}")
161
- config_dict = runpy.run_path(str(item), run_name=None)['config_dict']
158
+ config_dict = runpy.run_path(str(item), run_name=None)["config_dict"]
162
159
  res_custom_dev[item.stem] = config_dict
163
160
  except Exception as ex:
164
161
  print(f"āŒ Failed to load {item}: {ex}")
@@ -166,12 +163,12 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
166
163
  res_final["CUSTOM"] = res_custom
167
164
  res_final["CUSTOM_DEV"] = res_custom_dev
168
165
 
169
- print(f"āœ… Configuration loading complete:\n - OS_SPECIFIC: {len(res_final['OS_SPECIFIC'])} items\n - OS_GENERIC: {len(res_final['OS_GENERIC'])} items\n - CUSTOM: {len(res_final['CUSTOM'])} items\n{'='*80}")
166
+ print(f"āœ… Configuration loading complete:\n - OS_SPECIFIC: {len(res_final['OS_SPECIFIC'])} items\n - OS_GENERIC: {len(res_final['OS_GENERIC'])} items\n - CUSTOM: {len(res_final['CUSTOM'])} items\n{'=' * 80}")
170
167
  return res_final
171
168
 
172
169
 
173
- def install_all(installers: list[Installer], safe: bool=False, jobs: int = 10, fresh: bool=False):
174
- print(f"\n{'='*80}\nšŸš€ BULK INSTALLATION PROCESS šŸš€\n{'='*80}")
170
+ def install_all(installers: list[Installer], safe: bool = False, jobs: int = 10, fresh: bool = False):
171
+ print(f"\n{'=' * 80}\nšŸš€ BULK INSTALLATION PROCESS šŸš€\n{'=' * 80}")
175
172
  if fresh:
176
173
  print("🧹 Fresh install requested - clearing version cache...")
177
174
  INSTALL_VERSION_ROOT.delete(sure=True)
@@ -204,16 +201,13 @@ def install_all(installers: list[Installer], safe: bool=False, jobs: int = 10, f
204
201
  # return None
205
202
 
206
203
  print(f"šŸš€ Starting installation of {len(installers)} packages...")
207
- print(f"\n{'='*80}\nšŸ“¦ INSTALLING FIRST PACKAGE šŸ“¦\n{'='*80}")
204
+ print(f"\n{'=' * 80}\nšŸ“¦ INSTALLING FIRST PACKAGE šŸ“¦\n{'=' * 80}")
208
205
  installers[0].install(version=None)
209
206
  installers_remaining = installers[1:]
210
- print(f"\n{'='*80}\nšŸ“¦ INSTALLING REMAINING PACKAGES šŸ“¦\n{'='*80}")
207
+ print(f"\n{'=' * 80}\nšŸ“¦ INSTALLING REMAINING PACKAGES šŸ“¦\n{'=' * 80}")
211
208
 
212
209
  # Use joblib for parallel processing of remaining installers
213
- res = Parallel(n_jobs=jobs)(
214
- delayed(lambda x: x.install_robust(version=None))(installer)
215
- for installer in installers_remaining
216
- )
210
+ res = Parallel(n_jobs=jobs)(delayed(lambda x: x.install_robust(version=None))(installer) for installer in installers_remaining)
217
211
 
218
212
  console = Console()
219
213
 
@@ -222,19 +216,19 @@ def install_all(installers: list[Installer], safe: bool=False, jobs: int = 10, f
222
216
 
223
217
  print("\n")
224
218
  console.rule("āœ“ Same Version Apps")
225
- same_version_results = [r for r in res if r and 'same version' in str(r)]
219
+ same_version_results = [r for r in res if r and "same version" in str(r)]
226
220
  for result in same_version_results:
227
221
  print(f" {result}")
228
222
 
229
223
  print("\n")
230
224
  console.rule("ā¬†ļø Updated Apps")
231
- updated_results = [r for r in res if r and 'updated from' in str(r)]
225
+ updated_results = [r for r in res if r and "updated from" in str(r)]
232
226
  for result in updated_results:
233
227
  print(f" {result}")
234
228
 
235
229
  print("\n")
236
230
  console.rule("āŒ Failed Apps")
237
- failed_results = [r for r in res if r and 'Failed at' in str(r)]
231
+ failed_results = [r for r in res if r and "Failed at" in str(r)]
238
232
  for result in failed_results:
239
233
  print(f" {result}")
240
234
 
@@ -1,4 +1,4 @@
1
- from machineconfig.utils.path_reduced import P as PathExtended
1
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
2
2
  from typing import Optional, TypeAlias, Literal
3
3
  import subprocess
4
4
 
@@ -9,9 +9,10 @@ WINDOWS_INSTALL_PATH = PathExtended.home().joinpath("AppData/Local/Microsoft/Win
9
9
  CATEGORY: TypeAlias = Literal["OS_SPECIFIC", "OS_GENERIC", "CUSTOM", "OS_SPECIFIC_DEV", "OS_GENERIC_DEV", "CUSTOM_DEV"]
10
10
 
11
11
 
12
- def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optional[str] = None, delete: bool=True, rename_to: Optional[str] = None):
13
- print(f"\n{'='*80}\nšŸ” PROCESSING WINDOWS EXECUTABLE šŸ”\n{'='*80}")
14
- if exe_name is not None and ".exe" in exe_name: exe_name = exe_name.replace(".exe", "")
12
+ def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optional[str] = None, delete: bool = True, rename_to: Optional[str] = None):
13
+ print(f"\n{'=' * 80}\nšŸ” PROCESSING WINDOWS EXECUTABLE šŸ”\n{'=' * 80}")
14
+ if exe_name is not None and ".exe" in exe_name:
15
+ exe_name = exe_name.replace(".exe", "")
15
16
  if downloaded_file_path.is_file():
16
17
  exe = downloaded_file_path
17
18
  print(f"šŸ“„ Found direct executable file: {exe}")
@@ -49,12 +50,12 @@ def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optio
49
50
  downloaded_file_path.delete(sure=True)
50
51
  print("āœ… Temporary files removed")
51
52
 
52
- print(f"{'='*80}")
53
+ print(f"{'=' * 80}")
53
54
  return exe_new_location
54
55
 
55
56
 
56
57
  def find_move_delete_linux(downloaded: PathExtended, tool_name: str, delete: Optional[bool] = True, rename_to: Optional[str] = None):
57
- print(f"\n{'='*80}\nšŸ” PROCESSING LINUX EXECUTABLE šŸ”\n{'='*80}")
58
+ print(f"\n{'=' * 80}\nšŸ” PROCESSING LINUX EXECUTABLE šŸ”\n{'=' * 80}")
58
59
  if downloaded.is_file():
59
60
  exe = downloaded
60
61
  print(f"šŸ“„ Found direct executable file: {exe}")
@@ -108,5 +109,5 @@ def find_move_delete_linux(downloaded: PathExtended, tool_name: str, delete: Opt
108
109
  print("āœ… Temporary files removed")
109
110
 
110
111
  exe_new_location = PathExtended(LINUX_INSTALL_PATH).joinpath(exe.name)
111
- print(f"āœ… Executable installed at: {exe_new_location}\n{'='*80}")
112
+ print(f"āœ… Executable installed at: {exe_new_location}\n{'=' * 80}")
112
113
  return exe_new_location
@@ -1,5 +1,4 @@
1
-
2
- from machineconfig.utils.path_reduced import P as PathExtended
1
+ from machineconfig.utils.path_reduced import PathExtended as PathExtended
3
2
  from machineconfig.utils.installer_utils.installer_abc import find_move_delete_linux, find_move_delete_windows
4
3
  from machineconfig.utils.utils import INSTALL_TMP_DIR, INSTALL_VERSION_ROOT, LIBRARY_ROOT, check_tool_exists
5
4
  from machineconfig.utils.utils2 import pprint, read_json
@@ -11,45 +10,59 @@ from pathlib import Path
11
10
 
12
11
 
13
12
  class Installer:
14
- def __init__(self, repo_url: str, name: str, doc: str,
15
- strip_v: bool, exe_name: str,
16
- filename_template_windows_amd_64: str,
17
- filename_template_linux_amd_64: str,
18
- filename_template_windows_arm_64: Optional[str] = None,
19
- filename_template_linux_arm_64: Optional[str] = None,
20
- filename_template_macos_amd_64: Optional[str] = None,
21
- filename_template_macos_arm_64: Optional[str] = None,
22
- ):
23
- self.repo_url: str=repo_url
24
- self.name: str=name
25
- self.doc: str=doc
26
- self.filename_template_windows_amd_64: str=filename_template_windows_amd_64
27
- self.filename_template_linux_amd_64: str=filename_template_linux_amd_64
28
- self.filename_template_windows_arm_64: Optional[str]=filename_template_windows_arm_64
29
- self.filename_template_linux_arm_64: Optional[str]=filename_template_linux_arm_64
30
- self.filename_template_macos_amd_64: Optional[str]=filename_template_macos_amd_64
31
- self.filename_template_macos_arm_64: Optional[str]=filename_template_macos_arm_64
32
- self.strip_v: bool=strip_v
33
- self.exe_name: str=exe_name
34
- def __repr__(self) -> str: return f"Installer of {self.repo_url}"
13
+ def __init__(
14
+ self,
15
+ repo_url: str,
16
+ name: str,
17
+ doc: str,
18
+ strip_v: bool,
19
+ exe_name: str,
20
+ filename_template_windows_amd_64: str,
21
+ filename_template_linux_amd_64: str,
22
+ filename_template_windows_arm_64: Optional[str] = None,
23
+ filename_template_linux_arm_64: Optional[str] = None,
24
+ filename_template_macos_amd_64: Optional[str] = None,
25
+ filename_template_macos_arm_64: Optional[str] = None,
26
+ ):
27
+ self.repo_url: str = repo_url
28
+ self.name: str = name
29
+ self.doc: str = doc
30
+ self.filename_template_windows_amd_64: str = filename_template_windows_amd_64
31
+ self.filename_template_windows_arm_64: Optional[str] = filename_template_windows_arm_64
32
+ self.filename_template_linux_arm_64: Optional[str] = filename_template_linux_arm_64
33
+ self.filename_template_linux_amd_64: str = filename_template_linux_amd_64
34
+ self.filename_template_macos_amd_64: Optional[str] = filename_template_macos_amd_64
35
+ self.filename_template_macos_arm_64: Optional[str] = filename_template_macos_arm_64
36
+ self.strip_v: bool = strip_v
37
+ self.exe_name: str = exe_name
38
+
39
+ def __repr__(self) -> str:
40
+ return f"Installer of {self.exe_name} {self.name} @ {self.repo_url}"
41
+
35
42
  def get_description(self):
36
43
  # old_version_cli = Terminal().run(f"{self.exe_name} --version").op.replace("\n", "")
37
44
  # old_version_cli = os.system(f"{self.exe_name} --version").replace("\n", "")
38
- old_version_cli: bool=check_tool_exists(tool_name=self.exe_name)
45
+ old_version_cli: bool = check_tool_exists(tool_name=self.exe_name)
39
46
  old_version_cli_str = "āœ…" if old_version_cli else "āŒ"
40
47
  # name_version = f"{self.exe_name} {old_version_cli_str}"
41
48
  return f"{self.exe_name:<12} {old_version_cli_str} {self.doc}"
42
- def to_dict(self): return self.__dict__
49
+
50
+ def to_dict(self):
51
+ return self.__dict__
52
+
43
53
  @staticmethod
44
54
  def from_dict(d: dict[str, Any], name: str):
45
- try: return Installer(name=name, **d)
55
+ try:
56
+ return Installer(name=name, **d)
46
57
  except Exception as ex:
47
58
  pprint(d, "Installer Creation Error")
48
59
  raise ex
60
+
49
61
  @staticmethod
50
62
  def choose_app_and_install():
51
- print(f"\n{'='*80}\nšŸ” SELECT APPLICATION TO INSTALL šŸ”\n{'='*80}")
63
+ print(f"\n{'=' * 80}\nšŸ” SELECT APPLICATION TO INSTALL šŸ”\n{'=' * 80}")
52
64
  from machineconfig.utils.utils import choose_one_option
65
+
53
66
  print("šŸ“‚ Searching for configuration files...")
54
67
  jobs_dir = Path(LIBRARY_ROOT.joinpath("jobs"))
55
68
  config_paths = [Path(p) for p in jobs_dir.rglob("config.json")]
@@ -62,12 +75,12 @@ class Installer:
62
75
  installer = Installer.from_dict(d=config[app_name], name=app_name)
63
76
  print(f"šŸ“¦ Selected application: {installer.exe_name}")
64
77
  version = input(f"šŸ“ Enter version to install for {installer.exe_name} [latest]: ") or None
65
- print(f"\n{'='*80}\nšŸš€ INSTALLING {installer.exe_name.upper()} šŸš€\n{'='*80}")
78
+ print(f"\n{'=' * 80}\nšŸš€ INSTALLING {installer.exe_name.upper()} šŸš€\n{'=' * 80}")
66
79
  installer.install(version=version)
67
80
 
68
81
  def install_robust(self, version: Optional[str]):
69
82
  try:
70
- print(f"\n{'='*80}\nšŸš€ INSTALLING {self.exe_name.upper()} šŸš€\n{'='*80}")
83
+ print(f"\n{'=' * 80}\nšŸš€ INSTALLING {self.exe_name.upper()} šŸš€\n{'=' * 80}")
71
84
  result_old = subprocess.run(f"{self.exe_name} --version", shell=True, capture_output=True, text=True)
72
85
  old_version_cli = result_old.stdout.strip()
73
86
  print(f"šŸ“Š Current version: {old_version_cli or 'Not installed'}")
@@ -90,10 +103,11 @@ class Installer:
90
103
  return f"""echo "šŸ“¦ļø āŒ Failed to install `{self.name}` with error: {ex}" """
91
104
 
92
105
  def install(self, version: Optional[str]):
93
- print(f"\n{'='*80}\nšŸ”§ INSTALLATION PROCESS: {self.exe_name} šŸ”§\n{'='*80}")
106
+ print(f"\n{'=' * 80}\nšŸ”§ INSTALLATION PROCESS: {self.exe_name} šŸ”§\n{'=' * 80}")
94
107
  if self.repo_url == "CUSTOM":
95
108
  print(f"🧩 Using custom installer for {self.exe_name}")
96
109
  import machineconfig.jobs.python_custom_installers as python_custom_installers
110
+
97
111
  installer_path = Path(python_custom_installers.__file__).parent.joinpath(self.exe_name + ".py")
98
112
  if not installer_path.exists():
99
113
  installer_path = Path(python_custom_installers.__file__).parent.joinpath("dev", self.exe_name + ".py")
@@ -102,12 +116,15 @@ class Installer:
102
116
  print(f"šŸ” Found installer at: {installer_path}")
103
117
 
104
118
  import runpy
119
+
105
120
  print(f"āš™ļø Executing function 'main' from '{installer_path}'...")
106
- program: str = runpy.run_path(str(installer_path), run_name=None)['main'](version=version)
121
+ program: str = runpy.run_path(str(installer_path), run_name=None)["main"](version=version)
107
122
  # print(program)
108
123
  print("šŸš€ Running installation script...")
109
- if platform.system() == "Linux": script = "#!/bin/bash" + "\n" + program
110
- else: script = program
124
+ if platform.system() == "Linux":
125
+ script = "#!/bin/bash" + "\n" + program
126
+ else:
127
+ script = program
111
128
  script_file = PathExtended.tmpfile(name="tmp_shell_script", suffix=".ps1" if platform.system() == "Windows" else ".sh", folder="tmp_scripts").write_text(script, newline=None if platform.system() == "Windows" else "\n")
112
129
  if platform.system() == "Windows":
113
130
  start_cmd = "powershell"
@@ -117,7 +134,7 @@ class Installer:
117
134
  full_command = f"{start_cmd} {script_file}"
118
135
  subprocess.run(full_command, stdin=None, stdout=None, stderr=None, shell=True, text=True)
119
136
  version_to_be_installed = str(version)
120
- print(f"āœ… Custom installation completed\n{'='*80}")
137
+ print(f"āœ… Custom installation completed\n{'=' * 80}")
121
138
 
122
139
  elif "npm " in self.repo_url or "pip " in self.repo_url or "winget " in self.repo_url:
123
140
  package_manager = self.repo_url.split(" ", maxsplit=1)[0]
@@ -134,7 +151,7 @@ class Installer:
134
151
  if result.stderr:
135
152
  print(f"STDERR: {result.stderr}")
136
153
  print(f"Return code: {result.returncode}")
137
- print(f"āœ… Package manager installation completed\n{'='*80}")
154
+ print(f"āœ… Package manager installation completed\n{'=' * 80}")
138
155
 
139
156
  else:
140
157
  print("šŸ“„ Downloading from repository...")
@@ -154,7 +171,7 @@ class Installer:
154
171
  print(f"Return code: {result.returncode}")
155
172
  print("šŸ—‘ļø Cleaning up .deb package...")
156
173
  downloaded.delete(sure=True)
157
- print(f"āœ… DEB package installation completed\n{'='*80}")
174
+ print(f"āœ… DEB package installation completed\n{'=' * 80}")
158
175
  else:
159
176
  if platform.system() == "Windows":
160
177
  print("🪟 Installing on Windows...")
@@ -172,6 +189,7 @@ class Installer:
172
189
  if exe.name.replace(".exe", "") != self.exe_name.replace(".exe", ""):
173
190
  from rich import print as pprint
174
191
  from rich.panel import Panel
192
+
175
193
  print("āš ļø Warning: Executable name mismatch")
176
194
  pprint(Panel(f"Expected exe name: [red]{self.exe_name}[/red] \nAttained name: [red]{exe.name.replace('.exe', '')}[/red]", title="exe name mismatch", subtitle=self.repo_url))
177
195
  new_exe_name = self.exe_name + ".exe" if platform.system() == "Windows" else self.exe_name
@@ -181,30 +199,30 @@ class Installer:
181
199
  print(f"šŸ’¾ Saving version information to: {INSTALL_VERSION_ROOT.joinpath(self.exe_name)}")
182
200
  INSTALL_VERSION_ROOT.joinpath(self.exe_name).parent.mkdir(parents=True, exist_ok=True)
183
201
  INSTALL_VERSION_ROOT.joinpath(self.exe_name).write_text(version_to_be_installed, encoding="utf-8")
184
- print(f"āœ… Installation completed successfully!\n{'='*80}")
202
+ print(f"āœ… Installation completed successfully!\n{'=' * 80}")
185
203
 
186
204
  def download(self, version: Optional[str]):
187
- print(f"\n{'='*80}\nšŸ“„ DOWNLOADING: {self.exe_name} šŸ“„\n{'='*80}")
205
+ print(f"\n{'=' * 80}\nšŸ“„ DOWNLOADING: {self.exe_name} šŸ“„\n{'=' * 80}")
206
+ download_link: Optional[Path] = None
207
+ version_to_be_installed: Optional[str] = None
188
208
  if "github" not in self.repo_url or ".zip" in self.repo_url or ".tar.gz" in self.repo_url:
189
209
  download_link = Path(self.repo_url)
190
210
  version_to_be_installed = "predefined_url"
191
211
  print(f"šŸ”— Using direct download URL: {download_link}")
192
212
  print(f"šŸ“¦ Version to be installed: {version_to_be_installed}")
193
-
194
- elif "http" in self.filename_template_linux_amd_64 or "http" in self.filename_template_windows_amd_64:
195
- if platform.system() == "Windows":
196
- download_link = Path(self.filename_template_windows_amd_64)
197
- print(f"🪟 Using Windows-specific download URL: {download_link}")
198
- elif platform.system() in ["Linux", "Darwin"]:
199
- download_link = Path(self.filename_template_linux_amd_64)
200
- system_name = "Linux" if platform.system() == "Linux" else "macOS"
201
- print(f"🐧 Using {system_name}-specific download URL: {download_link}")
213
+ elif self._any_direct_http_template():
214
+ template, arch = self._select_template()
215
+ if not template.startswith("http"):
216
+ # Fall back to github-style handling below
217
+ pass
202
218
  else:
203
- error_msg = f"āŒ ERROR: System {platform.system()} not supported"
204
- print(error_msg)
205
- raise NotImplementedError(error_msg)
206
-
207
- version_to_be_installed = "predefined_url"
219
+ download_link = Path(template)
220
+ version_to_be_installed = "predefined_url"
221
+ system_name = self._system_name()
222
+ print(f"🧭 Detected system={system_name} arch={arch}")
223
+ print(f"šŸ”— Using architecture-specific direct URL: {download_link}")
224
+ print(f"šŸ“¦ Version to be installed: {version_to_be_installed}")
225
+ # continue to unified download logic below
208
226
 
209
227
  else:
210
228
  print("🌐 Retrieving release information from GitHub...")
@@ -215,36 +233,97 @@ class Installer:
215
233
  version_to_be_installed_stripped = version_to_be_installed.replace("v", "") if self.strip_v else version_to_be_installed
216
234
  version_to_be_installed_stripped = version_to_be_installed_stripped.replace("ipinfo-", "")
217
235
 
218
- if platform.system() == "Windows":
219
- file_name = self.filename_template_windows_amd_64.format(version_to_be_installed_stripped)
220
- print(f"🪟 Windows file name: {file_name}")
221
- elif platform.system() in ["Linux", "Darwin"]:
222
- file_name = self.filename_template_linux_amd_64.format(version_to_be_installed_stripped)
223
- system_name = "Linux" if platform.system() == "Linux" else "macOS"
224
- print(f"🐧 {system_name} file name: {file_name}")
225
- else:
226
- error_msg = f"āŒ ERROR: System {platform.system()} not supported"
227
- print(error_msg)
228
- raise NotImplementedError(error_msg)
236
+ template, arch = self._select_template()
237
+ system_name = self._system_name()
238
+ file_name = template.format(version_to_be_installed_stripped)
239
+ print(f"🧭 Detected system={system_name} arch={arch}")
240
+ print(f"šŸ“„ Using template: {template}")
241
+ print(f"šŸ—‚ļø Resolved file name: {file_name}")
229
242
 
230
243
  print(f"šŸ“„ File name: {file_name}")
231
244
  download_link = release_url.joinpath(file_name)
232
245
 
246
+ assert download_link is not None, "download_link must be set"
247
+ assert version_to_be_installed is not None, "version_to_be_installed must be set"
233
248
  print(f"šŸ“„ Downloading {self.name} from: {download_link}")
234
249
  downloaded = PathExtended(download_link).download(folder=INSTALL_TMP_DIR).decompress()
235
- print(f"āœ… Download and extraction completed to: {downloaded}\n{'='*80}")
250
+ print(f"āœ… Download and extraction completed to: {downloaded}\n{'=' * 80}")
236
251
  return downloaded, version_to_be_installed
237
252
 
253
+ # --------------------------- Arch / template helpers ---------------------------
254
+ def _normalized_arch(self) -> str:
255
+ arch_raw = platform.machine().lower()
256
+ if arch_raw in ("x86_64", "amd64"):
257
+ return "amd64"
258
+ if arch_raw in ("aarch64", "arm64", "armv8", "armv8l"):
259
+ return "arm64"
260
+ return arch_raw
261
+
262
+ def _system_name(self) -> str:
263
+ sys_ = platform.system()
264
+ if sys_ == "Darwin":
265
+ return "macOS"
266
+ return sys_
267
+
268
+ def _any_direct_http_template(self) -> bool:
269
+ templates: list[Optional[str]] = [
270
+ self.filename_template_windows_amd_64,
271
+ self.filename_template_windows_arm_64,
272
+ self.filename_template_linux_amd_64,
273
+ self.filename_template_linux_arm_64,
274
+ self.filename_template_macos_amd_64,
275
+ self.filename_template_macos_arm_64,
276
+ ]
277
+ return any(t for t in templates if t is not None and t.startswith("http"))
278
+
279
+ def _select_template(self) -> tuple[str, str]:
280
+ sys_name = platform.system()
281
+ arch = self._normalized_arch()
282
+ # mapping logic
283
+ candidates: list[str] = []
284
+ template: Optional[str] = None
285
+ if sys_name == "Windows":
286
+ if arch == "arm64" and self.filename_template_windows_arm_64:
287
+ template = self.filename_template_windows_arm_64
288
+ else:
289
+ template = self.filename_template_windows_amd_64
290
+ candidates = ["filename_template_windows_arm_64", "filename_template_windows_amd_64"]
291
+ elif sys_name == "Linux":
292
+ if arch == "arm64" and self.filename_template_linux_arm_64:
293
+ template = self.filename_template_linux_arm_64
294
+ else:
295
+ template = self.filename_template_linux_amd_64
296
+ candidates = ["filename_template_linux_arm_64", "filename_template_linux_amd_64"]
297
+ elif sys_name == "Darwin":
298
+ if arch == "arm64" and self.filename_template_macos_arm_64:
299
+ template = self.filename_template_macos_arm_64
300
+ elif arch == "amd64" and self.filename_template_macos_amd_64:
301
+ template = self.filename_template_macos_amd_64
302
+ else:
303
+ # fallback between available mac templates
304
+ template = self.filename_template_macos_arm_64 or self.filename_template_macos_amd_64
305
+ candidates = ["filename_template_macos_arm_64", "filename_template_macos_amd_64"]
306
+ else:
307
+ raise NotImplementedError(f"System {sys_name} not supported")
308
+
309
+ if template is None:
310
+ raise ValueError(f"No filename template available for system={sys_name} arch={arch}. Checked {candidates}")
311
+
312
+ return template, arch
313
+
238
314
  @staticmethod
239
315
  def get_github_release(repo_url: str, version: Optional[str] = None):
240
- print(f"\n{'='*80}\nšŸ” GITHUB RELEASE DETECTION šŸ”\n{'='*80}")
316
+ print(f"\n{'=' * 80}\nšŸ” GITHUB RELEASE DETECTION šŸ”\n{'=' * 80}")
241
317
  print(f"🌐 Inspecting releases at: {repo_url}")
242
318
  # with console.status("Installing..."): # makes troubles on linux when prompt asks for password to move file to /usr/bin
243
319
  if version is None:
244
320
  # see this: https://api.github.com/repos/cointop-sh/cointop/releases/latest
245
321
  print("šŸ” Finding latest version...")
246
322
  import requests # https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases
247
- _latest_version = requests.get(str(repo_url) + "/releases/latest", timeout=10).url.split("/")[-1] # this is to resolve the redirection that occures: https://stackoverflow.com/questions/36070821/how-to-get-redirect-url-using-python-requests
323
+
324
+ _latest_version = requests.get(str(repo_url) + "/releases/latest", timeout=10).url.split("/")[
325
+ -1
326
+ ] # this is to resolve the redirection that occures: https://stackoverflow.com/questions/36070821/how-to-get-redirect-url-using-python-requests
248
327
  version_to_be_installed = _latest_version
249
328
  print(f"āœ… Latest version detected: {version_to_be_installed}")
250
329
  # print(version_to_be_installed)
@@ -253,12 +332,12 @@ class Installer:
253
332
  print(f"šŸ“ Using specified version: {version_to_be_installed}")
254
333
 
255
334
  release_url = Path(repo_url + "/releases/download/" + version_to_be_installed)
256
- print(f"šŸ”— Release download URL: {release_url}\n{'='*80}")
335
+ print(f"šŸ”— Release download URL: {release_url}\n{'=' * 80}")
257
336
  return release_url, version_to_be_installed
258
337
 
259
338
  @staticmethod
260
339
  def check_if_installed_already(exe_name: str, version: str, use_cache: bool):
261
- print(f"\n{'='*80}\nšŸ” CHECKING INSTALLATION STATUS: {exe_name} šŸ”\n{'='*80}")
340
+ print(f"\n{'=' * 80}\nšŸ” CHECKING INSTALLATION STATUS: {exe_name} šŸ”\n{'=' * 80}")
262
341
  version_to_be_installed = version
263
342
  INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
264
343
  tmp_path = INSTALL_VERSION_ROOT.joinpath(exe_name)
@@ -274,7 +353,7 @@ class Installer:
274
353
  else:
275
354
  print("šŸ” Checking installed version directly...")
276
355
  result = subprocess.run([exe_name, "--version"], check=False, capture_output=True, text=True)
277
- if result.stdout.strip() == '':
356
+ if result.stdout.strip() == "":
278
357
  existing_version = None
279
358
  print("ā„¹ļø Could not detect installed version")
280
359
  else:
@@ -294,5 +373,5 @@ class Installer:
294
373
  print(f"šŸ“¦ {exe_name} is not installed. Will install version: {version_to_be_installed}")
295
374
  tmp_path.write_text(version_to_be_installed, encoding="utf-8")
296
375
 
297
- print(f"{'='*80}")
376
+ print(f"{'=' * 80}")
298
377
  return ("āš ļø NotInstalled", "None", version_to_be_installed.strip())