machineconfig 7.50__py3-none-any.whl → 8.14__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/remote/cloud_manager.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +23 -11
- machineconfig/cluster/sessions_managers/wt_local_manager.py +22 -19
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +3 -1
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +3 -1
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +2 -2
- machineconfig/jobs/installer/installer_data.json +1185 -165
- machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
- machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
- machineconfig/jobs/installer/package_groups.py +52 -84
- machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
- machineconfig/jobs/installer/{custom → python_scripts}/boxes.py +2 -2
- machineconfig/jobs/installer/{custom_dev → python_scripts}/brave.py +5 -3
- machineconfig/jobs/installer/python_scripts/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/code.py +4 -1
- machineconfig/jobs/installer/{custom_dev → python_scripts}/dubdb_adbc.py +1 -1
- machineconfig/jobs/installer/{custom → python_scripts}/hx.py +16 -12
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerdfont.py +2 -2
- machineconfig/jobs/installer/{custom_dev → python_scripts}/nerfont_windows_helper.py +27 -22
- machineconfig/jobs/installer/python_scripts/sysabc.py +139 -0
- machineconfig/jobs/installer/{custom_dev → python_scripts}/wezterm.py +2 -19
- machineconfig/jobs/installer/{custom_dev → python_scripts}/winget.py +10 -14
- machineconfig/jobs/installer/python_scripts/yazi.py +121 -0
- machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nfs +0 -1
- machineconfig/jobs/scripts/powershell_scripts/mount_ssh.ps1 +13 -0
- machineconfig/jobs/scripts/powershell_scripts/obs.ps1 +4 -0
- machineconfig/jobs/scripts_dynamic/a.py +25 -0
- machineconfig/logger.py +0 -1
- machineconfig/profile/create_helper.py +21 -22
- machineconfig/profile/create_links_export.py +25 -11
- machineconfig/profile/create_shell_profile.py +14 -3
- machineconfig/profile/mapper.toml +8 -6
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +20 -21
- machineconfig/scripts/python/agents.py +74 -50
- machineconfig/scripts/python/ai/initai.py +1 -1
- machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
- machineconfig/scripts/python/ai/{command_runner → scripts}/command_runner.sh +1 -1
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +1 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Thinking-Beast-Mode.chatmode.md → agents/Thinking-Beast-Mode.agent.md} +0 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md → agents/Ultimate-Transparent-Thinking-Beast-Mode.agent.md} +0 -1
- machineconfig/scripts/python/ai/solutions/copilot/{chatmodes/deepResearch.chatmode.md → agents/deepResearch.agent.md} +2 -2
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +5 -5
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +4 -0
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
- machineconfig/scripts/python/ai/solutions/generic.py +1 -1
- machineconfig/scripts/python/ai/{generate_files.py → utils/generate_files.py} +2 -2
- machineconfig/scripts/python/cloud.py +6 -6
- machineconfig/scripts/python/croshell.py +67 -60
- machineconfig/scripts/python/devops.py +41 -21
- machineconfig/scripts/python/devops_navigator.py +0 -4
- machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/fire_jobs.py +95 -67
- machineconfig/scripts/python/ftpx.py +44 -17
- 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_agents/agentic_frameworks/fire_crush.json +1 -1
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +9 -7
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +21 -8
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +0 -12
- machineconfig/scripts/python/helpers_agents/fire_agents_help_launch.py +30 -11
- machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +9 -2
- machineconfig/scripts/python/helpers_agents/privacy/configs/aichat/config.yaml +5 -0
- machineconfig/scripts/python/helpers_agents/privacy/configs/aider/.aider.conf.yml +2 -0
- machineconfig/scripts/python/helpers_agents/privacy/configs/copilot/config.yml +1 -0
- machineconfig/scripts/python/helpers_agents/privacy/configs/crush/crush.json +10 -0
- machineconfig/scripts/python/helpers_agents/privacy/configs/gemini/settings.json +12 -0
- machineconfig/scripts/python/helpers_agents/privacy/privacy.py +109 -0
- machineconfig/scripts/python/helpers_agents/templates/prompt.txt +8 -4
- machineconfig/scripts/python/helpers_agents/templates/template.sh +18 -8
- 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 +3 -3
- machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
- machineconfig/scripts/python/helpers_devops/cli_config.py +19 -25
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +22 -13
- machineconfig/scripts/python/helpers_devops/cli_nw.py +113 -26
- machineconfig/scripts/python/helpers_devops/cli_repos.py +37 -11
- machineconfig/scripts/python/helpers_devops/cli_self.py +95 -42
- 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 → cli_share_terminal.py} +15 -17
- 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_devops/run_script.py +180 -0
- machineconfig/scripts/python/helpers_devops/themes/choose_wezterm_theme.py +1 -1
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -19
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_args_helper.py +1 -0
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +25 -15
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfg +3 -3
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +58 -1
- machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
- machineconfig/scripts/python/helpers_network/address.py +176 -0
- machineconfig/scripts/python/helpers_network/address_switch.py +78 -0
- machineconfig/scripts/python/{nw → helpers_network}/mount_nfs.py +2 -2
- machineconfig/scripts/python/{nw → helpers_network}/mount_ssh.py +1 -1
- machineconfig/scripts/python/{nw/devops_add_identity.py → helpers_network/ssh_add_identity.py} +35 -1
- machineconfig/scripts/python/{nw/devops_add_ssh_key.py → helpers_network/ssh_add_ssh_key.py} +26 -7
- machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_linux.py +7 -7
- machineconfig/scripts/python/{nw → helpers_network}/ssh_debug_windows.py +4 -4
- machineconfig/scripts/python/helpers_repos/clone.py +0 -1
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +13 -5
- machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
- machineconfig/scripts/python/helpers_repos/record.py +2 -1
- machineconfig/scripts/python/helpers_repos/repo_analyzer_1.py +160 -0
- machineconfig/scripts/python/helpers_repos/{count_lines.py → repo_analyzer_2.py} +113 -192
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +19 -13
- machineconfig/scripts/python/helpers_utils/download.py +150 -0
- machineconfig/scripts/python/helpers_utils/pdf.py +96 -0
- machineconfig/scripts/python/helpers_utils/python.py +187 -0
- machineconfig/scripts/python/interactive.py +30 -31
- machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -5
- machineconfig/scripts/python/msearch.py +57 -6
- machineconfig/scripts/python/sessions.py +100 -31
- machineconfig/scripts/python/terminal.py +26 -17
- machineconfig/scripts/python/utils.py +17 -15
- machineconfig/scripts/windows/wrap_mcfg.ps1 +6 -3
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/linters/.ruff.toml +1 -1
- machineconfig/settings/shells/bash/init.sh +29 -2
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +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/wezterm/wezterm.lua +2 -0
- machineconfig/settings/shells/zsh/init.sh +1 -8
- machineconfig/settings/television/cable_unix/alias.toml +8 -0
- machineconfig/settings/television/cable_unix/aws-buckets.toml +14 -0
- machineconfig/settings/television/cable_unix/aws-instances.toml +13 -0
- machineconfig/settings/television/cable_unix/bash-history.toml +8 -0
- machineconfig/settings/television/cable_unix/channels.toml +19 -0
- machineconfig/settings/television/cable_unix/dirs.toml +13 -0
- machineconfig/settings/television/cable_unix/distrobox-list.toml +42 -0
- machineconfig/settings/television/cable_unix/docker-images.toml +13 -0
- machineconfig/settings/television/cable_unix/dotfiles.toml +11 -0
- machineconfig/settings/television/cable_unix/env.toml +17 -0
- machineconfig/settings/television/cable_unix/files.toml +11 -0
- machineconfig/settings/television/cable_unix/fish-history.toml +8 -0
- machineconfig/settings/television/cable_unix/git-branch.toml +11 -0
- machineconfig/settings/television/cable_unix/git-diff.toml +10 -0
- machineconfig/settings/television/cable_unix/git-log.toml +12 -0
- machineconfig/settings/television/cable_unix/git-reflog.toml +12 -0
- machineconfig/settings/television/cable_unix/git-repos.toml +16 -0
- machineconfig/settings/television/cable_unix/guix.toml +20 -0
- machineconfig/settings/television/cable_unix/just-recipes.toml +18 -0
- machineconfig/settings/television/cable_unix/k8s-deployments.toml +36 -0
- machineconfig/settings/television/cable_unix/k8s-pods.toml +50 -0
- machineconfig/settings/television/cable_unix/k8s-services.toml +36 -0
- machineconfig/settings/television/cable_unix/man-pages.toml +24 -0
- machineconfig/settings/television/cable_unix/nu-history.toml +7 -0
- machineconfig/settings/television/cable_unix/procs.toml +20 -0
- machineconfig/settings/television/cable_unix/text.toml +17 -0
- machineconfig/settings/television/cable_unix/tldr.toml +18 -0
- machineconfig/settings/television/cable_unix/zsh-history.toml +9 -0
- machineconfig/settings/television/cable_windows/alias.toml +7 -0
- machineconfig/settings/television/cable_windows/dirs.toml +13 -0
- machineconfig/settings/television/cable_windows/docker-images.toml +13 -0
- machineconfig/settings/television/cable_windows/dotfiles.toml +11 -0
- machineconfig/settings/television/cable_windows/env.toml +17 -0
- machineconfig/settings/television/cable_windows/files.toml +14 -0
- machineconfig/settings/television/cable_windows/git-branch.toml +11 -0
- machineconfig/settings/television/cable_windows/git-diff.toml +10 -0
- machineconfig/settings/television/cable_windows/git-log.toml +11 -0
- machineconfig/settings/television/cable_windows/git-reflog.toml +11 -0
- machineconfig/settings/television/cable_windows/git-repos.toml +15 -0
- machineconfig/settings/television/cable_windows/nu-history.toml +7 -0
- machineconfig/settings/television/cable_windows/pwsh-history.toml +6 -0
- machineconfig/settings/television/cable_windows/text.toml +17 -0
- machineconfig/settings/wt/__init__.py +0 -0
- machineconfig/settings/yazi/init.lua +49 -24
- machineconfig/settings/yazi/keymap_linux.toml +19 -4
- machineconfig/settings/yazi/keymap_windows.toml +0 -1
- machineconfig/settings/yazi/shell/yazi_cd.ps1 +29 -5
- machineconfig/settings/yazi/theme.toml +4 -0
- machineconfig/settings/yazi/yazi_linux.toml +84 -0
- machineconfig/settings/yazi/yazi_windows.toml +58 -0
- machineconfig/settings/zellij/layouts/st.kdl +39 -8
- machineconfig/setup_linux/__init__.py +1 -2
- machineconfig/setup_linux/apps_desktop.sh +8 -27
- machineconfig/setup_linux/web_shortcuts/interactive.sh +12 -10
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +31 -0
- machineconfig/setup_mac/__init__.py +2 -3
- machineconfig/setup_windows/__init__.py +3 -5
- machineconfig/setup_windows/ssh/openssh-server.ps1 +1 -1
- machineconfig/setup_windows/uv.ps1 +8 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +12 -10
- machineconfig/setup_windows/web_shortcuts/live_from_github.ps1 +30 -0
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +17 -0
- machineconfig/utils/accessories.py +7 -4
- machineconfig/utils/code.py +69 -27
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/github_release_bulk.py +156 -119
- machineconfig/utils/installer_utils/install_from_url.py +183 -0
- machineconfig/utils/installer_utils/installer_class.py +43 -100
- machineconfig/utils/installer_utils/installer_cli.py +175 -0
- machineconfig/utils/installer_utils/installer_helper.py +129 -0
- machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +36 -85
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +16 -59
- machineconfig/utils/io.py +0 -1
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/meta.py +30 -16
- machineconfig/utils/options.py +42 -24
- machineconfig/utils/options_tv.py +119 -0
- machineconfig/utils/path_extended.py +42 -20
- machineconfig/utils/path_helper.py +75 -22
- machineconfig/utils/procs.py +1 -1
- machineconfig/utils/scheduler.py +20 -53
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/ssh.py +159 -418
- machineconfig/utils/ssh_utils/abc.py +5 -0
- machineconfig/utils/ssh_utils/copy_from_here.py +111 -0
- machineconfig/utils/ssh_utils/copy_to_here.py +303 -0
- machineconfig/utils/ssh_utils/utils.py +142 -0
- machineconfig/utils/ssh_utils/wsl.py +210 -0
- machineconfig/utils/terminal.py +1 -0
- machineconfig/utils/upgrade_packages.py +6 -1
- machineconfig/utils/ve.py +12 -4
- machineconfig-8.14.dist-info/METADATA +132 -0
- {machineconfig-7.50.dist-info → machineconfig-8.14.dist-info}/RECORD +264 -215
- {machineconfig-7.50.dist-info → machineconfig-8.14.dist-info}/entry_points.txt +2 -4
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
- machineconfig/jobs/installer/powershell_scripts/archive_pygraphviz.ps1 +0 -12
- machineconfig/jobs/installer/powershell_scripts/openssh-server_add_key.ps1 +0 -7
- machineconfig/jobs/installer/powershell_scripts/openssh-server_copy-ssh-id.ps1 +0 -14
- machineconfig/scripts/linux/other/switch_ip +0 -20
- machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
- machineconfig/scripts/python/define.py +0 -31
- machineconfig/scripts/python/explore.py +0 -49
- machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -246
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfag +0 -17
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfrga +0 -21
- machineconfig/scripts/python/helpers_msearch/scripts_linux/skrg +0 -4
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfb.ps1 +0 -3
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfrga.bat +0 -20
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +0 -17
- machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +0 -66
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +0 -13
- machineconfig/settings/lf/linux/exe/fzf_nano.sh +0 -16
- machineconfig/settings/lf/windows/fzf_edit.ps1 +0 -6
- machineconfig/settings/lf/windows/tst.ps1 +0 -1
- machineconfig/settings/yazi/yazi.toml +0 -17
- machineconfig/setup_linux/apps.sh +0 -66
- machineconfig/setup_linux/others/cli_installation.sh +0 -137
- machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh +0 -30
- machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
- machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
- machineconfig/setup_mac/apps.sh +0 -73
- machineconfig/setup_windows/apps.ps1 +0 -62
- machineconfig/setup_windows/others/obs.ps1 +0 -4
- machineconfig/setup_windows/ssh/add_identity.ps1 +0 -11
- machineconfig/utils/installer_utils/installer.py +0 -221
- machineconfig-7.50.dist-info/METADATA +0 -92
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/__init__.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/alacritty.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/bypass_paywall.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/cursor.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/espanso.py +0 -0
- /machineconfig/jobs/installer/{custom → python_scripts}/gh.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/goes.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/lvim.py +0 -0
- /machineconfig/jobs/installer/{custom_dev → python_scripts}/redis.py +0 -0
- /machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/android.sh +0 -0
- /machineconfig/jobs/{installer/linux_scripts → scripts/bash_scripts}/lid.sh +0 -0
- /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_drive +0 -0
- /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_nw_drive +0 -0
- /machineconfig/{scripts/python/nw → jobs/scripts/bash_scripts}/mount_smb +0 -0
- /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_cloud.sh +0 -0
- /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/share_nfs +0 -0
- /machineconfig/{scripts/linux/other → jobs/scripts/bash_scripts}/start_docker +0 -0
- /machineconfig/{scripts → jobs/scripts/powershell_scripts}/Restore-ThunderbirdProfile.ps1 +0 -0
- /machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/docker.ps1 +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nfs.ps1 +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_nw.ps1 +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/mount_smb.ps1 +0 -0
- /machineconfig/{setup_windows/others → jobs/scripts/powershell_scripts}/power_options.ps1 +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_cloud.cmd +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/share_smb.ps1 +0 -0
- /machineconfig/{scripts/windows/mounts → jobs/scripts/powershell_scripts}/unlock_bitlocker.ps1 +0 -0
- /machineconfig/scripts/python/{nw → ai/utils}/__init__.py +0 -0
- /machineconfig/scripts/python/ai/{vscode_tasks.py → utils/vscode_tasks.py} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → scripts/python/helpers_network}/__init__.py +0 -0
- /machineconfig/scripts/python/{nw → helpers_network}/mount_nw_drive.py +0 -0
- /machineconfig/scripts/python/{nw → helpers_network}/onetimeshare.py +0 -0
- /machineconfig/scripts/python/{nw → helpers_network}/wifi_conn.py +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
- {machineconfig-7.50.dist-info → machineconfig-8.14.dist-info}/WHEEL +0 -0
- {machineconfig-7.50.dist-info → machineconfig-8.14.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
|
|
2
|
+
from machineconfig.utils.schemas.installer.installer_types import InstallerData
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from machineconfig.utils.path_extended import DECOMPRESS_SUPPORTED_FORMATS, PathExtended
|
|
5
|
+
from machineconfig.utils.source_of_truth import INSTALL_TMP_DIR
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_group_name_to_repr() -> dict[str, str]:
|
|
9
|
+
# Build category options and maintain a mapping from display text to actual category name
|
|
10
|
+
category_display_to_name: dict[str, str] = {}
|
|
11
|
+
for group_name, group_values in PACKAGE_GROUP2NAMES.items():
|
|
12
|
+
display = f"📦 {group_name:<20}" + " -- " + f"{'|'.join(group_values):<60}"
|
|
13
|
+
category_display_to_name[display] = group_name
|
|
14
|
+
return category_display_to_name
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def handle_installer_not_found(search_term: str, app_apps: list[InstallerData]) -> None: # type: ignore
|
|
18
|
+
"""Handle installer not found with friendly suggestions using fuzzy matching."""
|
|
19
|
+
from difflib import get_close_matches
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
from rich.panel import Panel
|
|
22
|
+
from rich.table import Table
|
|
23
|
+
all_names = sorted([inst["appName"] for inst in app_apps])
|
|
24
|
+
name_to_doc = {inst["appName"]: inst["doc"] for inst in app_apps}
|
|
25
|
+
all_descriptions = {f"{inst['appName']}: {inst['doc']}": inst["appName"] for inst in app_apps}
|
|
26
|
+
|
|
27
|
+
close_name_matches = get_close_matches(search_term, all_names, n=5, cutoff=0.4)
|
|
28
|
+
close_description_matches = get_close_matches(search_term, list(all_descriptions.keys()), n=5, cutoff=0.4)
|
|
29
|
+
|
|
30
|
+
search_lower = search_term.lower()
|
|
31
|
+
substring_matches = [
|
|
32
|
+
inst["appName"]
|
|
33
|
+
for inst in app_apps
|
|
34
|
+
if search_lower in inst["appName"].lower() or search_lower in inst["doc"].lower()
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
ordered_matches: list[str] = list(
|
|
38
|
+
dict.fromkeys(
|
|
39
|
+
close_name_matches
|
|
40
|
+
+ [all_descriptions[desc] for desc in close_description_matches]
|
|
41
|
+
+ substring_matches
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
top_matches = ordered_matches[:10]
|
|
45
|
+
console = Console()
|
|
46
|
+
|
|
47
|
+
console.print(f"\n❌ '[red]{search_term}[/red]' was not found.", style="bold")
|
|
48
|
+
if top_matches:
|
|
49
|
+
console.print("🤔 Did you mean one of these?", style="yellow")
|
|
50
|
+
table = Table(show_header=True, header_style="bold", box=None, pad_edge=False)
|
|
51
|
+
table.add_column("#", justify="right", width=3)
|
|
52
|
+
table.add_column("Installer", style="green")
|
|
53
|
+
table.add_column("Description", style="dim", overflow="fold")
|
|
54
|
+
for i, match in enumerate(top_matches, 1):
|
|
55
|
+
table.add_row(f"[cyan]{i}[/cyan]", match, name_to_doc.get(match, ""))
|
|
56
|
+
console.print(table)
|
|
57
|
+
else:
|
|
58
|
+
console.print("📋 Here are some available options:", style="blue")
|
|
59
|
+
# Show first 10 installers as examples
|
|
60
|
+
if len(all_names) > 10:
|
|
61
|
+
sample_names = all_names[:10]
|
|
62
|
+
else:
|
|
63
|
+
sample_names = all_names
|
|
64
|
+
table = Table(show_header=True, header_style="bold", box=None, pad_edge=False)
|
|
65
|
+
table.add_column("#", justify="right", width=3)
|
|
66
|
+
table.add_column("Installer", style="green")
|
|
67
|
+
table.add_column("Description", style="dim", overflow="fold")
|
|
68
|
+
for i, name in enumerate(sample_names, 1):
|
|
69
|
+
table.add_row(f"[cyan]{i}[/cyan]", name, name_to_doc.get(name, ""))
|
|
70
|
+
console.print(table)
|
|
71
|
+
if len(all_names) > 10:
|
|
72
|
+
console.print(f" [dim]... and {len(all_names) - 10} more[/dim]")
|
|
73
|
+
|
|
74
|
+
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")
|
|
75
|
+
console.print(panel)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def install_deb_package(downloaded: Path) -> None:
|
|
79
|
+
from rich import print as rprint
|
|
80
|
+
from rich.panel import Panel
|
|
81
|
+
print(f"📦 Installing .deb package: {downloaded}")
|
|
82
|
+
import platform
|
|
83
|
+
import subprocess
|
|
84
|
+
assert platform.system() == "Linux"
|
|
85
|
+
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
86
|
+
success = result.returncode == 0 and result.stderr == ""
|
|
87
|
+
if not success:
|
|
88
|
+
from rich.console import Group
|
|
89
|
+
desc = "Installing .deb"
|
|
90
|
+
sub_panels = []
|
|
91
|
+
if result.stdout:
|
|
92
|
+
sub_panels.append(Panel(result.stdout, title="STDOUT", style="blue"))
|
|
93
|
+
if result.stderr:
|
|
94
|
+
sub_panels.append(Panel(result.stderr, title="STDERR", style="red"))
|
|
95
|
+
group_content = Group(f"❌ {desc} failed\nReturn code: {result.returncode}", *sub_panels)
|
|
96
|
+
rprint(Panel(group_content, title=desc, style="red"))
|
|
97
|
+
print("🗑️ Cleaning up .deb package...")
|
|
98
|
+
if downloaded.is_file():
|
|
99
|
+
downloaded.unlink(missing_ok=True)
|
|
100
|
+
elif downloaded.is_dir():
|
|
101
|
+
import shutil
|
|
102
|
+
shutil.rmtree(downloaded, ignore_errors=True)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def download_and_prepare(download_url: str) -> PathExtended:
|
|
106
|
+
# archive_path = PathExtended(download_url).download(folder=INSTALL_TMP_DIR)
|
|
107
|
+
from machineconfig.scripts.python.helpers_utils.download import download
|
|
108
|
+
downloaded_object = download(download_url, output_dir=str(INSTALL_TMP_DIR))
|
|
109
|
+
if downloaded_object is None:
|
|
110
|
+
raise ValueError(f"Failed to download from URL: {download_url}")
|
|
111
|
+
archive_path = PathExtended(downloaded_object)
|
|
112
|
+
extracted_path = archive_path
|
|
113
|
+
if extracted_path.is_file() and any(ext in archive_path.suffixes for ext in DECOMPRESS_SUPPORTED_FORMATS):
|
|
114
|
+
extracted_path = archive_path.decompress()
|
|
115
|
+
# print(f"Decompressed {archive_path} to {extracted_path}")
|
|
116
|
+
archive_path.delete(sure=True)
|
|
117
|
+
if extracted_path.is_dir():
|
|
118
|
+
nested_items = list(extracted_path.glob("*"))
|
|
119
|
+
if len(nested_items) == 1:
|
|
120
|
+
nested_path = PathExtended(nested_items[0])
|
|
121
|
+
if nested_path.is_file() and any(ex in nested_path.suffixes for ex in DECOMPRESS_SUPPORTED_FORMATS):
|
|
122
|
+
extracted_path = nested_path.decompress()
|
|
123
|
+
nested_path.delete(sure=True)
|
|
124
|
+
elif extracted_path.is_dir() and len(extracted_path.search("*", r=True)) == 1:
|
|
125
|
+
only_file_in = next(extracted_path.glob("*"))
|
|
126
|
+
if only_file_in.is_file() and any(ext in str(only_file_in) for ext in DECOMPRESS_SUPPORTED_FORMATS): # further decompress
|
|
127
|
+
extracted_path = only_file_in.decompress()
|
|
128
|
+
only_file_in.delete(sure=True)
|
|
129
|
+
return extracted_path
|
|
@@ -7,23 +7,28 @@ import subprocess
|
|
|
7
7
|
import platform
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def find_move_delete_windows(downloaded_file_path: PathExtended,
|
|
11
|
-
print("🔍 PROCESSING WINDOWS EXECUTABLE 🔍")
|
|
12
|
-
if exe_name is not None and
|
|
13
|
-
|
|
10
|
+
def find_move_delete_windows(downloaded_file_path: PathExtended, tool_name: Optional[str], delete: bool, rename_to: Optional[str]):
|
|
11
|
+
# print("🔍 PROCESSING WINDOWS EXECUTABLE 🔍")
|
|
12
|
+
# if exe_name is not None and len(exe_name.split("+")) > 1:
|
|
13
|
+
# last_result = None
|
|
14
|
+
# for a_binary in [x.strip() for x in exe_name.split("+") if x.strip() != ""]:
|
|
15
|
+
# last_result = find_move_delete_windows(downloaded_file_path=downloaded_file_path, exe_name=a_binary, delete=delete, rename_to=rename_to)
|
|
16
|
+
# return last_result
|
|
17
|
+
if tool_name is not None and ".exe" in tool_name:
|
|
18
|
+
tool_name = tool_name.replace(".exe", "")
|
|
14
19
|
if downloaded_file_path.is_file():
|
|
15
20
|
exe = downloaded_file_path
|
|
16
21
|
print(f"📄 Found direct executable file: {exe}")
|
|
17
22
|
else:
|
|
18
23
|
print(f"🔎 Searching for executable in: {downloaded_file_path}")
|
|
19
|
-
if
|
|
24
|
+
if tool_name is None:
|
|
20
25
|
exe = downloaded_file_path.search("*.exe", r=True)[0]
|
|
21
26
|
print(f"✅ Found executable: {exe}")
|
|
22
27
|
else:
|
|
23
|
-
tmp = downloaded_file_path.search(f"{
|
|
28
|
+
tmp = downloaded_file_path.search(f"{tool_name}.exe", r=True)
|
|
24
29
|
if len(tmp) == 1:
|
|
25
30
|
exe = tmp[0]
|
|
26
|
-
print(f"✅ Found exact match for {
|
|
31
|
+
print(f"✅ Found exact match for {tool_name}.exe: {exe}")
|
|
27
32
|
else:
|
|
28
33
|
search_res = downloaded_file_path.search("*.exe", r=True)
|
|
29
34
|
if len(search_res) == 0:
|
|
@@ -52,7 +57,13 @@ def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optio
|
|
|
52
57
|
return exe_new_location
|
|
53
58
|
|
|
54
59
|
|
|
55
|
-
def find_move_delete_linux(downloaded: PathExtended, tool_name: str, delete:
|
|
60
|
+
def find_move_delete_linux(downloaded: PathExtended, tool_name: Optional[str], delete: bool, rename_to: Optional[str]):
|
|
61
|
+
# if len(tool_name.split("+")) > 1:
|
|
62
|
+
# last_result = None
|
|
63
|
+
# for a_binary in [x.strip() for x in tool_name.split("+") if x.strip() != ""]:
|
|
64
|
+
# last_result = find_move_delete_linux(downloaded=downloaded, tool_name=a_binary, delete=False, rename_to=rename_to)
|
|
65
|
+
# return last_result
|
|
66
|
+
|
|
56
67
|
print("🔍 PROCESSING LINUX EXECUTABLE 🔍")
|
|
57
68
|
if downloaded.is_file():
|
|
58
69
|
exe = downloaded
|
|
@@ -64,16 +75,24 @@ def find_move_delete_linux(downloaded: PathExtended, tool_name: str, delete: Opt
|
|
|
64
75
|
exe = res[0]
|
|
65
76
|
print(f"✅ Found match for pattern '*{tool_name}*': {exe}")
|
|
66
77
|
else:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
exe =
|
|
73
|
-
print(f"✅ Found exact match for '{tool_name}': {exe}")
|
|
74
|
-
else:
|
|
75
|
-
exe = max(exe_search_res, key=lambda x: x.size("kb"))
|
|
78
|
+
if tool_name is None: # no tool name provided, get the largest executable
|
|
79
|
+
search_res = downloaded.search("*", folders=False, files=True, r=True)
|
|
80
|
+
if len(search_res) == 0:
|
|
81
|
+
print(f"❌ ERROR: No search results in `{downloaded}`")
|
|
82
|
+
raise IndexError(f"No executable found in {downloaded}")
|
|
83
|
+
exe = max(search_res, key=lambda x: x.size("kb"))
|
|
76
84
|
print(f"✅ Selected largest executable ({exe.size('kb')} KB): {exe}")
|
|
85
|
+
else:
|
|
86
|
+
exe_search_res = downloaded.search(tool_name, folders=False, r=True)
|
|
87
|
+
if len(exe_search_res) == 0:
|
|
88
|
+
print(f"❌ ERROR: No search results for `{tool_name}` in `{downloaded}`")
|
|
89
|
+
raise IndexError(f"No executable found in {downloaded}")
|
|
90
|
+
elif len(exe_search_res) == 1:
|
|
91
|
+
exe = exe_search_res[0]
|
|
92
|
+
print(f"✅ Found exact match for '{tool_name}': {exe}")
|
|
93
|
+
else:
|
|
94
|
+
exe = max(exe_search_res, key=lambda x: x.size("kb"))
|
|
95
|
+
print(f"✅ Selected largest executable ({exe.size('kb')} KB): {exe}")
|
|
77
96
|
|
|
78
97
|
if rename_to and exe.name != rename_to:
|
|
79
98
|
print(f"🏷️ Renaming '{exe.name}' to '{rename_to}'")
|
|
@@ -191,71 +210,3 @@ def check_if_installed_already(exe_name: str, version: Optional[str], use_cache:
|
|
|
191
210
|
return ("⚠️ NotInstalled", "None", version or "unknown")
|
|
192
211
|
|
|
193
212
|
|
|
194
|
-
def parse_apps_installer_linux(txt: str) -> dict[str, tuple[str, str]]:
|
|
195
|
-
"""Parse Linux shell installation scripts into logical chunks.
|
|
196
|
-
|
|
197
|
-
Splits scripts by # --GROUP:<name>:<description> comment signatures into a dictionary
|
|
198
|
-
mapping block names to (description, shell script content) tuples.
|
|
199
|
-
|
|
200
|
-
Returns:
|
|
201
|
-
dict[str, tuple[str, str]]: Dictionary mapping block/section names to (description, installation_script) tuples
|
|
202
|
-
"""
|
|
203
|
-
chunks = txt.split('# --GROUP:')
|
|
204
|
-
res: dict[str, tuple[str, str]] = {}
|
|
205
|
-
|
|
206
|
-
for chunk in chunks[1:]: # Skip first empty chunk before first group
|
|
207
|
-
lines = chunk.split('\n')
|
|
208
|
-
# First line contains the group name and description in format "NAME:DESCRIPTION"
|
|
209
|
-
group_line = lines[0].strip()
|
|
210
|
-
|
|
211
|
-
# Extract group name and description
|
|
212
|
-
if ':' in group_line:
|
|
213
|
-
parts = group_line.split(':', 1) # Split only on first colon
|
|
214
|
-
group_name = parts[0].strip()
|
|
215
|
-
group_description = parts[1].strip() if len(parts) > 1 else ""
|
|
216
|
-
else:
|
|
217
|
-
group_name = group_line
|
|
218
|
-
group_description = ""
|
|
219
|
-
|
|
220
|
-
# Rest is the content
|
|
221
|
-
content = '\n'.join(lines[1:]).strip()
|
|
222
|
-
|
|
223
|
-
if group_name and content:
|
|
224
|
-
res[group_name] = (group_description, content)
|
|
225
|
-
|
|
226
|
-
return res
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def parse_apps_installer_windows(txt: str) -> dict[str, tuple[str, str]]:
|
|
230
|
-
"""Parse Windows PowerShell installation scripts into logical chunks.
|
|
231
|
-
|
|
232
|
-
Splits scripts by # --GROUP:<name>:<description> comment signatures into a dictionary
|
|
233
|
-
mapping block names to (description, PowerShell script content) tuples.
|
|
234
|
-
|
|
235
|
-
Returns:
|
|
236
|
-
dict[str, tuple[str, str]]: Dictionary mapping block/section names to (description, installation_script) tuples
|
|
237
|
-
"""
|
|
238
|
-
chunks = txt.split('# --GROUP:')
|
|
239
|
-
res: dict[str, tuple[str, str]] = {}
|
|
240
|
-
|
|
241
|
-
for chunk in chunks[1:]: # Skip first chunk before first group
|
|
242
|
-
lines = chunk.split('\n')
|
|
243
|
-
# First line contains the group name and description in format "NAME:DESCRIPTION"
|
|
244
|
-
group_line = lines[0].strip()
|
|
245
|
-
|
|
246
|
-
# Extract group name and description
|
|
247
|
-
if ':' in group_line:
|
|
248
|
-
parts = group_line.split(':', 1) # Split only on first colon
|
|
249
|
-
group_name = parts[0].strip()
|
|
250
|
-
group_description = parts[1].strip() if len(parts) > 1 else ""
|
|
251
|
-
else:
|
|
252
|
-
group_name = group_line
|
|
253
|
-
group_description = ""
|
|
254
|
-
|
|
255
|
-
# Rest is the content
|
|
256
|
-
content = '\n'.join(lines[1:]).strip()
|
|
257
|
-
|
|
258
|
-
if group_name and content:
|
|
259
|
-
res[group_name] = (group_description, content)
|
|
260
|
-
|
|
261
|
-
return res
|
|
@@ -1,9 +1,9 @@
|
|
|
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
|
-
from machineconfig.jobs.installer.package_groups import
|
|
6
|
+
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUP2NAMES
|
|
7
7
|
from machineconfig.utils.path_extended import PathExtended
|
|
8
8
|
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT, LINUX_INSTALL_PATH
|
|
9
9
|
from machineconfig.utils.io import read_json
|
|
@@ -18,7 +18,7 @@ from joblib import Parallel, delayed
|
|
|
18
18
|
def check_latest():
|
|
19
19
|
console = Console() # Added console initialization
|
|
20
20
|
console.print(Panel("🔍 CHECKING FOR LATEST VERSIONS", title="Status", expand=False)) # Replaced print with Panel
|
|
21
|
-
installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["
|
|
21
|
+
installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=["termabc"])
|
|
22
22
|
installers_github = []
|
|
23
23
|
for inst__ in installers:
|
|
24
24
|
app_name = inst__["appName"]
|
|
@@ -91,8 +91,12 @@ def get_installed_cli_apps():
|
|
|
91
91
|
return apps
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: Optional[list[
|
|
95
|
-
|
|
94
|
+
def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: Optional[list[str]]) -> list[InstallerData]:
|
|
95
|
+
import machineconfig.jobs.installer as module
|
|
96
|
+
from pathlib import Path
|
|
97
|
+
res_raw: InstallerDataFiles = read_json(Path(module.__file__).parent.joinpath("installer_data.json"))
|
|
98
|
+
res_all: list[InstallerData] = res_raw["installers"]
|
|
99
|
+
|
|
96
100
|
acceptable_apps_names: list[str] | None = None
|
|
97
101
|
if which_cats is not None:
|
|
98
102
|
acceptable_apps_names = []
|
|
@@ -105,40 +109,17 @@ def get_installers(os: OPERATING_SYSTEMS, arch: CPU_ARCHITECTURES, which_cats: O
|
|
|
105
109
|
if acceptable_apps_names is not None:
|
|
106
110
|
if installer_data["appName"] not in acceptable_apps_names:
|
|
107
111
|
continue
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
try:
|
|
113
|
+
if installer_data["fileNamePattern"][arch][os] is None:
|
|
114
|
+
continue
|
|
115
|
+
except KeyError as ke:
|
|
116
|
+
print(f"❌ ERROR: Missing key in installer data: {ke}")
|
|
117
|
+
print(f"Installer data: {installer_data}")
|
|
118
|
+
raise KeyError(f"Missing key in installer data: {ke}")
|
|
110
119
|
all_installers.append(installer_data)
|
|
111
120
|
return all_installers
|
|
112
121
|
|
|
113
122
|
|
|
114
|
-
def get_all_installer_data_files() -> list[InstallerData]:
|
|
115
|
-
import machineconfig.jobs.installer as module
|
|
116
|
-
from pathlib import Path
|
|
117
|
-
res_raw: InstallerDataFiles = read_json(Path(module.__file__).parent.joinpath("installer_data.json"))
|
|
118
|
-
res_final: list[InstallerData] = res_raw["installers"]
|
|
119
|
-
return res_final
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def dynamically_extract_installers_system_groups_from_scripts():
|
|
123
|
-
res_final: list[InstallerData] = []
|
|
124
|
-
from platform import system
|
|
125
|
-
if system() == "Windows":
|
|
126
|
-
from machineconfig.setup_windows import APPS
|
|
127
|
-
options_system = parse_apps_installer_windows(APPS.read_text(encoding="utf-8"))
|
|
128
|
-
elif system() == "Linux":
|
|
129
|
-
from machineconfig.setup_linux import APPS
|
|
130
|
-
options_system = parse_apps_installer_linux(APPS.read_text(encoding="utf-8"))
|
|
131
|
-
elif system() == "Darwin":
|
|
132
|
-
from machineconfig.setup_mac import APPS
|
|
133
|
-
options_system = parse_apps_installer_linux(APPS.read_text(encoding="utf-8"))
|
|
134
|
-
else:
|
|
135
|
-
raise NotImplementedError(f"❌ System {system()} not supported")
|
|
136
|
-
os_name = get_os_name()
|
|
137
|
-
for group_name, (docs, script) in options_system.items():
|
|
138
|
-
item: InstallerData = {"appName": group_name, "doc": docs, "repoURL": "CMD", "fileNamePattern": {"amd64": {os_name: script}, "arm64": {os_name: script}}}
|
|
139
|
-
res_final.append(item)
|
|
140
|
-
return res_final
|
|
141
|
-
|
|
142
123
|
|
|
143
124
|
def install_bulk(installers_data: list[InstallerData], safe: bool = False, jobs: int = 10, fresh: bool = False):
|
|
144
125
|
print("🚀 BULK INSTALLATION PROCESS 🚀")
|
|
@@ -148,30 +129,6 @@ def install_bulk(installers_data: list[InstallerData], safe: bool = False, jobs:
|
|
|
148
129
|
print("✅ Version cache cleared")
|
|
149
130
|
if safe:
|
|
150
131
|
pass
|
|
151
|
-
# print("⚠️ Safe installation mode activated...")
|
|
152
|
-
# from machineconfig.jobs.python.check_installations import APP_SUMMARY_PATH
|
|
153
|
-
# if platform.system().lower() == "windows":
|
|
154
|
-
# print("🪟 Moving applications to Windows Apps folder...")
|
|
155
|
-
# # PathExtended.get_env().WindowsPaths().WindowsApps)
|
|
156
|
-
# folder = PathExtended.home().joinpath("AppData/Local/Microsoft/WindowsApps")
|
|
157
|
-
# apps_dir.search("*").apply(lambda app: app.move(folder=folder))
|
|
158
|
-
# elif platform.system().lower() in ["linux", "darwin"]:
|
|
159
|
-
# system_name = "Linux" if platform.system().lower() == "linux" else "macOS"
|
|
160
|
-
# print(f"🐧 Moving applications to {system_name} bin folder...")
|
|
161
|
-
# if platform.system().lower() == "linux":
|
|
162
|
-
# install_path = LINUX_INSTALL_PATH
|
|
163
|
-
# else: # Darwin/macOS
|
|
164
|
-
# install_path = "/usr/local/bin"
|
|
165
|
-
# 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)
|
|
166
|
-
# else:
|
|
167
|
-
# error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
168
|
-
# print(error_msg)
|
|
169
|
-
# raise NotImplementedError(error_msg)
|
|
170
|
-
|
|
171
|
-
# apps_dir.delete(sure=True)
|
|
172
|
-
# print(f"✅ Safe installation completed\n{'='*80}")
|
|
173
|
-
# return None
|
|
174
|
-
|
|
175
132
|
print(f"🚀 Starting installation of {len(installers_data)} packages...")
|
|
176
133
|
print("📦 INSTALLING FIRST PACKAGE 📦")
|
|
177
134
|
Installer(installers_data[0]).install(version=None)
|
machineconfig/utils/io.py
CHANGED
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)
|
machineconfig/utils/meta.py
CHANGED
|
@@ -29,7 +29,8 @@ except (ImportError, ModuleNotFoundError) as ex:
|
|
|
29
29
|
return txt
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def lambda_to_python_script(lmb: Callable[[], Any],
|
|
32
|
+
def lambda_to_python_script(lmb: Callable[[], Any],
|
|
33
|
+
in_global: bool, import_module: bool) -> str:
|
|
33
34
|
"""
|
|
34
35
|
caveats: always use keyword arguments in the lambda call for best results.
|
|
35
36
|
return statement not allowed in the wrapped function (otherwise it can be put in the global space)
|
|
@@ -55,6 +56,16 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
|
|
|
55
56
|
import types as _types
|
|
56
57
|
from pathlib import Path as _Path
|
|
57
58
|
|
|
59
|
+
def _stringify_annotation(annotation: Any) -> Any:
|
|
60
|
+
if annotation is _inspect.Signature.empty or annotation is _inspect.Parameter.empty:
|
|
61
|
+
return annotation
|
|
62
|
+
if isinstance(annotation, str):
|
|
63
|
+
return annotation
|
|
64
|
+
try:
|
|
65
|
+
return _inspect.formatannotation(annotation)
|
|
66
|
+
except Exception:
|
|
67
|
+
return str(annotation)
|
|
68
|
+
|
|
58
69
|
# sanity checks
|
|
59
70
|
if not (callable(lmb) and isinstance(lmb, _types.LambdaType)):
|
|
60
71
|
raise TypeError("Expected a lambda function object")
|
|
@@ -174,16 +185,18 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
|
|
|
174
185
|
else:
|
|
175
186
|
new_default = param.default
|
|
176
187
|
|
|
177
|
-
|
|
188
|
+
normalized_annotation = _stringify_annotation(param.annotation)
|
|
189
|
+
|
|
178
190
|
if new_default is _inspect.Parameter.empty:
|
|
179
|
-
new_param = _inspect.Parameter(name, param.kind, annotation=
|
|
191
|
+
new_param = _inspect.Parameter(name, param.kind, annotation=normalized_annotation)
|
|
180
192
|
else:
|
|
181
193
|
new_param = _inspect.Parameter(
|
|
182
|
-
name, param.kind, default=new_default, annotation=
|
|
194
|
+
name, param.kind, default=new_default, annotation=normalized_annotation
|
|
183
195
|
)
|
|
184
196
|
new_params.append(new_param)
|
|
185
197
|
|
|
186
|
-
|
|
198
|
+
return_annotation = _stringify_annotation(sig.return_annotation)
|
|
199
|
+
new_sig = _inspect.Signature(parameters=new_params, return_annotation=return_annotation)
|
|
187
200
|
|
|
188
201
|
# If in_global mode, return kwargs as global assignments + dedented body
|
|
189
202
|
if in_global:
|
|
@@ -200,15 +213,11 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
|
|
|
200
213
|
|
|
201
214
|
# Build type annotation string if available
|
|
202
215
|
if param.annotation is not _inspect.Parameter.empty:
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
type_str = str(param.annotation)
|
|
209
|
-
except Exception:
|
|
210
|
-
type_str = str(param.annotation)
|
|
211
|
-
global_assignments.append(f"{name}: {type_str} = {repr(value)}")
|
|
216
|
+
annotation_literal = _stringify_annotation(param.annotation)
|
|
217
|
+
if isinstance(annotation_literal, str):
|
|
218
|
+
global_assignments.append(f"{name}: {repr(annotation_literal)} = {repr(value)}")
|
|
219
|
+
else:
|
|
220
|
+
global_assignments.append(f"{name} = {repr(value)}")
|
|
212
221
|
else:
|
|
213
222
|
global_assignments.append(f"{name} = {repr(value)}")
|
|
214
223
|
|
|
@@ -233,10 +242,15 @@ def lambda_to_python_script(lmb: Callable[[], Any], in_global: bool, import_modu
|
|
|
233
242
|
|
|
234
243
|
if "Optional" in result_text or "Any" in result_text or "Union" in result_text or "Literal" in result_text:
|
|
235
244
|
result_text = "from typing import Optional, Any, Union, Literal\n\n" + result_text
|
|
236
|
-
|
|
237
245
|
if import_prefix:
|
|
238
246
|
result_text = f"{import_prefix}{result_text}"
|
|
239
247
|
return result_text
|
|
240
248
|
|
|
241
249
|
if __name__ == "__main__":
|
|
242
|
-
|
|
250
|
+
from machineconfig.utils.code import print_code
|
|
251
|
+
import_code_robust = "<import_code_robust>"
|
|
252
|
+
res = lambda_to_python_script(
|
|
253
|
+
lambda: print_code(code=import_code_robust, lexer="python", desc="import as module code"),
|
|
254
|
+
in_global=True, import_module=False
|
|
255
|
+
)
|
|
256
|
+
print(res)
|
machineconfig/utils/options.py
CHANGED
|
@@ -1,36 +1,54 @@
|
|
|
1
|
+
|
|
1
2
|
from pathlib import Path
|
|
2
|
-
from machineconfig.utils.installer_utils.installer_abc 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
|
|
6
6
|
import subprocess
|
|
7
7
|
from typing import Optional, Union, Iterable, overload, Literal, cast
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
# def strip_ansi_codes(text: str) -> str:
|
|
11
|
-
# """Remove ANSI color codes from text."""
|
|
12
|
-
# import re
|
|
13
|
-
# return re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', text)
|
|
14
|
-
|
|
15
|
-
|
|
16
9
|
@overload
|
|
17
|
-
def choose_from_options[T](
|
|
10
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[False], custom_input: bool = False, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False) -> T: ...
|
|
18
11
|
@overload
|
|
19
|
-
def choose_from_options[T](
|
|
20
|
-
def choose_from_options[T](
|
|
12
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: Literal[True], custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, ) -> list[T]: ...
|
|
13
|
+
def choose_from_options[T](options: Iterable[T], msg: str, multi: bool, custom_input: bool = True, header: str = "", tail: str = "", prompt: str = "", default: Optional[T] = None, tv: bool = False, ) -> Union[T, list[T]]:
|
|
21
14
|
# TODO: replace with https://github.com/tmbo/questionary
|
|
22
15
|
# # also see https://github.com/charmbracelet/gum
|
|
23
16
|
options_strings: list[str] = [str(x) for x in options]
|
|
24
17
|
default_string = str(default) if default is not None else None
|
|
25
18
|
console = Console()
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
#
|
|
19
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import check_tool_exists
|
|
20
|
+
# from machineconfig.utils.installer_utils.installer_cli import check_tool_exists
|
|
21
|
+
# print("ch1")
|
|
22
|
+
if tv and check_tool_exists("tv"):
|
|
23
|
+
# from pyfzf.pyfzf import FzfPrompt
|
|
24
|
+
# fzf_prompt = FzfPrompt()
|
|
25
|
+
# nl = "\n"
|
|
26
|
+
# choice_string_multi: list[str] = fzf_prompt.prompt(choices=options_strings, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" --ansi') # --border-label={msg.replace(nl, ' ')}")
|
|
27
|
+
# print("ch2")
|
|
28
|
+
from machineconfig.utils.accessories import randstr
|
|
29
|
+
options_txt_path = Path.home().joinpath("tmp_results/tmp_files/choices_" + randstr(6) + ".txt")
|
|
30
|
+
options_txt_path.parent.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
options_txt_path.write_text("\n".join(options_strings), encoding="utf-8")
|
|
32
|
+
|
|
33
|
+
# Run `tv` interactively so the user can make selections. We redirect tv's
|
|
34
|
+
# stdout to a temporary output file so we can read the chosen lines after
|
|
35
|
+
# the interactive session completes. Do not capture_output or redirect
|
|
36
|
+
# stdin/stderr here so `tv` stays attached to the terminal.
|
|
37
|
+
tv_out_path = options_txt_path.with_name(options_txt_path.stem + "_out.txt")
|
|
38
|
+
tv_cmd = f"""cat {options_txt_path} | tv --preview-command "bat -n --color=always '{{}}'" --preview-size 30 --ansi true --source-output "{{strip_ansi}}" > {tv_out_path} """
|
|
39
|
+
res = subprocess.run(tv_cmd, shell=True)
|
|
40
|
+
|
|
41
|
+
# If tv returned a non-zero code and there is no output file, treat it as an error.
|
|
42
|
+
if res.returncode != 0 and not tv_out_path.exists():
|
|
43
|
+
raise RuntimeError(f"Got error running tv command: {tv_cmd}\nreturncode: {res.returncode}")
|
|
44
|
+
|
|
45
|
+
# Read selections (if any) from the output file created by tv.
|
|
46
|
+
out_text = tv_out_path.read_text(encoding="utf-8") if tv_out_path.exists() else ""
|
|
47
|
+
choice_string_multi = [x for x in out_text.splitlines() if x.strip() != ""]
|
|
48
|
+
|
|
49
|
+
# Cleanup temporary files
|
|
50
|
+
options_txt_path.unlink(missing_ok=True)
|
|
51
|
+
tv_out_path.unlink(missing_ok=True)
|
|
34
52
|
if not multi:
|
|
35
53
|
try:
|
|
36
54
|
choice_one_string = choice_string_multi[0]
|
|
@@ -65,7 +83,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
|
|
|
65
83
|
if choice_string == "":
|
|
66
84
|
if default_string is None:
|
|
67
85
|
console.print(Panel("🧨 Default option not available!", title="Error", expand=False))
|
|
68
|
-
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default,
|
|
86
|
+
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
|
|
69
87
|
choice_idx = options_strings.index(default_string)
|
|
70
88
|
assert default is not None, "🧨 Default option not available!"
|
|
71
89
|
choice_one: T = default
|
|
@@ -83,7 +101,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
|
|
|
83
101
|
_ = ie
|
|
84
102
|
# raise ValueError(f"Unknown choice. {choice_string}") from ie
|
|
85
103
|
console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
|
|
86
|
-
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default,
|
|
104
|
+
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
|
|
87
105
|
except (TypeError, ValueError) as te: # int(choice_string) failed due to # either the number is invalid, or the input is custom.
|
|
88
106
|
if choice_string in options_strings: # string input
|
|
89
107
|
choice_idx = options_strings.index(choice_one) # type: ignore
|
|
@@ -94,7 +112,7 @@ def choose_from_options[T](msg: str, options: Iterable[T], multi: bool, custom_i
|
|
|
94
112
|
_ = te
|
|
95
113
|
# raise ValueError(f"Unknown choice. {choice_string}") from te
|
|
96
114
|
console.print(Panel(f"❓ Unknown choice: '{choice_string}'", title="Error", expand=False))
|
|
97
|
-
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default,
|
|
115
|
+
return choose_from_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, tv=tv, multi=multi, custom_input=custom_input)
|
|
98
116
|
console.print(Panel(f"✅ Selected option {choice_idx}: {choice_one}", title="Selected", expand=False))
|
|
99
117
|
if multi:
|
|
100
118
|
return [choice_one]
|
|
@@ -113,7 +131,7 @@ def choose_cloud_interactively() -> str:
|
|
|
113
131
|
raise ValueError(f"Got {tmp} from rclone listremotes")
|
|
114
132
|
if len(remotes) == 0:
|
|
115
133
|
raise RuntimeError("You don't have remotes. Configure your rclone first to get cloud services access.")
|
|
116
|
-
cloud: str = choose_from_options(msg="WHICH CLOUD?", multi=False, options=list(remotes), default=remotes[0],
|
|
134
|
+
cloud: str = choose_from_options(msg="WHICH CLOUD?", multi=False, options=list(remotes), default=remotes[0], tv=True)
|
|
117
135
|
console.print(Panel(f"✅ SELECTED CLOUD | {cloud}", border_style="bold blue", expand=False))
|
|
118
136
|
return cloud
|
|
119
137
|
|
|
@@ -131,4 +149,4 @@ def choose_ssh_host(multi: Literal[False]) -> str: ...
|
|
|
131
149
|
@overload
|
|
132
150
|
def choose_ssh_host(multi: Literal[True]) -> list[str]: ...
|
|
133
151
|
def choose_ssh_host(multi: bool):
|
|
134
|
-
return choose_from_options(msg="", options=get_ssh_hosts(), multi=multi,
|
|
152
|
+
return choose_from_options(msg="", options=get_ssh_hosts(), multi=multi, tv=True)
|