machineconfig 5.15__py3-none-any.whl → 7.98__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.
- machineconfig/__init__.py +0 -28
- machineconfig/cluster/remote/cloud_manager.py +1 -1
- machineconfig/cluster/remote/distribute.py +0 -1
- machineconfig/cluster/remote/file_manager.py +0 -2
- machineconfig/cluster/remote/script_execution.py +0 -1
- machineconfig/cluster/sessions_managers/{utils → helpers}/enhanced_command_runner.py +4 -6
- machineconfig/cluster/sessions_managers/utils/load_balancer.py +1 -1
- machineconfig/cluster/sessions_managers/utils/maker.py +69 -0
- machineconfig/cluster/sessions_managers/wt_local.py +114 -289
- machineconfig/cluster/sessions_managers/wt_local_manager.py +70 -210
- machineconfig/cluster/sessions_managers/wt_remote.py +51 -43
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +52 -198
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +6 -19
- machineconfig/cluster/sessions_managers/wt_utils/manager_persistence.py +52 -0
- machineconfig/cluster/sessions_managers/wt_utils/monitoring_helpers.py +50 -0
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +4 -2
- machineconfig/cluster/sessions_managers/wt_utils/status_reporting.py +76 -0
- machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +81 -375
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +25 -170
- machineconfig/cluster/sessions_managers/zellij_remote.py +40 -41
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +16 -12
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +4 -8
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +5 -20
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +3 -9
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +3 -1
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper.py +298 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_restart.py +77 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_helper_with_panes.py +228 -0
- machineconfig/cluster/sessions_managers/zellij_utils/zellij_local_manager_helper.py +165 -0
- machineconfig/jobs/{python → installer}/check_installations.py +2 -3
- machineconfig/jobs/installer/custom/boxes.py +61 -0
- machineconfig/jobs/installer/custom/hx.py +76 -19
- machineconfig/jobs/installer/custom/yazi.py +119 -0
- machineconfig/jobs/installer/custom_dev/alacritty.py +4 -4
- machineconfig/jobs/installer/custom_dev/brave.py +5 -9
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/code.py +4 -1
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +30 -0
- machineconfig/jobs/installer/custom_dev/nerdfont.py +1 -1
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +33 -28
- machineconfig/jobs/installer/custom_dev/sysabc.py +139 -0
- machineconfig/jobs/installer/custom_dev/wezterm.py +2 -19
- machineconfig/jobs/installer/custom_dev/winget.py +10 -14
- machineconfig/jobs/installer/installer_data.json +1487 -229
- machineconfig/jobs/installer/linux_scripts/brave.sh +4 -14
- machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +5 -17
- machineconfig/jobs/installer/linux_scripts/docker.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/docker_start.sh +6 -14
- machineconfig/jobs/installer/linux_scripts/edge.sh +3 -11
- machineconfig/jobs/{linux/msc → installer/linux_scripts}/lid.sh +2 -8
- machineconfig/jobs/installer/linux_scripts/nerdfont.sh +5 -17
- machineconfig/jobs/{linux/msc → installer/linux_scripts}/network.sh +2 -8
- machineconfig/jobs/installer/linux_scripts/q.sh +10 -6
- machineconfig/jobs/installer/linux_scripts/redis.sh +6 -17
- machineconfig/jobs/installer/linux_scripts/vscode.sh +5 -17
- machineconfig/jobs/installer/linux_scripts/wezterm.sh +4 -12
- machineconfig/jobs/installer/package_groups.py +106 -177
- machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
- machineconfig/logger.py +0 -1
- machineconfig/profile/backup.toml +49 -0
- machineconfig/profile/bash_shell_profiles.md +11 -0
- machineconfig/profile/create_helper.py +62 -0
- machineconfig/profile/create_links.py +288 -0
- machineconfig/profile/create_links_export.py +100 -0
- machineconfig/profile/create_shell_profile.py +147 -0
- machineconfig/profile/mapper.toml +263 -0
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/{share_cloud.sh → other/share_cloud.sh} +14 -25
- machineconfig/scripts/linux/wrap_mcfg +46 -0
- machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
- machineconfig/scripts/python/agents.py +123 -117
- machineconfig/scripts/python/ai/initai.py +3 -28
- machineconfig/scripts/python/ai/scripts/command_runner.ps1 +33 -0
- machineconfig/scripts/python/ai/scripts/command_runner.sh +9 -0
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.ps1 +17 -18
- machineconfig/scripts/python/ai/scripts/lint_and_type_check.sh +17 -18
- machineconfig/scripts/python/ai/solutions/_shared.py +9 -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 +5 -1
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/watch_exec.prompt.md +20 -0
- machineconfig/scripts/python/ai/solutions/copilot/prompts/pyright_fix.md +16 -0
- machineconfig/scripts/python/ai/solutions/generic.py +28 -5
- machineconfig/scripts/python/ai/utils/generate_files.py +348 -0
- machineconfig/scripts/python/ai/utils/vscode_tasks.py +37 -0
- machineconfig/scripts/python/cloud.py +29 -0
- machineconfig/scripts/python/croshell.py +137 -113
- machineconfig/scripts/python/devops.py +61 -101
- machineconfig/scripts/python/devops_navigator.py +6 -0
- machineconfig/scripts/python/env_manager/__init__.py +1 -0
- machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
- machineconfig/scripts/python/env_manager/path_manager_backend.py +47 -0
- machineconfig/scripts/python/env_manager/path_manager_tui.py +228 -0
- machineconfig/scripts/python/fire_jobs.py +110 -150
- machineconfig/scripts/python/ftpx.py +51 -24
- 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/run_py_script.py +79 -0
- machineconfig/scripts/python/helpers/symantic_search.py +25 -0
- machineconfig/scripts/python/helpers/tmp_py_scripts/a.py +26 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.json +14 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +39 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_cursor_agents.py +22 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_gemini.py +55 -0
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_qwen.py +30 -0
- machineconfig/scripts/python/helpers_agents/fire_agents_help_launch.py +126 -0
- machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +41 -0
- machineconfig/scripts/python/helpers_agents/templates/prompt.txt +10 -0
- machineconfig/scripts/python/helpers_agents/templates/template.ps1 +14 -0
- machineconfig/scripts/python/helpers_agents/templates/template.sh +32 -0
- machineconfig/scripts/python/{cloud_copy.py → helpers_cloud/cloud_copy.py} +30 -23
- machineconfig/scripts/python/{cloud_mount.py → helpers_cloud/cloud_mount.py} +29 -35
- machineconfig/scripts/python/{cloud_sync.py → helpers_cloud/cloud_sync.py} +12 -18
- machineconfig/scripts/python/{helpers → helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers_croshell/crosh.py +39 -0
- machineconfig/scripts/python/{start_slidev.py → helpers_croshell/start_slidev.py} +8 -9
- machineconfig/scripts/python/helpers_devops/cli_config.py +105 -0
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +89 -0
- machineconfig/scripts/python/helpers_devops/cli_data.py +25 -0
- machineconfig/scripts/python/helpers_devops/cli_nw.py +214 -0
- machineconfig/scripts/python/helpers_devops/cli_repos.py +215 -0
- machineconfig/scripts/python/helpers_devops/cli_self.py +172 -0
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +137 -0
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +142 -0
- machineconfig/scripts/python/{share_terminal.py → helpers_devops/cli_share_terminal.py} +45 -35
- machineconfig/scripts/python/helpers_devops/cli_utils.py +96 -0
- machineconfig/scripts/python/{devops_backup_retrieve.py → helpers_devops/devops_backup_retrieve.py} +7 -10
- machineconfig/scripts/python/helpers_devops/devops_status.py +499 -0
- machineconfig/scripts/python/{devops_update_repos.py → helpers_devops/devops_update_repos.py} +68 -49
- machineconfig/scripts/python/helpers_devops/themes/choose_pwsh_theme.ps1 +81 -0
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +3 -0
- machineconfig/scripts/python/{choose_wezterm_theme.py → helpers_devops/themes/choose_wezterm_theme.py} +3 -3
- machineconfig/scripts/python/helpers_fire_command/__init__.py +0 -0
- machineconfig/scripts/python/helpers_fire_command/f.py +0 -0
- machineconfig/scripts/python/{helpers/helpers4.py → helpers_fire_command/file_wrangler.py} +56 -20
- machineconfig/scripts/python/{fire_jobs_args_helper.py → helpers_fire_command/fire_jobs_args_helper.py} +5 -1
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +121 -0
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_streamlit_helper.py +0 -0
- machineconfig/scripts/python/helpers_msearch/__init__.py +5 -0
- machineconfig/scripts/{linux → python/helpers_msearch/scripts_linux}/fzfg +3 -3
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +59 -0
- machineconfig/scripts/python/helpers_navigator/__init__.py +20 -0
- machineconfig/scripts/python/helpers_navigator/command_builder.py +111 -0
- machineconfig/scripts/python/helpers_navigator/command_detail.py +44 -0
- machineconfig/scripts/python/helpers_navigator/command_tree.py +620 -0
- machineconfig/scripts/python/helpers_navigator/data_models.py +28 -0
- machineconfig/scripts/python/helpers_navigator/main_app.py +272 -0
- machineconfig/scripts/python/helpers_navigator/search_bar.py +15 -0
- machineconfig/scripts/python/helpers_network/__init__.py +0 -0
- machineconfig/scripts/python/helpers_network/address.py +132 -0
- machineconfig/scripts/python/{devops_add_identity.py → helpers_network/devops_add_identity.py} +0 -2
- machineconfig/scripts/python/helpers_network/devops_add_ssh_key.py +153 -0
- machineconfig/scripts/{linux → python/helpers_network}/mount_nfs +0 -1
- machineconfig/scripts/python/{mount_nfs.py → helpers_network/mount_nfs.py} +3 -3
- machineconfig/scripts/{linux → python/helpers_network}/mount_nw_drive +1 -2
- machineconfig/scripts/python/{mount_ssh.py → helpers_network/mount_ssh.py} +3 -3
- machineconfig/scripts/python/{onetimeshare.py → helpers_network/onetimeshare.py} +0 -1
- machineconfig/scripts/python/helpers_network/ssh_debug_linux.py +391 -0
- machineconfig/scripts/python/helpers_network/ssh_debug_windows.py +338 -0
- machineconfig/scripts/python/{wifi_conn.py → helpers_network/wifi_conn.py} +1 -53
- machineconfig/scripts/python/{wsl_windows_transfer.py → helpers_network/wsl_windows_transfer.py} +5 -4
- machineconfig/scripts/python/helpers_repos/action.py +209 -0
- machineconfig/scripts/python/helpers_repos/action_helper.py +150 -0
- machineconfig/scripts/python/{repos_helper_clone.py → helpers_repos/clone.py} +2 -3
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +218 -0
- machineconfig/scripts/python/{repos_helper.py → helpers_repos/entrypoint.py} +9 -17
- machineconfig/scripts/python/helpers_repos/grource.py +340 -0
- machineconfig/scripts/python/{repos_helper_record.py → helpers_repos/record.py} +4 -3
- machineconfig/scripts/python/helpers_repos/repo_analyzer_1.py +160 -0
- machineconfig/scripts/python/{count_lines.py → helpers_repos/repo_analyzer_2.py} +113 -192
- machineconfig/scripts/python/helpers_repos/sync.py +66 -0
- machineconfig/scripts/python/{repos_helper_update.py → helpers_repos/update.py} +3 -3
- machineconfig/scripts/python/helpers_sessions/__init__.py +0 -0
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +65 -0
- machineconfig/scripts/python/helpers_utils/download.py +150 -0
- machineconfig/scripts/python/helpers_utils/path.py +185 -0
- machineconfig/scripts/python/interactive.py +64 -84
- machineconfig/scripts/python/mcfg_entry.py +58 -0
- machineconfig/scripts/python/msearch.py +71 -0
- machineconfig/scripts/python/sessions.py +119 -45
- machineconfig/scripts/python/terminal.py +133 -0
- machineconfig/scripts/python/utils.py +64 -0
- machineconfig/scripts/windows/mounts/Restore-ThunderbirdProfile.ps1 +92 -0
- machineconfig/scripts/windows/{mount_nfs.ps1 → mounts/mount_nfs.ps1} +1 -3
- machineconfig/scripts/windows/{mount_ssh.ps1 → mounts/mount_ssh.ps1} +1 -1
- machineconfig/scripts/windows/{share_smb.ps1 → mounts/share_smb.ps1} +0 -6
- machineconfig/scripts/windows/wrap_mcfg.ps1 +63 -0
- machineconfig/settings/broot/br.sh +0 -4
- machineconfig/settings/broot/conf.toml +1 -1
- machineconfig/settings/helix/config.toml +16 -0
- machineconfig/settings/helix/languages.toml +13 -4
- machineconfig/settings/helix/yazi-picker.sh +12 -0
- machineconfig/settings/lf/linux/exe/lfcd.sh +1 -0
- machineconfig/settings/lf/linux/exe/previewer.sh +9 -3
- machineconfig/settings/lf/linux/lfrc +10 -12
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/lf/windows/lfrc +18 -38
- machineconfig/settings/lf/windows/mkfile.ps1 +1 -1
- machineconfig/settings/linters/.ruff.toml +1 -1
- machineconfig/settings/lvim/windows/archive/config_additional.lua +0 -6
- machineconfig/settings/marimo/marimo.toml +80 -0
- machineconfig/settings/marimo/snippets/globalize.py +34 -0
- machineconfig/settings/pistol/pistol.conf +1 -1
- machineconfig/settings/shells/bash/init.sh +82 -31
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +1 -1
- machineconfig/settings/shells/nushell/config.nu +2 -35
- machineconfig/settings/shells/nushell/env.nu +45 -6
- machineconfig/settings/shells/nushell/init.nu +314 -0
- machineconfig/settings/shells/pwsh/init.ps1 +61 -43
- machineconfig/settings/shells/starship/starship.toml +16 -0
- machineconfig/settings/shells/wezterm/wezterm.lua +2 -0
- machineconfig/settings/shells/wt/settings.json +32 -17
- machineconfig/settings/shells/zsh/init.sh +89 -0
- machineconfig/settings/svim/linux/init.toml +0 -4
- machineconfig/settings/svim/windows/init.toml +0 -3
- 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/yazi/init.lua +61 -0
- machineconfig/settings/yazi/keymap_linux.toml +94 -0
- machineconfig/settings/yazi/keymap_windows.toml +78 -0
- machineconfig/settings/yazi/shell/yazi_cd.ps1 +33 -0
- machineconfig/settings/yazi/shell/yazi_cd.sh +8 -0
- machineconfig/settings/yazi/theme.toml +4 -0
- machineconfig/settings/yazi/yazi_linux.toml +84 -0
- machineconfig/settings/yazi/yazi_windows.toml +58 -0
- machineconfig/setup_linux/__init__.py +11 -0
- machineconfig/setup_linux/apps_desktop.sh +89 -0
- machineconfig/setup_linux/apps_gui.sh +64 -0
- machineconfig/setup_linux/ssh/openssh_all.sh +25 -0
- machineconfig/setup_linux/ssh/openssh_wsl.sh +38 -0
- machineconfig/setup_linux/uv.sh +15 -0
- machineconfig/setup_linux/web_shortcuts/interactive.sh +26 -6
- machineconfig/setup_linux/web_shortcuts/live_from_github.sh +31 -0
- machineconfig/setup_mac/__init__.py +16 -0
- machineconfig/setup_mac/apps_gui.sh +248 -0
- machineconfig/setup_mac/ssh/openssh_setup.sh +114 -0
- machineconfig/setup_mac/uv.sh +36 -0
- machineconfig/setup_windows/__init__.py +11 -0
- machineconfig/setup_windows/others/power_options.ps1 +7 -0
- machineconfig/setup_windows/ssh/add-sshkey.ps1 +29 -0
- machineconfig/setup_windows/ssh/add_identity.ps1 +11 -0
- machineconfig/setup_windows/ssh/openssh-server.ps1 +37 -0
- machineconfig/setup_windows/uv.ps1 +17 -0
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +27 -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 -5
- machineconfig/utils/cloud/onedrive/README.md +139 -0
- machineconfig/utils/code.py +155 -105
- machineconfig/utils/files/art/fat_croco.txt +10 -0
- machineconfig/utils/files/art/halfwit_croco.txt +9 -0
- machineconfig/utils/files/art/happy_croco.txt +22 -0
- machineconfig/utils/files/art/water_croco.txt +11 -0
- machineconfig/utils/files/ascii_art.py +1 -1
- machineconfig/utils/files/dbms.py +257 -0
- machineconfig/utils/files/headers.py +11 -14
- machineconfig/utils/files/ouch/__init__.py +0 -0
- machineconfig/utils/files/ouch/decompress.py +45 -0
- machineconfig/utils/files/read.py +10 -18
- 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 +64 -181
- 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} +66 -97
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +49 -82
- machineconfig/utils/io.py +77 -23
- machineconfig/utils/links.py +254 -162
- machineconfig/utils/meta.py +256 -0
- machineconfig/utils/notifications.py +1 -1
- machineconfig/utils/options.py +46 -18
- machineconfig/utils/options_tv.py +119 -0
- machineconfig/utils/path_extended.py +48 -101
- machineconfig/utils/path_helper.py +76 -23
- machineconfig/utils/procs.py +50 -70
- machineconfig/utils/scheduler.py +88 -124
- machineconfig/utils/scheduling.py +0 -3
- machineconfig/utils/schemas/fire_agents/fire_agents_input.py +1 -1
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/source_of_truth.py +3 -6
- machineconfig/utils/ssh.py +263 -274
- 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 +302 -0
- machineconfig/utils/ssh_utils/utils.py +142 -0
- machineconfig/utils/ssh_utils/wsl.py +210 -0
- machineconfig/utils/terminal.py +3 -113
- machineconfig/utils/tst.py +20 -0
- machineconfig/utils/upgrade_packages.py +114 -28
- machineconfig/utils/ve.py +12 -4
- machineconfig-7.98.dist-info/METADATA +132 -0
- machineconfig-7.98.dist-info/RECORD +504 -0
- machineconfig-7.98.dist-info/entry_points.txt +13 -0
- machineconfig/cluster/sessions_managers/ffile.py +0 -4
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -49
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -85
- machineconfig/jobs/linux/msc/cli_agents.sh +0 -16
- machineconfig/jobs/python/python_ve_symlink.py +0 -37
- machineconfig/jobs/python/vscode/api.py +0 -57
- machineconfig/jobs/python/vscode/sync_code.py +0 -73
- machineconfig/jobs/windows/archive/archive_pygraphviz.ps1 +0 -14
- machineconfig/jobs/windows/start_terminal.ps1 +0 -6
- machineconfig/jobs/windows/startup_file.cmd +0 -2
- machineconfig/profile/create.py +0 -303
- machineconfig/profile/shell.py +0 -176
- machineconfig/scripts/cloud/init.sh +0 -119
- machineconfig/scripts/linux/agents +0 -2
- machineconfig/scripts/linux/choose_wezterm_theme +0 -3
- machineconfig/scripts/linux/cloud_copy +0 -2
- machineconfig/scripts/linux/cloud_mount +0 -2
- machineconfig/scripts/linux/cloud_repo_sync +0 -2
- machineconfig/scripts/linux/cloud_sync +0 -2
- machineconfig/scripts/linux/croshell +0 -3
- machineconfig/scripts/linux/devops +0 -2
- machineconfig/scripts/linux/fire +0 -2
- machineconfig/scripts/linux/ftpx +0 -2
- machineconfig/scripts/linux/fzf2g +0 -21
- machineconfig/scripts/linux/fzfag +0 -17
- machineconfig/scripts/linux/fzffg +0 -25
- machineconfig/scripts/linux/fzfrga +0 -21
- machineconfig/scripts/linux/gh_models +0 -2
- machineconfig/scripts/linux/initai +0 -2
- machineconfig/scripts/linux/kill_process +0 -2
- machineconfig/scripts/linux/scheduler +0 -2
- machineconfig/scripts/linux/sessions +0 -2
- machineconfig/scripts/linux/share_smb +0 -1
- machineconfig/scripts/linux/skrg +0 -4
- machineconfig/scripts/linux/start_slidev +0 -2
- machineconfig/scripts/linux/start_terminals +0 -3
- machineconfig/scripts/linux/warp-cli.sh +0 -122
- machineconfig/scripts/linux/wifi_conn +0 -2
- machineconfig/scripts/linux/z_ls +0 -104
- machineconfig/scripts/python/ai/generate_files.py +0 -83
- machineconfig/scripts/python/ai/solutions/copilot/prompts/allLintersAndTypeCheckers.prompt.md +0 -5
- machineconfig/scripts/python/cloud_repo_sync.py +0 -190
- machineconfig/scripts/python/count_lines_frontend.py +0 -16
- machineconfig/scripts/python/devops_add_ssh_key.py +0 -120
- machineconfig/scripts/python/dotfile.py +0 -78
- machineconfig/scripts/python/fire_agents_help_launch.py +0 -120
- machineconfig/scripts/python/fire_agents_helper_types.py +0 -12
- machineconfig/scripts/python/fire_jobs_route_helper.py +0 -65
- machineconfig/scripts/python/get_zellij_cmd.py +0 -15
- machineconfig/scripts/python/gh_models.py +0 -104
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +0 -116
- machineconfig/scripts/python/repos.py +0 -132
- machineconfig/scripts/python/repos_helper_action.py +0 -378
- machineconfig/scripts/python/snapshot.py +0 -25
- machineconfig/scripts/python/start_terminals.py +0 -121
- machineconfig/scripts/python/t4.py +0 -17
- machineconfig/scripts/windows/agents.ps1 +0 -1
- machineconfig/scripts/windows/choose_wezterm_theme.ps1 +0 -1
- machineconfig/scripts/windows/cloud_copy.ps1 +0 -1
- machineconfig/scripts/windows/cloud_mount.ps1 +0 -1
- machineconfig/scripts/windows/cloud_repo_sync.ps1 +0 -1
- machineconfig/scripts/windows/cloud_sync.ps1 +0 -1
- machineconfig/scripts/windows/croshell.ps1 +0 -1
- machineconfig/scripts/windows/devops.ps1 +0 -1
- machineconfig/scripts/windows/dotfile.ps1 +0 -1
- machineconfig/scripts/windows/fire.ps1 +0 -1
- machineconfig/scripts/windows/ftpx.ps1 +0 -1
- machineconfig/scripts/windows/fzfb.ps1 +0 -3
- machineconfig/scripts/windows/fzfg.ps1 +0 -2
- machineconfig/scripts/windows/fzfrga.bat +0 -20
- machineconfig/scripts/windows/gpt.ps1 +0 -1
- machineconfig/scripts/windows/grep.ps1 +0 -2
- machineconfig/scripts/windows/initai.ps1 +0 -1
- machineconfig/scripts/windows/kill_process.ps1 +0 -1
- machineconfig/scripts/windows/nano.ps1 +0 -3
- machineconfig/scripts/windows/pomodoro.ps1 +0 -1
- machineconfig/scripts/windows/reload_path.ps1 +0 -3
- machineconfig/scripts/windows/scheduler.ps1 +0 -1
- machineconfig/scripts/windows/sessions.ps1 +0 -1
- machineconfig/scripts/windows/snapshot.ps1 +0 -1
- machineconfig/scripts/windows/start_slidev.ps1 +0 -1
- machineconfig/scripts/windows/start_terminals.ps1 +0 -1
- machineconfig/scripts/windows/wifi_conn.ps1 +0 -2
- machineconfig/scripts/windows/wsl_rdp_windows_port_forwarding.ps1 +0 -46
- machineconfig/scripts/windows/wsl_ssh_windows_port_forwarding.ps1 +0 -76
- 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 -4
- machineconfig/setup_linux/nix/cli_installation.sh +0 -157
- machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +0 -57
- machineconfig/setup_linux/web_shortcuts/croshell.sh +0 -11
- machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -52
- machineconfig/setup_windows/web_shortcuts/all.ps1 +0 -18
- machineconfig/setup_windows/web_shortcuts/ascii_art.ps1 +0 -36
- machineconfig/setup_windows/web_shortcuts/croshell.ps1 +0 -16
- machineconfig/setup_windows/web_shortcuts/ssh.ps1 +0 -11
- machineconfig/utils/ai/generate_file_checklist.py +0 -68
- machineconfig/utils/installer_utils/installer.py +0 -189
- machineconfig-5.15.dist-info/METADATA +0 -188
- machineconfig-5.15.dist-info/RECORD +0 -415
- machineconfig-5.15.dist-info/entry_points.txt +0 -18
- machineconfig/cluster/sessions_managers/{utils → helpers}/load_balancer_helper.py +0 -0
- machineconfig/scripts/linux/{share_nfs → other/share_nfs} +0 -0
- machineconfig/scripts/linux/{start_docker → other/start_docker} +0 -0
- machineconfig/scripts/linux/{switch_ip → other/switch_ip} +0 -0
- machineconfig/{jobs/python → scripts/python/ai/utils}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers → helpers_agents}/__init__.py +0 -0
- machineconfig/{jobs/windows/msc/cli_agents.bat → scripts/python/helpers_agents/agentic_frameworks/__init__.py} +0 -0
- machineconfig/scripts/python/{fire_agents_help_search.py → helpers_agents/fire_agents_help_search.py} +0 -0
- machineconfig/scripts/python/{fire_agents_load_balancer.py → helpers_agents/fire_agents_load_balancer.py} +0 -0
- machineconfig/{jobs/windows/msc/cli_agents.ps1 → scripts/python/helpers_cloud/__init__.py} +0 -0
- machineconfig/scripts/python/{helpers → helpers_cloud}/cloud_helpers.py +1 -1
- /machineconfig/scripts/python/{helpers → helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{fire_jobs_streamlit_helper.py → helpers_croshell/__init__.py} +0 -0
- /machineconfig/scripts/python/{pomodoro.py → helpers_croshell/pomodoro.py} +0 -0
- /machineconfig/scripts/python/{scheduler.py → helpers_croshell/scheduler.py} +0 -0
- /machineconfig/scripts/python/{viewer.py → helpers_croshell/viewer.py} +0 -0
- /machineconfig/scripts/python/{viewer_template.py → helpers_croshell/viewer_template.py} +0 -0
- /machineconfig/scripts/{windows/share_nfs.ps1 → python/helpers_devops/__init__.py} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_devops/themes/__init__.py} +0 -0
- /machineconfig/{settings/yazi/keymap.toml → scripts/python/helpers_devops/themes/choose_starship_theme.ps1} +0 -0
- /machineconfig/scripts/python/{cloud_manager.py → helpers_fire_command/cloud_manager.py} +0 -0
- /machineconfig/scripts/{linux → python/helpers_network}/mount_drive +0 -0
- /machineconfig/scripts/python/{mount_nw_drive.py → helpers_network/mount_nw_drive.py} +0 -0
- /machineconfig/scripts/{linux → python/helpers_network}/mount_smb +0 -0
- /machineconfig/scripts/windows/{mount_nw.ps1 → mounts/mount_nw.ps1} +0 -0
- /machineconfig/scripts/windows/{mount_smb.ps1 → mounts/mount_smb.ps1} +0 -0
- /machineconfig/scripts/windows/{share_cloud.cmd → mounts/share_cloud.cmd} +0 -0
- /machineconfig/scripts/windows/{unlock_bitlocker.ps1 → mounts/unlock_bitlocker.ps1} +0 -0
- /machineconfig/setup_linux/{web_shortcuts → others}/android.sh +0 -0
- /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_add_key.ps1 +0 -0
- /machineconfig/{jobs/windows/archive → setup_windows/ssh}/openssh-server_copy-ssh-id.ps1 +0 -0
- {machineconfig-5.15.dist-info → machineconfig-7.98.dist-info}/WHEEL +0 -0
- {machineconfig-5.15.dist-info → machineconfig-7.98.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from machineconfig.utils.installer_utils.installer_helper import install_deb_package, download_and_prepare
|
|
7
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import find_move_delete_linux, find_move_delete_windows
|
|
8
|
+
from machineconfig.utils.installer_utils.github_release_bulk import (
|
|
9
|
+
get_repo_name_from_url,
|
|
10
|
+
fetch_github_release_data,
|
|
11
|
+
extract_release_info,
|
|
12
|
+
AssetInfo,
|
|
13
|
+
)
|
|
14
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
15
|
+
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
|
|
20
|
+
SUPPORTED_GITHUB_HOSTS = {"github.com", "www.github.com"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _format_size(size_bytes: int) -> str:
|
|
24
|
+
if size_bytes <= 0:
|
|
25
|
+
return "0 B"
|
|
26
|
+
units = ("B", "KiB", "MiB", "GiB", "TiB")
|
|
27
|
+
value = float(size_bytes)
|
|
28
|
+
index = 0
|
|
29
|
+
while value >= 1024 and index < len(units) - 1:
|
|
30
|
+
value /= 1024
|
|
31
|
+
index += 1
|
|
32
|
+
return f"{value:.1f} {units[index]}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _derive_tool_name(repo_name: str, asset_name: Optional[str]) -> Optional[str]:
|
|
36
|
+
repo_segment = repo_name.split("/", maxsplit=1)[-1]
|
|
37
|
+
repo_clean = repo_segment.replace(".git", "").lower()
|
|
38
|
+
repo_filtered = "".join(char for char in repo_clean if char.isalnum())
|
|
39
|
+
if repo_filtered:
|
|
40
|
+
return repo_filtered
|
|
41
|
+
if asset_name is None:
|
|
42
|
+
return None
|
|
43
|
+
asset_clean = asset_name.lower()
|
|
44
|
+
asset_filtered = "".join(char for char in asset_clean if char.isalnum())
|
|
45
|
+
if asset_filtered:
|
|
46
|
+
return asset_filtered
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _finalize_install(repo_name: str, asset_name: Optional[str], version: str, extracted_path: PathExtended, console: "Console") -> None:
|
|
51
|
+
from rich.panel import Panel
|
|
52
|
+
if extracted_path.suffix == ".deb":
|
|
53
|
+
install_deb_package(extracted_path)
|
|
54
|
+
tool_name_deb = _derive_tool_name(repo_name, asset_name)
|
|
55
|
+
if tool_name_deb is not None:
|
|
56
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name_deb).parent.mkdir(parents=True, exist_ok=True)
|
|
57
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name_deb).write_text(version, encoding="utf-8")
|
|
58
|
+
console.print(Panel(f"Installed Debian package for [green]{tool_name_deb}[/green]", title="✅ Complete", border_style="green"))
|
|
59
|
+
return
|
|
60
|
+
system_name = platform.system()
|
|
61
|
+
tool_name = _derive_tool_name(repo_name, asset_name)
|
|
62
|
+
rename_target = f"{tool_name}.exe" if system_name == "Windows" else tool_name
|
|
63
|
+
try:
|
|
64
|
+
if system_name == "Windows":
|
|
65
|
+
installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, tool_name=tool_name, delete=True, rename_to=rename_target)
|
|
66
|
+
elif system_name in {"Linux", "Darwin"}:
|
|
67
|
+
installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name=tool_name, delete=True, rename_to=rename_target)
|
|
68
|
+
else:
|
|
69
|
+
console.print(Panel(f"Unsupported operating system: {system_name}", title="❌ Error", border_style="red"))
|
|
70
|
+
return None
|
|
71
|
+
except IndexError:
|
|
72
|
+
if system_name == "Windows":
|
|
73
|
+
installed_path = find_move_delete_windows(downloaded_file_path=extracted_path, tool_name=None, delete=True, rename_to=rename_target)
|
|
74
|
+
elif system_name in {"Linux", "Darwin"}:
|
|
75
|
+
installed_path = find_move_delete_linux(downloaded=extracted_path, tool_name="", delete=True, rename_to=rename_target)
|
|
76
|
+
else:
|
|
77
|
+
raise
|
|
78
|
+
if tool_name is not None:
|
|
79
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name).parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
INSTALL_VERSION_ROOT.joinpath(tool_name).write_text(version, encoding="utf-8")
|
|
81
|
+
console.print(Panel(f"Installed [green]{tool_name}[/green] to {installed_path}\nVersion: {version}", title="✅ Complete", border_style="green"))
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def install_from_github_url(github_url: str) -> None:
|
|
85
|
+
from machineconfig.utils.options import choose_from_options
|
|
86
|
+
from rich.console import Console
|
|
87
|
+
from rich.panel import Panel
|
|
88
|
+
|
|
89
|
+
console = Console()
|
|
90
|
+
repo_info = get_repo_name_from_url(github_url)
|
|
91
|
+
if repo_info is None:
|
|
92
|
+
console.print(Panel(f"Invalid GitHub URL: {github_url}", title="❌ Error", border_style="red"))
|
|
93
|
+
return None
|
|
94
|
+
owner, repo = repo_info
|
|
95
|
+
repo_name = f"{owner}/{repo}"
|
|
96
|
+
console.print(Panel(f"Fetching latest release for [green]{repo_name}[/green]", title="🌐 GitHub", border_style="blue"))
|
|
97
|
+
release_raw = fetch_github_release_data(owner, repo)
|
|
98
|
+
if not release_raw:
|
|
99
|
+
console.print(Panel("No releases available for this repository.", title="❌ Error", border_style="red"))
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
release_info = extract_release_info(release_raw)
|
|
103
|
+
if not release_info:
|
|
104
|
+
console.print(Panel("Failed to parse release information.", title="❌ Error", border_style="red"))
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
assets = release_info["assets"]
|
|
108
|
+
if not assets:
|
|
109
|
+
console.print(Panel("No downloadable assets found in the latest release.", title="❌ Error", border_style="red"))
|
|
110
|
+
return None
|
|
111
|
+
binary_assets = assets
|
|
112
|
+
selection_pool = binary_assets if binary_assets else assets
|
|
113
|
+
if not selection_pool:
|
|
114
|
+
console.print(Panel("No assets available for installation.", title="❌ Error", border_style="red"))
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
# First pass: collect all formatted data and calculate column widths
|
|
118
|
+
asset_data = []
|
|
119
|
+
for asset in selection_pool:
|
|
120
|
+
name = asset["name"]
|
|
121
|
+
download_url = asset["browser_download_url"]
|
|
122
|
+
if name == "" or download_url == "":
|
|
123
|
+
continue
|
|
124
|
+
size = asset["size"]
|
|
125
|
+
download_count = asset.get("download_count", 0)
|
|
126
|
+
created_at = asset.get("created_at", "")
|
|
127
|
+
|
|
128
|
+
# Format each field
|
|
129
|
+
size_str = f"[{_format_size(size)}]"
|
|
130
|
+
downloads_str = f"{download_count:,}"
|
|
131
|
+
date_str = created_at.split("T")[0] if created_at else "N/A"
|
|
132
|
+
|
|
133
|
+
asset_data.append({
|
|
134
|
+
"name": name,
|
|
135
|
+
"size_str": size_str,
|
|
136
|
+
"downloads_str": downloads_str,
|
|
137
|
+
"date_str": date_str,
|
|
138
|
+
"asset": asset
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
# Calculate maximum widths for alignment
|
|
142
|
+
max_name_len = max(len(item["name"]) for item in asset_data) if asset_data else 0
|
|
143
|
+
max_size_len = max(len(item["size_str"]) for item in asset_data) if asset_data else 0
|
|
144
|
+
max_downloads_len = max(len(item["downloads_str"]) for item in asset_data) if asset_data else 0
|
|
145
|
+
|
|
146
|
+
# Second pass: build aligned labels
|
|
147
|
+
options_map: dict[str, AssetInfo] = {}
|
|
148
|
+
for item in asset_data:
|
|
149
|
+
name_padded = item["name"].ljust(max_name_len)
|
|
150
|
+
size_padded = item["size_str"].ljust(max_size_len)
|
|
151
|
+
downloads_padded = item["downloads_str"].rjust(max_downloads_len)
|
|
152
|
+
|
|
153
|
+
label = f"{name_padded} {size_padded} | ⬇ {downloads_padded} | 📅 {item['date_str']}"
|
|
154
|
+
options_map[label] = item["asset"]
|
|
155
|
+
|
|
156
|
+
if not options_map:
|
|
157
|
+
console.print(Panel("Release assets lack download URLs.", title="❌ Error", border_style="red"))
|
|
158
|
+
return None
|
|
159
|
+
selection_label = choose_from_options(options=list(options_map.keys()), msg="Select a release asset", multi=False, header="📦 GitHub Release Assets", tv=True)
|
|
160
|
+
selected_asset = options_map[selection_label]
|
|
161
|
+
download_url_value = selected_asset["browser_download_url"]
|
|
162
|
+
asset_name_value = selected_asset["name"]
|
|
163
|
+
if download_url_value == "":
|
|
164
|
+
console.print(Panel("Selected asset lacks a download URL.", title="❌ Error", border_style="red"))
|
|
165
|
+
return None
|
|
166
|
+
asset_name = asset_name_value if asset_name_value != "" else "github_binary"
|
|
167
|
+
version = release_info["tag_name"] if release_info["tag_name"] != "" else "latest"
|
|
168
|
+
console.print(Panel(f"Downloading [cyan]{asset_name}[/cyan]", title="⬇️ Download", border_style="magenta"))
|
|
169
|
+
extracted_path = download_and_prepare(download_url_value)
|
|
170
|
+
_finalize_install(repo_name=repo_name, asset_name=asset_name, version=version, extracted_path=extracted_path, console=console)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def install_from_binary_url(binary_url: str) -> None:
|
|
174
|
+
from rich.console import Console
|
|
175
|
+
# from rich.panel import Panel
|
|
176
|
+
console = Console()
|
|
177
|
+
# parsed = urlparse(binary_url)
|
|
178
|
+
# asset_candidate = parsed.path.split("/")[-1] if parsed.path else ""
|
|
179
|
+
# asset_name = asset_candidate if asset_candidate != "" else "binary_asset"
|
|
180
|
+
# host = parsed.netloc if parsed.netloc != "" else "remote host"
|
|
181
|
+
# console.print(Panel(f"Downloading from [green]{binary_url}[/green]", title="⬇️ Download", border_style="magenta"))
|
|
182
|
+
extracted_path = download_and_prepare(binary_url)
|
|
183
|
+
_finalize_install(repo_name="", asset_name=None, version="latest", extracted_path=extracted_path, console=console)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
from machineconfig.utils.installer_utils.installer_helper import install_deb_package, download_and_prepare
|
|
1
2
|
from machineconfig.utils.path_extended import PathExtended
|
|
2
|
-
from machineconfig.utils.
|
|
3
|
-
from machineconfig.utils.
|
|
4
|
-
from machineconfig.utils.
|
|
5
|
-
from machineconfig.utils.
|
|
6
|
-
|
|
3
|
+
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT
|
|
4
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import find_move_delete_linux, find_move_delete_windows, check_tool_exists
|
|
5
|
+
from machineconfig.utils.schemas.installer.installer_types import InstallerData, get_os_name, get_normalized_arch
|
|
6
|
+
from machineconfig.utils.installer_utils.github_release_bulk import (
|
|
7
|
+
get_repo_name_from_url,
|
|
8
|
+
get_release_info,
|
|
9
|
+
)
|
|
7
10
|
|
|
8
11
|
import platform
|
|
9
12
|
import subprocess
|
|
10
|
-
import
|
|
11
|
-
from typing import Optional, Any
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from urllib.parse import urlparse
|
|
13
|
+
from typing import Optional
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class Installer:
|
|
@@ -32,64 +32,21 @@ class Installer:
|
|
|
32
32
|
|
|
33
33
|
def _get_exe_name(self) -> str:
|
|
34
34
|
"""Derive executable name from app name by converting to lowercase and removing spaces."""
|
|
35
|
-
return self.installer_data["appName"].lower().replace(" ", "").replace("-", "")
|
|
36
|
-
|
|
37
|
-
@staticmethod
|
|
38
|
-
def choose_app_and_install():
|
|
39
|
-
print(f"\n{'=' * 80}\n🔍 SELECT APPLICATION TO INSTALL 🔍\n{'=' * 80}")
|
|
40
|
-
from machineconfig.utils.options import choose_from_options
|
|
41
|
-
|
|
42
|
-
print("📂 Searching for configuration files...")
|
|
43
|
-
jobs_dir = Path(LIBRARY_ROOT.joinpath("jobs"))
|
|
44
|
-
config_paths = [Path(p) for p in jobs_dir.rglob("config.json")]
|
|
45
|
-
path = choose_from_options(multi=False, options=config_paths, msg="Choose one option")
|
|
46
|
-
print(f"📄 Loading configuration from: {path}")
|
|
47
|
-
config_data = read_json(path)
|
|
48
|
-
installer_data_files = InstallerDataFiles(config_data)
|
|
49
|
-
|
|
50
|
-
# Extract app names from the installers
|
|
51
|
-
app_names = [installer["appName"] for installer in installer_data_files["installers"]]
|
|
52
|
-
print("🔍 Select an application to install:")
|
|
53
|
-
app_name = choose_from_options(multi=False, options=app_names, fzf=True, msg="Choose one option")
|
|
54
|
-
|
|
55
|
-
# Find the selected installer data
|
|
56
|
-
selected_installer_data = None
|
|
57
|
-
for installer_data in installer_data_files["installers"]:
|
|
58
|
-
if installer_data["appName"] == app_name:
|
|
59
|
-
selected_installer_data = installer_data
|
|
60
|
-
break
|
|
61
|
-
|
|
62
|
-
if selected_installer_data is None:
|
|
63
|
-
raise ValueError(f"Could not find installer data for {app_name}")
|
|
64
|
-
|
|
65
|
-
installer = Installer(installer_data=selected_installer_data)
|
|
66
|
-
exe_name = installer._get_exe_name()
|
|
67
|
-
print(f"📦 Selected application: {exe_name}")
|
|
68
|
-
version = input(f"📝 Enter version to install for {exe_name} [latest]: ") or None
|
|
69
|
-
print(f"\n{'=' * 80}\n🚀 INSTALLING {exe_name.upper()} 🚀\n{'=' * 80}")
|
|
70
|
-
installer.install(version=version)
|
|
35
|
+
return self.installer_data["appName"].lower().replace(" ", "") # .replace("-", "")
|
|
71
36
|
|
|
72
37
|
def install_robust(self, version: Optional[str]) -> str:
|
|
73
38
|
try:
|
|
74
39
|
exe_name = self._get_exe_name()
|
|
75
|
-
print(f"\n{'=' * 80}\n🚀 INSTALLING {exe_name.upper()} 🚀\n{'=' * 80}")
|
|
76
40
|
result_old = subprocess.run(f"{exe_name} --version", shell=True, capture_output=True, text=True)
|
|
77
41
|
old_version_cli = result_old.stdout.strip()
|
|
78
|
-
print(f"📊 Current version: {old_version_cli or 'Not installed'}")
|
|
79
|
-
|
|
42
|
+
print(f"🚀 INSTALLING {exe_name.upper()} 🚀. 📊 Current version: {old_version_cli or 'Not installed'}")
|
|
80
43
|
self.install(version=version)
|
|
81
|
-
|
|
82
44
|
result_new = subprocess.run(f"{exe_name} --version", shell=True, capture_output=True, text=True)
|
|
83
45
|
new_version_cli = result_new.stdout.strip()
|
|
84
|
-
print(f"📊 New version: {new_version_cli}")
|
|
85
|
-
|
|
86
46
|
if old_version_cli == new_version_cli:
|
|
87
|
-
print(f"ℹ️ Same version detected: {old_version_cli}")
|
|
88
47
|
return f"""📦️ 😑 {exe_name}, same version: {old_version_cli}"""
|
|
89
48
|
else:
|
|
90
|
-
print(f"🚀 Update successful: {old_version_cli} ➡️ {new_version_cli}")
|
|
91
49
|
return f"""📦️ 🤩 {exe_name} updated from {old_version_cli} ➡️ TO ➡️ {new_version_cli}"""
|
|
92
|
-
|
|
93
50
|
except Exception as ex:
|
|
94
51
|
exe_name = self._get_exe_name()
|
|
95
52
|
app_name = self.installer_data["appName"]
|
|
@@ -104,28 +61,27 @@ class Installer:
|
|
|
104
61
|
installer_arch_os = self.installer_data["fileNamePattern"][arch][os_name]
|
|
105
62
|
if installer_arch_os is None:
|
|
106
63
|
raise ValueError(f"No installation pattern for {exe_name} on {os_name} {arch}")
|
|
107
|
-
|
|
108
|
-
print(f"\n{'=' * 80}\n🔧 INSTALLATION PROCESS: {exe_name} 🔧\n{'=' * 80}")
|
|
109
64
|
version_to_be_installed: str = "unknown" # Initialize to ensure it's always bound
|
|
110
65
|
if repo_url == "CMD":
|
|
111
|
-
if
|
|
66
|
+
if any(pm in installer_arch_os for pm in ["npm ", "pip ", "winget ", "brew ", "curl "]):
|
|
67
|
+
from rich import print as rprint
|
|
68
|
+
from rich.panel import Panel
|
|
69
|
+
from rich.console import Group
|
|
112
70
|
package_manager = installer_arch_os.split(" ", maxsplit=1)[0]
|
|
113
|
-
print(f"📦 Using package manager: {
|
|
71
|
+
print(f"📦 Using package manager: {installer_arch_os}")
|
|
114
72
|
desc = package_manager + " installation"
|
|
115
73
|
version_to_be_installed = package_manager + "Latest"
|
|
116
|
-
|
|
117
|
-
result = subprocess.run(installer_arch_os, shell=True, capture_output=True, text=True)
|
|
74
|
+
result = subprocess.run(installer_arch_os, shell=True, capture_output=False, text=True)
|
|
118
75
|
success = result.returncode == 0 and result.stderr == ""
|
|
119
76
|
if not success:
|
|
120
|
-
|
|
77
|
+
sub_panels = []
|
|
121
78
|
if result.stdout:
|
|
122
|
-
|
|
79
|
+
sub_panels.append(Panel(result.stdout, title="STDOUT", style="blue"))
|
|
123
80
|
if result.stderr:
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
81
|
+
sub_panels.append(Panel(result.stderr, title="STDERR", style="red"))
|
|
82
|
+
group_content = Group(f"❌ {desc} failed\nReturn code: {result.returncode}", *sub_panels)
|
|
83
|
+
rprint(Panel(group_content, title=desc, style="red"))
|
|
127
84
|
elif installer_arch_os.endswith((".sh", ".py", ".ps1")):
|
|
128
|
-
# search for the script, see which path ends with the script name
|
|
129
85
|
import machineconfig.jobs.installer as module
|
|
130
86
|
from pathlib import Path
|
|
131
87
|
search_root = Path(module.__file__).parent
|
|
@@ -139,34 +95,22 @@ class Installer:
|
|
|
139
95
|
if installer_arch_os.endswith(".sh"):
|
|
140
96
|
if platform.system() not in ["Linux", "Darwin"]:
|
|
141
97
|
raise NotImplementedError(f"Shell script installation not supported on {platform.system()}")
|
|
142
|
-
print(f"🚀 Running shell script: {installer_path}")
|
|
143
98
|
subprocess.run(f"bash {installer_path}", shell=True, check=True)
|
|
144
99
|
version_to_be_installed = "scripted_installation"
|
|
145
|
-
print(f"✅ Shell script installation completed\n{'=' * 80}")
|
|
146
100
|
elif installer_arch_os.endswith(".ps1"):
|
|
147
101
|
if platform.system() != "Windows":
|
|
148
102
|
raise NotImplementedError(f"PowerShell script installation not supported on {platform.system()}")
|
|
149
|
-
print(f"🚀 Running PowerShell script: {installer_path}")
|
|
150
103
|
subprocess.run(f"powershell -ExecutionPolicy Bypass -File {installer_path}", shell=True, check=True)
|
|
151
104
|
version_to_be_installed = "scripted_installation"
|
|
152
|
-
print(f"✅ PowerShell script installation completed\n{'=' * 80}")
|
|
153
105
|
elif installer_arch_os.endswith(".py"):
|
|
154
106
|
import runpy
|
|
155
107
|
runpy.run_path(str(installer_path), run_name=None)["main"](self.installer_data, version=version)
|
|
156
108
|
version_to_be_installed = str(version)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
print(f"📥 Downloading object from URL: {installer_arch_os}")
|
|
160
|
-
downloaded_object = PathExtended(installer_arch_os).download(folder=INSTALL_TMP_DIR)
|
|
161
|
-
# object is either a zip containing a binary or a straight out binary.
|
|
162
|
-
if downloaded_object.suffix in [".zip", ".tar.gz"]:
|
|
163
|
-
print(f"📦 Decompressing downloaded archive: {downloaded_object}")
|
|
164
|
-
downloaded_object = downloaded_object.decompress()
|
|
165
|
-
print(f"✅ Decompression completed to: {downloaded_object}")
|
|
109
|
+
elif installer_arch_os.startswith("https://") or installer_arch_os.startswith("http://"):
|
|
110
|
+
downloaded_object = download_and_prepare(installer_arch_os)
|
|
166
111
|
if downloaded_object.suffix in [".exe", ""]: # likely an executable
|
|
167
112
|
if platform.system() == "Windows":
|
|
168
|
-
|
|
169
|
-
exe = find_move_delete_windows(downloaded_file_path=downloaded_object, exe_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
113
|
+
exe = find_move_delete_windows(downloaded_file_path=downloaded_object, tool_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
170
114
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
171
115
|
system_name = "Linux" if platform.system() == "Linux" else "macOS"
|
|
172
116
|
print(f"🐧 Installing on {system_name}...")
|
|
@@ -175,7 +119,6 @@ class Installer:
|
|
|
175
119
|
error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
176
120
|
print(error_msg)
|
|
177
121
|
raise NotImplementedError(error_msg)
|
|
178
|
-
|
|
179
122
|
_ = exe
|
|
180
123
|
if exe.name.replace(".exe", "") != exe_name.replace(".exe", ""):
|
|
181
124
|
from rich import print as pprint
|
|
@@ -186,33 +129,20 @@ class Installer:
|
|
|
186
129
|
print(f"🔄 Renaming to correct name: {new_exe_name}")
|
|
187
130
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
188
131
|
version_to_be_installed = "downloaded_binary"
|
|
189
|
-
|
|
132
|
+
elif downloaded_object.suffix in [".deb"]:
|
|
133
|
+
install_deb_package(downloaded_object)
|
|
134
|
+
version_to_be_installed = "downloaded_deb"
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(f"Downloaded file is not an executable: {downloaded_object}")
|
|
190
137
|
else:
|
|
191
138
|
raise NotImplementedError(f"CMD installation method not implemented for: {installer_arch_os}")
|
|
192
139
|
else:
|
|
193
140
|
assert repo_url.startswith("https://github.com/"), f"repoURL must be a GitHub URL, got {repo_url}"
|
|
194
|
-
|
|
195
|
-
downloaded
|
|
196
|
-
if str(downloaded).endswith(".deb"):
|
|
197
|
-
print(f"📦 Installing .deb package: {downloaded}")
|
|
198
|
-
assert platform.system() == "Linux"
|
|
199
|
-
result = subprocess.run(f"sudo nala install -y {downloaded}", shell=True, capture_output=True, text=True)
|
|
200
|
-
success = result.returncode == 0 and result.stderr == ""
|
|
201
|
-
if not success:
|
|
202
|
-
desc = "Installing .deb"
|
|
203
|
-
print(f"❌ {desc} failed")
|
|
204
|
-
if result.stdout:
|
|
205
|
-
print(f"STDOUT: {result.stdout}")
|
|
206
|
-
if result.stderr:
|
|
207
|
-
print(f"STDERR: {result.stderr}")
|
|
208
|
-
print(f"Return code: {result.returncode}")
|
|
209
|
-
print("🗑️ Cleaning up .deb package...")
|
|
210
|
-
downloaded.delete(sure=True)
|
|
211
|
-
print(f"✅ DEB package installation completed\n{'=' * 80}")
|
|
141
|
+
downloaded, version_to_be_installed = self.binary_download(version=version)
|
|
142
|
+
if str(downloaded).endswith(".deb"): install_deb_package(downloaded)
|
|
212
143
|
else:
|
|
213
144
|
if platform.system() == "Windows":
|
|
214
|
-
|
|
215
|
-
exe = find_move_delete_windows(downloaded_file_path=downloaded, exe_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
145
|
+
exe = find_move_delete_windows(downloaded_file_path=downloaded, tool_name=exe_name, delete=True, rename_to=exe_name.replace(".exe", "") + ".exe")
|
|
216
146
|
elif platform.system() in ["Linux", "Darwin"]:
|
|
217
147
|
system_name = "Linux" if platform.system() == "Linux" else "macOS"
|
|
218
148
|
print(f"🐧 Installing on {system_name}...")
|
|
@@ -221,33 +151,24 @@ class Installer:
|
|
|
221
151
|
error_msg = f"❌ ERROR: System {platform.system()} not supported"
|
|
222
152
|
print(error_msg)
|
|
223
153
|
raise NotImplementedError(error_msg)
|
|
224
|
-
|
|
225
154
|
_ = exe
|
|
226
155
|
if exe.name.replace(".exe", "") != exe_name.replace(".exe", ""):
|
|
227
156
|
from rich import print as pprint
|
|
228
157
|
from rich.panel import Panel
|
|
229
|
-
|
|
230
158
|
print("⚠️ Warning: Executable name mismatch")
|
|
231
159
|
pprint(Panel(f"Expected exe name: [red]{exe_name}[/red] \nAttained name: [red]{exe.name.replace('.exe', '')}[/red]", title="exe name mismatch", subtitle=repo_url))
|
|
232
160
|
new_exe_name = exe_name + ".exe" if platform.system() == "Windows" else exe_name
|
|
233
161
|
print(f"🔄 Renaming to correct name: {new_exe_name}")
|
|
234
162
|
exe.with_name(name=new_exe_name, inplace=True, overwrite=True)
|
|
235
|
-
|
|
236
|
-
print(f"💾 Saving version information to: {INSTALL_VERSION_ROOT.joinpath(exe_name)}")
|
|
237
163
|
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
238
164
|
INSTALL_VERSION_ROOT.joinpath(exe_name).write_text(version_to_be_installed or "unknown", encoding="utf-8")
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
def download(self, version: Optional[str]) -> tuple[PathExtended, str]:
|
|
165
|
+
def binary_download(self, version: Optional[str]) -> tuple[PathExtended, str]:
|
|
242
166
|
exe_name = self._get_exe_name()
|
|
243
167
|
repo_url = self.installer_data["repoURL"]
|
|
244
|
-
app_name = self.installer_data["appName"]
|
|
245
|
-
print(f"\n{'=' * 80}\n📥 DOWNLOADING: {exe_name} 📥\n{'=' * 80}")
|
|
246
|
-
|
|
168
|
+
# app_name = self.installer_data["appName"]
|
|
247
169
|
download_link: Optional[str] = None
|
|
248
170
|
version_to_be_installed: Optional[str] = None
|
|
249
|
-
|
|
250
|
-
if "github" not in repo_url or ".zip" in repo_url or ".tar.gz" in repo_url:
|
|
171
|
+
if "github" not in repo_url:
|
|
251
172
|
# Direct download URL
|
|
252
173
|
download_link = repo_url
|
|
253
174
|
version_to_be_installed = "predefined_url"
|
|
@@ -259,71 +180,19 @@ class Installer:
|
|
|
259
180
|
arch = get_normalized_arch()
|
|
260
181
|
os_name = get_os_name()
|
|
261
182
|
print(f"🧭 Detected system={os_name} arch={arch}")
|
|
262
|
-
|
|
263
183
|
# Use existing get_github_release method to get download link and version
|
|
264
184
|
download_link, version_to_be_installed = self.get_github_release(repo_url, version)
|
|
265
|
-
|
|
185
|
+
# print(f"🌟 Retrieved download link from GitHub: {download_link}")
|
|
186
|
+
# print(f"📦 Version to be installed: {version_to_be_installed}")
|
|
266
187
|
if download_link is None:
|
|
267
188
|
raise ValueError(f"Could not retrieve download link for {exe_name} version {version or 'latest'}")
|
|
268
|
-
|
|
269
189
|
print(f"📦 Version to be installed: {version_to_be_installed}")
|
|
270
190
|
print(f"🔗 Download URL: {download_link}")
|
|
271
|
-
|
|
272
191
|
assert download_link is not None, "download_link must be set"
|
|
273
192
|
assert version_to_be_installed is not None, "version_to_be_installed must be set"
|
|
274
|
-
|
|
275
|
-
downloaded = PathExtended(download_link).download(folder=INSTALL_TMP_DIR).decompress()
|
|
276
|
-
print(f"✅ Download and extraction completed to: {downloaded}\n{'=' * 80}")
|
|
193
|
+
downloaded = download_and_prepare(download_link)
|
|
277
194
|
return downloaded, version_to_be_installed
|
|
278
195
|
|
|
279
|
-
# --------------------------- Arch / template helpers ---------------------------
|
|
280
|
-
|
|
281
|
-
@staticmethod
|
|
282
|
-
def _get_repo_name_from_url(repo_url: str) -> str:
|
|
283
|
-
"""Extract owner/repo from GitHub URL."""
|
|
284
|
-
try:
|
|
285
|
-
parsed = urlparse(repo_url)
|
|
286
|
-
path_parts = parsed.path.strip("/").split("/")
|
|
287
|
-
return f"{path_parts[0]}/{path_parts[1]}"
|
|
288
|
-
except (IndexError, AttributeError):
|
|
289
|
-
return ""
|
|
290
|
-
|
|
291
|
-
@staticmethod
|
|
292
|
-
def _fetch_github_release_data(repo_name: str, version: Optional[str] = None) -> Optional[dict[str, Any]]:
|
|
293
|
-
"""Fetch release data from GitHub API using requests."""
|
|
294
|
-
import requests
|
|
295
|
-
|
|
296
|
-
try:
|
|
297
|
-
if version and version.lower() != "latest":
|
|
298
|
-
# Fetch specific version
|
|
299
|
-
url = f"https://api.github.com/repos/{repo_name}/releases/tags/{version}"
|
|
300
|
-
else:
|
|
301
|
-
# Fetch latest release
|
|
302
|
-
url = f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
303
|
-
|
|
304
|
-
response = requests.get(url, timeout=30)
|
|
305
|
-
|
|
306
|
-
if response.status_code != 200:
|
|
307
|
-
print(f"❌ Failed to fetch data for {repo_name}: HTTP {response.status_code}")
|
|
308
|
-
return None
|
|
309
|
-
|
|
310
|
-
response_data = response.json()
|
|
311
|
-
|
|
312
|
-
# Check if API returned an error
|
|
313
|
-
if "message" in response_data:
|
|
314
|
-
if "API rate limit exceeded" in response_data.get("message", ""):
|
|
315
|
-
print(f"🚫 Rate limit exceeded for {repo_name}")
|
|
316
|
-
return None
|
|
317
|
-
elif "Not Found" in response_data.get("message", ""):
|
|
318
|
-
print(f"🔍 No releases found for {repo_name}")
|
|
319
|
-
return None
|
|
320
|
-
|
|
321
|
-
return response_data
|
|
322
|
-
|
|
323
|
-
except (requests.RequestException, requests.Timeout, json.JSONDecodeError) as e:
|
|
324
|
-
print(f"❌ Error fetching {repo_name}: {e}")
|
|
325
|
-
return None
|
|
326
|
-
|
|
327
196
|
def get_github_release(self, repo_url: str, version: Optional[str]) -> tuple[Optional[str], Optional[str]]:
|
|
328
197
|
"""
|
|
329
198
|
Get download link and version from GitHub release based on fileNamePattern.
|
|
@@ -334,25 +203,39 @@ class Installer:
|
|
|
334
203
|
filename_pattern = self.installer_data["fileNamePattern"][arch][os_name]
|
|
335
204
|
if filename_pattern is None:
|
|
336
205
|
raise ValueError(f"No fileNamePattern for {self._get_exe_name()} on {os_name} {arch}")
|
|
337
|
-
|
|
338
|
-
if not
|
|
206
|
+
repo_info = get_repo_name_from_url(repo_url)
|
|
207
|
+
if not repo_info:
|
|
339
208
|
print(f"❌ Invalid repository URL: {repo_url}")
|
|
340
209
|
return None, None
|
|
341
|
-
|
|
342
|
-
|
|
210
|
+
username, repository = repo_info
|
|
211
|
+
release_info = get_release_info(username, repository, version)
|
|
212
|
+
if not release_info:
|
|
343
213
|
return None, None
|
|
344
|
-
|
|
345
|
-
actual_version = release_data.get("tag_name", "unknown")
|
|
214
|
+
actual_version = release_info.get("tag_name", "unknown") or "unknown"
|
|
346
215
|
filename = filename_pattern.format(version=actual_version)
|
|
347
216
|
|
|
348
217
|
available_filenames: list[str] = []
|
|
349
|
-
for asset in
|
|
350
|
-
an_dl = asset
|
|
218
|
+
for asset in release_info["assets"]:
|
|
219
|
+
an_dl = asset["browser_download_url"]
|
|
351
220
|
available_filenames.append(an_dl.split("/")[-1])
|
|
352
221
|
if filename not in available_filenames:
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
222
|
+
candidates = [
|
|
223
|
+
filename,
|
|
224
|
+
filename_pattern.format(version=actual_version),
|
|
225
|
+
filename_pattern.format(version=actual_version.replace("v", "")),
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
# Include hyphen/underscore variants
|
|
229
|
+
variants = []
|
|
230
|
+
for f in candidates:
|
|
231
|
+
variants += [f, f.replace("-", "_"), f.replace("_", "-")]
|
|
232
|
+
|
|
233
|
+
for f in variants:
|
|
234
|
+
if f in available_filenames:
|
|
235
|
+
filename = f
|
|
236
|
+
break
|
|
237
|
+
else:
|
|
238
|
+
print(f"❌ Filename not found in assets. Tried: {variants}\nAvailable: {available_filenames}")
|
|
356
239
|
return None, None
|
|
357
240
|
browser_download_url = f"{repo_url}/releases/download/{actual_version}/{filename}"
|
|
358
241
|
return browser_download_url, actual_version
|