machineconfig 7.58__py3-none-any.whl → 7.60__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 +7 -5
- 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 +9 -27
- machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
- machineconfig/jobs/installer/installer_data.json +57 -23
- machineconfig/jobs/installer/package_groups.py +20 -13
- machineconfig/scripts/python/croshell.py +4 -4
- machineconfig/scripts/python/devops.py +2 -3
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
- machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
- machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -72
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +3 -2
- 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 +151 -0
- machineconfig/scripts/python/helpers_utils/path.py +1 -1
- machineconfig/scripts/python/interactive.py +2 -2
- 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 +6 -7
- machineconfig/scripts/python/utils.py +2 -1
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/settings/shells/zsh/init.sh +0 -7
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/installer_class.py +39 -41
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +59 -58
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
- machineconfig/utils/options.py +1 -1
- machineconfig/utils/path_extended.py +2 -2
- machineconfig/utils/path_helper.py +34 -31
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/ssh.py +1 -1
- {machineconfig-7.58.dist-info → machineconfig-7.60.dist-info}/METADATA +1 -1
- {machineconfig-7.58.dist-info → machineconfig-7.60.dist-info}/RECORD +56 -55
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
- {machineconfig-7.58.dist-info → machineconfig-7.60.dist-info}/WHEEL +0 -0
- {machineconfig-7.58.dist-info → machineconfig-7.60.dist-info}/entry_points.txt +0 -0
- {machineconfig-7.58.dist-info → machineconfig-7.60.dist-info}/top_level.txt +0 -0
|
@@ -72,15 +72,9 @@ alias x='. $CONFIG_ROOT/scripts/wrap_mcfg explore'
|
|
|
72
72
|
# gh copilot explain "Input command is: $x The output is this: $y"
|
|
73
73
|
# }
|
|
74
74
|
|
|
75
|
-
# 📦 Node Version Manager
|
|
76
|
-
# export NVM_DIR="$HOME/.nvm"
|
|
77
|
-
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
|
78
|
-
# [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
|
79
|
-
|
|
80
75
|
|
|
81
76
|
# https://github.com/atuinsh/atuin
|
|
82
77
|
# eval "$(atuin init bash)"
|
|
83
|
-
|
|
84
78
|
# source /home/alex/.config/broot/launcher/bash/br
|
|
85
79
|
# eval "$(thefuck --alias)"
|
|
86
80
|
# from https://github.com/ajeetdsouza/zoxide
|
|
@@ -90,7 +84,6 @@ eval "$(starship init zsh)"
|
|
|
90
84
|
# LEVE THIS IN THE END TO AVOID EXECUTION FAILURE OF THE REST OF THE SCRIPT
|
|
91
85
|
# from https://github.com/cantino/mcfly
|
|
92
86
|
eval "$(mcfly init zsh)"
|
|
93
|
-
|
|
94
87
|
# Show elapsed runtime
|
|
95
88
|
# _show_elapsed
|
|
96
89
|
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
. <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/uv.sh")
|
|
3
3
|
. <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/linux/wrap_mcfg")
|
|
4
4
|
|
|
5
|
-
alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
6
|
-
alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
7
|
-
alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
8
|
-
alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
9
|
-
alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
10
|
-
alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
11
|
-
alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
12
|
-
alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
13
|
-
alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
14
|
-
alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
5
|
+
alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" devops'
|
|
6
|
+
alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" cloud'
|
|
7
|
+
alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" agents'
|
|
8
|
+
alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" sessions'
|
|
9
|
+
alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" ftpx'
|
|
10
|
+
alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" fire'
|
|
11
|
+
alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" croshell'
|
|
12
|
+
alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" utils'
|
|
13
|
+
alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" terminal'
|
|
14
|
+
alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.60" msearch'
|
|
15
15
|
|
|
16
16
|
alias d='wrap_in_shell_script devops'
|
|
17
17
|
alias c='wrap_in_shell_script cloud'
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/uv.ps1").Content
|
|
4
4
|
iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/windows/wrap_mcfg.ps1").Content
|
|
5
5
|
|
|
6
|
-
function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
7
|
-
function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
8
|
-
function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
9
|
-
function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
10
|
-
function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
11
|
-
function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
12
|
-
function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
13
|
-
function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
14
|
-
function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
15
|
-
function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
6
|
+
function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" devops $args }
|
|
7
|
+
function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" cloud $args }
|
|
8
|
+
function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" agents $args }
|
|
9
|
+
function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" sessions $args }
|
|
10
|
+
function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" ftpx $args }
|
|
11
|
+
function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" fire $args }
|
|
12
|
+
function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" croshell $args }
|
|
13
|
+
function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" utils $args }
|
|
14
|
+
function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" terminal $args }
|
|
15
|
+
function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.60" msearch $args }
|
|
16
16
|
|
|
17
17
|
function d { wrap_in_shell_script devops @args }
|
|
18
18
|
function c { wrap_in_shell_script cloud @args }
|
|
@@ -25,7 +25,7 @@ def print_header():
|
|
|
25
25
|
table.add_row("Virtual Environment", os.getenv('VIRTUAL_ENV', 'None'))
|
|
26
26
|
table.add_row("Running @", str(Path.cwd()))
|
|
27
27
|
|
|
28
|
-
from machineconfig.utils.
|
|
28
|
+
from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
|
|
29
29
|
|
|
30
30
|
console.print(Panel(table, title=f"[bold blue]✨ 🐊 Machineconfig Shell {get_machineconfig_version()} ✨ Made with 🐍 | Built with ❤️[/bold blue]", border_style="blue"))
|
|
31
31
|
def print_logo(logo: str):
|
|
@@ -44,7 +44,7 @@ def print_logo(logo: str):
|
|
|
44
44
|
_default_art = Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*")))))
|
|
45
45
|
print(_default_art.read_text())
|
|
46
46
|
elif platform.system() in ["Linux", "Darwin"]: # Explicitly handle both Linux and macOS
|
|
47
|
-
from machineconfig.utils.installer_utils.
|
|
47
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import is_executable_in_path
|
|
48
48
|
avail_cowsay = is_executable_in_path("cowsay")
|
|
49
49
|
avail_lolcat = is_executable_in_path("lolcat")
|
|
50
50
|
avail_boxes = is_executable_in_path("boxes")
|
|
@@ -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,24 @@ 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
|
+
print(f"📦 Installing .deb package: {downloaded}")
|
|
16
|
+
assert platform.system() == "Linux"
|
|
17
|
+
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
18
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
19
|
+
if not success:
|
|
20
|
+
desc = "Installing .deb"
|
|
21
|
+
print(f"❌ {desc} failed")
|
|
22
|
+
if result.stdout:
|
|
23
|
+
print(f"STDOUT: {result.stdout}")
|
|
24
|
+
if result.stderr:
|
|
25
|
+
print(f"STDERR: {result.stderr}")
|
|
26
|
+
print(f"Return code: {result.returncode}")
|
|
27
|
+
print("🗑️ Cleaning up .deb package...")
|
|
28
|
+
downloaded.delete(sure=True)
|
|
29
|
+
|
|
30
|
+
|
|
14
31
|
class Installer:
|
|
15
32
|
def __init__(self, installer_data: InstallerData):
|
|
16
33
|
self.installer_data: InstallerData = installer_data
|
|
@@ -42,10 +59,8 @@ class Installer:
|
|
|
42
59
|
result_new = subprocess.run(f"{exe_name} --version", shell=True, capture_output=True, text=True)
|
|
43
60
|
new_version_cli = result_new.stdout.strip()
|
|
44
61
|
if old_version_cli == new_version_cli:
|
|
45
|
-
# print(f"ℹ️ Same version detected: {old_version_cli}")
|
|
46
62
|
return f"""📦️ 😑 {exe_name}, same version: {old_version_cli}"""
|
|
47
63
|
else:
|
|
48
|
-
# print(f"🚀 Update successful: {old_version_cli} ➡️ {new_version_cli}")
|
|
49
64
|
return f"""📦️ 🤩 {exe_name} updated from {old_version_cli} ➡️ TO ➡️ {new_version_cli}"""
|
|
50
65
|
except Exception as ex:
|
|
51
66
|
exe_name = self._get_exe_name()
|
|
@@ -69,8 +84,6 @@ class Installer:
|
|
|
69
84
|
desc = package_manager + " installation"
|
|
70
85
|
version_to_be_installed = package_manager + "Latest"
|
|
71
86
|
result = subprocess.run(installer_arch_os, shell=True, capture_output=True, text=False)
|
|
72
|
-
# from machineconfig.utils.code import run_shell_script
|
|
73
|
-
# result = run_shell_script(installer_arch_os)
|
|
74
87
|
success = result.returncode == 0 and result.stderr == "".encode()
|
|
75
88
|
if not success:
|
|
76
89
|
print(f"❌ {desc} failed")
|
|
@@ -80,7 +93,6 @@ class Installer:
|
|
|
80
93
|
print(f"STDERR: {result.stderr}")
|
|
81
94
|
print(f"Return code: {result.returncode}")
|
|
82
95
|
elif installer_arch_os.endswith((".sh", ".py", ".ps1")):
|
|
83
|
-
# search for the script, see which path ends with the script name
|
|
84
96
|
import machineconfig.jobs.installer as module
|
|
85
97
|
from pathlib import Path
|
|
86
98
|
search_root = Path(module.__file__).parent
|
|
@@ -106,9 +118,14 @@ class Installer:
|
|
|
106
118
|
runpy.run_path(str(installer_path), run_name=None)["main"](self.installer_data, version=version)
|
|
107
119
|
version_to_be_installed = str(version)
|
|
108
120
|
elif installer_arch_os.startswith("https://"): # its a url to be downloaded
|
|
109
|
-
downloaded_object = PathExtended(installer_arch_os).download(folder=INSTALL_TMP_DIR)
|
|
121
|
+
# downloaded_object = PathExtended(installer_arch_os).download(folder=INSTALL_TMP_DIR)
|
|
122
|
+
from machineconfig.scripts.python.helpers_utils.download import download
|
|
123
|
+
downloaded_object = download(installer_arch_os, output_dir=str(INSTALL_TMP_DIR))
|
|
124
|
+
if downloaded_object is None:
|
|
125
|
+
raise ValueError(f"Failed to download from URL: {installer_arch_os}")
|
|
110
126
|
# object is either a zip containing a binary or a straight out binary.
|
|
111
|
-
|
|
127
|
+
downloaded_object = PathExtended(downloaded_object)
|
|
128
|
+
if downloaded_object.suffix in DECOMPRESS_SUPPORTED_FORMATS:
|
|
112
129
|
downloaded_object = downloaded_object.decompress()
|
|
113
130
|
if downloaded_object.suffix in [".exe", ""]: # likely an executable
|
|
114
131
|
if platform.system() == "Windows":
|
|
@@ -131,26 +148,17 @@ class Installer:
|
|
|
131
148
|
print(f"🔄 Renaming to correct name: {new_exe_name}")
|
|
132
149
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
133
150
|
version_to_be_installed = "downloaded_binary"
|
|
151
|
+
elif downloaded_object.suffix in [".deb"]:
|
|
152
|
+
install_deb_package(downloaded_object)
|
|
153
|
+
version_to_be_installed = "downloaded_deb"
|
|
154
|
+
else:
|
|
155
|
+
raise ValueError(f"Downloaded file is not an executable: {downloaded_object}")
|
|
134
156
|
else:
|
|
135
157
|
raise NotImplementedError(f"CMD installation method not implemented for: {installer_arch_os}")
|
|
136
158
|
else:
|
|
137
159
|
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)
|
|
160
|
+
downloaded, version_to_be_installed = self.binary_download(version=version)
|
|
161
|
+
if str(downloaded).endswith(".deb"): install_deb_package(downloaded)
|
|
154
162
|
else:
|
|
155
163
|
if platform.system() == "Windows":
|
|
156
164
|
exe = find_move_delete_windows(downloaded_file_path=downloaded, exe_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
@@ -173,13 +181,13 @@ class Installer:
|
|
|
173
181
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
174
182
|
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
175
183
|
INSTALL_VERSION_ROOT.joinpath(exe_name).write_text(version_to_be_installed or "unknown", encoding="utf-8")
|
|
176
|
-
def
|
|
184
|
+
def binary_download(self, version: Optional[str]) -> tuple[PathExtended, str]:
|
|
177
185
|
exe_name = self._get_exe_name()
|
|
178
186
|
repo_url = self.installer_data["repoURL"]
|
|
179
187
|
# app_name = self.installer_data["appName"]
|
|
180
188
|
download_link: Optional[str] = None
|
|
181
189
|
version_to_be_installed: Optional[str] = None
|
|
182
|
-
if "github" not in repo_url or
|
|
190
|
+
if "github" not in repo_url or (any(ext in repo_url for ext in DECOMPRESS_SUPPORTED_FORMATS)):
|
|
183
191
|
# Direct download URL
|
|
184
192
|
download_link = repo_url
|
|
185
193
|
version_to_be_installed = "predefined_url"
|
|
@@ -202,12 +210,9 @@ class Installer:
|
|
|
202
210
|
downloaded = PathExtended(download_link).download(folder=INSTALL_TMP_DIR).decompress()
|
|
203
211
|
if downloaded.is_dir() and len(downloaded.search("*", r=True)) == 1:
|
|
204
212
|
only_file_in = next(downloaded.glob("*"))
|
|
205
|
-
if only_file_in.is_file() and only_file_in.suffix in
|
|
213
|
+
if only_file_in.is_file() and only_file_in.suffix in DECOMPRESS_SUPPORTED_FORMATS: # further decompress
|
|
206
214
|
downloaded = only_file_in.decompress()
|
|
207
215
|
return downloaded, version_to_be_installed
|
|
208
|
-
|
|
209
|
-
# --------------------------- Arch / template helpers ---------------------------
|
|
210
|
-
|
|
211
216
|
@staticmethod
|
|
212
217
|
def _get_repo_name_from_url(repo_url: str) -> str:
|
|
213
218
|
"""Extract owner/repo from GitHub URL."""
|
|
@@ -222,23 +227,16 @@ class Installer:
|
|
|
222
227
|
def _fetch_github_release_data(repo_name: str, version: Optional[str] = None) -> Optional[dict[str, Any]]:
|
|
223
228
|
"""Fetch release data from GitHub API using requests."""
|
|
224
229
|
import requests
|
|
225
|
-
|
|
226
230
|
try:
|
|
227
|
-
if version and version.lower() != "latest":
|
|
228
|
-
# Fetch specific version
|
|
231
|
+
if version and version.lower() != "latest": # Fetch specific version
|
|
229
232
|
url = f"https://api.github.com/repos/{repo_name}/releases/tags/{version}"
|
|
230
|
-
else:
|
|
231
|
-
# Fetch latest release
|
|
233
|
+
else: # Fetch latest release
|
|
232
234
|
url = f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
233
|
-
|
|
234
235
|
response = requests.get(url, timeout=30)
|
|
235
|
-
|
|
236
236
|
if response.status_code != 200:
|
|
237
237
|
print(f"❌ Failed to fetch data for {repo_name}: HTTP {response.status_code}")
|
|
238
238
|
return None
|
|
239
|
-
|
|
240
239
|
response_data = response.json()
|
|
241
|
-
|
|
242
240
|
# Check if API returned an error
|
|
243
241
|
if "message" in response_data:
|
|
244
242
|
if "API rate limit exceeded" in response_data.get("message", ""):
|
|
@@ -1,55 +1,14 @@
|
|
|
1
1
|
"""Devops Devapps Install"""
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
|
-
from rich.console import Console
|
|
5
|
-
from rich.panel import Panel
|
|
6
|
-
from rich.table import Table
|
|
7
4
|
from typing import Optional, Annotated
|
|
8
5
|
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
|
|
9
6
|
|
|
10
|
-
console = Console()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def _handle_installer_not_found(search_term: str, all_names: list[str]) -> None: # type: ignore
|
|
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
7
|
|
|
49
8
|
def main(
|
|
50
9
|
which: Annotated[Optional[str], typer.Argument(..., help="Comma-separated list of program/groups names to install (if --group flag is set).")] = None,
|
|
51
10
|
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", "-
|
|
11
|
+
interactive: Annotated[bool, typer.Option(..., "--interactive", "-i", help="Interactive selection of programs to install.")] = False,
|
|
53
12
|
) -> None:
|
|
54
13
|
if interactive:
|
|
55
14
|
return install_interactively()
|
|
@@ -61,6 +20,10 @@ def main(
|
|
|
61
20
|
return install_clis(clis_names=[x.strip() for x in which.split(",") if x.strip() != ""])
|
|
62
21
|
else:
|
|
63
22
|
if group:
|
|
23
|
+
from rich.console import Console
|
|
24
|
+
from rich.table import Table
|
|
25
|
+
console = Console()
|
|
26
|
+
|
|
64
27
|
typer.echo("❌ You must provide a group name when using the --group/-g option.")
|
|
65
28
|
res = get_group_name_to_repr()
|
|
66
29
|
console.print("[bold blue]Here are the available groups:[/bold blue]")
|
|
@@ -98,20 +61,18 @@ def get_group_name_to_repr() -> dict[str, str]:
|
|
|
98
61
|
def install_interactively():
|
|
99
62
|
from machineconfig.utils.options import choose_from_options
|
|
100
63
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
101
|
-
from machineconfig.utils.
|
|
64
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers
|
|
102
65
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
66
|
+
from rich.console import Console
|
|
67
|
+
from rich.panel import Panel
|
|
68
|
+
# from rich.table import Table
|
|
103
69
|
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
|
-
|
|
70
|
+
installer_options = [Installer(installer_data=x).get_description() for x in installers]
|
|
108
71
|
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",
|
|
72
|
+
options = list(category_display_to_name.keys()) + installer_options
|
|
73
|
+
program_names = choose_from_options(multi=True, msg="Categories are prefixed with 📦", options=options, header="🚀 CHOOSE DEV APP OR CATEGORY", fzf=True)
|
|
111
74
|
installation_messages: list[str] = []
|
|
112
75
|
for _an_idx, a_program_name in enumerate(program_names):
|
|
113
|
-
if a_program_name.startswith("─"): # 50 dashes separator
|
|
114
|
-
continue
|
|
115
76
|
if a_program_name.startswith("📦 "):
|
|
116
77
|
category_name = category_display_to_name.get(a_program_name)
|
|
117
78
|
if category_name:
|
|
@@ -122,24 +83,65 @@ def install_interactively():
|
|
|
122
83
|
status_message = Installer(an_installer_data).install_robust(version=None) # finish the task - this returns a status message, not a command
|
|
123
84
|
installation_messages.append(status_message)
|
|
124
85
|
if installation_messages:
|
|
86
|
+
console = Console()
|
|
87
|
+
|
|
125
88
|
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
89
|
console.print(panel)
|
|
127
90
|
|
|
128
91
|
|
|
129
92
|
def install_group(package_group: str):
|
|
130
|
-
from machineconfig.utils.
|
|
93
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers, install_bulk
|
|
131
94
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
95
|
+
from rich.console import Console
|
|
96
|
+
from rich.panel import Panel
|
|
97
|
+
# from rich.table import Table
|
|
132
98
|
if package_group in PACKAGE_GROUP2NAMES:
|
|
133
99
|
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))
|
|
100
|
+
console = Console()
|
|
134
101
|
console.print(panel)
|
|
135
102
|
installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=[package_group])
|
|
136
103
|
install_bulk(installers_data=installers_)
|
|
137
104
|
return
|
|
138
|
-
|
|
105
|
+
console = Console()
|
|
106
|
+
console.print(f"❌ ERROR: Unknown package group: {package_group}. Available groups are: {list(PACKAGE_GROUP2NAMES.keys())}")
|
|
107
|
+
def _handle_installer_not_found(search_term: str, all_names: list[str]) -> None: # type: ignore
|
|
108
|
+
"""Handle installer not found with friendly suggestions using fuzzy matching."""
|
|
109
|
+
from difflib import get_close_matches
|
|
110
|
+
from rich.console import Console
|
|
111
|
+
from rich.panel import Panel
|
|
112
|
+
from rich.table import Table
|
|
113
|
+
close_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
|
|
114
|
+
console = Console()
|
|
115
|
+
|
|
116
|
+
console.print(f"\n❌ '[red]{search_term}[/red]' was not found.", style="bold")
|
|
117
|
+
if close_matches:
|
|
118
|
+
console.print("🤔 Did you mean one of these?", style="yellow")
|
|
119
|
+
table = Table(show_header=False, box=None, pad_edge=False)
|
|
120
|
+
for i, match in enumerate(close_matches, 1):
|
|
121
|
+
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{match}[/green]")
|
|
122
|
+
console.print(table)
|
|
123
|
+
else:
|
|
124
|
+
console.print("📋 Here are some available options:", style="blue")
|
|
125
|
+
# Show first 10 installers as examples
|
|
126
|
+
if len(all_names) > 10:
|
|
127
|
+
sample_names = all_names[:10]
|
|
128
|
+
else:
|
|
129
|
+
sample_names = all_names
|
|
130
|
+
table = Table(show_header=False, box=None, pad_edge=False)
|
|
131
|
+
for i, name in enumerate(sample_names, 1):
|
|
132
|
+
table.add_row(f"[cyan]{i}.[/cyan]", f"[green]{name}[/green]")
|
|
133
|
+
console.print(table)
|
|
134
|
+
if len(all_names) > 10:
|
|
135
|
+
console.print(f" [dim]... and {len(all_names) - 10} more[/dim]")
|
|
136
|
+
|
|
137
|
+
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")
|
|
138
|
+
console.print(panel)
|
|
139
|
+
|
|
139
140
|
def install_clis(clis_names: list[str]):
|
|
140
141
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
141
|
-
from machineconfig.utils.
|
|
142
|
+
from machineconfig.utils.installer_utils.installer_runner import get_installers
|
|
142
143
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
144
|
+
from rich.console import Console
|
|
143
145
|
total_messages: list[str] = []
|
|
144
146
|
for a_which in clis_names:
|
|
145
147
|
all_installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=None)
|
|
@@ -155,20 +157,19 @@ def install_clis(clis_names: list[str]):
|
|
|
155
157
|
message = Installer(selected_installer).install_robust(version=None) # finish the task
|
|
156
158
|
total_messages.append(message)
|
|
157
159
|
if total_messages:
|
|
160
|
+
console = Console()
|
|
158
161
|
console.print("\n[bold green]📊 Installation Results:[/bold green]")
|
|
159
162
|
for a_message in total_messages:
|
|
160
163
|
console.print(f"[blue]• {a_message}[/blue]")
|
|
161
164
|
return None
|
|
162
|
-
|
|
163
|
-
|
|
164
165
|
def install_if_missing(which: str):
|
|
165
|
-
from machineconfig.utils.installer_utils.
|
|
166
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
|
|
166
167
|
exists = check_tool_exists(which)
|
|
167
168
|
if exists:
|
|
168
169
|
print(f"✅ {which} is already installed.")
|
|
169
170
|
return
|
|
170
171
|
print(f"⏳ {which} not found. Installing...")
|
|
171
|
-
from machineconfig.utils.installer_utils.
|
|
172
|
+
from machineconfig.utils.installer_utils.installer_cli import main
|
|
172
173
|
main(which=which, interactive=False)
|
|
173
174
|
|
|
174
175
|
|
|
@@ -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/options.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from machineconfig.utils.installer_utils.
|
|
2
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
|
|
3
3
|
from rich.text import Text
|
|
4
4
|
from rich.panel import Panel
|
|
5
5
|
from rich.console import Console
|
|
@@ -16,6 +16,7 @@ OPLike: TypeAlias = Union[str, "PathExtended", Path, None]
|
|
|
16
16
|
PLike: TypeAlias = Union[str, "PathExtended", Path]
|
|
17
17
|
FILE_MODE: TypeAlias = Literal["r", "w", "x", "a"]
|
|
18
18
|
SHUTIL_FORMATS: TypeAlias = Literal["zip", "tar", "gztar", "bztar", "xztar"]
|
|
19
|
+
DECOMPRESS_SUPPORTED_FORMATS = [".tar.gz", ".tgz", ".tar", ".gz", ".tar.bz", ".tbz", ".tar.xz", ".zip", ".7z"]
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def _is_user_admin() -> bool:
|
|
@@ -152,7 +153,6 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
152
153
|
# ======================================= File Editing / Reading ===================================
|
|
153
154
|
def download(self, folder: OPLike = None, name: Optional[str] = None, allow_redirects: bool = True, timeout: Optional[int] = None, params: Any = None) -> "PathExtended":
|
|
154
155
|
import requests
|
|
155
|
-
|
|
156
156
|
response = requests.get(self.as_url_str(), allow_redirects=allow_redirects, timeout=timeout, params=params) # Alternative: from urllib import request; request.urlopen(url).read().decode('utf-8').
|
|
157
157
|
assert response.status_code == 200, f"Download failed with status code {response.status_code}\n{response.text}"
|
|
158
158
|
if name is not None:
|
|
@@ -809,7 +809,7 @@ class PathExtended(type(Path()), Path): # type: ignore # pylint: disable=E0241
|
|
|
809
809
|
path = self
|
|
810
810
|
else:
|
|
811
811
|
try:
|
|
812
|
-
path = self.
|
|
812
|
+
path = PathExtended(self.expanduser().absolute().relative_to(Path.home()))
|
|
813
813
|
except ValueError as ve:
|
|
814
814
|
if strict:
|
|
815
815
|
raise ve
|