machineconfig 1.97__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.
- machineconfig/cluster/cloud_manager.py +22 -29
- machineconfig/cluster/data_transfer.py +2 -3
- machineconfig/cluster/distribute.py +0 -2
- machineconfig/cluster/file_manager.py +4 -5
- machineconfig/cluster/job_params.py +1 -4
- machineconfig/cluster/loader_runner.py +8 -11
- machineconfig/cluster/remote_machine.py +4 -5
- machineconfig/cluster/script_execution.py +2 -2
- machineconfig/cluster/script_notify_upon_completion.py +0 -1
- machineconfig/cluster/sessions_managers/archive/create_zellij_template.py +4 -6
- machineconfig/cluster/sessions_managers/archive/session_managers.py +0 -1
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +35 -75
- machineconfig/cluster/sessions_managers/wt_local.py +113 -185
- machineconfig/cluster/sessions_managers/wt_local_manager.py +127 -197
- machineconfig/cluster/sessions_managers/wt_remote.py +60 -67
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +110 -149
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +61 -64
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +72 -172
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +27 -60
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +58 -137
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +46 -74
- machineconfig/cluster/sessions_managers/zellij_local.py +91 -147
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +165 -190
- machineconfig/cluster/sessions_managers/zellij_remote.py +51 -58
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +40 -46
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +19 -17
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +30 -31
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +64 -134
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +7 -11
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +27 -55
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +14 -13
- machineconfig/cluster/templates/cli_click.py +0 -1
- machineconfig/cluster/templates/cli_gooey.py +0 -2
- machineconfig/cluster/templates/cli_trogon.py +0 -1
- machineconfig/cluster/templates/run_cloud.py +0 -1
- machineconfig/cluster/templates/run_cluster.py +0 -1
- machineconfig/cluster/templates/run_remote.py +0 -1
- machineconfig/cluster/templates/utils.py +27 -11
- machineconfig/jobs/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/linux/msc/cli_agents.sh +16 -0
- machineconfig/jobs/python/check_installations.py +9 -9
- machineconfig/jobs/python/create_bootable_media.py +0 -2
- machineconfig/jobs/python/python_cargo_build_share.py +2 -2
- machineconfig/jobs/python/python_ve_symlink.py +9 -11
- machineconfig/jobs/python/tasks.py +0 -1
- machineconfig/jobs/python/vscode/api.py +5 -5
- machineconfig/jobs/python/vscode/link_ve.py +20 -21
- machineconfig/jobs/python/vscode/select_interpreter.py +28 -29
- machineconfig/jobs/python/vscode/sync_code.py +14 -18
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +15 -15
- machineconfig/jobs/python_custom_installers/dev/aider.py +10 -18
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +12 -21
- machineconfig/jobs/python_custom_installers/dev/brave.py +13 -22
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +13 -20
- machineconfig/jobs/python_custom_installers/dev/code.py +17 -24
- machineconfig/jobs/python_custom_installers/dev/cursor.py +10 -21
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +12 -11
- machineconfig/jobs/python_custom_installers/dev/espanso.py +19 -23
- machineconfig/jobs/python_custom_installers/dev/goes.py +9 -16
- machineconfig/jobs/python_custom_installers/dev/lvim.py +13 -21
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +15 -22
- machineconfig/jobs/python_custom_installers/dev/redis.py +15 -23
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +15 -22
- machineconfig/jobs/python_custom_installers/dev/winget.py +32 -50
- machineconfig/jobs/python_custom_installers/docker.py +15 -24
- machineconfig/jobs/python_custom_installers/gh.py +18 -26
- machineconfig/jobs/python_custom_installers/hx.py +33 -17
- machineconfig/jobs/python_custom_installers/warp-cli.py +15 -23
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_generic_installers/config.json +412 -389
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/jobs/python_windows_installers/dev/config.json +1 -1
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +1 -1
- machineconfig/jobs/windows/msc/cli_agents.bat +0 -0
- machineconfig/jobs/windows/msc/cli_agents.ps1 +0 -0
- machineconfig/jobs/windows/start_terminal.ps1 +1 -1
- machineconfig/logger.py +50 -0
- machineconfig/profile/create.py +50 -36
- machineconfig/profile/create_hardlinks.py +33 -26
- machineconfig/profile/shell.py +87 -60
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/cloud/init.sh +2 -2
- machineconfig/scripts/linux/checkout_versions +1 -1
- machineconfig/scripts/linux/choose_wezterm_theme +1 -1
- machineconfig/scripts/linux/cloud_copy +1 -1
- machineconfig/scripts/linux/cloud_manager +1 -1
- machineconfig/scripts/linux/cloud_mount +1 -1
- machineconfig/scripts/linux/cloud_repo_sync +1 -1
- machineconfig/scripts/linux/cloud_sync +1 -1
- machineconfig/scripts/linux/croshell +1 -1
- machineconfig/scripts/linux/devops +3 -5
- machineconfig/scripts/linux/fire +2 -1
- machineconfig/scripts/linux/fire_agents +3 -3
- machineconfig/scripts/linux/ftpx +1 -1
- machineconfig/scripts/linux/gh_models +1 -1
- machineconfig/scripts/linux/kill_process +1 -1
- machineconfig/scripts/linux/mcinit +2 -2
- machineconfig/scripts/linux/repos +1 -1
- machineconfig/scripts/linux/scheduler +1 -1
- machineconfig/scripts/linux/start_slidev +1 -1
- machineconfig/scripts/linux/start_terminals +1 -1
- machineconfig/scripts/linux/url2md +1 -1
- machineconfig/scripts/linux/warp-cli.sh +122 -0
- machineconfig/scripts/linux/wifi_conn +1 -1
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__init__.py +0 -0
- machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
- machineconfig/scripts/python/ai/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
- machineconfig/scripts/python/ai/chatmodes/deepResearch.chatmode.md +81 -0
- machineconfig/scripts/python/ai/configs/.gemini/settings.json +81 -0
- machineconfig/scripts/python/ai/generate_files.py +84 -0
- machineconfig/scripts/python/ai/instructions/python/dev.instructions.md +45 -0
- machineconfig/scripts/python/ai/mcinit.py +107 -0
- machineconfig/scripts/python/ai/prompts/allLintersAndTypeCheckers.prompt.md +5 -0
- machineconfig/scripts/python/ai/prompts/research-report-skeleton.prompt.md +38 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +52 -0
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +3 -3
- machineconfig/scripts/python/choose_wezterm_theme.py +2 -2
- machineconfig/scripts/python/cloud_copy.py +20 -19
- machineconfig/scripts/python/cloud_mount.py +10 -8
- machineconfig/scripts/python/cloud_repo_sync.py +15 -15
- machineconfig/scripts/python/cloud_sync.py +1 -1
- machineconfig/scripts/python/croshell.py +18 -16
- machineconfig/scripts/python/devops.py +6 -6
- machineconfig/scripts/python/devops_add_identity.py +9 -7
- machineconfig/scripts/python/devops_add_ssh_key.py +19 -19
- machineconfig/scripts/python/devops_backup_retrieve.py +14 -14
- machineconfig/scripts/python/devops_devapps_install.py +3 -3
- machineconfig/scripts/python/devops_update_repos.py +141 -53
- machineconfig/scripts/python/dotfile.py +3 -3
- machineconfig/scripts/python/fire_agents.py +202 -41
- machineconfig/scripts/python/fire_jobs.py +20 -21
- machineconfig/scripts/python/ftpx.py +4 -3
- machineconfig/scripts/python/gh_models.py +94 -94
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +3 -3
- machineconfig/scripts/python/helpers/helpers2.py +3 -3
- machineconfig/scripts/python/helpers/helpers4.py +8 -7
- machineconfig/scripts/python/helpers/helpers5.py +7 -7
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +2 -2
- machineconfig/scripts/python/mount_nfs.py +4 -3
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +4 -3
- machineconfig/scripts/python/repos.py +9 -9
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +9 -8
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/scripts/python/viewer.py +40 -40
- machineconfig/scripts/python/wifi_conn.py +65 -66
- machineconfig/scripts/python/wsl_windows_transfer.py +2 -2
- machineconfig/scripts/windows/checkout_version.ps1 +1 -3
- machineconfig/scripts/windows/choose_wezterm_theme.ps1 +1 -3
- machineconfig/scripts/windows/cloud_copy.ps1 +2 -6
- machineconfig/scripts/windows/cloud_manager.ps1 +1 -1
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +1 -2
- machineconfig/scripts/windows/cloud_sync.ps1 +2 -2
- machineconfig/scripts/windows/croshell.ps1 +2 -2
- machineconfig/scripts/windows/devops.ps1 +1 -4
- machineconfig/scripts/windows/dotfile.ps1 +1 -3
- machineconfig/scripts/windows/fire.ps1 +1 -1
- machineconfig/scripts/windows/ftpx.ps1 +2 -2
- machineconfig/scripts/windows/gpt.ps1 +1 -1
- machineconfig/scripts/windows/kill_process.ps1 +1 -2
- machineconfig/scripts/windows/mcinit.ps1 +2 -2
- machineconfig/scripts/windows/mount_nfs.ps1 +1 -1
- machineconfig/scripts/windows/mount_ssh.ps1 +1 -1
- machineconfig/scripts/windows/pomodoro.ps1 +1 -1
- machineconfig/scripts/windows/py2exe.ps1 +1 -3
- machineconfig/scripts/windows/repos.ps1 +1 -1
- machineconfig/scripts/windows/scheduler.ps1 +1 -1
- machineconfig/scripts/windows/snapshot.ps1 +2 -2
- machineconfig/scripts/windows/start_slidev.ps1 +1 -1
- machineconfig/scripts/windows/start_terminals.ps1 +1 -1
- machineconfig/scripts/windows/wifi_conn.ps1 +1 -1
- machineconfig/scripts/windows/wsl_windows_transfer.ps1 +1 -3
- machineconfig/settings/lf/linux/lfrc +1 -1
- machineconfig/settings/linters/.ruff.toml +2 -2
- machineconfig/settings/linters/.ruff_cache/.gitignore +2 -0
- machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +1 -0
- machineconfig/settings/lvim/windows/archive/config_additional.lua +1 -1
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +71 -71
- machineconfig/settings/shells/wt/settings.json +8 -8
- machineconfig/settings/svim/linux/init.toml +1 -1
- machineconfig/settings/svim/windows/init.toml +1 -1
- machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -54
- machineconfig/setup_linux/web_shortcuts/interactive.sh +6 -6
- machineconfig/setup_linux/web_shortcuts/tmp.sh +2 -0
- machineconfig/setup_windows/web_shortcuts/all.ps1 +2 -2
- machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/croshell.ps1 +1 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +5 -5
- machineconfig/setup_windows/wt_and_pwsh/install_fonts.ps1 +51 -15
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +75 -18
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +52 -42
- machineconfig/utils/ai/browser_user_wrapper.py +5 -5
- machineconfig/utils/ai/generate_file_checklist.py +19 -22
- machineconfig/utils/ai/url2md.py +5 -3
- machineconfig/utils/cloud/onedrive/setup_oauth.py +5 -4
- machineconfig/utils/cloud/onedrive/transaction.py +192 -227
- machineconfig/utils/code.py +71 -43
- machineconfig/utils/installer.py +77 -85
- machineconfig/utils/installer_utils/installer_abc.py +29 -17
- machineconfig/utils/installer_utils/installer_class.py +188 -83
- machineconfig/utils/io_save.py +3 -15
- machineconfig/utils/links.py +22 -11
- machineconfig/utils/notifications.py +197 -0
- machineconfig/utils/options.py +38 -25
- machineconfig/utils/path.py +18 -6
- machineconfig/utils/path_reduced.py +637 -316
- machineconfig/utils/procs.py +69 -63
- machineconfig/utils/scheduling.py +11 -13
- machineconfig/utils/ssh.py +351 -0
- machineconfig/utils/terminal.py +225 -0
- machineconfig/utils/utils.py +13 -12
- machineconfig/utils/utils2.py +43 -10
- machineconfig/utils/utils5.py +242 -46
- machineconfig/utils/ve.py +11 -6
- {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/METADATA +15 -9
- {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/RECORD +232 -235
- machineconfig/cluster/self_ssh.py +0 -57
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/archive/python_tools.txt +0 -12
- machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/update.py +0 -3
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/linux/activate_ve +0 -87
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
- machineconfig/scripts/python/ai/init.py +0 -56
- machineconfig/scripts/python/ai/rules/python/dev.md +0 -31
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/windows/activate_ve.ps1 +0 -54
- {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/WHEEL +0 -0
- {machineconfig-1.97.dist-info → machineconfig-2.1.dist-info}/top_level.txt +0 -0
|
@@ -1,55 +1,68 @@
|
|
|
1
|
-
|
|
2
|
-
from machineconfig.utils.path_reduced import P as PathExtended
|
|
3
|
-
from crocodile.meta import Terminal
|
|
1
|
+
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
4
2
|
from machineconfig.utils.installer_utils.installer_abc import find_move_delete_linux, find_move_delete_windows
|
|
5
3
|
from machineconfig.utils.utils import INSTALL_TMP_DIR, INSTALL_VERSION_ROOT, LIBRARY_ROOT, check_tool_exists
|
|
6
4
|
from machineconfig.utils.utils2 import pprint, read_json
|
|
7
5
|
|
|
8
6
|
import platform
|
|
7
|
+
import subprocess
|
|
9
8
|
from typing import Any, Optional
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class Installer:
|
|
14
|
-
def __init__(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
self.
|
|
29
|
-
self.
|
|
30
|
-
self.
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
33
|
-
self.
|
|
34
|
-
|
|
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
|
-
|
|
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:
|
|
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,18 +75,20 @@ 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}")
|
|
71
|
-
|
|
83
|
+
print(f"\n{'=' * 80}\n🚀 INSTALLING {self.exe_name.upper()} 🚀\n{'=' * 80}")
|
|
84
|
+
result_old = subprocess.run(f"{self.exe_name} --version", shell=True, capture_output=True, text=True)
|
|
85
|
+
old_version_cli = result_old.stdout.strip()
|
|
72
86
|
print(f"📊 Current version: {old_version_cli or 'Not installed'}")
|
|
73
87
|
|
|
74
88
|
self.install(version=version)
|
|
75
89
|
|
|
76
|
-
|
|
90
|
+
result_new = subprocess.run(f"{self.exe_name} --version", shell=True, capture_output=True, text=True)
|
|
91
|
+
new_version_cli = result_new.stdout.strip()
|
|
77
92
|
print(f"📊 New version: {new_version_cli}")
|
|
78
93
|
|
|
79
94
|
if old_version_cli == new_version_cli:
|
|
@@ -88,10 +103,11 @@ class Installer:
|
|
|
88
103
|
return f"""echo "📦️ ❌ Failed to install `{self.name}` with error: {ex}" """
|
|
89
104
|
|
|
90
105
|
def install(self, version: Optional[str]):
|
|
91
|
-
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}")
|
|
92
107
|
if self.repo_url == "CUSTOM":
|
|
93
108
|
print(f"🧩 Using custom installer for {self.exe_name}")
|
|
94
109
|
import machineconfig.jobs.python_custom_installers as python_custom_installers
|
|
110
|
+
|
|
95
111
|
installer_path = Path(python_custom_installers.__file__).parent.joinpath(self.exe_name + ".py")
|
|
96
112
|
if not installer_path.exists():
|
|
97
113
|
installer_path = Path(python_custom_installers.__file__).parent.joinpath("dev", self.exe_name + ".py")
|
|
@@ -100,15 +116,25 @@ class Installer:
|
|
|
100
116
|
print(f"🔍 Found installer at: {installer_path}")
|
|
101
117
|
|
|
102
118
|
import runpy
|
|
119
|
+
|
|
103
120
|
print(f"⚙️ Executing function 'main' from '{installer_path}'...")
|
|
104
|
-
program: str = runpy.run_path(str(installer_path), run_name=None)[
|
|
121
|
+
program: str = runpy.run_path(str(installer_path), run_name=None)["main"](version=version)
|
|
105
122
|
# print(program)
|
|
106
123
|
print("🚀 Running installation script...")
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
124
|
+
if platform.system() == "Linux":
|
|
125
|
+
script = "#!/bin/bash" + "\n" + program
|
|
126
|
+
else:
|
|
127
|
+
script = program
|
|
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")
|
|
129
|
+
if platform.system() == "Windows":
|
|
130
|
+
start_cmd = "powershell"
|
|
131
|
+
full_command = f"{start_cmd} {script_file}"
|
|
132
|
+
else:
|
|
133
|
+
start_cmd = "bash"
|
|
134
|
+
full_command = f"{start_cmd} {script_file}"
|
|
135
|
+
subprocess.run(full_command, stdin=None, stdout=None, stderr=None, shell=True, text=True)
|
|
110
136
|
version_to_be_installed = str(version)
|
|
111
|
-
print(f"✅ Custom installation completed\n{'='*80}")
|
|
137
|
+
print(f"✅ Custom installation completed\n{'=' * 80}")
|
|
112
138
|
|
|
113
139
|
elif "npm " in self.repo_url or "pip " in self.repo_url or "winget " in self.repo_url:
|
|
114
140
|
package_manager = self.repo_url.split(" ", maxsplit=1)[0]
|
|
@@ -116,8 +142,16 @@ class Installer:
|
|
|
116
142
|
desc = package_manager + " installation"
|
|
117
143
|
version_to_be_installed = package_manager + "Latest"
|
|
118
144
|
print(f"🚀 Running: {self.repo_url}")
|
|
119
|
-
|
|
120
|
-
|
|
145
|
+
result = subprocess.run(self.repo_url, shell=True, capture_output=True, text=True)
|
|
146
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
147
|
+
if not success:
|
|
148
|
+
print(f"❌ {desc} failed")
|
|
149
|
+
if result.stdout:
|
|
150
|
+
print(f"STDOUT: {result.stdout}")
|
|
151
|
+
if result.stderr:
|
|
152
|
+
print(f"STDERR: {result.stderr}")
|
|
153
|
+
print(f"Return code: {result.returncode}")
|
|
154
|
+
print(f"✅ Package manager installation completed\n{'=' * 80}")
|
|
121
155
|
|
|
122
156
|
else:
|
|
123
157
|
print("📥 Downloading from repository...")
|
|
@@ -125,10 +159,19 @@ class Installer:
|
|
|
125
159
|
if str(downloaded).endswith(".deb"):
|
|
126
160
|
print(f"📦 Installing .deb package: {downloaded}")
|
|
127
161
|
assert platform.system() == "Linux"
|
|
128
|
-
|
|
162
|
+
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
163
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
164
|
+
if not success:
|
|
165
|
+
desc = "Installing .deb"
|
|
166
|
+
print(f"❌ {desc} failed")
|
|
167
|
+
if result.stdout:
|
|
168
|
+
print(f"STDOUT: {result.stdout}")
|
|
169
|
+
if result.stderr:
|
|
170
|
+
print(f"STDERR: {result.stderr}")
|
|
171
|
+
print(f"Return code: {result.returncode}")
|
|
129
172
|
print("🗑️ Cleaning up .deb package...")
|
|
130
173
|
downloaded.delete(sure=True)
|
|
131
|
-
print(f"✅ DEB package installation completed\n{'='*80}")
|
|
174
|
+
print(f"✅ DEB package installation completed\n{'=' * 80}")
|
|
132
175
|
else:
|
|
133
176
|
if platform.system() == "Windows":
|
|
134
177
|
print("🪟 Installing on Windows...")
|
|
@@ -146,6 +189,7 @@ class Installer:
|
|
|
146
189
|
if exe.name.replace(".exe", "") != self.exe_name.replace(".exe", ""):
|
|
147
190
|
from rich import print as pprint
|
|
148
191
|
from rich.panel import Panel
|
|
192
|
+
|
|
149
193
|
print("⚠️ Warning: Executable name mismatch")
|
|
150
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))
|
|
151
195
|
new_exe_name = self.exe_name + ".exe" if platform.system() == "Windows" else self.exe_name
|
|
@@ -154,31 +198,31 @@ class Installer:
|
|
|
154
198
|
|
|
155
199
|
print(f"💾 Saving version information to: {INSTALL_VERSION_ROOT.joinpath(self.exe_name)}")
|
|
156
200
|
INSTALL_VERSION_ROOT.joinpath(self.exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
-
INSTALL_VERSION_ROOT.joinpath(self.exe_name).write_text(version_to_be_installed)
|
|
158
|
-
print(f"✅ Installation completed successfully!\n{'='*80}")
|
|
201
|
+
INSTALL_VERSION_ROOT.joinpath(self.exe_name).write_text(version_to_be_installed, encoding="utf-8")
|
|
202
|
+
print(f"✅ Installation completed successfully!\n{'=' * 80}")
|
|
159
203
|
|
|
160
204
|
def download(self, version: Optional[str]):
|
|
161
|
-
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
|
|
162
208
|
if "github" not in self.repo_url or ".zip" in self.repo_url or ".tar.gz" in self.repo_url:
|
|
163
209
|
download_link = Path(self.repo_url)
|
|
164
210
|
version_to_be_installed = "predefined_url"
|
|
165
211
|
print(f"🔗 Using direct download URL: {download_link}")
|
|
166
212
|
print(f"📦 Version to be installed: {version_to_be_installed}")
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
elif platform.system() in ["Linux", "Darwin"]:
|
|
173
|
-
download_link = Path(self.filename_template_linux_amd_64)
|
|
174
|
-
system_name = "Linux" if platform.system() == "Linux" else "macOS"
|
|
175
|
-
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
|
|
176
218
|
else:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
|
182
226
|
|
|
183
227
|
else:
|
|
184
228
|
print("🌐 Retrieving release information from GitHub...")
|
|
@@ -189,36 +233,97 @@ class Installer:
|
|
|
189
233
|
version_to_be_installed_stripped = version_to_be_installed.replace("v", "") if self.strip_v else version_to_be_installed
|
|
190
234
|
version_to_be_installed_stripped = version_to_be_installed_stripped.replace("ipinfo-", "")
|
|
191
235
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
print(f"🐧 {system_name} file name: {file_name}")
|
|
199
|
-
else:
|
|
200
|
-
error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
201
|
-
print(error_msg)
|
|
202
|
-
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}")
|
|
203
242
|
|
|
204
243
|
print(f"📄 File name: {file_name}")
|
|
205
244
|
download_link = release_url.joinpath(file_name)
|
|
206
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"
|
|
207
248
|
print(f"📥 Downloading {self.name} from: {download_link}")
|
|
208
249
|
downloaded = PathExtended(download_link).download(folder=INSTALL_TMP_DIR).decompress()
|
|
209
|
-
print(f"✅ Download and extraction completed to: {downloaded}\n{'='*80}")
|
|
250
|
+
print(f"✅ Download and extraction completed to: {downloaded}\n{'=' * 80}")
|
|
210
251
|
return downloaded, version_to_be_installed
|
|
211
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
|
+
|
|
212
314
|
@staticmethod
|
|
213
315
|
def get_github_release(repo_url: str, version: Optional[str] = None):
|
|
214
|
-
print(f"\n{'='*80}\n🔍 GITHUB RELEASE DETECTION 🔍\n{'='*80}")
|
|
316
|
+
print(f"\n{'=' * 80}\n🔍 GITHUB RELEASE DETECTION 🔍\n{'=' * 80}")
|
|
215
317
|
print(f"🌐 Inspecting releases at: {repo_url}")
|
|
216
318
|
# with console.status("Installing..."): # makes troubles on linux when prompt asks for password to move file to /usr/bin
|
|
217
319
|
if version is None:
|
|
218
320
|
# see this: https://api.github.com/repos/cointop-sh/cointop/releases/latest
|
|
219
321
|
print("🔍 Finding latest version...")
|
|
220
322
|
import requests # https://docs.github.com/en/repositories/releasing-projects-on-github/linking-to-releases
|
|
221
|
-
|
|
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
|
|
222
327
|
version_to_be_installed = _latest_version
|
|
223
328
|
print(f"✅ Latest version detected: {version_to_be_installed}")
|
|
224
329
|
# print(version_to_be_installed)
|
|
@@ -227,12 +332,12 @@ class Installer:
|
|
|
227
332
|
print(f"📝 Using specified version: {version_to_be_installed}")
|
|
228
333
|
|
|
229
334
|
release_url = Path(repo_url + "/releases/download/" + version_to_be_installed)
|
|
230
|
-
print(f"🔗 Release download URL: {release_url}\n{'='*80}")
|
|
335
|
+
print(f"🔗 Release download URL: {release_url}\n{'=' * 80}")
|
|
231
336
|
return release_url, version_to_be_installed
|
|
232
337
|
|
|
233
338
|
@staticmethod
|
|
234
339
|
def check_if_installed_already(exe_name: str, version: str, use_cache: bool):
|
|
235
|
-
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}")
|
|
236
341
|
version_to_be_installed = version
|
|
237
342
|
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
238
343
|
tmp_path = INSTALL_VERSION_ROOT.joinpath(exe_name)
|
|
@@ -240,19 +345,19 @@ class Installer:
|
|
|
240
345
|
if use_cache:
|
|
241
346
|
print("🗂️ Using cached version information...")
|
|
242
347
|
if tmp_path.exists():
|
|
243
|
-
existing_version = tmp_path.read_text().rstrip()
|
|
348
|
+
existing_version = tmp_path.read_text(encoding="utf-8").rstrip()
|
|
244
349
|
print(f"📄 Found cached version: {existing_version}")
|
|
245
350
|
else:
|
|
246
351
|
existing_version = None
|
|
247
352
|
print("ℹ️ No cached version information found")
|
|
248
353
|
else:
|
|
249
354
|
print("🔍 Checking installed version directly...")
|
|
250
|
-
|
|
251
|
-
if
|
|
355
|
+
result = subprocess.run([exe_name, "--version"], check=False, capture_output=True, text=True)
|
|
356
|
+
if result.stdout.strip() == "":
|
|
252
357
|
existing_version = None
|
|
253
358
|
print("ℹ️ Could not detect installed version")
|
|
254
359
|
else:
|
|
255
|
-
existing_version =
|
|
360
|
+
existing_version = result.stdout.strip()
|
|
256
361
|
print(f"📄 Detected installed version: {existing_version}")
|
|
257
362
|
|
|
258
363
|
if existing_version is not None:
|
|
@@ -262,11 +367,11 @@ class Installer:
|
|
|
262
367
|
return ("✅ Uptodate", version.strip(), version_to_be_installed.strip())
|
|
263
368
|
else:
|
|
264
369
|
print(f"🔄 {exe_name} needs update: {existing_version.rstrip()} → {version_to_be_installed}")
|
|
265
|
-
tmp_path.write_text(version_to_be_installed)
|
|
370
|
+
tmp_path.write_text(version_to_be_installed, encoding="utf-8")
|
|
266
371
|
return ("❌ Outdated", existing_version.strip(), version_to_be_installed.strip())
|
|
267
372
|
else:
|
|
268
373
|
print(f"📦 {exe_name} is not installed. Will install version: {version_to_be_installed}")
|
|
269
|
-
tmp_path.write_text(version_to_be_installed)
|
|
374
|
+
tmp_path.write_text(version_to_be_installed, encoding="utf-8")
|
|
270
375
|
|
|
271
|
-
print(f"{'='*80}")
|
|
272
|
-
return ("⚠️ NotInstalled", "None", version_to_be_installed.strip())
|
|
376
|
+
print(f"{'=' * 80}")
|
|
377
|
+
return ("⚠️ NotInstalled", "None", version_to_be_installed.strip())
|
machineconfig/utils/io_save.py
CHANGED
|
@@ -5,16 +5,8 @@ from pathlib import Path
|
|
|
5
5
|
import json
|
|
6
6
|
import pickle
|
|
7
7
|
import configparser
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import toml # type: ignore
|
|
11
|
-
except Exception: # pragma: no cover
|
|
12
|
-
toml = None # type: ignore
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
import yaml # type: ignore
|
|
16
|
-
except Exception: # pragma: no cover
|
|
17
|
-
yaml = None # type: ignore
|
|
8
|
+
import toml
|
|
9
|
+
import yaml
|
|
18
10
|
|
|
19
11
|
|
|
20
12
|
PathLike = Union[str, Path]
|
|
@@ -46,8 +38,6 @@ def save_json(obj: Any, path: PathLike, indent: Optional[int] = None, verbose: b
|
|
|
46
38
|
|
|
47
39
|
|
|
48
40
|
def save_toml(obj: Mapping[str, Any], path: PathLike, verbose: bool = False) -> Path:
|
|
49
|
-
if toml is None:
|
|
50
|
-
raise RuntimeError("toml package is required to write TOML files")
|
|
51
41
|
path_obj = _ensure_parent(path)
|
|
52
42
|
with open(path_obj, "w", encoding="utf-8") as fh:
|
|
53
43
|
toml.dump(obj, fh)
|
|
@@ -57,8 +47,6 @@ def save_toml(obj: Mapping[str, Any], path: PathLike, verbose: bool = False) ->
|
|
|
57
47
|
|
|
58
48
|
|
|
59
49
|
def save_yaml(obj: Any, path: PathLike, verbose: bool = False) -> Path:
|
|
60
|
-
if yaml is None:
|
|
61
|
-
raise RuntimeError("PyYAML is required to write YAML files")
|
|
62
50
|
path_obj = _ensure_parent(path)
|
|
63
51
|
with open(path_obj, "w", encoding="utf-8") as fh:
|
|
64
52
|
yaml.safe_dump(obj, fh, sort_keys=False)
|
|
@@ -80,7 +68,7 @@ def save_ini(path: PathLike, obj: Mapping[str, Mapping[str, Any]], verbose: bool
|
|
|
80
68
|
|
|
81
69
|
|
|
82
70
|
class Save:
|
|
83
|
-
"""
|
|
71
|
+
"""
|
|
84
72
|
|
|
85
73
|
Provides static methods for common serialization formats while ensuring
|
|
86
74
|
parent directories exist and returning a `P` path object.
|
machineconfig/utils/links.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from machineconfig.utils.path_reduced import
|
|
1
|
+
from machineconfig.utils.path_reduced import PathExtended as PathExtended, PLike
|
|
2
2
|
from machineconfig.utils.utils2 import randstr
|
|
3
3
|
from rich.console import Console
|
|
4
4
|
from rich.panel import Panel
|
|
@@ -19,6 +19,7 @@ def build_links(target_paths: list[tuple[PLike, str]], repo_root: PLike):
|
|
|
19
19
|
target_dirs_filtered.append((a_dir_obj, a_name))
|
|
20
20
|
|
|
21
21
|
import git
|
|
22
|
+
|
|
22
23
|
repo = git.Repo(repo_root, search_parent_directories=True)
|
|
23
24
|
root_maybe = repo.working_tree_dir
|
|
24
25
|
assert root_maybe is not None
|
|
@@ -31,7 +32,8 @@ def build_links(target_paths: list[tuple[PLike, str]], repo_root: PLike):
|
|
|
31
32
|
links_path = repo_root_obj.joinpath("links", a_name)
|
|
32
33
|
links_path.symlink_to(target=a_target_path)
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
|
|
36
|
+
def symlink_func(this: PathExtended, to_this: PathExtended, prioritize_to_this: bool = True):
|
|
35
37
|
"""helper function. creates a symlink from `this` to `to_this`.
|
|
36
38
|
What can go wrong?
|
|
37
39
|
depending on this and to_this existence, one will be prioretized depending on overwrite value.
|
|
@@ -39,15 +41,19 @@ def symlink_func(this: PathExtended, to_this: PathExtended, prioritize_to_this:
|
|
|
39
41
|
False means to_this will potentially be overwittten."""
|
|
40
42
|
this = PathExtended(this).expanduser().absolute()
|
|
41
43
|
to_this = PathExtended(to_this).expanduser().absolute()
|
|
42
|
-
if this.is_symlink():
|
|
44
|
+
if this.is_symlink():
|
|
45
|
+
this.delete(sure=True) # delete if it exists as symblic link, not a concrete path.
|
|
43
46
|
if this.exists(): # this is a problem. It will be resolved via `overwrite`
|
|
44
47
|
if prioritize_to_this is True: # it *can* be deleted, but let's look at target first.
|
|
45
48
|
if to_this.exists(): # this exists, to_this as well. to_this is prioritized.
|
|
46
49
|
this.append(f".orig_{randstr()}", inplace=True) # rename is better than deletion
|
|
47
|
-
else:
|
|
50
|
+
else:
|
|
51
|
+
this.move(path=to_this) # this exists, to_this doesn't. to_this is prioritized.
|
|
48
52
|
elif prioritize_to_this is False: # don't sacrefice this, sacrefice to_this.
|
|
49
|
-
if to_this.exists():
|
|
50
|
-
|
|
53
|
+
if to_this.exists():
|
|
54
|
+
this.move(path=to_this, overwrite=True) # this exists, to_this as well, this is prioritized. # now we are readly to make the link
|
|
55
|
+
else:
|
|
56
|
+
this.move(path=to_this) # this exists, to_this doesn't, this is prioritized.
|
|
51
57
|
else: # this doesn't exist.
|
|
52
58
|
if not to_this.exists():
|
|
53
59
|
to_this.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -58,18 +64,23 @@ def symlink_func(this: PathExtended, to_this: PathExtended, prioritize_to_this:
|
|
|
58
64
|
except Exception as ex:
|
|
59
65
|
console.print(Panel(f"❌ ERROR | Failed at linking {this} ➡️ {to_this}. Reason: {ex}", title="Error", expand=False))
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
|
|
68
|
+
def symlink_copy(this: PathExtended, to_this: PathExtended, prioritize_to_this: bool = True):
|
|
62
69
|
this = PathExtended(this).expanduser().absolute()
|
|
63
70
|
to_this = PathExtended(to_this).expanduser().absolute()
|
|
64
|
-
if this.is_symlink():
|
|
71
|
+
if this.is_symlink():
|
|
72
|
+
this.delete(sure=True) # delete if it exists as symblic link, not a concrete path.
|
|
65
73
|
if this.exists(): # this is a problem. It will be resolved via `overwrite`
|
|
66
74
|
if prioritize_to_this is True: # it *can* be deleted, but let's look at target first.
|
|
67
75
|
if to_this.exists(): # this exists, to_this as well. to_this is prioritized.
|
|
68
76
|
this.append(f".orig_{randstr()}", inplace=True) # rename is better than deletion
|
|
69
|
-
else:
|
|
77
|
+
else:
|
|
78
|
+
this.move(path=to_this) # this exists, to_this doesn't. to_this is prioritized.
|
|
70
79
|
elif prioritize_to_this is False: # don't sacrefice this, sacrefice to_this.
|
|
71
|
-
if to_this.exists():
|
|
72
|
-
|
|
80
|
+
if to_this.exists():
|
|
81
|
+
this.move(path=to_this, overwrite=True) # this exists, to_this as well, this is prioritized. # now we are readly to make the link
|
|
82
|
+
else:
|
|
83
|
+
this.move(path=to_this) # this exists, to_this doesn't, this is prioritized.
|
|
73
84
|
else: # this doesn't exist.
|
|
74
85
|
if not to_this.exists():
|
|
75
86
|
to_this.parent.mkdir(parents=True, exist_ok=True)
|