machineconfig 7.57__py3-none-any.whl → 7.79__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of machineconfig might be problematic. Click here for more details.
- machineconfig/cluster/sessions_managers/utils/maker.py +21 -9
- machineconfig/jobs/installer/custom/boxes.py +2 -2
- machineconfig/jobs/installer/custom/hx.py +3 -3
- machineconfig/jobs/installer/custom_dev/cloudflare_warp_cli.py +23 -0
- machineconfig/jobs/installer/custom_dev/dubdb_adbc.py +1 -1
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +1 -1
- machineconfig/jobs/installer/custom_dev/sysabc.py +36 -28
- machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
- machineconfig/jobs/installer/installer_data.json +127 -25
- machineconfig/jobs/installer/package_groups.py +20 -13
- machineconfig/profile/create_links_export.py +2 -2
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/linux/wrap_mcfg +1 -1
- machineconfig/scripts/python/agents.py +22 -17
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +3 -0
- machineconfig/scripts/python/croshell.py +22 -17
- machineconfig/scripts/python/devops.py +3 -4
- machineconfig/scripts/python/devops_navigator.py +0 -4
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/fire_jobs.py +19 -18
- machineconfig/scripts/python/ftpx.py +36 -12
- machineconfig/scripts/python/helpers/ast_search.py +74 -0
- machineconfig/scripts/python/helpers/qr_code.py +166 -0
- machineconfig/scripts/python/helpers/repo_rag.py +325 -0
- machineconfig/scripts/python/helpers/symantic_search.py +25 -0
- machineconfig/scripts/python/helpers_cloud/cloud_copy.py +28 -21
- machineconfig/scripts/python/helpers_cloud/cloud_helpers.py +1 -1
- machineconfig/scripts/python/helpers_cloud/cloud_mount.py +19 -17
- machineconfig/scripts/python/helpers_cloud/cloud_sync.py +8 -7
- machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
- machineconfig/scripts/python/helpers_croshell/start_slidev.py +6 -7
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
- machineconfig/scripts/python/helpers_devops/cli_nw.py +88 -7
- machineconfig/scripts/python/helpers_devops/cli_self.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +9 -9
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +13 -12
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +7 -6
- machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -73
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
- machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +23 -13
- machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +7 -4
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/helpers_repos/entrypoint.py +2 -1
- machineconfig/scripts/python/helpers_repos/record.py +2 -1
- machineconfig/scripts/python/helpers_sessions/sessions_multiprocess.py +5 -5
- machineconfig/scripts/python/helpers_utils/download.py +152 -0
- machineconfig/scripts/python/helpers_utils/path.py +81 -31
- machineconfig/scripts/python/interactive.py +2 -2
- machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
- machineconfig/scripts/python/msearch.py +21 -2
- machineconfig/scripts/python/nw/address.py +132 -0
- machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
- machineconfig/scripts/python/nw/ssh_debug_linux.py +7 -7
- machineconfig/scripts/python/nw/ssh_debug_windows.py +4 -4
- machineconfig/scripts/python/nw/wsl_windows_transfer.py +3 -2
- machineconfig/scripts/python/sessions.py +35 -20
- machineconfig/scripts/python/terminal.py +2 -2
- machineconfig/scripts/python/utils.py +12 -10
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/settings/lf/windows/lfcd.ps1 +1 -1
- machineconfig/settings/shells/nushell/config.nu +2 -2
- machineconfig/settings/shells/nushell/env.nu +45 -6
- machineconfig/settings/shells/nushell/init.nu +282 -95
- machineconfig/settings/shells/pwsh/init.ps1 +1 -0
- machineconfig/settings/shells/zsh/init.sh +0 -7
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/uv.ps1 +8 -1
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/setup_windows/web_shortcuts/quick_init.ps1 +3 -2
- machineconfig/utils/accessories.py +7 -4
- machineconfig/utils/code.py +6 -4
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/install_from_url.py +180 -0
- machineconfig/utils/installer_utils/installer_class.py +53 -47
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +71 -65
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/meta.py +30 -16
- machineconfig/utils/options.py +4 -4
- machineconfig/utils/path_extended.py +3 -3
- machineconfig/utils/path_helper.py +33 -31
- machineconfig/utils/schemas/layouts/layout_types.py +1 -1
- machineconfig/utils/ssh.py +143 -409
- machineconfig/utils/ssh_utils/abc.py +8 -0
- machineconfig/utils/ssh_utils/copy_from_here.py +110 -0
- machineconfig/utils/ssh_utils/copy_to_here.py +302 -0
- machineconfig/utils/ssh_utils/utils.py +141 -0
- machineconfig/utils/ssh_utils/wsl.py +168 -0
- machineconfig/utils/upgrade_packages.py +2 -1
- machineconfig/utils/ve.py +11 -4
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/METADATA +1 -1
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/RECORD +102 -92
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/entry_points.txt +2 -2
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- machineconfig/scripts/python/explore.py +0 -49
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/{settings/shells/pwsh/profile.ps1 → scripts/python/helpers_fire_command/f.py} +0 -0
- /machineconfig/scripts/{Restore-ThunderbirdProfile.ps1 → windows/mounts/Restore-ThunderbirdProfile.ps1} +0 -0
- /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/WHEEL +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.79.dist-info}/top_level.txt +0 -0
|
@@ -188,15 +188,7 @@ class CommandTree(Tree[CommandInfo]):
|
|
|
188
188
|
description="Configure your shell profile",
|
|
189
189
|
command="devops config shell",
|
|
190
190
|
parent="config",
|
|
191
|
-
help_text="devops config shell <
|
|
192
|
-
))
|
|
193
|
-
|
|
194
|
-
config_node.add("🔗 path - Navigate PATH variable", data=CommandInfo(
|
|
195
|
-
name="path",
|
|
196
|
-
description="Navigate PATH variable with TUI",
|
|
197
|
-
command="devops config path",
|
|
198
|
-
parent="config",
|
|
199
|
-
help_text="devops config path"
|
|
191
|
+
help_text="devops config shell --which <default|nushell>"
|
|
200
192
|
))
|
|
201
193
|
|
|
202
194
|
config_node.add("🔗 starship-theme - Select starship theme", data=CommandInfo(
|
|
@@ -560,7 +552,23 @@ class CommandTree(Tree[CommandInfo]):
|
|
|
560
552
|
description="Choose a process to kill interactively",
|
|
561
553
|
command="utils kill-process",
|
|
562
554
|
parent="utils",
|
|
563
|
-
help_text="utils kill-process"
|
|
555
|
+
help_text="utils kill-process --interactive"
|
|
556
|
+
))
|
|
557
|
+
|
|
558
|
+
utils_node.add("📚 path - Navigate PATH variable", data=CommandInfo(
|
|
559
|
+
name="path",
|
|
560
|
+
description="Navigate PATH variable with TUI",
|
|
561
|
+
command="utils path",
|
|
562
|
+
parent="utils",
|
|
563
|
+
help_text="utils path"
|
|
564
|
+
))
|
|
565
|
+
|
|
566
|
+
utils_node.add("⬆️ upgrade-packages - Upgrade dependencies", data=CommandInfo(
|
|
567
|
+
name="upgrade-packages",
|
|
568
|
+
description="Upgrade project dependencies",
|
|
569
|
+
command="utils upgrade-packages",
|
|
570
|
+
parent="utils",
|
|
571
|
+
help_text="utils upgrade-packages"
|
|
564
572
|
))
|
|
565
573
|
|
|
566
574
|
utils_node.add("⬇️ download - Download file", data=CommandInfo(
|
|
@@ -571,18 +579,42 @@ class CommandTree(Tree[CommandInfo]):
|
|
|
571
579
|
help_text="utils download <url> --destination <path> --decompress"
|
|
572
580
|
))
|
|
573
581
|
|
|
574
|
-
utils_node.add("📄 merge-pdfs - Merge PDF files", data=CommandInfo(
|
|
575
|
-
name="merge-pdfs",
|
|
576
|
-
description="Merge two PDF files into one",
|
|
577
|
-
command="utils merge-pdfs",
|
|
578
|
-
parent="utils",
|
|
579
|
-
help_text="utils merge-pdfs <file1> <file2> --output <file>"
|
|
580
|
-
))
|
|
581
|
-
|
|
582
582
|
utils_node.add("🖥️ get-machine-specs - Get machine specifications", data=CommandInfo(
|
|
583
583
|
name="get-machine-specs",
|
|
584
584
|
description="Get machine specifications",
|
|
585
585
|
command="utils get-machine-specs",
|
|
586
586
|
parent="utils",
|
|
587
587
|
help_text="utils get-machine-specs"
|
|
588
|
+
))
|
|
589
|
+
|
|
590
|
+
utils_node.add("🚀 init-project - Initialize project", data=CommandInfo(
|
|
591
|
+
name="init-project",
|
|
592
|
+
description="Initialize a project with a uv virtual environment and install dev packages",
|
|
593
|
+
command="utils init-project",
|
|
594
|
+
parent="utils",
|
|
595
|
+
help_text="utils init-project"
|
|
596
|
+
))
|
|
597
|
+
|
|
598
|
+
utils_node.add("✏️ edit - Open file in editor", data=CommandInfo(
|
|
599
|
+
name="edit",
|
|
600
|
+
description="Open a file in the default editor",
|
|
601
|
+
command="utils edit",
|
|
602
|
+
parent="utils",
|
|
603
|
+
help_text="utils edit <file>"
|
|
604
|
+
))
|
|
605
|
+
|
|
606
|
+
utils_node.add("📄 pdf-merge - Merge PDF files", data=CommandInfo(
|
|
607
|
+
name="pdf-merge",
|
|
608
|
+
description="Merge two PDF files into one",
|
|
609
|
+
command="utils pdf-merge",
|
|
610
|
+
parent="utils",
|
|
611
|
+
help_text="utils pdf-merge <file1> <file2> --output <file>"
|
|
612
|
+
))
|
|
613
|
+
|
|
614
|
+
utils_node.add("� pdf-compress - Compress PDF file", data=CommandInfo(
|
|
615
|
+
name="pdf-compress",
|
|
616
|
+
description="Compress a PDF file",
|
|
617
|
+
command="utils pdf-compress",
|
|
618
|
+
parent="utils",
|
|
619
|
+
help_text="utils pdf-compress <file> --output <file>"
|
|
588
620
|
))
|
|
@@ -61,8 +61,9 @@ def main(
|
|
|
61
61
|
typer.Exit(code=1)
|
|
62
62
|
return ""
|
|
63
63
|
repo_local_root = PathExtended(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
|
|
64
|
+
local_relative_home = PathExtended(repo_local_root.expanduser().absolute().relative_to(Path.home()))
|
|
64
65
|
PathExtended(CONFIG_ROOT).joinpath("remote").mkdir(parents=True, exist_ok=True)
|
|
65
|
-
repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote",
|
|
66
|
+
repo_remote_root = PathExtended(CONFIG_ROOT).joinpath("remote", local_relative_home)
|
|
66
67
|
repo_remote_root.delete(sure=True)
|
|
67
68
|
try:
|
|
68
69
|
console.print(Panel("📥 DOWNLOADING REMOTE REPOSITORY", title_align="left", border_style="blue"))
|
|
@@ -104,7 +105,7 @@ git pull originEnc master
|
|
|
104
105
|
uv_project_dir = f"""{str(Path.home().joinpath("code/machineconfig"))}"""
|
|
105
106
|
uv_with = None
|
|
106
107
|
else:
|
|
107
|
-
uv_with = ["machineconfig>=7.
|
|
108
|
+
uv_with = ["machineconfig>=7.79"]
|
|
108
109
|
uv_project_dir = None
|
|
109
110
|
|
|
110
111
|
import tempfile
|
|
@@ -135,7 +136,8 @@ git pull originEnc master
|
|
|
135
136
|
def func2(remote_repo: str, local_repo: str, cloud: str):
|
|
136
137
|
from machineconfig.scripts.python.helpers_repos.sync import delete_remote_repo_copy_and_push_local
|
|
137
138
|
delete_remote_repo_copy_and_push_local(remote_repo=remote_repo, local_repo=local_repo, cloud=cloud)
|
|
138
|
-
program_1_py = lambda_to_python_script(lambda: func2(remote_repo=str(repo_remote_root), local_repo=str(repo_local_root), cloud=str(cloud_resolved)),
|
|
139
|
+
program_1_py = lambda_to_python_script(lambda: func2(remote_repo=str(repo_remote_root), local_repo=str(repo_local_root), cloud=str(cloud_resolved)),
|
|
140
|
+
in_global=True, import_module=False)
|
|
139
141
|
program1, _pyfile1 = get_uv_command_executing_python_script(python_script=program_1_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
|
|
140
142
|
# ================================================================================
|
|
141
143
|
option2 = "Delete local repo and replace it with remote copy:"
|
|
@@ -160,7 +162,8 @@ sudo chmod +x $HOME/dotfiles/scripts/linux -R
|
|
|
160
162
|
inspect_repos(repo_local_root=repo_local_root, repo_remote_root=repo_remote_root)
|
|
161
163
|
# program_3_py = function_to_script(func=func, call_with_kwargs={"repo_local_root": str(repo_local_root), "repo_remote_root": str(repo_remote_root)})
|
|
162
164
|
# shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_path=None, executable=executable)
|
|
163
|
-
program_3_py = lambda_to_python_script(lambda: func(repo_local_root=str(repo_local_root), repo_remote_root=str(repo_remote_root)),
|
|
165
|
+
program_3_py = lambda_to_python_script(lambda: func(repo_local_root=str(repo_local_root), repo_remote_root=str(repo_remote_root)),
|
|
166
|
+
in_global=True, import_module=False)
|
|
164
167
|
program3, _pyfile3 = get_uv_command_executing_python_script(python_script=program_3_py, uv_with=uv_with, uv_project_dir=uv_project_dir)
|
|
165
168
|
# ================================================================================
|
|
166
169
|
|
|
@@ -8,7 +8,7 @@ def analyze_repo_development(repo_path: Annotated[str, typer.Argument(..., help=
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
count_lines_path = Path(count_lines.__file__)
|
|
10
10
|
# --project $HOME/code/ machineconfig --group plot
|
|
11
|
-
cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.
|
|
11
|
+
cmd = f"""uv run --python 3.14 --with "machineconfig[plot]>=7.79" {count_lines_path} analyze-over-time {repo_path}"""
|
|
12
12
|
from machineconfig.utils.code import run_shell_script
|
|
13
13
|
run_shell_script(cmd)
|
|
14
14
|
|
|
@@ -38,7 +38,8 @@ def resolve_spec_path(directory: Optional[str], cloud: Optional[str]) -> Path:
|
|
|
38
38
|
repos_root = resolve_directory(directory)
|
|
39
39
|
from machineconfig.utils.path_extended import PathExtended
|
|
40
40
|
if not repos_root.exists() or repos_root.name != "repos.json":
|
|
41
|
-
|
|
41
|
+
relative_repos_root = PathExtended(repos_root).expanduser().absolute().relative_to(Path.home())
|
|
42
|
+
candidate = Path(CONFIG_ROOT).joinpath("repos").joinpath(relative_repos_root).joinpath("repos.json")
|
|
42
43
|
repos_root = candidate
|
|
43
44
|
if not repos_root.exists():
|
|
44
45
|
cloud_name: Optional[str]
|
|
@@ -242,7 +242,8 @@ def main_record(repos_root: Path):
|
|
|
242
242
|
tree_structure = build_tree_structure(repo_records, repos_root)
|
|
243
243
|
print(tree_structure)
|
|
244
244
|
|
|
245
|
-
|
|
245
|
+
relative_repos_root = PathExtended(repos_root).expanduser().absolute().relative_to(Path.home())
|
|
246
|
+
save_path = CONFIG_ROOT.joinpath("repos").joinpath(relative_repos_root).joinpath("repos.json")
|
|
246
247
|
save_json(obj=res, path=save_path, indent=4)
|
|
247
248
|
pprint(f"📁 Result saved at {PathExtended(save_path)}")
|
|
248
249
|
print(">>>>>>>>> Finished Recording")
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional, Annotated
|
|
4
|
-
from pathlib import Path
|
|
5
4
|
import typer
|
|
6
5
|
|
|
7
6
|
|
|
@@ -13,20 +12,21 @@ def create_from_function(
|
|
|
13
12
|
from machineconfig.utils.ve import get_ve_activate_line, get_ve_path_and_ipython_profile
|
|
14
13
|
from machineconfig.utils.options import choose_from_options
|
|
15
14
|
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
16
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
17
15
|
from machineconfig.utils.accessories import get_repo_root
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
18
|
|
|
19
19
|
path_obj = sanitize_path(path)
|
|
20
20
|
if not path_obj.exists():
|
|
21
21
|
suffixes = {".py"}
|
|
22
|
-
choice_file = match_file_name(sub_string=path, search_root=
|
|
22
|
+
choice_file = match_file_name(sub_string=path, search_root=Path.cwd(), suffixes=suffixes)
|
|
23
23
|
elif path_obj.is_dir():
|
|
24
24
|
from machineconfig.utils.path_helper import search_for_files_of_interest
|
|
25
25
|
print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
|
|
26
26
|
files = search_for_files_of_interest(path_obj, suffixes={".py", ".sh", ".ps1"})
|
|
27
27
|
print(f"🔍 Got #{len(files)} results.")
|
|
28
28
|
choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
|
|
29
|
-
choice_file =
|
|
29
|
+
choice_file = Path(choice_file)
|
|
30
30
|
else:
|
|
31
31
|
choice_file = path_obj
|
|
32
32
|
|
|
@@ -52,7 +52,7 @@ def create_from_function(
|
|
|
52
52
|
from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
|
|
53
53
|
layout: LayoutConfig = {"layoutName": "fireNprocess", "layoutTabs": []}
|
|
54
54
|
for an_arg in range(num_process):
|
|
55
|
-
layout["layoutTabs"].append({"tabName": f"tab{an_arg}", "startDir": str(
|
|
55
|
+
layout["layoutTabs"].append({"tabName": f"tab{an_arg}", "startDir": str(Path.cwd()), "command": f"uv run -m fire {choice_file} {choice_function} --idx={an_arg} --idx_max={num_process}"})
|
|
56
56
|
print(layout)
|
|
57
57
|
run_zellij_layout(layout_config=layout)
|
|
58
58
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Optional
|
|
4
|
+
import typer
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def download(
|
|
9
|
+
url: Annotated[Optional[str], typer.Argument(..., help="The URL to download the file from.")] = None,
|
|
10
|
+
decompress: Annotated[bool, typer.Option(..., "--decompress", "-d", help="Decompress the file if it's an archive.")] = False,
|
|
11
|
+
output: Annotated[Optional[str], typer.Option("--output", "-o", help="The output file path.")] = None,
|
|
12
|
+
output_dir: Annotated[Optional[str], typer.Option("--output-dir", help="Directory to place the downloaded file in.")] = None,
|
|
13
|
+
) -> Optional["Path"]:
|
|
14
|
+
|
|
15
|
+
import subprocess
|
|
16
|
+
from urllib.parse import parse_qs, unquote, urlparse
|
|
17
|
+
from requests import Response
|
|
18
|
+
import requests
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
if url is None:
|
|
21
|
+
typer.echo("❌ Error: URL is required.", err=True)
|
|
22
|
+
return None
|
|
23
|
+
if output is not None and output_dir is not None:
|
|
24
|
+
typer.echo("❌ Error: --output and --output-dir cannot be used together.", err=True)
|
|
25
|
+
return None
|
|
26
|
+
typer.echo(f"📥 Downloading from: {url}")
|
|
27
|
+
|
|
28
|
+
def _sanitize_candidate_filename(name: str) -> Optional[str]:
|
|
29
|
+
candidate = Path(name).name.strip()
|
|
30
|
+
if not candidate or candidate in {".", ".."}:
|
|
31
|
+
return None
|
|
32
|
+
return candidate
|
|
33
|
+
|
|
34
|
+
def _filename_from_content_disposition(header_value: Optional[str]) -> Optional[str]:
|
|
35
|
+
if header_value is None:
|
|
36
|
+
return None
|
|
37
|
+
parts = [segment.strip() for segment in header_value.split(";")]
|
|
38
|
+
for part in parts:
|
|
39
|
+
lower = part.lower()
|
|
40
|
+
if lower.startswith("filename*="):
|
|
41
|
+
value = part.split("=", 1)[1]
|
|
42
|
+
value = value.strip().strip('"')
|
|
43
|
+
if "''" in value:
|
|
44
|
+
value = value.split("''", 1)[1]
|
|
45
|
+
decoded = unquote(value)
|
|
46
|
+
sanitized = _sanitize_candidate_filename(decoded)
|
|
47
|
+
if sanitized is not None:
|
|
48
|
+
return sanitized
|
|
49
|
+
if lower.startswith("filename="):
|
|
50
|
+
value = part.split("=", 1)[1].strip().strip('"')
|
|
51
|
+
decoded = unquote(value)
|
|
52
|
+
sanitized = _sanitize_candidate_filename(decoded)
|
|
53
|
+
if sanitized is not None:
|
|
54
|
+
return sanitized
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
def _filename_from_url(source_url: str) -> Optional[str]:
|
|
58
|
+
parsed = urlparse(source_url)
|
|
59
|
+
url_candidate = _sanitize_candidate_filename(unquote(Path(parsed.path).name))
|
|
60
|
+
if url_candidate is not None:
|
|
61
|
+
return url_candidate
|
|
62
|
+
query_params = parse_qs(parsed.query, keep_blank_values=True)
|
|
63
|
+
for key, values in query_params.items():
|
|
64
|
+
lower_key = key.lower()
|
|
65
|
+
if "name" in lower_key or "file" in lower_key:
|
|
66
|
+
for value in values:
|
|
67
|
+
sanitized = _sanitize_candidate_filename(unquote(value))
|
|
68
|
+
if sanitized is not None:
|
|
69
|
+
return sanitized
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
def _resolve_download_path(request_url: str, response: Response, requested_output: Optional[str], requested_output_dir: Optional[str]) -> Path:
|
|
73
|
+
if requested_output is not None:
|
|
74
|
+
return Path(requested_output)
|
|
75
|
+
header_candidate = _filename_from_content_disposition(response.headers.get("content-disposition"))
|
|
76
|
+
if header_candidate is None:
|
|
77
|
+
header_candidate = _filename_from_url(response.url)
|
|
78
|
+
if header_candidate is None:
|
|
79
|
+
header_candidate = _filename_from_url(request_url)
|
|
80
|
+
if header_candidate is None:
|
|
81
|
+
header_candidate = "downloaded_file"
|
|
82
|
+
if requested_output_dir is not None:
|
|
83
|
+
return Path(requested_output_dir) / header_candidate
|
|
84
|
+
return Path(header_candidate)
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
with requests.get(url, allow_redirects=True, stream=True, timeout=60) as response:
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
download_path = _resolve_download_path(url, response, output, output_dir)
|
|
90
|
+
download_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
total_size_header = response.headers.get("content-length", "0")
|
|
92
|
+
try:
|
|
93
|
+
total_size = int(total_size_header)
|
|
94
|
+
except (TypeError, ValueError):
|
|
95
|
+
total_size = 0
|
|
96
|
+
if total_size <= 0:
|
|
97
|
+
with open(download_path, "wb") as file_handle:
|
|
98
|
+
file_handle.write(response.content)
|
|
99
|
+
else:
|
|
100
|
+
downloaded = 0
|
|
101
|
+
chunk_size = 8192 * 4
|
|
102
|
+
with open(download_path, "wb") as file_handle:
|
|
103
|
+
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
104
|
+
if not chunk:
|
|
105
|
+
continue
|
|
106
|
+
file_handle.write(chunk)
|
|
107
|
+
downloaded += len(chunk)
|
|
108
|
+
progress = (downloaded / total_size) * 100
|
|
109
|
+
typer.echo(f"\r⏬ Progress: {progress:.1f}% ({downloaded}/{total_size} bytes)", nl=False)
|
|
110
|
+
typer.echo()
|
|
111
|
+
except requests.exceptions.RequestException as exception:
|
|
112
|
+
typer.echo(f"❌ Download failed: {exception}", err=True)
|
|
113
|
+
return None
|
|
114
|
+
except OSError as exception:
|
|
115
|
+
typer.echo(f"❌ File write error: {exception}", err=True)
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
typer.echo(f"✅ Downloaded to: {download_path}")
|
|
119
|
+
result_path: Path = download_path
|
|
120
|
+
|
|
121
|
+
if decompress:
|
|
122
|
+
typer.echo(f"📦 Decompressing: {download_path}")
|
|
123
|
+
base_name = download_path.stem
|
|
124
|
+
if base_name in {"", ".", ".."}:
|
|
125
|
+
base_name = "extracted"
|
|
126
|
+
extract_dir = download_path.parent / base_name
|
|
127
|
+
extract_dir.mkdir(parents=True, exist_ok=True)
|
|
128
|
+
try:
|
|
129
|
+
subprocess.run(
|
|
130
|
+
["ouch", "decompress", str(download_path), "--dir", str(extract_dir)],
|
|
131
|
+
check=True,
|
|
132
|
+
capture_output=True,
|
|
133
|
+
text=True,
|
|
134
|
+
)
|
|
135
|
+
typer.echo(f"✅ Decompressed to: {extract_dir}")
|
|
136
|
+
if download_path.exists():
|
|
137
|
+
download_path.unlink()
|
|
138
|
+
typer.echo(f"🗑️ Removed archive: {download_path}")
|
|
139
|
+
result_path = extract_dir
|
|
140
|
+
except subprocess.CalledProcessError as exception:
|
|
141
|
+
typer.echo(f"❌ Decompression failed: {exception.stderr}", err=True)
|
|
142
|
+
return None
|
|
143
|
+
except FileNotFoundError:
|
|
144
|
+
typer.echo("❌ Error: ouch command not found. Please install ouch.", err=True)
|
|
145
|
+
typer.echo("💡 Install with: cargo install ouch", err=True)
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
return result_path.resolve()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if __name__ == "__main__":
|
|
152
|
+
pass
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
5
|
|
|
6
|
-
from typing import Optional
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Annotated, Literal, TypedDict
|
|
6
|
+
from typing import Optional, Annotated, Literal, TypedDict
|
|
9
7
|
|
|
10
8
|
|
|
11
9
|
def path():
|
|
@@ -17,43 +15,76 @@ def path():
|
|
|
17
15
|
uv_with = ["textual"]
|
|
18
16
|
uv_project_dir = None
|
|
19
17
|
if not Path.home().joinpath("code/machineconfig").exists():
|
|
20
|
-
uv_with.append("machineconfig>=7.
|
|
18
|
+
uv_with.append("machineconfig>=7.79")
|
|
21
19
|
else:
|
|
22
20
|
uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
|
|
23
21
|
run_shell_script(get_uv_command_executing_python_script(python_script=path.read_text(encoding="utf-8"), uv_with=uv_with, uv_project_dir=uv_project_dir)[0])
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
def init_project(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
24
|
+
def init_project(
|
|
25
|
+
name: Annotated[Optional[str], typer.Option("--name", "-n", help="Name of the project.")]= None,
|
|
26
|
+
tmp_directory: Annotated[bool, typer.Option("--tmp-directory/--no-tmp-directory", "-t/-nt", help="Use a temporary directory for the project initialization.")]= False,
|
|
27
|
+
python: Annotated[Literal["3.13", "3.14"], typer.Option("--python", "-p", help="Python version for the uv virtual environment.")]= "3.13",
|
|
28
|
+
packages: Annotated[Optional[str], typer.Option("--packages", "-p", help="Additional packages to include in the uv virtual environment.")]= None,
|
|
29
|
+
group: Annotated[Optional[str], typer.Option("--group", "-g", help="Group name for the packages.")]= "plot",
|
|
30
|
+
types_packages: Annotated[bool, typer.Option("--types-packages/--no-types-packages", "-T/-NT", help="Include types packages for better type hinting.")]= True,
|
|
31
|
+
linting_debug_packages: Annotated[bool, typer.Option("--linting-debug-packages/--no-linting-debug-packages", "-L/-NL", help="Include linting and debugging packages.")]= True,
|
|
32
|
+
ia_packages: Annotated[bool, typer.Option("--ia-packages/--no-ia-packages", "-I/-NI", help="Include interactive and IA packages.")]= True,
|
|
33
|
+
plot_packages: Annotated[bool, typer.Option("--plot-packages/--no-plot-packages", "-P/-NP", help="Include plotting packages.")]= True,
|
|
34
|
+
data_packages: Annotated[bool, typer.Option("--data-packages/--no-data-packages", "-D/-ND", help="Include data manipulation packages.")]= True,
|
|
35
|
+
) -> None:
|
|
36
|
+
if packages is not None:
|
|
37
|
+
packages_add_line = f"uv add {packages}"
|
|
38
|
+
else:
|
|
39
|
+
packages_add_line = ""
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
if not tmp_directory:
|
|
42
|
+
repo_root = Path.cwd()
|
|
43
|
+
if not (repo_root / "pyproject.toml").exists():
|
|
44
|
+
typer.echo("❌ Error: pyproject.toml not found.", err=True)
|
|
45
|
+
raise typer.Exit(code=1)
|
|
46
|
+
starting_code = ""
|
|
47
|
+
else:
|
|
48
|
+
if name is not None:
|
|
49
|
+
from machineconfig.utils.accessories import randstr
|
|
50
|
+
repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{name}")
|
|
51
|
+
else:
|
|
52
|
+
from machineconfig.utils.accessories import randstr
|
|
53
|
+
repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{randstr(6)}")
|
|
54
|
+
repo_root.mkdir(parents=True, exist_ok=True)
|
|
55
|
+
print(f"Using temporary directory for project initialization: {repo_root}")
|
|
56
|
+
starting_code = f"""
|
|
57
|
+
cd {repo_root}
|
|
58
|
+
uv init --python {python}
|
|
59
|
+
uv venv
|
|
51
60
|
"""
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
print(f"Adding group `{group}` with common data science and plotting packages...")
|
|
62
|
+
total_packages: list[str] = []
|
|
63
|
+
|
|
64
|
+
if types_packages:
|
|
65
|
+
total_packages.append("types-python-dateutil types-pyyaml types-requests types-tqdm types-mysqlclient types-paramiko types-pytz types-sqlalchemy types-toml types-urllib3")
|
|
66
|
+
if linting_debug_packages:
|
|
67
|
+
total_packages.append("mypy pyright ruff pylint pyrefly cleanpy ipdb pudb")
|
|
68
|
+
if ia_packages:
|
|
69
|
+
total_packages.append("ipython ipykernel jupyterlab nbformat marimo")
|
|
70
|
+
if plot_packages:
|
|
71
|
+
total_packages.append("python-magic matplotlib plotly kaleido")
|
|
72
|
+
if data_packages:
|
|
73
|
+
total_packages.append("numpy pandas polars duckdb-engine sqlalchemy psycopg2-binary pyarrow tqdm openpyxl")
|
|
74
|
+
from machineconfig.utils.ve import get_ve_activate_line
|
|
75
|
+
script = f"""
|
|
76
|
+
{starting_code}
|
|
77
|
+
{packages_add_line}
|
|
78
|
+
uv add --group {group} {" ".join(total_packages)}
|
|
79
|
+
{get_ve_activate_line(ve_root=str(repo_root.joinpath(".venv")))}
|
|
80
|
+
ls
|
|
81
|
+
"""
|
|
82
|
+
from machineconfig.utils.code import exit_then_run_shell_script
|
|
83
|
+
exit_then_run_shell_script(script)
|
|
54
84
|
|
|
55
85
|
|
|
56
86
|
def edit_file_with_hx(path: Annotated[Optional[str], typer.Argument(..., help="The root directory of the project to edit, or a file path.")] = None) -> None:
|
|
87
|
+
from pathlib import Path
|
|
57
88
|
if path is None:
|
|
58
89
|
root_path = Path.cwd()
|
|
59
90
|
print(f"No path provided. Using current working directory: {root_path}")
|
|
@@ -82,6 +113,13 @@ class MachineSpecs(TypedDict):
|
|
|
82
113
|
system: str
|
|
83
114
|
distro: str
|
|
84
115
|
home_dir: str
|
|
116
|
+
hostname: str
|
|
117
|
+
release: str
|
|
118
|
+
version: str
|
|
119
|
+
machine: str
|
|
120
|
+
processor: str
|
|
121
|
+
python_version: str
|
|
122
|
+
user: str
|
|
85
123
|
|
|
86
124
|
|
|
87
125
|
def get_machine_specs() -> MachineSpecs:
|
|
@@ -90,11 +128,21 @@ def get_machine_specs() -> MachineSpecs:
|
|
|
90
128
|
UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
|
|
91
129
|
command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
92
130
|
import subprocess
|
|
131
|
+
from pathlib import Path
|
|
132
|
+
import socket
|
|
133
|
+
import os
|
|
93
134
|
distro = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
|
|
94
135
|
specs: MachineSpecs = {
|
|
95
136
|
"system": platform.system(),
|
|
96
137
|
"distro": distro,
|
|
97
138
|
"home_dir": str(Path.home()),
|
|
139
|
+
"hostname": socket.gethostname(),
|
|
140
|
+
"release": platform.release(),
|
|
141
|
+
"version": platform.version(),
|
|
142
|
+
"machine": platform.machine(),
|
|
143
|
+
"processor": platform.processor() or "Unknown",
|
|
144
|
+
"python_version": platform.python_version(),
|
|
145
|
+
"user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
|
|
98
146
|
}
|
|
99
147
|
print(specs)
|
|
100
148
|
from machineconfig.utils.source_of_truth import CONFIG_ROOT
|
|
@@ -104,3 +152,5 @@ def get_machine_specs() -> MachineSpecs:
|
|
|
104
152
|
path.write_text(json.dumps(specs, indent=4), encoding="utf-8")
|
|
105
153
|
return specs
|
|
106
154
|
|
|
155
|
+
if __name__ == "__main__":
|
|
156
|
+
get_machine_specs()
|
|
@@ -31,7 +31,7 @@ console = Console()
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def display_header() -> None:
|
|
34
|
-
from machineconfig.utils.
|
|
34
|
+
from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
|
|
35
35
|
from rich.align import Align
|
|
36
36
|
|
|
37
37
|
# Fancy ASCII art header
|
|
@@ -111,7 +111,7 @@ def execute_installations(selected_options: list[str]) -> None:
|
|
|
111
111
|
console.print(Panel("⚡ [bold bright_yellow]CLI APPLICATIONS[/bold bright_yellow]\n[italic]Command-line tools installation[/italic]", border_style="bright_yellow"))
|
|
112
112
|
console.print("🔧 Installing CLI applications", style="bold cyan")
|
|
113
113
|
try:
|
|
114
|
-
from machineconfig.utils.installer_utils.
|
|
114
|
+
from machineconfig.utils.installer_utils.installer_cli import main_installer_cli as devops_devapps_install_main
|
|
115
115
|
devops_devapps_install_main(group=True, which=maybe_a_group, interactive=False)
|
|
116
116
|
console.print("✅ CLI applications installed successfully", style="bold green")
|
|
117
117
|
except Exception as e:
|
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
|
|
2
2
|
import typer
|
|
3
|
+
from typing import Annotated
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
def
|
|
6
|
+
def machineconfig_search(
|
|
7
|
+
directory: Annotated[str, typer.Option(..., "--directory", "-d", help="The directory to search")] = ".",
|
|
8
|
+
ast: Annotated[bool, typer.Option(..., "--ast", "-a", help="The abstract syntax tree search/ tree sitter search of symbols")] = False):
|
|
9
|
+
|
|
10
|
+
if ast:
|
|
11
|
+
from machineconfig.scripts.python.helpers.ast_search import get_repo_symbols
|
|
12
|
+
symbols = get_repo_symbols(directory)
|
|
13
|
+
from machineconfig.utils.options import choose_from_options
|
|
14
|
+
try:
|
|
15
|
+
res = choose_from_options(options=symbols, msg="Select a symbol to search for:", fzf=True, multi=False)
|
|
16
|
+
from rich import print_json
|
|
17
|
+
import json
|
|
18
|
+
res_json = json.dumps(res, indent=4)
|
|
19
|
+
print_json(res_json)
|
|
20
|
+
return None
|
|
21
|
+
except Exception as e:
|
|
22
|
+
print(f"❌ Error during selection: {e}")
|
|
23
|
+
return None
|
|
24
|
+
|
|
6
25
|
from machineconfig.scripts.python.helpers_msearch import FZFG_LINUX_PATH, FZFG_WINDOWS_PATH
|
|
7
26
|
import platform
|
|
8
27
|
if platform.system() == "Linux":
|
|
@@ -17,5 +36,5 @@ def machineconfig_find():
|
|
|
17
36
|
|
|
18
37
|
def main():
|
|
19
38
|
app = typer.Typer(add_completion=False, no_args_is_help=True)
|
|
20
|
-
app.command(name="msearch", help="machineconfig search helper", no_args_is_help=False)(
|
|
39
|
+
app.command(name="msearch", help="machineconfig search helper", no_args_is_help=False)(machineconfig_search)
|
|
21
40
|
app()
|