machineconfig 6.23__py3-none-any.whl → 8.12__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/remote/distribute.py +0 -1
- machineconfig/cluster/remote/file_manager.py +0 -2
- 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 +16 -221
- machineconfig/cluster/sessions_managers/wt_local_manager.py +55 -193
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +42 -198
- 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_reporting.py +76 -0
- machineconfig/cluster/sessions_managers/wt_utils/wt_helpers.py +199 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +3 -3
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +5 -3
- machineconfig/cluster/sessions_managers/zellij_remote.py +2 -2
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +3 -2
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +2 -2
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +3 -7
- machineconfig/cluster/sessions_managers/{helpers → zellij_utils}/zellij_local_helper_with_panes.py +1 -1
- machineconfig/jobs/installer/check_installations.py +0 -1
- machineconfig/jobs/installer/installer_data.json +1408 -201
- machineconfig/jobs/installer/linux_scripts/q.sh +10 -7
- machineconfig/jobs/installer/linux_scripts/redis.sh +1 -0
- machineconfig/jobs/installer/package_groups.py +63 -92
- machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
- machineconfig/jobs/installer/python_scripts/boxes.py +61 -0
- 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 +75 -18
- 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 +56 -18
- machineconfig/profile/create_links.py +2 -1
- machineconfig/profile/create_links_export.py +64 -18
- machineconfig/profile/create_shell_profile.py +90 -132
- machineconfig/profile/mapper.toml +18 -8
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +46 -0
- machineconfig/scripts/nu/wrap_mcfg.nu +69 -0
- machineconfig/scripts/python/agents.py +82 -60
- machineconfig/scripts/python/ai/initai.py +1 -19
- 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/ai/{vscode_tasks.py → utils/vscode_tasks.py} +7 -2
- machineconfig/scripts/python/cloud.py +14 -9
- machineconfig/scripts/python/croshell.py +135 -117
- machineconfig/scripts/python/devops.py +48 -25
- machineconfig/scripts/python/devops_navigator.py +1 -5
- machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
- machineconfig/scripts/python/env_manager/path_manager_tui.py +18 -9
- machineconfig/scripts/python/fire_jobs.py +127 -118
- 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_fire → helpers_agents}/agentic_frameworks/fire_crush.json +1 -1
- machineconfig/scripts/python/helpers_agents/agentic_frameworks/fire_crush.py +39 -0
- machineconfig/scripts/python/{helpers_fire → helpers_agents}/agentic_frameworks/fire_cursor_agents.py +3 -4
- 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_fire → helpers_agents}/fire_agents_help_launch.py +37 -15
- machineconfig/scripts/python/helpers_agents/fire_agents_helper_types.py +41 -0
- 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 +10 -0
- machineconfig/scripts/python/helpers_agents/templates/template.sh +34 -0
- machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/cloud_copy.py +28 -21
- machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/cloud_mount.py +19 -17
- machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/cloud_sync.py +12 -11
- machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/helpers2.py +1 -1
- machineconfig/scripts/python/helpers_croshell/crosh.py +39 -0
- machineconfig/scripts/python/{croshell_helpers → helpers_croshell}/start_slidev.py +6 -7
- 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 +221 -0
- machineconfig/scripts/python/{devops_helpers → helpers_devops}/cli_repos.py +60 -36
- 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/{devops_helpers/cli_terminal.py → helpers_devops/cli_share_terminal.py} +15 -17
- machineconfig/scripts/python/{devops_helpers → helpers_devops}/devops_backup_retrieve.py +7 -10
- machineconfig/scripts/python/{devops_helpers → helpers_devops}/devops_status.py +7 -19
- machineconfig/scripts/python/{devops_helpers → helpers_devops}/devops_update_repos.py +1 -1
- machineconfig/scripts/python/helpers_devops/run_script.py +168 -0
- machineconfig/scripts/python/helpers_devops/themes/choose_starship_theme.bash +3 -0
- machineconfig/scripts/python/{devops_helpers → helpers_devops}/themes/choose_wezterm_theme.py +1 -1
- machineconfig/scripts/python/{helpers_fire/helpers4.py → helpers_fire_command/file_wrangler.py} +57 -20
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_args_helper.py +2 -0
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +26 -16
- 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/{helper_navigator → helpers_navigator}/command_builder.py +1 -1
- machineconfig/scripts/python/{helper_navigator → helpers_navigator}/command_detail.py +1 -1
- machineconfig/scripts/python/{helper_navigator → helpers_navigator}/command_tree.py +160 -23
- machineconfig/scripts/python/{helper_navigator → helpers_navigator}/main_app.py +5 -5
- 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/{nw → helpers_network}/wifi_conn.py +1 -53
- machineconfig/scripts/python/helpers_repos/action.py +209 -0
- machineconfig/scripts/python/helpers_repos/action_helper.py +150 -0
- machineconfig/scripts/python/{repos_helpers → helpers_repos}/clone.py +0 -1
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +80 -37
- machineconfig/scripts/python/{repos_helpers → helpers_repos}/entrypoint.py +5 -5
- machineconfig/scripts/python/helpers_repos/grource.py +2 -2
- machineconfig/scripts/python/{repos_helpers → helpers_repos}/record.py +3 -2
- machineconfig/scripts/python/helpers_repos/repo_analyzer_1.py +160 -0
- machineconfig/scripts/python/{repos_helpers/count_lines.py → helpers_repos/repo_analyzer_2.py} +113 -192
- machineconfig/scripts/python/{repos_helpers → helpers_repos}/sync.py +5 -5
- machineconfig/scripts/python/{sessions_helpers → 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 +26 -35
- machineconfig/scripts/python/{entry.py → mcfg_entry.py} +24 -10
- machineconfig/scripts/python/msearch.py +72 -0
- machineconfig/scripts/python/sessions.py +101 -38
- machineconfig/scripts/python/terminal.py +136 -0
- machineconfig/scripts/python/utils.py +62 -0
- machineconfig/scripts/windows/wrap_mcfg.ps1 +63 -0
- 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 +3 -2
- machineconfig/settings/lf/linux/lfrc +10 -11
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/lf/windows/lfrc +15 -17
- machineconfig/settings/lf/windows/mkfile.ps1 +1 -1
- machineconfig/settings/linters/.ruff.toml +1 -1
- machineconfig/settings/marimo/marimo.toml +80 -0
- machineconfig/settings/marimo/snippets/globalize.py +34 -0
- machineconfig/settings/shells/bash/init.sh +57 -10
- 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 +59 -23
- 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/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/settings/zellij/layouts/st.kdl +39 -8
- machineconfig/setup_linux/__init__.py +2 -2
- machineconfig/setup_linux/apps_desktop.sh +8 -27
- machineconfig/setup_linux/web_shortcuts/interactive.sh +27 -11
- 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 +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 +26 -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/code.py +143 -167
- 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/headers.py +6 -11
- machineconfig/utils/files/read.py +3 -9
- 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 +44 -101
- 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} +39 -87
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +17 -63
- machineconfig/utils/io.py +77 -4
- machineconfig/utils/links.py +56 -38
- machineconfig/utils/meta.py +235 -145
- machineconfig/utils/options.py +46 -18
- machineconfig/utils/options_tv.py +119 -0
- machineconfig/utils/path_extended.py +46 -97
- machineconfig/utils/path_helper.py +76 -23
- machineconfig/utils/procs.py +10 -23
- machineconfig/utils/scheduler.py +84 -115
- 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/ssh.py +214 -476
- 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 +3 -113
- machineconfig/utils/upgrade_packages.py +114 -28
- machineconfig/utils/ve.py +12 -4
- machineconfig-8.12.dist-info/METADATA +132 -0
- machineconfig-8.12.dist-info/RECORD +504 -0
- {machineconfig-6.23.dist-info → machineconfig-8.12.dist-info}/entry_points.txt +5 -1
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/jobs/installer/linux_scripts/timescaledb.sh +0 -71
- 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/windows/archive/archive_pygraphviz.ps1 +0 -12
- machineconfig/jobs/windows/archive/openssh-server_add_key.ps1 +0 -7
- machineconfig/jobs/windows/archive/openssh-server_copy-ssh-id.ps1 +0 -14
- 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/other/share_smb +0 -1
- machineconfig/scripts/linux/other/switch_ip +0 -20
- machineconfig/scripts/linux/skrg +0 -4
- machineconfig/scripts/linux/warp-cli.sh +0 -122
- machineconfig/scripts/linux/z_ls +0 -104
- machineconfig/scripts/python/ai/command_runner/prompt.txt +0 -9
- machineconfig/scripts/python/devops_helpers/cli_config.py +0 -81
- machineconfig/scripts/python/devops_helpers/cli_config_dotfile.py +0 -84
- machineconfig/scripts/python/devops_helpers/cli_data.py +0 -18
- machineconfig/scripts/python/devops_helpers/cli_nw.py +0 -73
- machineconfig/scripts/python/devops_helpers/cli_self.py +0 -117
- machineconfig/scripts/python/devops_helpers/cli_share_server.py +0 -104
- machineconfig/scripts/python/helper_navigator/__init__.py +0 -20
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_crush.py +0 -37
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_gemini.py +0 -44
- machineconfig/scripts/python/helpers_fire/agentic_frameworks/fire_qwen.py +0 -43
- machineconfig/scripts/python/helpers_fire/fire_agents_helper_types.py +0 -30
- machineconfig/scripts/python/helpers_fire/prompt.txt +0 -2
- machineconfig/scripts/python/helpers_fire/template.sh +0 -15
- machineconfig/scripts/python/helpers_repos/secure_repo.py +0 -15
- machineconfig/scripts/python/nw/add_ssh_key.py +0 -148
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +0 -66
- machineconfig/scripts/python/repos_helpers/action.py +0 -378
- machineconfig/scripts/python/repos_helpers/count_lines_frontend.py +0 -17
- machineconfig/scripts/windows/fzfb.ps1 +0 -3
- machineconfig/scripts/windows/fzfg.ps1 +0 -2
- machineconfig/scripts/windows/fzfrga.bat +0 -20
- 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/shells/pwsh/profile.ps1 +0 -0
- machineconfig/settings/yazi/keymap.toml +0 -0
- machineconfig/settings/yazi/yazi.toml +0 -4
- machineconfig/setup_linux/apps.sh +0 -66
- machineconfig/setup_linux/nix/cli_installation.sh +0 -137
- machineconfig/setup_linux/ssh/openssh_all.sh +0 -25
- machineconfig/setup_linux/ssh/openssh_wsl.sh +0 -38
- 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/setup_windows/wt_and_pwsh/__init__.py +0 -0
- machineconfig/utils/installer_utils/installer.py +0 -225
- machineconfig-6.23.dist-info/METADATA +0 -84
- machineconfig-6.23.dist-info/RECORD +0 -428
- machineconfig/cluster/sessions_managers/{utils → helpers}/load_balancer_helper.py +0 -0
- machineconfig/cluster/sessions_managers/{helpers → zellij_utils}/zellij_local_helper.py +0 -0
- machineconfig/cluster/sessions_managers/{helpers → zellij_utils}/zellij_local_helper_restart.py +0 -0
- machineconfig/cluster/sessions_managers/{helpers → zellij_utils}/zellij_local_manager_helper.py +0 -0
- machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- machineconfig/jobs/{linux/msc → installer/linux_scripts}/network.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/web_shortcuts → jobs/scripts/bash_scripts}/android.sh +0 -0
- machineconfig/jobs/{linux/msc → scripts/bash_scripts}/lid.sh +0 -0
- machineconfig/{setup_linux/others → jobs/scripts/bash_scripts}/mint_keyboard_shortcuts.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/{jobs/python → scripts/python/ai/utils}/__init__.py +0 -0
- machineconfig/scripts/python/{cloud_helpers → helpers_agents}/__init__.py +0 -0
- machineconfig/scripts/python/{croshell_helpers → helpers_agents/agentic_frameworks}/__init__.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_help_search.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers_agents}/fire_agents_load_balancer.py +0 -0
- machineconfig/scripts/python/{helpers_fire → helpers_agents/templates}/template.ps1 +0 -0
- machineconfig/scripts/python/{devops_helpers → helpers_cloud}/__init__.py +0 -0
- machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/cloud_helpers.py +1 -1
- /machineconfig/scripts/python/{cloud_helpers → helpers_cloud}/helpers5.py +0 -0
- /machineconfig/scripts/python/{devops_helpers/themes → helpers_croshell}/__init__.py +0 -0
- /machineconfig/scripts/python/{croshell_helpers → helpers_croshell}/pomodoro.py +0 -0
- /machineconfig/scripts/python/{croshell_helpers → helpers_croshell}/scheduler.py +0 -0
- /machineconfig/scripts/python/{croshell_helpers → helpers_croshell}/viewer.py +0 -0
- /machineconfig/scripts/python/{croshell_helpers → helpers_croshell}/viewer_template.py +0 -0
- /machineconfig/scripts/python/{helpers_fire → helpers_devops}/__init__.py +0 -0
- /machineconfig/scripts/python/{helpers_fire/agentic_frameworks → helpers_devops/themes}/__init__.py +0 -0
- /machineconfig/scripts/python/{devops_helpers → helpers_devops}/themes/choose_pwsh_theme.ps1 +0 -0
- /machineconfig/{jobs/windows/msc/cli_agents.bat → scripts/python/helpers_devops/themes/choose_starship_theme.ps1} +0 -0
- /machineconfig/{jobs/windows/msc/cli_agents.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
- /machineconfig/scripts/python/{helper_navigator → helpers_navigator}/data_models.py +0 -0
- /machineconfig/scripts/python/{helper_navigator → helpers_navigator}/search_bar.py +0 -0
- /machineconfig/scripts/python/{helpers_repos → 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/{repos_helpers → helpers_repos}/update.py +0 -0
- /machineconfig/scripts/python/{nw → helpers_sessions}/__init__.py +0 -0
- /machineconfig/{scripts/python/sessions_helpers → settings/wt}/__init__.py +0 -0
- /machineconfig/{setup_windows/wt_and_pwsh → settings/wt}/set_wt_settings.py +0 -0
- {machineconfig-6.23.dist-info → machineconfig-8.12.dist-info}/WHEEL +0 -0
- {machineconfig-6.23.dist-info → machineconfig-8.12.dist-info}/top_level.txt +0 -0
machineconfig/utils/ssh.py
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
from typing import Callable, Optional, Any, Union
|
|
1
|
+
from typing import Callable, Optional, Any, cast, Union, Literal
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
import platform
|
|
5
|
+
from machineconfig.scripts.python.helpers_utils.python import MachineSpecs
|
|
6
|
+
from machineconfig.utils.code import get_uv_run_command
|
|
4
7
|
import rich.console
|
|
5
|
-
from machineconfig.utils.terminal import Response
|
|
6
|
-
from machineconfig.utils.accessories import pprint
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
MACHINECONFIG_VERSION = "machineconfig>=5.87"
|
|
10
|
-
DEFAULT_PICKLE_SUBDIR = "tmp_results/tmp_scripts/ssh"
|
|
8
|
+
from machineconfig.utils.terminal import Response
|
|
9
|
+
from machineconfig.utils.accessories import pprint, randstr
|
|
10
|
+
from machineconfig.utils.meta import lambda_to_python_script
|
|
11
|
+
from machineconfig.utils.ssh_utils.abc import DEFAULT_PICKLE_SUBDIR
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class SSH:
|
|
15
|
+
@staticmethod
|
|
16
|
+
def from_config_file(host: str) -> "SSH":
|
|
17
|
+
"""Create SSH instance from SSH config file entry."""
|
|
18
|
+
return SSH(host=host, username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
|
|
19
|
+
|
|
14
20
|
def __init__(
|
|
15
|
-
self,
|
|
21
|
+
self,
|
|
22
|
+
host: Optional[str],
|
|
23
|
+
username: Optional[str],
|
|
24
|
+
hostname: Optional[str],
|
|
25
|
+
ssh_key_path: Optional[str],
|
|
26
|
+
password: Optional[str],
|
|
27
|
+
port: int,
|
|
28
|
+
enable_compression: bool,
|
|
29
|
+
):
|
|
16
30
|
self.password = password
|
|
17
31
|
self.enable_compression = enable_compression
|
|
18
32
|
|
|
@@ -21,7 +35,6 @@ class SSH:
|
|
|
21
35
|
self.username: str
|
|
22
36
|
self.port: int = port
|
|
23
37
|
self.proxycommand: Optional[str] = None
|
|
24
|
-
import platform
|
|
25
38
|
import paramiko # type: ignore
|
|
26
39
|
import getpass
|
|
27
40
|
|
|
@@ -48,7 +61,9 @@ class SSH:
|
|
|
48
61
|
else:
|
|
49
62
|
ssh_key_path = wildcard_identity_file
|
|
50
63
|
except (FileNotFoundError, KeyError):
|
|
51
|
-
assert "@" in host or ":" in host,
|
|
64
|
+
assert "@" in host or ":" in host, (
|
|
65
|
+
f"Host must be in the form of `username@hostname:port` or `username@hostname` or `hostname:port`, but it is: {host}"
|
|
66
|
+
)
|
|
52
67
|
if "@" in host:
|
|
53
68
|
self.username, self.hostname = host.split("@")
|
|
54
69
|
else:
|
|
@@ -68,7 +83,10 @@ class SSH:
|
|
|
68
83
|
self.ssh = paramiko.SSHClient()
|
|
69
84
|
self.ssh.load_system_host_keys()
|
|
70
85
|
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
71
|
-
pprint(
|
|
86
|
+
pprint(
|
|
87
|
+
dict(host=self.host, hostname=self.hostname, username=self.username, password="***", port=self.port, key_filename=self.ssh_key_path),
|
|
88
|
+
title="SSHing To",
|
|
89
|
+
)
|
|
72
90
|
sock = paramiko.ProxyCommand(self.proxycommand) if self.proxycommand is not None else None
|
|
73
91
|
try:
|
|
74
92
|
if password is None:
|
|
@@ -77,11 +95,31 @@ class SSH:
|
|
|
77
95
|
else:
|
|
78
96
|
allow_agent = False
|
|
79
97
|
look_for_keys = False
|
|
80
|
-
self.ssh.connect(
|
|
98
|
+
self.ssh.connect(
|
|
99
|
+
hostname=self.hostname,
|
|
100
|
+
username=self.username,
|
|
101
|
+
password=self.password,
|
|
102
|
+
port=self.port,
|
|
103
|
+
key_filename=self.ssh_key_path,
|
|
104
|
+
compress=self.enable_compression,
|
|
105
|
+
sock=sock,
|
|
106
|
+
allow_agent=allow_agent,
|
|
107
|
+
look_for_keys=look_for_keys,
|
|
108
|
+
) # type: ignore
|
|
81
109
|
except Exception as _err:
|
|
82
110
|
rich.console.Console().print_exception()
|
|
83
111
|
self.password = getpass.getpass(f"Enter password for {self.username}@{self.hostname}: ")
|
|
84
|
-
self.ssh.connect(
|
|
112
|
+
self.ssh.connect(
|
|
113
|
+
hostname=self.hostname,
|
|
114
|
+
username=self.username,
|
|
115
|
+
password=self.password,
|
|
116
|
+
port=self.port,
|
|
117
|
+
key_filename=self.ssh_key_path,
|
|
118
|
+
compress=self.enable_compression,
|
|
119
|
+
sock=sock,
|
|
120
|
+
allow_agent=False,
|
|
121
|
+
look_for_keys=False,
|
|
122
|
+
) # type: ignore
|
|
85
123
|
try:
|
|
86
124
|
self.sftp: Optional[paramiko.SFTPClient] = self.ssh.open_sftp()
|
|
87
125
|
except Exception as err:
|
|
@@ -96,7 +134,9 @@ class SSH:
|
|
|
96
134
|
self.task: Optional[Any] = None
|
|
97
135
|
|
|
98
136
|
def __enter__(self) -> "RichProgressWrapper":
|
|
99
|
-
self.progress = Progress(
|
|
137
|
+
self.progress = Progress(
|
|
138
|
+
SpinnerColumn(), TextColumn("[bold blue]{task.description}"), BarColumn(), FileSizeColumn(), TransferSpeedColumn()
|
|
139
|
+
)
|
|
100
140
|
self.progress.start()
|
|
101
141
|
self.task = self.progress.add_task("Transferring...", total=0)
|
|
102
142
|
return self
|
|
@@ -108,514 +148,212 @@ class SSH:
|
|
|
108
148
|
def view_bar(self, transferred: int, total: int) -> None:
|
|
109
149
|
if self.progress and self.task is not None:
|
|
110
150
|
self.progress.update(self.task, completed=transferred, total=total)
|
|
111
|
-
|
|
112
151
|
self.tqdm_wrap = RichProgressWrapper
|
|
113
|
-
|
|
114
|
-
self.
|
|
115
|
-
|
|
152
|
+
from machineconfig.scripts.python.helpers_utils.python import get_machine_specs
|
|
153
|
+
self.local_specs: MachineSpecs = get_machine_specs()
|
|
154
|
+
resp = self.run_shell_cmd_on_remote(
|
|
155
|
+
command="""~/.local/bin/utils get-machine-specs """,
|
|
156
|
+
verbose_output=False,
|
|
157
|
+
description="Getting remote machine specs",
|
|
158
|
+
strict_stderr=False,
|
|
159
|
+
strict_return_code=False,
|
|
160
|
+
)
|
|
161
|
+
json_str = resp.op
|
|
162
|
+
import ast
|
|
163
|
+
self.remote_specs: MachineSpecs = cast(MachineSpecs, ast.literal_eval(json_str))
|
|
116
164
|
self.terminal_responses: list[Response] = []
|
|
117
|
-
|
|
165
|
+
|
|
166
|
+
from rich import inspect
|
|
167
|
+
|
|
168
|
+
# local_info = dict(distro=self.local_specs.get("distro"), system=self.local_specs.get("system"), home_dir=self.local_specs.get("home_dir"))
|
|
169
|
+
# remote_info = dict(distro=self.remote_specs.get("distro"), system=self.remote_specs.get("system"), home_dir=self.remote_specs.get("home_dir"))
|
|
170
|
+
|
|
171
|
+
console = rich.console.Console()
|
|
172
|
+
|
|
173
|
+
from io import StringIO
|
|
174
|
+
|
|
175
|
+
local_buffer = StringIO()
|
|
176
|
+
remote_buffer = StringIO()
|
|
177
|
+
local_console = rich.console.Console(file=local_buffer, width=40)
|
|
178
|
+
remote_console = rich.console.Console(file=remote_buffer, width=40)
|
|
179
|
+
inspect(
|
|
180
|
+
type("LocalInfo", (object,), dict(self.local_specs))(), value=False, title="SSHing From", docs=False, dunder=False, sort=False, console=local_console
|
|
181
|
+
)
|
|
182
|
+
inspect(
|
|
183
|
+
type("RemoteInfo", (object,), dict(self.remote_specs))(), value=False, title="SSHing To", docs=False, dunder=False, sort=False, console=remote_console
|
|
184
|
+
)
|
|
185
|
+
local_lines = local_buffer.getvalue().split("\n")
|
|
186
|
+
remote_lines = remote_buffer.getvalue().split("\n")
|
|
187
|
+
max_lines = max(len(local_lines), len(remote_lines))
|
|
188
|
+
for i in range(max_lines):
|
|
189
|
+
left = local_lines[i] if i < len(local_lines) else ""
|
|
190
|
+
right = remote_lines[i] if i < len(remote_lines) else ""
|
|
191
|
+
console.print(f"{left:<50} {right}")
|
|
118
192
|
|
|
119
193
|
def __enter__(self) -> "SSH":
|
|
120
194
|
return self
|
|
195
|
+
|
|
121
196
|
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
122
197
|
self.close()
|
|
198
|
+
|
|
123
199
|
def close(self) -> None:
|
|
124
200
|
if self.sftp is not None:
|
|
125
201
|
self.sftp.close()
|
|
126
202
|
self.sftp = None
|
|
127
203
|
self.ssh.close()
|
|
128
|
-
|
|
129
|
-
if self._remote_machine is None:
|
|
130
|
-
windows_test1 = self.run_shell(command="$env:OS", verbose_output=False, description="Testing Remote OS Type", strict_stderr=False, strict_return_code=False).op
|
|
131
|
-
windows_test2 = self.run_shell(command="echo %OS%", verbose_output=False, description="Testing Remote OS Type Again", strict_stderr=False, strict_return_code=False).op
|
|
132
|
-
if windows_test1 == "Windows_NT" or windows_test2 == "Windows_NT":
|
|
133
|
-
self._remote_machine = "Windows"
|
|
134
|
-
else:
|
|
135
|
-
self._remote_machine = "Linux"
|
|
136
|
-
return self._remote_machine
|
|
137
|
-
def get_local_distro(self) -> str:
|
|
138
|
-
if self._local_distro is None:
|
|
139
|
-
command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
140
|
-
import subprocess
|
|
141
|
-
res = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
|
|
142
|
-
self._local_distro = res
|
|
143
|
-
return res
|
|
144
|
-
return self._local_distro
|
|
145
|
-
def get_remote_distro(self) -> str:
|
|
146
|
-
if self._remote_distro is None:
|
|
147
|
-
command_str = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
148
|
-
res = self.run_shell(command=command_str, verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
|
|
149
|
-
self._remote_distro = res.op_if_successfull_or_default() or ""
|
|
150
|
-
return self._remote_distro
|
|
204
|
+
|
|
151
205
|
def restart_computer(self) -> Response:
|
|
152
|
-
return self.
|
|
206
|
+
return self.run_shell_cmd_on_remote(
|
|
207
|
+
command="Restart-Computer -Force" if self.remote_specs["system"] == "Windows" else "sudo reboot",
|
|
208
|
+
verbose_output=True,
|
|
209
|
+
description="",
|
|
210
|
+
strict_stderr=False,
|
|
211
|
+
strict_return_code=False,
|
|
212
|
+
)
|
|
213
|
+
|
|
153
214
|
def send_ssh_key(self) -> Response:
|
|
154
|
-
self.copy_from_here(source_path=
|
|
155
|
-
if self.
|
|
215
|
+
self.copy_from_here(source_path="~/.ssh/id_rsa.pub", target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
|
|
216
|
+
if self.remote_specs["system"] != "Windows":
|
|
156
217
|
raise RuntimeError("send_ssh_key is only supported for Windows remote machines")
|
|
157
|
-
code_url = "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/refs/heads/main/src/machineconfig/setup_windows/openssh-server_add-sshkey.ps1"
|
|
218
|
+
code_url = "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/refs/heads/main/src/machineconfig/setup_windows/ssh/openssh-server_add-sshkey.ps1"
|
|
158
219
|
import urllib.request
|
|
159
220
|
with urllib.request.urlopen(code_url) as response:
|
|
160
221
|
code = response.read().decode("utf-8")
|
|
161
|
-
return self.
|
|
222
|
+
return self.run_shell_cmd_on_remote(command=code, verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
|
|
162
223
|
|
|
163
224
|
def get_remote_repr(self, add_machine: bool = False) -> str:
|
|
164
|
-
return f"{self.username}@{self.hostname}:{self.port}" + (
|
|
225
|
+
return f"{self.username}@{self.hostname}:{self.port}" + (
|
|
226
|
+
f" [{self.remote_specs['system']}][{self.remote_specs['distro']}]" if add_machine else ""
|
|
227
|
+
)
|
|
165
228
|
def get_local_repr(self, add_machine: bool = False) -> str:
|
|
166
229
|
import getpass
|
|
167
|
-
return f"{getpass.getuser()}@{
|
|
230
|
+
return f"{getpass.getuser()}@{platform.node()}" + (f" [{platform.system()}][{self.local_specs['distro']}]" if add_machine else "")
|
|
231
|
+
|
|
168
232
|
def get_ssh_conn_str(self, command: str) -> str:
|
|
169
|
-
return
|
|
233
|
+
return (
|
|
234
|
+
"ssh "
|
|
235
|
+
+ (f" -i {self.ssh_key_path}" if self.ssh_key_path else "")
|
|
236
|
+
+ self.get_remote_repr(add_machine=False).replace(":", " -p ")
|
|
237
|
+
+ (f" -t {command} " if command != "" else " ")
|
|
238
|
+
)
|
|
239
|
+
|
|
170
240
|
def __repr__(self) -> str:
|
|
171
241
|
return f"local {self.get_local_repr(add_machine=True)} >>> SSH TO >>> remote {self.get_remote_repr(add_machine=True)}"
|
|
172
242
|
|
|
173
|
-
def
|
|
174
|
-
print(f"""💻 [LOCAL EXECUTION] Running command on node: {self.
|
|
243
|
+
def run_shell_cmd_on_local(self, command: str) -> Response:
|
|
244
|
+
print(f"""💻 [LOCAL EXECUTION] Running command on node: {self.local_specs["system"]} Command: {command}""")
|
|
175
245
|
res = Response(cmd=command)
|
|
176
246
|
res.output.returncode = os.system(command)
|
|
177
247
|
return res
|
|
178
248
|
|
|
179
|
-
|
|
180
|
-
def run_shell(self, command: str, verbose_output: bool, description: str, strict_stderr: bool, strict_return_code: bool) -> Response:
|
|
249
|
+
def run_shell_cmd_on_remote(self, command: str, verbose_output: bool, description: str, strict_stderr: bool, strict_return_code: bool) -> Response:
|
|
181
250
|
raw = self.ssh.exec_command(command)
|
|
182
251
|
res = Response(stdin=raw[0], stdout=raw[1], stderr=raw[2], cmd=command, desc=description) # type: ignore
|
|
183
252
|
if verbose_output:
|
|
184
253
|
res.print()
|
|
185
254
|
else:
|
|
186
|
-
res.capture().print_if_unsuccessful(
|
|
187
|
-
|
|
255
|
+
res.capture().print_if_unsuccessful(
|
|
256
|
+
desc=description, strict_err=strict_stderr, strict_returncode=strict_return_code, assert_success=False
|
|
257
|
+
)
|
|
258
|
+
# self.terminal_responses.append(res)
|
|
188
259
|
return res
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
with_clause = " --with " + '"', ",".join(dependencies) + '"'
|
|
260
|
+
|
|
261
|
+
def _run_py_prep(self, python_code: str, uv_with: Optional[list[str]], uv_project_dir: Optional[str], on: Literal["local", "remote"]) -> str:
|
|
262
|
+
py_path = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/runpy_{randstr()}.py")
|
|
263
|
+
py_path.parent.mkdir(parents=True, exist_ok=True)
|
|
264
|
+
py_path.write_text(python_code, encoding="utf-8")
|
|
265
|
+
self.copy_from_here(source_path=str(py_path), target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
|
|
266
|
+
if uv_with is not None and len(uv_with) > 0:
|
|
267
|
+
with_clause = " --with " + '"' + ",".join(uv_with) + '"'
|
|
198
268
|
else:
|
|
199
269
|
with_clause = ""
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
270
|
+
if uv_project_dir is not None:
|
|
271
|
+
with_clause += f" --project {uv_project_dir}"
|
|
272
|
+
else:
|
|
273
|
+
with_clause += ""
|
|
274
|
+
match on:
|
|
275
|
+
case "local":
|
|
276
|
+
uv_run_cmd = get_uv_run_command(platform=self.local_specs["system"])
|
|
277
|
+
case "remote":
|
|
278
|
+
uv_run_cmd = get_uv_run_command(platform=self.remote_specs["system"])
|
|
279
|
+
case _:
|
|
280
|
+
raise ValueError(f"Invalid value for 'on': {on}. Must be 'local' or 'remote'")
|
|
281
|
+
uv_cmd = f"""{uv_run_cmd} {with_clause} python {py_path.relative_to(Path.home())}"""
|
|
282
|
+
return uv_cmd
|
|
209
283
|
|
|
210
|
-
def
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
284
|
+
def run_py_remotely(
|
|
285
|
+
self,
|
|
286
|
+
python_code: str,
|
|
287
|
+
uv_with: Optional[list[str]],
|
|
288
|
+
uv_project_dir: Optional[str],
|
|
289
|
+
description: str,
|
|
290
|
+
verbose_output: bool,
|
|
291
|
+
strict_stderr: bool,
|
|
292
|
+
strict_return_code: bool,
|
|
293
|
+
) -> Response:
|
|
294
|
+
uv_cmd = self._run_py_prep(python_code=python_code, uv_with=uv_with, uv_project_dir=uv_project_dir, on="remote")
|
|
295
|
+
return self.run_shell_cmd_on_remote(
|
|
296
|
+
command=uv_cmd,
|
|
297
|
+
verbose_output=verbose_output,
|
|
298
|
+
description=description or f"run_py on {self.get_remote_repr(add_machine=False)}",
|
|
299
|
+
strict_stderr=strict_stderr,
|
|
300
|
+
strict_return_code=strict_return_code,
|
|
301
|
+
)
|
|
214
302
|
|
|
215
|
-
def
|
|
303
|
+
def run_lambda_function(self, func: Callable[..., Any], import_module: bool, uv_with: Optional[list[str]], uv_project_dir: Optional[str]):
|
|
304
|
+
command = lambda_to_python_script(func,
|
|
305
|
+
in_global=True, import_module=import_module)
|
|
306
|
+
# turns ou that the code below for some reason runs but zellij doesn't start, looks like things are assigned to different user.
|
|
307
|
+
# return self.run_py(python_code=command, uv_with=uv_with, uv_project_dir=uv_project_dir,
|
|
308
|
+
# description=f"run_py_func {func.__name__} on {self.get_remote_repr(add_machine=False)}",
|
|
309
|
+
# verbose_output=True, strict_stderr=True, strict_return_code=True)
|
|
310
|
+
uv_cmd = self._run_py_prep(python_code=command, uv_with=uv_with, uv_project_dir=uv_project_dir, on="remote")
|
|
311
|
+
if self.remote_specs["system"] == "Linux":
|
|
312
|
+
uv_cmd_modified = f'bash -l -c "{uv_cmd}"'
|
|
313
|
+
else:
|
|
314
|
+
uv_cmd_modified = uv_cmd
|
|
315
|
+
# This works even withou the modified uv cmd:
|
|
316
|
+
# from machineconfig.utils.code import run_shell_script
|
|
317
|
+
# assert self.host is not None, "SSH host must be specified to run remote commands"
|
|
318
|
+
# process = run_shell_script(f"ssh {self.host} -n '. ~/.profile; . ~/.bashrc; {uv_cmd}'")
|
|
319
|
+
# return process
|
|
320
|
+
return self.run_shell_cmd_on_remote(
|
|
321
|
+
command=uv_cmd_modified,
|
|
322
|
+
verbose_output=True,
|
|
323
|
+
description=f"run_py_func {func.__name__} on {self.get_remote_repr(add_machine=False)}",
|
|
324
|
+
strict_stderr=True,
|
|
325
|
+
strict_return_code=True,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def simple_sftp_get(self, remote_path: str, local_path: Path) -> None:
|
|
216
329
|
"""Simple SFTP get without any recursion or path expansion - for internal use only."""
|
|
217
330
|
if self.sftp is None:
|
|
218
331
|
raise RuntimeError(f"SFTP connection not available for {self.hostname}")
|
|
219
332
|
local_path.parent.mkdir(parents=True, exist_ok=True)
|
|
220
333
|
self.sftp.get(remotepath=remote_path, localpath=str(local_path))
|
|
221
334
|
|
|
222
|
-
def
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
from pathlib import Path
|
|
226
|
-
import shutil
|
|
227
|
-
import json
|
|
228
|
-
directory_path = Path(target_dir_path).expanduser()
|
|
229
|
-
if overwrite and directory_path.exists():
|
|
230
|
-
if directory_path.is_dir():
|
|
231
|
-
shutil.rmtree(directory_path)
|
|
232
|
-
else:
|
|
233
|
-
directory_path.unlink()
|
|
234
|
-
directory_path.parent.mkdir(parents=True, exist_ok=True)
|
|
235
|
-
result_path_posix = directory_path.as_posix()
|
|
236
|
-
json_result_path = Path(json_output_path)
|
|
237
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
238
|
-
json_result_path.write_text(json.dumps(result_path_posix, indent=2), encoding="utf-8")
|
|
239
|
-
print(json_result_path.as_posix())
|
|
240
|
-
return result_path_posix
|
|
241
|
-
from machineconfig.utils.meta import function_to_script
|
|
242
|
-
from machineconfig.utils.accessories import randstr
|
|
243
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
244
|
-
command = function_to_script(func=create_target_dir, call_with_kwargs={"target_dir_path": Path(target_path).as_posix(), "overwrite": overwrite_existing, "json_output_path": remote_json_output})
|
|
245
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description=f"Creating target directory `{Path(target_path).parent.as_posix()}` @ {self.get_remote_repr(add_machine=False)}", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
246
|
-
remote_json_path = response.op.strip()
|
|
247
|
-
if not remote_json_path:
|
|
248
|
-
raise RuntimeError(f"Failed to create target directory {target_path} - no response from remote")
|
|
249
|
-
from machineconfig.utils.accessories import randstr
|
|
250
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
251
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
252
|
-
import json
|
|
253
|
-
try:
|
|
254
|
-
result = json.loads(local_json.read_text(encoding="utf-8"))
|
|
255
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
256
|
-
raise RuntimeError(f"Failed to create target directory {target_path} - invalid JSON response: {err}") from err
|
|
257
|
-
finally:
|
|
258
|
-
if local_json.exists():
|
|
259
|
-
local_json.unlink()
|
|
260
|
-
assert isinstance(result, str), f"Failed to create target directory {target_path} on remote"
|
|
261
|
-
return result
|
|
335
|
+
def create_parent_dir_and_check_if_exists(self, path_rel2home: str, overwrite_existing: bool) -> None:
|
|
336
|
+
from machineconfig.utils.ssh_utils.utils import create_dir_and_check_if_exists
|
|
337
|
+
return create_dir_and_check_if_exists(self, path_rel2home=path_rel2home, overwrite_existing=overwrite_existing)
|
|
262
338
|
|
|
339
|
+
def check_remote_is_dir(self, source_path: Union[str, Path]) -> bool:
|
|
340
|
+
from machineconfig.utils.ssh_utils.utils import check_remote_is_dir
|
|
341
|
+
return check_remote_is_dir(self, source_path=source_path)
|
|
342
|
+
|
|
343
|
+
def expand_remote_path(self, source_path: Union[str, Path]) -> str:
|
|
344
|
+
from machineconfig.utils.ssh_utils.utils import expand_remote_path
|
|
345
|
+
return expand_remote_path(self, source_path=source_path)
|
|
346
|
+
|
|
347
|
+
def copy_from_here(self, source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool) -> None:
|
|
348
|
+
from machineconfig.utils.ssh_utils.copy_from_here import copy_from_here
|
|
349
|
+
return copy_from_here(self, source_path=source_path, target_rel2home=target_rel2home, compress_with_zip=compress_with_zip, recursive=recursive, overwrite_existing=overwrite_existing)
|
|
350
|
+
|
|
351
|
+
def copy_to_here(self, source: Union[str, Path], target: Optional[Union[str, Path]], compress_with_zip: bool, recursive: bool, internal_call: bool = False) -> None:
|
|
352
|
+
from machineconfig.utils.ssh_utils.copy_to_here import copy_to_here
|
|
353
|
+
return copy_to_here(self, source=source, target=target, compress_with_zip=compress_with_zip, recursive=recursive, internal_call=internal_call)
|
|
354
|
+
|
|
355
|
+
|
|
263
356
|
|
|
264
|
-
def copy_from_here(self, source_path: Union[str, Path], target_path: Optional[Union[str, Path]], compress_with_zip: bool, recursive: bool, overwrite_existing: bool) -> Path:
|
|
265
|
-
if self.sftp is None:
|
|
266
|
-
raise RuntimeError(f"SFTP connection not available for {self.hostname}. Cannot transfer files.")
|
|
267
|
-
|
|
268
|
-
source_obj = Path(source_path).expanduser().absolute()
|
|
269
|
-
if not source_obj.exists():
|
|
270
|
-
raise RuntimeError(f"SSH Error: source `{source_obj}` does not exist!")
|
|
271
|
-
|
|
272
|
-
if target_path is None:
|
|
273
|
-
try:
|
|
274
|
-
target_path_relative = source_obj.relative_to(Path.home())
|
|
275
|
-
target_path = Path("~") / target_path_relative
|
|
276
|
-
except ValueError:
|
|
277
|
-
raise RuntimeError(f"If target is not specified, source must be relative to home directory, but got: {source_obj}")
|
|
278
|
-
|
|
279
|
-
if not compress_with_zip and source_obj.is_dir():
|
|
280
|
-
if not recursive:
|
|
281
|
-
raise RuntimeError(f"SSH Error: source `{source_obj}` is a directory! Set `recursive=True` for recursive sending or `compress_with_zip=True` to zip it first.")
|
|
282
|
-
file_paths_to_upload: list[Path] = [file_path for file_path in source_obj.rglob("*") if file_path.is_file()]
|
|
283
|
-
remote_root = self._create_remote_target_dir(target_path=target_path, overwrite_existing=overwrite_existing)
|
|
284
|
-
for idx, file_path in enumerate(file_paths_to_upload):
|
|
285
|
-
print(f" {idx + 1:03d}. {file_path}")
|
|
286
|
-
for file_path in file_paths_to_upload:
|
|
287
|
-
remote_file_target = Path(remote_root).joinpath(file_path.relative_to(source_obj))
|
|
288
|
-
self.copy_from_here(source_path=file_path, target_path=remote_file_target, compress_with_zip=False, recursive=False, overwrite_existing=overwrite_existing)
|
|
289
|
-
return Path(remote_root)
|
|
290
|
-
if compress_with_zip:
|
|
291
|
-
print("🗜️ ZIPPING ...")
|
|
292
|
-
import shutil
|
|
293
|
-
zip_path = Path(str(source_obj) + "_archive")
|
|
294
|
-
if source_obj.is_dir():
|
|
295
|
-
shutil.make_archive(str(zip_path), "zip", source_obj)
|
|
296
|
-
else:
|
|
297
|
-
shutil.make_archive(str(zip_path), "zip", source_obj.parent, source_obj.name)
|
|
298
|
-
source_obj = Path(str(zip_path) + ".zip")
|
|
299
|
-
if not str(target_path).endswith(".zip"):
|
|
300
|
-
target_path = Path(str(target_path) + ".zip")
|
|
301
|
-
remotepath_str = self._create_remote_target_dir(target_path=target_path, overwrite_existing=overwrite_existing)
|
|
302
|
-
remotepath = Path(remotepath_str)
|
|
303
|
-
print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {remotepath.as_posix()}""")
|
|
304
|
-
try:
|
|
305
|
-
with self.tqdm_wrap(ascii=True, unit="b", unit_scale=True) as pbar:
|
|
306
|
-
if self.sftp is None: # type: ignore[unreachable]
|
|
307
|
-
raise RuntimeError(f"SFTP connection lost for {self.hostname}")
|
|
308
|
-
self.sftp.put(localpath=str(source_obj), remotepath=remotepath.as_posix(), callback=pbar.view_bar) # type: ignore
|
|
309
|
-
except Exception:
|
|
310
|
-
if compress_with_zip and source_obj.exists() and str(source_obj).endswith("_archive.zip"):
|
|
311
|
-
source_obj.unlink()
|
|
312
|
-
raise
|
|
313
|
-
|
|
314
|
-
if compress_with_zip:
|
|
315
|
-
def unzip_archive(zip_file_path: str, overwrite_flag: bool) -> None:
|
|
316
|
-
from pathlib import Path
|
|
317
|
-
import shutil
|
|
318
|
-
import zipfile
|
|
319
|
-
archive_path = Path(zip_file_path).expanduser()
|
|
320
|
-
extraction_directory = archive_path.parent / archive_path.stem
|
|
321
|
-
if overwrite_flag and extraction_directory.exists():
|
|
322
|
-
shutil.rmtree(extraction_directory)
|
|
323
|
-
with zipfile.ZipFile(archive_path, "r") as archive_handle:
|
|
324
|
-
archive_handle.extractall(extraction_directory)
|
|
325
|
-
archive_path.unlink()
|
|
326
|
-
from machineconfig.utils.meta import function_to_script
|
|
327
|
-
command = function_to_script(func=unzip_archive, call_with_kwargs={"zip_file_path": remotepath.as_posix(), "overwrite_flag": overwrite_existing})
|
|
328
|
-
_resp = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description=f"UNZIPPING {remotepath.as_posix()}", verbose_output=False, strict_stderr=True, strict_return_code=True)
|
|
329
|
-
source_obj.unlink()
|
|
330
|
-
print("\n")
|
|
331
|
-
return source_obj
|
|
332
|
-
|
|
333
|
-
def _check_remote_is_dir(self, source_path: Union[str, Path]) -> bool:
|
|
334
|
-
"""Helper to check if a remote path is a directory."""
|
|
335
|
-
def check_is_dir(path_to_check: str, json_output_path: str) -> bool:
|
|
336
|
-
from pathlib import Path
|
|
337
|
-
import json
|
|
338
|
-
is_directory = Path(path_to_check).expanduser().absolute().is_dir()
|
|
339
|
-
json_result_path = Path(json_output_path)
|
|
340
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
341
|
-
json_result_path.write_text(json.dumps(is_directory, indent=2), encoding="utf-8")
|
|
342
|
-
print(json_result_path.as_posix())
|
|
343
|
-
return is_directory
|
|
344
|
-
|
|
345
|
-
from machineconfig.utils.meta import function_to_script
|
|
346
|
-
from machineconfig.utils.accessories import randstr
|
|
347
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
348
|
-
command = function_to_script(func=check_is_dir, call_with_kwargs={"path_to_check": str(source_path), "json_output_path": remote_json_output})
|
|
349
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description=f"Check if source `{source_path}` is a dir", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
350
|
-
remote_json_path = response.op.strip()
|
|
351
|
-
if not remote_json_path:
|
|
352
|
-
raise RuntimeError(f"Failed to check if {source_path} is directory - no response from remote")
|
|
353
|
-
from machineconfig.utils.accessories import randstr
|
|
354
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
355
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
356
|
-
import json
|
|
357
|
-
try:
|
|
358
|
-
result = json.loads(local_json.read_text(encoding="utf-8"))
|
|
359
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
360
|
-
raise RuntimeError(f"Failed to check if {source_path} is directory - invalid JSON response: {err}") from err
|
|
361
|
-
finally:
|
|
362
|
-
if local_json.exists():
|
|
363
|
-
local_json.unlink()
|
|
364
|
-
assert isinstance(result, bool), f"Failed to check if {source_path} is directory"
|
|
365
|
-
return result
|
|
366
|
-
|
|
367
|
-
def _expand_remote_path(self, source_path: Union[str, Path]) -> str:
|
|
368
|
-
"""Helper to expand a path on the remote machine."""
|
|
369
|
-
def expand_source(path_to_expand: str, json_output_path: str) -> str:
|
|
370
|
-
from pathlib import Path
|
|
371
|
-
import json
|
|
372
|
-
expanded_path_posix = Path(path_to_expand).expanduser().absolute().as_posix()
|
|
373
|
-
json_result_path = Path(json_output_path)
|
|
374
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
375
|
-
json_result_path.write_text(json.dumps(expanded_path_posix, indent=2), encoding="utf-8")
|
|
376
|
-
print(json_result_path.as_posix())
|
|
377
|
-
return expanded_path_posix
|
|
378
|
-
|
|
379
|
-
from machineconfig.utils.meta import function_to_script
|
|
380
|
-
from machineconfig.utils.accessories import randstr
|
|
381
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
382
|
-
command = function_to_script(func=expand_source, call_with_kwargs={"path_to_expand": str(source_path), "json_output_path": remote_json_output})
|
|
383
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description="Resolving source path by expanding user", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
384
|
-
remote_json_path = response.op.strip()
|
|
385
|
-
if not remote_json_path:
|
|
386
|
-
raise RuntimeError(f"Could not resolve source path {source_path} - no response from remote")
|
|
387
|
-
from machineconfig.utils.accessories import randstr
|
|
388
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
389
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
390
|
-
import json
|
|
391
|
-
try:
|
|
392
|
-
result = json.loads(local_json.read_text(encoding="utf-8"))
|
|
393
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
394
|
-
raise RuntimeError(f"Could not resolve source path {source_path} - invalid JSON response: {err}") from err
|
|
395
|
-
finally:
|
|
396
|
-
if local_json.exists():
|
|
397
|
-
local_json.unlink()
|
|
398
|
-
assert isinstance(result, str), f"Could not resolve source path {source_path}"
|
|
399
|
-
return result
|
|
400
|
-
|
|
401
|
-
def copy_to_here(self, source: Union[str, Path], target: Optional[Union[str, Path]], compress_with_zip: bool = False, recursive: bool = False, internal_call: bool = False) -> Path:
|
|
402
|
-
if self.sftp is None:
|
|
403
|
-
raise RuntimeError(f"SFTP connection not available for {self.hostname}. Cannot transfer files.")
|
|
404
|
-
|
|
405
|
-
if not internal_call:
|
|
406
|
-
print(f"{'⬇️' * 5} SFTP DOWNLOADING FROM `{source}` TO `{target}`")
|
|
407
|
-
|
|
408
|
-
source_obj = Path(source)
|
|
409
|
-
expanded_source = self._expand_remote_path(source_path=source_obj)
|
|
410
|
-
|
|
411
|
-
if not compress_with_zip:
|
|
412
|
-
is_dir = self._check_remote_is_dir(source_path=expanded_source)
|
|
413
|
-
|
|
414
|
-
if is_dir:
|
|
415
|
-
if not recursive:
|
|
416
|
-
raise RuntimeError(f"SSH Error: source `{source_obj}` is a directory! Set recursive=True for recursive transfer or compress_with_zip=True to zip it.")
|
|
417
|
-
|
|
418
|
-
def search_files(directory_path: str, json_output_path: str) -> list[str]:
|
|
419
|
-
from pathlib import Path
|
|
420
|
-
import json
|
|
421
|
-
file_paths_list = [file_path.as_posix() for file_path in Path(directory_path).expanduser().absolute().rglob("*") if file_path.is_file()]
|
|
422
|
-
json_result_path = Path(json_output_path)
|
|
423
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
424
|
-
json_result_path.write_text(json.dumps(file_paths_list, indent=2), encoding="utf-8")
|
|
425
|
-
print(json_result_path.as_posix())
|
|
426
|
-
return file_paths_list
|
|
427
|
-
|
|
428
|
-
from machineconfig.utils.meta import function_to_script
|
|
429
|
-
from machineconfig.utils.accessories import randstr
|
|
430
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
431
|
-
command = function_to_script(func=search_files, call_with_kwargs={"directory_path": expanded_source, "json_output_path": remote_json_output})
|
|
432
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description="Searching for files in source", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
433
|
-
remote_json_path = response.op.strip()
|
|
434
|
-
if not remote_json_path:
|
|
435
|
-
raise RuntimeError(f"Could not resolve source path {source} - no response from remote")
|
|
436
|
-
from machineconfig.utils.accessories import randstr
|
|
437
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
438
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
439
|
-
import json
|
|
440
|
-
try:
|
|
441
|
-
source_list_str = json.loads(local_json.read_text(encoding="utf-8"))
|
|
442
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
443
|
-
raise RuntimeError(f"Could not resolve source path {source} - invalid JSON response: {err}") from err
|
|
444
|
-
finally:
|
|
445
|
-
if local_json.exists():
|
|
446
|
-
local_json.unlink()
|
|
447
|
-
assert isinstance(source_list_str, list), f"Could not resolve source path {source}"
|
|
448
|
-
file_paths_to_download = [Path(file_path_str) for file_path_str in source_list_str]
|
|
449
|
-
|
|
450
|
-
if target is None:
|
|
451
|
-
def collapse_to_home_dir(absolute_path: str, json_output_path: str) -> str:
|
|
452
|
-
from pathlib import Path
|
|
453
|
-
import json
|
|
454
|
-
source_absolute_path = Path(absolute_path).expanduser().absolute()
|
|
455
|
-
try:
|
|
456
|
-
relative_to_home = source_absolute_path.relative_to(Path.home())
|
|
457
|
-
collapsed_path_posix = (Path("~") / relative_to_home).as_posix()
|
|
458
|
-
json_result_path = Path(json_output_path)
|
|
459
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
460
|
-
json_result_path.write_text(json.dumps(collapsed_path_posix, indent=2), encoding="utf-8")
|
|
461
|
-
print(json_result_path.as_posix())
|
|
462
|
-
return collapsed_path_posix
|
|
463
|
-
except ValueError:
|
|
464
|
-
raise RuntimeError(f"Source path must be relative to home directory: {source_absolute_path}")
|
|
465
|
-
|
|
466
|
-
from machineconfig.utils.meta import function_to_script
|
|
467
|
-
from machineconfig.utils.accessories import randstr
|
|
468
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
469
|
-
command = function_to_script(func=collapse_to_home_dir, call_with_kwargs={"absolute_path": expanded_source, "json_output_path": remote_json_output})
|
|
470
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description="Finding default target via relative source path", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
471
|
-
remote_json_path_dir = response.op.strip()
|
|
472
|
-
if not remote_json_path_dir:
|
|
473
|
-
raise RuntimeError("Could not resolve target path - no response from remote")
|
|
474
|
-
from machineconfig.utils.accessories import randstr
|
|
475
|
-
local_json_dir = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
476
|
-
self._simple_sftp_get(remote_path=remote_json_path_dir, local_path=local_json_dir)
|
|
477
|
-
import json
|
|
478
|
-
try:
|
|
479
|
-
target_dir_str = json.loads(local_json_dir.read_text(encoding="utf-8"))
|
|
480
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
481
|
-
raise RuntimeError(f"Could not resolve target path - invalid JSON response: {err}") from err
|
|
482
|
-
finally:
|
|
483
|
-
if local_json_dir.exists():
|
|
484
|
-
local_json_dir.unlink()
|
|
485
|
-
assert isinstance(target_dir_str, str), "Could not resolve target path"
|
|
486
|
-
target = Path(target_dir_str)
|
|
487
|
-
|
|
488
|
-
target_dir = Path(target).expanduser().absolute()
|
|
489
|
-
|
|
490
|
-
for idx, file_path in enumerate(file_paths_to_download):
|
|
491
|
-
print(f" {idx + 1:03d}. {file_path}")
|
|
492
|
-
|
|
493
|
-
for file_path in file_paths_to_download:
|
|
494
|
-
local_file_target = target_dir.joinpath(Path(file_path).relative_to(expanded_source))
|
|
495
|
-
self.copy_to_here(source=file_path, target=local_file_target, compress_with_zip=False, recursive=False, internal_call=True)
|
|
496
|
-
|
|
497
|
-
return target_dir
|
|
498
|
-
|
|
499
|
-
if compress_with_zip:
|
|
500
|
-
print("🗜️ ZIPPING ...")
|
|
501
|
-
def zip_source(path_to_zip: str, json_output_path: str) -> str:
|
|
502
|
-
from pathlib import Path
|
|
503
|
-
import shutil
|
|
504
|
-
import json
|
|
505
|
-
source_to_compress = Path(path_to_zip).expanduser().absolute()
|
|
506
|
-
archive_base_path = source_to_compress.parent / (source_to_compress.name + "_archive")
|
|
507
|
-
if source_to_compress.is_dir():
|
|
508
|
-
shutil.make_archive(str(archive_base_path), "zip", source_to_compress)
|
|
509
|
-
else:
|
|
510
|
-
shutil.make_archive(str(archive_base_path), "zip", source_to_compress.parent, source_to_compress.name)
|
|
511
|
-
zip_file_path = str(archive_base_path) + ".zip"
|
|
512
|
-
json_result_path = Path(json_output_path)
|
|
513
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
514
|
-
json_result_path.write_text(json.dumps(zip_file_path, indent=2), encoding="utf-8")
|
|
515
|
-
print(json_result_path.as_posix())
|
|
516
|
-
return zip_file_path
|
|
517
|
-
|
|
518
|
-
from machineconfig.utils.meta import function_to_script
|
|
519
|
-
from machineconfig.utils.accessories import randstr
|
|
520
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
521
|
-
command = function_to_script(func=zip_source, call_with_kwargs={"path_to_zip": expanded_source, "json_output_path": remote_json_output})
|
|
522
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description=f"Zipping source file {source}", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
523
|
-
remote_json_path = response.op.strip()
|
|
524
|
-
if not remote_json_path:
|
|
525
|
-
raise RuntimeError(f"Could not zip {source} - no response from remote")
|
|
526
|
-
from machineconfig.utils.accessories import randstr
|
|
527
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
528
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
529
|
-
import json
|
|
530
|
-
try:
|
|
531
|
-
zipped_path = json.loads(local_json.read_text(encoding="utf-8"))
|
|
532
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
533
|
-
raise RuntimeError(f"Could not zip {source} - invalid JSON response: {err}") from err
|
|
534
|
-
finally:
|
|
535
|
-
if local_json.exists():
|
|
536
|
-
local_json.unlink()
|
|
537
|
-
assert isinstance(zipped_path, str), f"Could not zip {source}"
|
|
538
|
-
source_obj = Path(zipped_path)
|
|
539
|
-
expanded_source = zipped_path
|
|
540
|
-
|
|
541
|
-
if target is None:
|
|
542
|
-
def collapse_to_home(absolute_path: str, json_output_path: str) -> str:
|
|
543
|
-
from pathlib import Path
|
|
544
|
-
import json
|
|
545
|
-
source_absolute_path = Path(absolute_path).expanduser().absolute()
|
|
546
|
-
try:
|
|
547
|
-
relative_to_home = source_absolute_path.relative_to(Path.home())
|
|
548
|
-
collapsed_path_posix = (Path("~") / relative_to_home).as_posix()
|
|
549
|
-
json_result_path = Path(json_output_path)
|
|
550
|
-
json_result_path.parent.mkdir(parents=True, exist_ok=True)
|
|
551
|
-
json_result_path.write_text(json.dumps(collapsed_path_posix, indent=2), encoding="utf-8")
|
|
552
|
-
print(json_result_path.as_posix())
|
|
553
|
-
return collapsed_path_posix
|
|
554
|
-
except ValueError:
|
|
555
|
-
raise RuntimeError(f"Source path must be relative to home directory: {source_absolute_path}")
|
|
556
|
-
|
|
557
|
-
from machineconfig.utils.meta import function_to_script
|
|
558
|
-
from machineconfig.utils.accessories import randstr
|
|
559
|
-
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
560
|
-
command = function_to_script(func=collapse_to_home, call_with_kwargs={"absolute_path": expanded_source, "json_output_path": remote_json_output})
|
|
561
|
-
response = self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description="Finding default target via relative source path", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
562
|
-
remote_json_path = response.op.strip()
|
|
563
|
-
if not remote_json_path:
|
|
564
|
-
raise RuntimeError("Could not resolve target path - no response from remote")
|
|
565
|
-
from machineconfig.utils.accessories import randstr
|
|
566
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
567
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
568
|
-
import json
|
|
569
|
-
try:
|
|
570
|
-
target_str = json.loads(local_json.read_text(encoding="utf-8"))
|
|
571
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
572
|
-
raise RuntimeError(f"Could not resolve target path - invalid JSON response: {err}") from err
|
|
573
|
-
finally:
|
|
574
|
-
if local_json.exists():
|
|
575
|
-
local_json.unlink()
|
|
576
|
-
assert isinstance(target_str, str), "Could not resolve target path"
|
|
577
|
-
target = Path(target_str)
|
|
578
|
-
assert str(target).startswith("~"), f"If target is not specified, source must be relative to home.\n{target=}"
|
|
579
|
-
|
|
580
|
-
target_obj = Path(target).expanduser().absolute()
|
|
581
|
-
target_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
582
|
-
|
|
583
|
-
if compress_with_zip and target_obj.suffix != ".zip":
|
|
584
|
-
target_obj = target_obj.with_suffix(target_obj.suffix + ".zip")
|
|
585
|
-
|
|
586
|
-
print(f"""📥 [DOWNLOAD] Receiving: {expanded_source} ==> Local Path: {target_obj}""")
|
|
587
|
-
try:
|
|
588
|
-
with self.tqdm_wrap(ascii=True, unit="b", unit_scale=True) as pbar:
|
|
589
|
-
if self.sftp is None: # type: ignore[unreachable]
|
|
590
|
-
raise RuntimeError(f"SFTP connection lost for {self.hostname}")
|
|
591
|
-
self.sftp.get(remotepath=expanded_source, localpath=str(target_obj), callback=pbar.view_bar) # type: ignore
|
|
592
|
-
except Exception:
|
|
593
|
-
if target_obj.exists():
|
|
594
|
-
target_obj.unlink()
|
|
595
|
-
raise
|
|
596
|
-
|
|
597
|
-
if compress_with_zip:
|
|
598
|
-
import zipfile
|
|
599
|
-
extract_to = target_obj.parent / target_obj.stem
|
|
600
|
-
with zipfile.ZipFile(target_obj, "r") as zip_ref:
|
|
601
|
-
zip_ref.extractall(extract_to)
|
|
602
|
-
target_obj.unlink()
|
|
603
|
-
target_obj = extract_to
|
|
604
|
-
|
|
605
|
-
def delete_temp_zip(path_to_delete: str) -> None:
|
|
606
|
-
from pathlib import Path
|
|
607
|
-
import shutil
|
|
608
|
-
file_or_dir_path = Path(path_to_delete)
|
|
609
|
-
if file_or_dir_path.exists():
|
|
610
|
-
if file_or_dir_path.is_dir():
|
|
611
|
-
shutil.rmtree(file_or_dir_path)
|
|
612
|
-
else:
|
|
613
|
-
file_or_dir_path.unlink()
|
|
614
|
-
|
|
615
|
-
from machineconfig.utils.meta import function_to_script
|
|
616
|
-
command = function_to_script(func=delete_temp_zip, call_with_kwargs={"path_to_delete": expanded_source})
|
|
617
|
-
self.run_py(python_code=command, dependencies=[MACHINECONFIG_VERSION], venv_path=None, description="Cleaning temp zip files @ remote.", verbose_output=False, strict_stderr=True, strict_return_code=True)
|
|
618
|
-
|
|
619
|
-
print("\n")
|
|
620
|
-
return target_obj
|
|
621
357
|
|
|
358
|
+
if __name__ == "__main__":
|
|
359
|
+
ssh = SSH(host="p51s", username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
|