machineconfig 7.57__py3-none-any.whl → 7.79__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/sessions_managers/utils/maker.py +21 -9
- machineconfig/jobs/installer/custom/boxes.py +2 -2
- machineconfig/jobs/installer/custom/hx.py +3 -3
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
- machineconfig/jobs/installer/custom_dev/sysabc.py +36 -28
- machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
- machineconfig/jobs/installer/installer_data.json +127 -25
- machineconfig/jobs/installer/package_groups.py +20 -13
- machineconfig/profile/create_links_export.py +2 -2
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +1 -1
- machineconfig/scripts/python/agents.py +22 -17
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
- machineconfig/scripts/python/croshell.py +22 -17
- machineconfig/scripts/python/devops.py +3 -4
- machineconfig/scripts/python/devops_navigator.py +0 -4
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/fire_jobs.py +19 -18
- machineconfig/scripts/python/ftpx.py +36 -12
- machineconfig/scripts/python/helpers/ast_search.py +74 -0
- machineconfig/scripts/python/helpers/qr_code.py +166 -0
- machineconfig/scripts/python/helpers/repo_rag.py +325 -0
- machineconfig/scripts/python/helpers/symantic_search.py +25 -0
- machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
- machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
- machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
- machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
- machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
- machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
- machineconfig/scripts/python/helpers_devops/cli_nw.py +88 -7
- machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +9 -9
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +13 -12
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
- machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +23 -13
- machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +7 -4
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
- machineconfig/scripts/python/helpers_repos/record.py +2 -1
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
- machineconfig/scripts/python/helpers_utils/download.py +152 -0
- machineconfig/scripts/python/helpers_utils/path.py +81 -31
- machineconfig/scripts/python/interactive.py +2 -2
- machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
- machineconfig/scripts/python/msearch.py +21 -2
- machineconfig/scripts/python/nw/address.py +132 -0
- machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
- machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
- machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
- machineconfig/scripts/python/sessions.py +35 -20
- machineconfig/scripts/python/terminal.py +2 -2
- machineconfig/scripts/python/utils.py +12 -10
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/shells/nushell/config.nu +2 -2
- machineconfig/settings/shells/nushell/env.nu +45 -6
- machineconfig/settings/shells/nushell/init.nu +282 -95
- machineconfig/settings/shells/pwsh/init.ps1 +1 -0
- machineconfig/settings/shells/zsh/init.sh +0 -7
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/uv.ps1 +8 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
- machineconfig/utils/accessories.py +7 -4
- machineconfig/utils/code.py +6 -4
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/install_from_url.py +180 -0
- machineconfig/utils/installer_utils/installer_class.py +53 -47
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/meta.py +30 -16
- machineconfig/utils/options.py +4 -4
- machineconfig/utils/path_extended.py +3 -3
- machineconfig/utils/path_helper.py +33 -31
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/ssh.py +143 -409
- machineconfig/utils/ssh_utils/abc.py +8 -0
- machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
- machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
- machineconfig/utils/ssh_utils/utils.py +141 -0
- machineconfig/utils/ssh_utils/wsl.py +168 -0
- machineconfig/utils/upgrade_packages.py +2 -1
- machineconfig/utils/ve.py +11 -4
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/METADATA +1 -1
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/RECORD +102 -92
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/entry_points.txt +2 -2
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/scripts/python/explore.py +0 -49
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
- /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
- /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/WHEEL +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from typing import Optional, TypeAlias, cast
|
|
8
|
+
|
|
9
|
+
from machineconfig.utils.installer_utils.installer_class import install_deb_package
|
|
10
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import find_move_delete_linux, find_move_delete_windows
|
|
11
|
+
from machineconfig.utils.path_extended import DECOMPRESS_SUPPORTED_FORMATS, PathExtended
|
|
12
|
+
from machineconfig.utils.source_of_truth import INSTALL_TMP_DIR, INSTALL_VERSION_ROOT
|
|
13
|
+
|
|
14
|
+
SUPPORTED_GITHUB_HOSTS = {"github.com", "www.github.com"}
|
|
15
|
+
|
|
16
|
+
GitHubAsset: TypeAlias = dict[str, object]
|
|
17
|
+
GitHubRelease: TypeAlias = dict[str, object]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _extract_repo_name(github_url: str) -> str:
|
|
21
|
+
parsed = urlparse(github_url)
|
|
22
|
+
parts = [part for part in parsed.path.strip("/").split("/") if part]
|
|
23
|
+
if len(parts) < 2:
|
|
24
|
+
return ""
|
|
25
|
+
owner, repo = parts[0], parts[1]
|
|
26
|
+
if repo == "":
|
|
27
|
+
return ""
|
|
28
|
+
return f"{owner}/{repo}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fetch_latest_release(repo_name: str) -> Optional[GitHubRelease]:
|
|
32
|
+
import json
|
|
33
|
+
import requests
|
|
34
|
+
try:
|
|
35
|
+
response = requests.get(f"https://api.github.com/repos/{repo_name}/releases/latest", timeout=30)
|
|
36
|
+
except requests.RequestException:
|
|
37
|
+
return None
|
|
38
|
+
if response.status_code != 200:
|
|
39
|
+
return None
|
|
40
|
+
try:
|
|
41
|
+
data = response.json()
|
|
42
|
+
except json.JSONDecodeError:
|
|
43
|
+
return None
|
|
44
|
+
if not isinstance(data, dict):
|
|
45
|
+
return None
|
|
46
|
+
return cast(GitHubRelease, data)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _format_size(size_bytes: int) -> str:
|
|
50
|
+
if size_bytes <= 0:
|
|
51
|
+
return "0 B"
|
|
52
|
+
units = ("B", "KiB", "MiB", "GiB", "TiB")
|
|
53
|
+
value = float(size_bytes)
|
|
54
|
+
index = 0
|
|
55
|
+
while value >= 1024 and index < len(units) - 1:
|
|
56
|
+
value /= 1024
|
|
57
|
+
index += 1
|
|
58
|
+
return f"{value:.1f} {units[index]}"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _derive_tool_name(repo_name: str, asset_name: str) -> str:
|
|
62
|
+
repo_segment = repo_name.split("/", maxsplit=1)[-1]
|
|
63
|
+
repo_clean = repo_segment.replace(".git", "").lower()
|
|
64
|
+
repo_filtered = "".join(char for char in repo_clean if char.isalnum())
|
|
65
|
+
if repo_filtered:
|
|
66
|
+
return repo_filtered
|
|
67
|
+
asset_clean = asset_name.lower()
|
|
68
|
+
asset_filtered = "".join(char for char in asset_clean if char.isalnum())
|
|
69
|
+
if asset_filtered:
|
|
70
|
+
return asset_filtered
|
|
71
|
+
return "githubapp"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def install_from_github_url(github_url: str) -> None:
|
|
75
|
+
from machineconfig.utils.options import choose_from_options
|
|
76
|
+
from rich.console import Console
|
|
77
|
+
from rich.panel import Panel
|
|
78
|
+
|
|
79
|
+
console = Console()
|
|
80
|
+
repo_name = _extract_repo_name(github_url)
|
|
81
|
+
if repo_name == "":
|
|
82
|
+
console.print(Panel(f"Invalid GitHub URL: {github_url}", title="❌ Error", border_style="red"))
|
|
83
|
+
raise typer.Exit(1)
|
|
84
|
+
console.print(Panel(f"Fetching latest release for [green]{repo_name}[/green]", title="🌐 GitHub", border_style="blue"))
|
|
85
|
+
release_raw = _fetch_latest_release(repo_name)
|
|
86
|
+
if not release_raw:
|
|
87
|
+
console.print(Panel("No releases available for this repository.", title="❌ Error", border_style="red"))
|
|
88
|
+
raise typer.Exit(1)
|
|
89
|
+
release = release_raw
|
|
90
|
+
assets_value = release.get("assets", [])
|
|
91
|
+
assets: list[GitHubAsset] = []
|
|
92
|
+
if isinstance(assets_value, list):
|
|
93
|
+
for asset in assets_value:
|
|
94
|
+
if isinstance(asset, dict):
|
|
95
|
+
typed_asset: GitHubAsset = {}
|
|
96
|
+
name_value = asset.get("name")
|
|
97
|
+
url_value = asset.get("browser_download_url")
|
|
98
|
+
size_value = asset.get("size")
|
|
99
|
+
content_value = asset.get("content_type")
|
|
100
|
+
if isinstance(name_value, str):
|
|
101
|
+
typed_asset["name"] = name_value
|
|
102
|
+
if isinstance(url_value, str):
|
|
103
|
+
typed_asset["browser_download_url"] = url_value
|
|
104
|
+
if isinstance(size_value, int):
|
|
105
|
+
typed_asset["size"] = size_value
|
|
106
|
+
if isinstance(content_value, str):
|
|
107
|
+
typed_asset["content_type"] = content_value
|
|
108
|
+
assets.append(typed_asset)
|
|
109
|
+
if not assets:
|
|
110
|
+
console.print(Panel("No downloadable assets found in the latest release.", title="❌ Error", border_style="red"))
|
|
111
|
+
raise typer.Exit(1)
|
|
112
|
+
binary_assets = assets
|
|
113
|
+
selection_pool = binary_assets if binary_assets else assets
|
|
114
|
+
if not selection_pool:
|
|
115
|
+
console.print(Panel("No assets available for installation.", title="❌ Error", border_style="red"))
|
|
116
|
+
raise typer.Exit(1)
|
|
117
|
+
options_map: dict[str, GitHubAsset] = {}
|
|
118
|
+
for asset in selection_pool:
|
|
119
|
+
name = asset.get("name")
|
|
120
|
+
download_url = asset.get("browser_download_url")
|
|
121
|
+
if not isinstance(name, str) or not isinstance(download_url, str) or name == "" or download_url == "":
|
|
122
|
+
continue
|
|
123
|
+
size_value = asset.get("size")
|
|
124
|
+
size = size_value if isinstance(size_value, int) else 0
|
|
125
|
+
label = f"{name} [{_format_size(size)}]"
|
|
126
|
+
options_map[label] = asset
|
|
127
|
+
if not options_map:
|
|
128
|
+
console.print(Panel("Release assets lack download URLs.", title="❌ Error", border_style="red"))
|
|
129
|
+
raise typer.Exit(1)
|
|
130
|
+
selection_label = choose_from_options(options=list(options_map.keys()), msg="Select a release asset", multi=False, header="📦 GitHub Release Assets", fzf=True)
|
|
131
|
+
selected_asset = options_map[selection_label]
|
|
132
|
+
download_url_value = selected_asset.get("browser_download_url")
|
|
133
|
+
asset_name_value = selected_asset.get("name")
|
|
134
|
+
if not isinstance(download_url_value, str) or download_url_value == "":
|
|
135
|
+
console.print(Panel("Selected asset lacks a download URL.", title="❌ Error", border_style="red"))
|
|
136
|
+
raise typer.Exit(1)
|
|
137
|
+
asset_name = asset_name_value if isinstance(asset_name_value, str) else "github_binary"
|
|
138
|
+
version_value = release.get("tag_name")
|
|
139
|
+
version = version_value if isinstance(version_value, str) and version_value != "" else "latest"
|
|
140
|
+
console.print(Panel(f"Downloading [cyan]{asset_name}[/cyan]", title="⬇️ Download", border_style="magenta"))
|
|
141
|
+
archive_path = PathExtended(download_url_value).download(folder=INSTALL_TMP_DIR)
|
|
142
|
+
extracted_path = archive_path
|
|
143
|
+
if extracted_path.suffix in DECOMPRESS_SUPPORTED_FORMATS:
|
|
144
|
+
extracted_path = archive_path.decompress()
|
|
145
|
+
archive_path.delete(sure=True)
|
|
146
|
+
if extracted_path.is_dir():
|
|
147
|
+
nested_items = list(extracted_path.glob("*"))
|
|
148
|
+
if len(nested_items) == 1:
|
|
149
|
+
nested_path = PathExtended(nested_items[0])
|
|
150
|
+
if nested_path.suffix in DECOMPRESS_SUPPORTED_FORMATS:
|
|
151
|
+
extracted_path = nested_path.decompress()
|
|
152
|
+
nested_path.delete(sure=True)
|
|
153
|
+
if extracted_path.suffix == ".deb":
|
|
154
|
+
install_deb_package(extracted_path)
|
|
155
|
+
tool_name_deb = _derive_tool_name(repo_name, asset_name)
|
|
156
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name_deb).parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name_deb).write_text(version, encoding="utf-8")
|
|
158
|
+
console.print(Panel(f"Installed Debian package for [green]{tool_name_deb}[/green]", title="✅ Complete", border_style="green"))
|
|
159
|
+
return
|
|
160
|
+
system_name = platform.system()
|
|
161
|
+
tool_name = _derive_tool_name(repo_name, asset_name)
|
|
162
|
+
rename_target = f"{tool_name}.exe" if system_name == "Windows" else tool_name
|
|
163
|
+
try:
|
|
164
|
+
if system_name == "Windows":
|
|
165
|
+
installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, exe_name=tool_name, delete=True, rename_to=rename_target)
|
|
166
|
+
elif system_name in {"Linux", "Darwin"}:
|
|
167
|
+
installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name=tool_name, delete=True, rename_to=rename_target)
|
|
168
|
+
else:
|
|
169
|
+
console.print(Panel(f"Unsupported operating system: {system_name}", title="❌ Error", border_style="red"))
|
|
170
|
+
raise typer.Exit(1)
|
|
171
|
+
except IndexError:
|
|
172
|
+
if system_name == "Windows":
|
|
173
|
+
installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, exe_name=None, delete=True, rename_to=rename_target)
|
|
174
|
+
elif system_name in {"Linux", "Darwin"}:
|
|
175
|
+
installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name="", delete=True, rename_to=rename_target)
|
|
176
|
+
else:
|
|
177
|
+
raise
|
|
178
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name).parent.mkdir(parents=True, exist_ok=True)
|
|
179
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name).write_text(version, encoding="utf-8")
|
|
180
|
+
console.print(Panel(f"Installed [green]{tool_name}[/green] to {installed_path}\nVersion: {version}", title="✅ Complete", border_style="green"))
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
2
|
-
from machineconfig.utils.installer_utils.installer_abc import find_move_delete_linux, find_move_delete_windows
|
|
1
|
+
from machineconfig.utils.path_extended import PathExtended, DECOMPRESS_SUPPORTED_FORMATS
|
|
3
2
|
from machineconfig.utils.source_of_truth import INSTALL_TMP_DIR, INSTALL_VERSION_ROOT
|
|
4
|
-
from machineconfig.utils.installer_utils.
|
|
3
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import find_move_delete_linux, find_move_delete_windows, check_tool_exists
|
|
5
4
|
from machineconfig.utils.schemas.installer.installer_types import InstallerData, get_os_name, get_normalized_arch
|
|
6
5
|
|
|
7
6
|
import platform
|
|
@@ -11,6 +10,28 @@ from typing import Optional, Any
|
|
|
11
10
|
from urllib.parse import urlparse
|
|
12
11
|
|
|
13
12
|
|
|
13
|
+
|
|
14
|
+
def install_deb_package(downloaded: PathExtended) -> None:
|
|
15
|
+
from rich import print as rprint
|
|
16
|
+
from rich.panel import Panel
|
|
17
|
+
print(f"📦 Installing .deb package: {downloaded}")
|
|
18
|
+
assert platform.system() == "Linux"
|
|
19
|
+
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
20
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
21
|
+
if not success:
|
|
22
|
+
from rich.console import Group
|
|
23
|
+
desc = "Installing .deb"
|
|
24
|
+
sub_panels = []
|
|
25
|
+
if result.stdout:
|
|
26
|
+
sub_panels.append(Panel(result.stdout, title="STDOUT", style="blue"))
|
|
27
|
+
if result.stderr:
|
|
28
|
+
sub_panels.append(Panel(result.stderr, title="STDERR", style="red"))
|
|
29
|
+
group_content = Group(f"❌ {desc} failed\nReturn code: {result.returncode}", *sub_panels)
|
|
30
|
+
rprint(Panel(group_content, title=desc, style="red"))
|
|
31
|
+
print("🗑️ Cleaning up .deb package...")
|
|
32
|
+
downloaded.delete(sure=True)
|
|
33
|
+
|
|
34
|
+
|
|
14
35
|
class Installer:
|
|
15
36
|
def __init__(self, installer_data: InstallerData):
|
|
16
37
|
self.installer_data: InstallerData = installer_data
|
|
@@ -42,10 +63,8 @@ class Installer:
|
|
|
42
63
|
result_new = subprocess.run(f"{exe_name} --version", shell=True, capture_output=True, text=True)
|
|
43
64
|
new_version_cli = result_new.stdout.strip()
|
|
44
65
|
if old_version_cli == new_version_cli:
|
|
45
|
-
# print(f"ℹ️ Same version detected: {old_version_cli}")
|
|
46
66
|
return f"""📦️ 😑 {exe_name}, same version: {old_version_cli}"""
|
|
47
67
|
else:
|
|
48
|
-
# print(f"🚀 Update successful: {old_version_cli} ➡️ {new_version_cli}")
|
|
49
68
|
return f"""📦️ 🤩 {exe_name} updated from {old_version_cli} ➡️ TO ➡️ {new_version_cli}"""
|
|
50
69
|
except Exception as ex:
|
|
51
70
|
exe_name = self._get_exe_name()
|
|
@@ -64,23 +83,24 @@ class Installer:
|
|
|
64
83
|
version_to_be_installed: str = "unknown" # Initialize to ensure it's always bound
|
|
65
84
|
if repo_url == "CMD":
|
|
66
85
|
if any(pm in installer_arch_os for pm in ["npm ", "pip ", "winget ", "brew ", "curl "]):
|
|
86
|
+
from rich import print as rprint
|
|
87
|
+
from rich.panel import Panel
|
|
88
|
+
from rich.console import Group
|
|
67
89
|
package_manager = installer_arch_os.split(" ", maxsplit=1)[0]
|
|
68
90
|
print(f"📦 Using package manager: {installer_arch_os}")
|
|
69
91
|
desc = package_manager + " installation"
|
|
70
92
|
version_to_be_installed = package_manager + "Latest"
|
|
71
|
-
result = subprocess.run(installer_arch_os, shell=True, capture_output=
|
|
72
|
-
|
|
73
|
-
# result = run_shell_script(installer_arch_os)
|
|
74
|
-
success = result.returncode == 0 and result.stderr == "".encode()
|
|
93
|
+
result = subprocess.run(installer_arch_os, shell=True, capture_output=False, text=True)
|
|
94
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
75
95
|
if not success:
|
|
76
|
-
|
|
96
|
+
sub_panels = []
|
|
77
97
|
if result.stdout:
|
|
78
|
-
|
|
98
|
+
sub_panels.append(Panel(result.stdout, title="STDOUT", style="blue"))
|
|
79
99
|
if result.stderr:
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
sub_panels.append(Panel(result.stderr, title="STDERR", style="red"))
|
|
101
|
+
group_content = Group(f"❌ {desc} failed\nReturn code: {result.returncode}", *sub_panels)
|
|
102
|
+
rprint(Panel(group_content, title=desc, style="red"))
|
|
82
103
|
elif installer_arch_os.endswith((".sh", ".py", ".ps1")):
|
|
83
|
-
# search for the script, see which path ends with the script name
|
|
84
104
|
import machineconfig.jobs.installer as module
|
|
85
105
|
from pathlib import Path
|
|
86
106
|
search_root = Path(module.__file__).parent
|
|
@@ -106,9 +126,14 @@ class Installer:
|
|
|
106
126
|
runpy.run_path(str(installer_path), run_name=None)["main"](self.installer_data, version=version)
|
|
107
127
|
version_to_be_installed = str(version)
|
|
108
128
|
elif installer_arch_os.startswith("https://"): # its a url to be downloaded
|
|
109
|
-
downloaded_object = PathExtended(installer_arch_os).download(folder=INSTALL_TMP_DIR)
|
|
129
|
+
# downloaded_object = PathExtended(installer_arch_os).download(folder=INSTALL_TMP_DIR)
|
|
130
|
+
from machineconfig.scripts.python.helpers_utils.download import download
|
|
131
|
+
downloaded_object = download(installer_arch_os, output_dir=str(INSTALL_TMP_DIR))
|
|
132
|
+
if downloaded_object is None:
|
|
133
|
+
raise ValueError(f"Failed to download from URL: {installer_arch_os}")
|
|
110
134
|
# object is either a zip containing a binary or a straight out binary.
|
|
111
|
-
|
|
135
|
+
downloaded_object = PathExtended(downloaded_object)
|
|
136
|
+
if downloaded_object.suffix in DECOMPRESS_SUPPORTED_FORMATS:
|
|
112
137
|
downloaded_object = downloaded_object.decompress()
|
|
113
138
|
if downloaded_object.suffix in [".exe", ""]: # likely an executable
|
|
114
139
|
if platform.system() == "Windows":
|
|
@@ -131,26 +156,17 @@ class Installer:
|
|
|
131
156
|
print(f"🔄 Renaming to correct name: {new_exe_name}")
|
|
132
157
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
133
158
|
version_to_be_installed = "downloaded_binary"
|
|
159
|
+
elif downloaded_object.suffix in [".deb"]:
|
|
160
|
+
install_deb_package(downloaded_object)
|
|
161
|
+
version_to_be_installed = "downloaded_deb"
|
|
162
|
+
else:
|
|
163
|
+
raise ValueError(f"Downloaded file is not an executable: {downloaded_object}")
|
|
134
164
|
else:
|
|
135
165
|
raise NotImplementedError(f"CMD installation method not implemented for: {installer_arch_os}")
|
|
136
166
|
else:
|
|
137
167
|
assert repo_url.startswith("https://github.com/"), f"repoURL must be a GitHub URL, got {repo_url}"
|
|
138
|
-
downloaded, version_to_be_installed = self.
|
|
139
|
-
if str(downloaded).endswith(".deb"):
|
|
140
|
-
print(f"📦 Installing .deb package: {downloaded}")
|
|
141
|
-
assert platform.system() == "Linux"
|
|
142
|
-
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
143
|
-
success = result.returncode == 0 and result.stderr == ""
|
|
144
|
-
if not success:
|
|
145
|
-
desc = "Installing .deb"
|
|
146
|
-
print(f"❌ {desc} failed")
|
|
147
|
-
if result.stdout:
|
|
148
|
-
print(f"STDOUT: {result.stdout}")
|
|
149
|
-
if result.stderr:
|
|
150
|
-
print(f"STDERR: {result.stderr}")
|
|
151
|
-
print(f"Return code: {result.returncode}")
|
|
152
|
-
print("🗑️ Cleaning up .deb package...")
|
|
153
|
-
downloaded.delete(sure=True)
|
|
168
|
+
downloaded, version_to_be_installed = self.binary_download(version=version)
|
|
169
|
+
if str(downloaded).endswith(".deb"): install_deb_package(downloaded)
|
|
154
170
|
else:
|
|
155
171
|
if platform.system() == "Windows":
|
|
156
172
|
exe = find_move_delete_windows(downloaded_file_path=downloaded, exe_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
@@ -173,13 +189,13 @@ class Installer:
|
|
|
173
189
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
174
190
|
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
175
191
|
INSTALL_VERSION_ROOT.joinpath(exe_name).write_text(version_to_be_installed or "unknown", encoding="utf-8")
|
|
176
|
-
def
|
|
192
|
+
def binary_download(self, version: Optional[str]) -> tuple[PathExtended, str]:
|
|
177
193
|
exe_name = self._get_exe_name()
|
|
178
194
|
repo_url = self.installer_data["repoURL"]
|
|
179
195
|
# app_name = self.installer_data["appName"]
|
|
180
196
|
download_link: Optional[str] = None
|
|
181
197
|
version_to_be_installed: Optional[str] = None
|
|
182
|
-
if "github" not in repo_url or
|
|
198
|
+
if "github" not in repo_url or (any(ext in repo_url for ext in DECOMPRESS_SUPPORTED_FORMATS)):
|
|
183
199
|
# Direct download URL
|
|
184
200
|
download_link = repo_url
|
|
185
201
|
version_to_be_installed = "predefined_url"
|
|
@@ -202,12 +218,9 @@ class Installer:
|
|
|
202
218
|
downloaded = PathExtended(download_link).download(folder=INSTALL_TMP_DIR).decompress()
|
|
203
219
|
if downloaded.is_dir() and len(downloaded.search("*", r=True)) == 1:
|
|
204
220
|
only_file_in = next(downloaded.glob("*"))
|
|
205
|
-
if only_file_in.is_file() and only_file_in.suffix in
|
|
221
|
+
if only_file_in.is_file() and only_file_in.suffix in DECOMPRESS_SUPPORTED_FORMATS: # further decompress
|
|
206
222
|
downloaded = only_file_in.decompress()
|
|
207
223
|
return downloaded, version_to_be_installed
|
|
208
|
-
|
|
209
|
-
# --------------------------- Arch / template helpers ---------------------------
|
|
210
|
-
|
|
211
224
|
@staticmethod
|
|
212
225
|
def _get_repo_name_from_url(repo_url: str) -> str:
|
|
213
226
|
"""Extract owner/repo from GitHub URL."""
|
|
@@ -222,23 +235,16 @@ class Installer:
|
|
|
222
235
|
def _fetch_github_release_data(repo_name: str, version: Optional[str] = None) -> Optional[dict[str, Any]]:
|
|
223
236
|
"""Fetch release data from GitHub API using requests."""
|
|
224
237
|
import requests
|
|
225
|
-
|
|
226
238
|
try:
|
|
227
|
-
if version and version.lower() != "latest":
|
|
228
|
-
# Fetch specific version
|
|
239
|
+
if version and version.lower() != "latest": # Fetch specific version
|
|
229
240
|
url = f"https://api.github.com/repos/{repo_name}/releases/tags/{version}"
|
|
230
|
-
else:
|
|
231
|
-
# Fetch latest release
|
|
241
|
+
else: # Fetch latest release
|
|
232
242
|
url = f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
233
|
-
|
|
234
243
|
response = requests.get(url, timeout=30)
|
|
235
|
-
|
|
236
244
|
if response.status_code != 200:
|
|
237
245
|
print(f"❌ Failed to fetch data for {repo_name}: HTTP {response.status_code}")
|
|
238
246
|
return None
|
|
239
|
-
|
|
240
247
|
response_data = response.json()
|
|
241
|
-
|
|
242
248
|
# Check if API returned an error
|
|
243
249
|
if "message" in response_data:
|
|
244
250
|
if "API rate limit exceeded" in response_data.get("message", ""):
|
|
@@ -1,55 +1,16 @@
|
|
|
1
1
|
"""Devops Devapps Install"""
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
|
-
from
|
|
5
|
-
from rich.panel import Panel
|
|
6
|
-
from rich.table import Table
|
|
7
|
-
from typing import Optional, Annotated
|
|
4
|
+
from typing import Annotated, Optional
|
|
8
5
|
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
|
|
6
|
+
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
9
7
|
|
|
10
|
-
console = Console()
|
|
11
8
|
|
|
12
9
|
|
|
13
|
-
def
|
|
14
|
-
"""Handle installer not found with friendly suggestions using fuzzy matching."""
|
|
15
|
-
from difflib import get_close_matches
|
|
16
|
-
close_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
|
|
17
|
-
console.print(f"\n❌ '[red]{search_term}[/red]' was not found.", style="bold")
|
|
18
|
-
if close_matches:
|
|
19
|
-
console.print("🤔 Did you mean one of these?", style="yellow")
|
|
20
|
-
table = Table(show_header=False, box=None, pad_edge=False)
|
|
21
|
-
for i, match in enumerate(close_matches, 1):
|
|
22
|
-
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{match}[/green]")
|
|
23
|
-
console.print(table)
|
|
24
|
-
else:
|
|
25
|
-
console.print("📋 Here are some available options:", style="blue")
|
|
26
|
-
# Show first 10 installers as examples
|
|
27
|
-
if len(all_names) > 10:
|
|
28
|
-
sample_names = all_names[:10]
|
|
29
|
-
else:
|
|
30
|
-
sample_names = all_names
|
|
31
|
-
table = Table(show_header=False, box=None, pad_edge=False)
|
|
32
|
-
for i, name in enumerate(sample_names, 1):
|
|
33
|
-
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{name}[/green]")
|
|
34
|
-
console.print(table)
|
|
35
|
-
if len(all_names) > 10:
|
|
36
|
-
console.print(f" [dim]... and {len(all_names) - 10} more[/dim]")
|
|
37
|
-
|
|
38
|
-
panel = Panel(f"[bold blue]💡 Use 'ia' to interactively browse all available installers.[/bold blue]\n[bold blue]💡 Use one of the categories: {list(PACKAGE_GROUP2NAMES.keys())}[/bold blue]", title="[yellow]Helpful Tips[/yellow]", border_style="yellow")
|
|
39
|
-
console.print(panel)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def main_with_parser():
|
|
43
|
-
import typer
|
|
44
|
-
app = typer.Typer()
|
|
45
|
-
app.command()(main)
|
|
46
|
-
app()
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def main(
|
|
10
|
+
def main_installer_cli(
|
|
50
11
|
which: Annotated[Optional[str], typer.Argument(..., help="Comma-separated list of program/groups names to install (if --group flag is set).")] = None,
|
|
51
12
|
group: Annotated[bool, typer.Option(..., "--group", "-g", help="Treat 'which' as a group name. A group is bundle of apps.")] = False,
|
|
52
|
-
interactive: Annotated[bool, typer.Option(..., "--interactive", "-
|
|
13
|
+
interactive: Annotated[bool, typer.Option(..., "--interactive", "-i", help="Interactive selection of programs to install.")] = False,
|
|
53
14
|
) -> None:
|
|
54
15
|
if interactive:
|
|
55
16
|
return install_interactively()
|
|
@@ -61,6 +22,10 @@ def main(
|
|
|
61
22
|
return install_clis(clis_names=[x.strip() for x in which.split(",") if x.strip() != ""])
|
|
62
23
|
else:
|
|
63
24
|
if group:
|
|
25
|
+
from rich.console import Console
|
|
26
|
+
from rich.table import Table
|
|
27
|
+
console = Console()
|
|
28
|
+
|
|
64
29
|
typer.echo("❌ You must provide a group name when using the --group/-g option.")
|
|
65
30
|
res = get_group_name_to_repr()
|
|
66
31
|
console.print("[bold blue]Here are the available groups:[/bold blue]")
|
|
@@ -98,20 +63,18 @@ def get_group_name_to_repr() -> dict[str, str]:
|
|
|
98
63
|
def install_interactively():
|
|
99
64
|
from machineconfig.utils.options import choose_from_options
|
|
100
65
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
101
|
-
from machineconfig.utils.
|
|
66
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers
|
|
102
67
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
68
|
+
from rich.console import Console
|
|
69
|
+
from rich.panel import Panel
|
|
70
|
+
# from rich.table import Table
|
|
103
71
|
installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=None)
|
|
104
|
-
installer_options = []
|
|
105
|
-
for x in installers:
|
|
106
|
-
installer_options.append(Installer(installer_data=x).get_description())
|
|
107
|
-
|
|
72
|
+
installer_options = [Installer(installer_data=x).get_description() for x in installers]
|
|
108
73
|
category_display_to_name = get_group_name_to_repr()
|
|
109
|
-
options = list(category_display_to_name.keys()) +
|
|
110
|
-
program_names = choose_from_options(multi=True, msg="Categories are prefixed with 📦", options=options, header="🚀 CHOOSE DEV APP OR CATEGORY",
|
|
74
|
+
options = list(category_display_to_name.keys()) + installer_options
|
|
75
|
+
program_names = choose_from_options(multi=True, msg="Categories are prefixed with 📦", options=options, header="🚀 CHOOSE DEV APP OR CATEGORY", fzf=True)
|
|
111
76
|
installation_messages: list[str] = []
|
|
112
77
|
for _an_idx, a_program_name in enumerate(program_names):
|
|
113
|
-
if a_program_name.startswith("─"): # 50 dashes separator
|
|
114
|
-
continue
|
|
115
78
|
if a_program_name.startswith("📦 "):
|
|
116
79
|
category_name = category_display_to_name.get(a_program_name)
|
|
117
80
|
if category_name:
|
|
@@ -122,59 +85,102 @@ def install_interactively():
|
|
|
122
85
|
status_message = Installer(an_installer_data).install_robust(version=None) # finish the task - this returns a status message, not a command
|
|
123
86
|
installation_messages.append(status_message)
|
|
124
87
|
if installation_messages:
|
|
88
|
+
console = Console()
|
|
89
|
+
|
|
125
90
|
panel = Panel("\n".join([f"[blue]• {message}[/blue]" for message in installation_messages]), title="[bold green]📊 Installation Summary[/bold green]", border_style="green", padding=(1, 2))
|
|
126
91
|
console.print(panel)
|
|
127
92
|
|
|
128
93
|
|
|
129
94
|
def install_group(package_group: str):
|
|
130
|
-
from machineconfig.utils.
|
|
95
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers, install_bulk
|
|
131
96
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
97
|
+
from rich.console import Console
|
|
98
|
+
from rich.panel import Panel
|
|
99
|
+
# from rich.table import Table
|
|
132
100
|
if package_group in PACKAGE_GROUP2NAMES:
|
|
133
101
|
panel = Panel(f"[bold yellow]Installing programs from category: [green]{package_group}[/green][/bold yellow]", title="[bold blue]📦 Category Installation[/bold blue]", border_style="blue", padding=(1, 2))
|
|
102
|
+
console = Console()
|
|
134
103
|
console.print(panel)
|
|
135
104
|
installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=[package_group])
|
|
136
105
|
install_bulk(installers_data=installers_)
|
|
137
106
|
return
|
|
138
|
-
|
|
107
|
+
console = Console()
|
|
108
|
+
console.print(f"❌ ERROR: Unknown package group: {package_group}. Available groups are: {list(PACKAGE_GROUP2NAMES.keys())}")
|
|
109
|
+
def _handle_installer_not_found(search_term: str, all_names: list[str]) -> None: # type: ignore
|
|
110
|
+
"""Handle installer not found with friendly suggestions using fuzzy matching."""
|
|
111
|
+
from difflib import get_close_matches
|
|
112
|
+
from rich.console import Console
|
|
113
|
+
from rich.panel import Panel
|
|
114
|
+
from rich.table import Table
|
|
115
|
+
close_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
|
|
116
|
+
console = Console()
|
|
117
|
+
|
|
118
|
+
console.print(f"\n❌ '[red]{search_term}[/red]' was not found.", style="bold")
|
|
119
|
+
if close_matches:
|
|
120
|
+
console.print("🤔 Did you mean one of these?", style="yellow")
|
|
121
|
+
table = Table(show_header=False, box=None, pad_edge=False)
|
|
122
|
+
for i, match in enumerate(close_matches, 1):
|
|
123
|
+
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{match}[/green]")
|
|
124
|
+
console.print(table)
|
|
125
|
+
else:
|
|
126
|
+
console.print("📋 Here are some available options:", style="blue")
|
|
127
|
+
# Show first 10 installers as examples
|
|
128
|
+
if len(all_names) > 10:
|
|
129
|
+
sample_names = all_names[:10]
|
|
130
|
+
else:
|
|
131
|
+
sample_names = all_names
|
|
132
|
+
table = Table(show_header=False, box=None, pad_edge=False)
|
|
133
|
+
for i, name in enumerate(sample_names, 1):
|
|
134
|
+
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{name}[/green]")
|
|
135
|
+
console.print(table)
|
|
136
|
+
if len(all_names) > 10:
|
|
137
|
+
console.print(f" [dim]... and {len(all_names) - 10} more[/dim]")
|
|
138
|
+
|
|
139
|
+
panel = Panel(f"[bold blue]💡 Use 'ia' to interactively browse all available installers.[/bold blue]\n[bold blue]💡 Use one of the categories: {list(PACKAGE_GROUP2NAMES.keys())}[/bold blue]", title="[yellow]Helpful Tips[/yellow]", border_style="yellow")
|
|
140
|
+
console.print(panel)
|
|
141
|
+
|
|
139
142
|
def install_clis(clis_names: list[str]):
|
|
140
143
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
141
|
-
from machineconfig.utils.
|
|
144
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers
|
|
142
145
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
146
|
+
from rich.console import Console
|
|
147
|
+
all_installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=None)
|
|
143
148
|
total_messages: list[str] = []
|
|
144
|
-
for
|
|
145
|
-
|
|
149
|
+
for a_cli_name in clis_names:
|
|
150
|
+
if "github.com" in a_cli_name.lower():
|
|
151
|
+
from machineconfig.utils.installer_utils.install_from_url import install_from_github_url
|
|
152
|
+
install_from_github_url(github_url=a_cli_name)
|
|
153
|
+
continue
|
|
146
154
|
selected_installer = None
|
|
147
155
|
for installer in all_installers:
|
|
148
156
|
app_name = installer["appName"]
|
|
149
|
-
if app_name.lower() ==
|
|
157
|
+
if app_name.lower() == a_cli_name.lower():
|
|
150
158
|
selected_installer = installer
|
|
151
159
|
break
|
|
152
160
|
if selected_installer is None:
|
|
153
|
-
_handle_installer_not_found(
|
|
161
|
+
_handle_installer_not_found(a_cli_name, all_names=[inst["appName"] for inst in all_installers])
|
|
154
162
|
return None
|
|
155
163
|
message = Installer(selected_installer).install_robust(version=None) # finish the task
|
|
156
164
|
total_messages.append(message)
|
|
157
165
|
if total_messages:
|
|
166
|
+
console = Console()
|
|
158
167
|
console.print("\n[bold green]📊 Installation Results:[/bold green]")
|
|
159
168
|
for a_message in total_messages:
|
|
160
169
|
console.print(f"[blue]• {a_message}[/blue]")
|
|
161
170
|
return None
|
|
162
|
-
|
|
163
|
-
|
|
164
171
|
def install_if_missing(which: str):
|
|
165
|
-
from machineconfig.utils.installer_utils.
|
|
172
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
|
|
166
173
|
exists = check_tool_exists(which)
|
|
167
174
|
if exists:
|
|
168
175
|
print(f"✅ {which} is already installed.")
|
|
169
176
|
return
|
|
170
177
|
print(f"⏳ {which} not found. Installing...")
|
|
171
|
-
from machineconfig.utils.installer_utils.
|
|
172
|
-
|
|
178
|
+
from machineconfig.utils.installer_utils.installer_cli import main_installer_cli
|
|
179
|
+
main_installer_cli(which=which, interactive=False)
|
|
173
180
|
|
|
174
181
|
|
|
175
182
|
if __name__ == "__main__":
|
|
176
183
|
from machineconfig.utils.schemas.installer.installer_types import InstallerData
|
|
177
184
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
178
|
-
|
|
179
185
|
_ = InstallerData, Installer
|
|
180
186
|
pass
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""package manager"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.installer_utils.
|
|
3
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_if_installed_already
|
|
4
4
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
5
5
|
from machineconfig.utils.schemas.installer.installer_types import InstallerData, InstallerDataFiles, get_normalized_arch, get_os_name, OPERATING_SYSTEMS, CPU_ARCHITECTURES
|
|
6
6
|
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
|
|
@@ -132,30 +132,6 @@ def install_bulk(installers_data: list[InstallerData], safe: bool = False, jobs:
|
|
|
132
132
|
print("✅ Version cache cleared")
|
|
133
133
|
if safe:
|
|
134
134
|
pass
|
|
135
|
-
# print("⚠️ Safe installation mode activated...")
|
|
136
|
-
# from machineconfig.jobs.python.check_installations import APP_SUMMARY_PATH
|
|
137
|
-
# if platform.system().lower() == "windows":
|
|
138
|
-
# print("🪟 Moving applications to Windows Apps folder...")
|
|
139
|
-
# # PathExtended.get_env().WindowsPaths().WindowsApps)
|
|
140
|
-
# folder = PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps")
|
|
141
|
-
# apps_dir.search("*").apply(lambda app: app.move(folder=folder))
|
|
142
|
-
# elif platform.system().lower() in ["linux", "darwin"]:
|
|
143
|
-
# system_name = "Linux" if platform.system().lower() == "linux" else "macOS"
|
|
144
|
-
# print(f"🐧 Moving applications to {system_name} bin folder...")
|
|
145
|
-
# if platform.system().lower() == "linux":
|
|
146
|
-
# install_path = LINUX_INSTALL_PATH
|
|
147
|
-
# else: # Darwin/macOS
|
|
148
|
-
# install_path = "/usr/local/bin"
|
|
149
|
-
# Terminal().run(f"sudo mv {apps_dir.as_posix()}/* {install_path}/").capture().print_if_unsuccessful(desc=f"MOVING executable to {install_path}", strict_err=True, strict_returncode=True)
|
|
150
|
-
# else:
|
|
151
|
-
# error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
152
|
-
# print(error_msg)
|
|
153
|
-
# raise NotImplementedError(error_msg)
|
|
154
|
-
|
|
155
|
-
# apps_dir.delete(sure=True)
|
|
156
|
-
# print(f"✅ Safe installation completed\n{'='*80}")
|
|
157
|
-
# return None
|
|
158
|
-
|
|
159
135
|
print(f"🚀 Starting installation of {len(installers_data)} packages...")
|
|
160
136
|
print("📦 INSTALLING FIRST PACKAGE 📦")
|
|
161
137
|
Installer(installers_data[0]).install(version=None)
|
machineconfig/utils/links.py
CHANGED
|
@@ -164,7 +164,7 @@ def symlink_map(config_file_default_path: PathExtended, self_managed_config_file
|
|
|
164
164
|
else:
|
|
165
165
|
# Files are different, use on_conflict strategy
|
|
166
166
|
import subprocess
|
|
167
|
-
command = f"""delta --side-by-side "{config_file_default_path}" "{self_managed_config_file_path}" """
|
|
167
|
+
command = f"""delta --paging never --side-by-side "{config_file_default_path}" "{self_managed_config_file_path}" """
|
|
168
168
|
try:
|
|
169
169
|
console.print(Panel(f"🆘 CONFLICT DETECTED | Showing diff between {config_file_default_path} and {self_managed_config_file_path}", title="Conflict Detected", expand=False))
|
|
170
170
|
subprocess.run(command, shell=True, check=True)
|
|
@@ -293,7 +293,7 @@ def copy_map(config_file_default_path: PathExtended, self_managed_config_file_pa
|
|
|
293
293
|
else:
|
|
294
294
|
# Files are different, use on_conflict strategy
|
|
295
295
|
import subprocess
|
|
296
|
-
command = f"""delta --side-by-side "{config_file_default_path}" "{self_managed_config_file_path}" """
|
|
296
|
+
command = f"""delta --paging never --side-by-side "{config_file_default_path}" "{self_managed_config_file_path}" """
|
|
297
297
|
try:
|
|
298
298
|
console.print(Panel(f"🆘 CONFLICT DETECTED | Showing diff between {config_file_default_path} and {self_managed_config_file_path}", title="Conflict Detected", expand=False))
|
|
299
299
|
subprocess.run(command, shell=True, check=True)
|