machineconfig 7.64__py3-none-any.whl → 7.83__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 +4 -2
- machineconfig/jobs/installer/custom/yazi.py +120 -0
- machineconfig/jobs/installer/custom_dev/nerdfont.py +1 -1
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +26 -12
- machineconfig/jobs/installer/custom_dev/sysabc.py +26 -5
- machineconfig/jobs/installer/installer_data.json +232 -96
- machineconfig/jobs/installer/powershell_scripts/install_fonts.ps1 +129 -34
- machineconfig/profile/create_helper.py +0 -12
- machineconfig/profile/create_links_export.py +2 -2
- machineconfig/profile/mapper.toml +2 -2
- machineconfig/scripts/__init__.py +0 -4
- machineconfig/scripts/python/agents.py +22 -17
- machineconfig/scripts/python/ai/solutions/copilot/instructions/python/dev.instructions.md +4 -0
- machineconfig/scripts/python/croshell.py +22 -17
- machineconfig/scripts/python/devops.py +1 -1
- machineconfig/scripts/python/devops_navigator.py +0 -4
- machineconfig/scripts/python/env_manager/env_manager_tui.py +204 -0
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/fire_jobs.py +13 -13
- 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/start_slidev.py +6 -7
- machineconfig/scripts/python/helpers_devops/cli_config.py +10 -0
- machineconfig/scripts/python/helpers_devops/cli_nw.py +90 -10
- machineconfig/scripts/python/helpers_devops/cli_self.py +8 -7
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +7 -7
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +12 -11
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +8 -10
- machineconfig/scripts/python/helpers_devops/cli_utils.py +2 -1
- machineconfig/scripts/python/helpers_devops/devops_status.py +7 -19
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +20 -9
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfg +2 -2
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfg.ps1 +58 -1
- machineconfig/scripts/python/helpers_navigator/command_tree.py +50 -18
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +5 -3
- machineconfig/scripts/python/helpers_repos/count_lines.py +40 -11
- machineconfig/scripts/python/helpers_repos/count_lines_frontend.py +1 -1
- machineconfig/scripts/python/helpers_utils/download.py +4 -3
- machineconfig/scripts/python/helpers_utils/path.py +87 -34
- machineconfig/scripts/python/interactive.py +1 -1
- machineconfig/scripts/python/{machineconfig.py → mcfg_entry.py} +4 -0
- machineconfig/scripts/python/msearch.py +55 -6
- machineconfig/scripts/python/nw/address.py +132 -0
- machineconfig/scripts/python/nw/devops_add_ssh_key.py +8 -5
- machineconfig/scripts/python/terminal.py +2 -2
- machineconfig/scripts/python/utils.py +12 -11
- 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/yazi/init.lua +4 -0
- machineconfig/settings/yazi/keymap_linux.toml +11 -4
- machineconfig/settings/yazi/theme.toml +4 -0
- machineconfig/settings/yazi/yazi_linux.toml +84 -0
- machineconfig/settings/yazi/yazi_windows.toml +58 -0
- machineconfig/setup_linux/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 +4 -2
- machineconfig/utils/installer_utils/github_release_bulk.py +104 -62
- machineconfig/utils/installer_utils/install_from_url.py +200 -0
- machineconfig/utils/installer_utils/installer_class.py +25 -74
- machineconfig/utils/installer_utils/installer_cli.py +40 -50
- machineconfig/utils/installer_utils/installer_helper.py +100 -0
- machineconfig/utils/installer_utils/installer_runner.py +5 -8
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/meta.py +2 -2
- machineconfig/utils/options.py +3 -3
- machineconfig/utils/path_extended.py +1 -1
- machineconfig/utils/path_helper.py +0 -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 +210 -0
- machineconfig/utils/upgrade_packages.py +2 -1
- machineconfig/utils/ve.py +11 -4
- {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/METADATA +2 -2
- {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/RECORD +96 -89
- {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/entry_points.txt +2 -2
- machineconfig/scripts/python/explore.py +0 -49
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfag +0 -17
- machineconfig/scripts/python/helpers_msearch/scripts_linux/fzfrga +0 -21
- machineconfig/scripts/python/helpers_msearch/scripts_linux/skrg +0 -4
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfb.ps1 +0 -3
- machineconfig/scripts/python/helpers_msearch/scripts_windows/fzfrga.bat +0 -20
- machineconfig/settings/yazi/yazi.toml +0 -17
- machineconfig/setup_linux/others/cli_installation.sh +0 -137
- /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-7.64.dist-info → machineconfig-7.83.dist-info}/WHEEL +0 -0
- {machineconfig-7.64.dist-info → machineconfig-7.83.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,9 @@ from collections import defaultdict
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
from rich.console import Console
|
|
8
9
|
from rich.progress import track
|
|
10
|
+
from rich.table import Table
|
|
9
11
|
import typer
|
|
10
12
|
|
|
11
13
|
|
|
@@ -14,9 +16,6 @@ if TYPE_CHECKING:
|
|
|
14
16
|
import polars as pl
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
app = typer.Typer()
|
|
18
|
-
|
|
19
|
-
|
|
20
19
|
def count_lines_in_commit(commit: "Any") -> int:
|
|
21
20
|
_total_lines = 0
|
|
22
21
|
for _file in commit.stats.files:
|
|
@@ -81,7 +80,10 @@ def get_default_branch(repo: Repo) -> str:
|
|
|
81
80
|
return repo.head.reference.name # If neither exists, get the branch the HEAD is pointing to
|
|
82
81
|
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
|
|
84
|
+
console = Console()
|
|
85
|
+
|
|
86
|
+
|
|
85
87
|
def count_historical(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
|
|
86
88
|
"""Count total historical lines of Python code in the repository."""
|
|
87
89
|
print(f"Analyzing repository: {repo_path}")
|
|
@@ -89,7 +91,7 @@ def count_historical(repo_path: Annotated[str, typer.Argument(..., help="Path to
|
|
|
89
91
|
print(f"\nTotal historical lines of Python code: {total_loc}")
|
|
90
92
|
|
|
91
93
|
|
|
92
|
-
|
|
94
|
+
|
|
93
95
|
def analyze_over_time(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
|
|
94
96
|
"""Analyze a git repository to track Python code size over time with visualization."""
|
|
95
97
|
repo: Repo = Repo(repo_path)
|
|
@@ -170,8 +172,14 @@ def analyze_over_time(repo_path: Annotated[str, typer.Argument(..., help="Path t
|
|
|
170
172
|
html_path = plot_dir.joinpath("code_size_evolution.html")
|
|
171
173
|
png_path = plot_dir.joinpath("code_size_evolution.png")
|
|
172
174
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
try:
|
|
176
|
+
fig.write_html(html_path, include_plotlyjs="cdn")
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f"❌ Error saving HTML plot: {str(e)}")
|
|
179
|
+
try:
|
|
180
|
+
fig.write_image(png_path, width=1200, height=700, scale=2)
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"❌ Error saving PNG plot: {str(e)}")
|
|
175
183
|
|
|
176
184
|
print(f"🖼️ Interactive plot saved as {html_path}")
|
|
177
185
|
print(f"🖼️ Static image saved as {png_path}")
|
|
@@ -232,8 +240,17 @@ def _print_python_files_by_size_impl(repo_path: str) -> "Union[pl.DataFrame, Exc
|
|
|
232
240
|
file_count: int = len(df)
|
|
233
241
|
|
|
234
242
|
# Print the DataFrame
|
|
235
|
-
print("\n📊 Python Files Line Count (sorted max to min):")
|
|
236
|
-
|
|
243
|
+
console.print("\n📊 Python Files Line Count (sorted max to min):")
|
|
244
|
+
|
|
245
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
246
|
+
table.add_column("#", justify="right")
|
|
247
|
+
table.add_column("File", overflow="fold")
|
|
248
|
+
table.add_column("Lines", justify="right")
|
|
249
|
+
|
|
250
|
+
for idx, row in enumerate(df.iter_rows(named=True), 1):
|
|
251
|
+
table.add_row(str(idx), row["filename"], f"{row['lines']:,}")
|
|
252
|
+
|
|
253
|
+
console.print(table)
|
|
237
254
|
print(f"\n📁 Total Python files: {file_count}")
|
|
238
255
|
print(f"📝 Total lines of Python code: {total_lines:,}")
|
|
239
256
|
|
|
@@ -335,7 +352,7 @@ def _print_python_files_by_size_impl(repo_path: str) -> "Union[pl.DataFrame, Exc
|
|
|
335
352
|
return Exception(f"❌ Error analyzing repository: {str(e)}")
|
|
336
353
|
|
|
337
354
|
|
|
338
|
-
|
|
355
|
+
|
|
339
356
|
def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., help="Path to the git repository")]):
|
|
340
357
|
"""Print Python files sorted by size with visualizations."""
|
|
341
358
|
result = _print_python_files_by_size_impl(repo_path)
|
|
@@ -344,5 +361,17 @@ def print_python_files_by_size(repo_path: Annotated[str, typer.Argument(..., hel
|
|
|
344
361
|
return
|
|
345
362
|
|
|
346
363
|
|
|
364
|
+
def get_app():
|
|
365
|
+
app = typer.Typer()
|
|
366
|
+
app.command(name="count-historical", no_args_is_help=True, help="[c] Count lines of code in a git repository")(count_historical)
|
|
367
|
+
app.command(name="c", hidden=True, no_args_is_help=True, help="Alias for count-historical")(count_historical)
|
|
368
|
+
app.command(name="analyze-over-time", no_args_is_help=True, help="[a] Analyze code size over time in a git repository")(analyze_over_time)
|
|
369
|
+
app.command(name="a", hidden=True, no_args_is_help=True, help="Alias for analyze-over-time")(analyze_over_time)
|
|
370
|
+
app.command(name="print-python-files-by-size", no_args_is_help=True, help="[p] Print Python files by size in a git repository")(print_python_files_by_size)
|
|
371
|
+
app.command(name="p", hidden=True, no_args_is_help=True, help="Alias for print-python-files-by-size")(print_python_files_by_size)
|
|
372
|
+
return app
|
|
373
|
+
|
|
374
|
+
|
|
347
375
|
if __name__ == "__main__":
|
|
348
|
-
|
|
376
|
+
get_app()()
|
|
377
|
+
|
|
@@ -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.83" {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
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Annotated, Optional
|
|
4
4
|
import typer
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def download(
|
|
@@ -12,11 +13,10 @@ def download(
|
|
|
12
13
|
) -> Optional["Path"]:
|
|
13
14
|
|
|
14
15
|
import subprocess
|
|
15
|
-
from pathlib import Path
|
|
16
16
|
from urllib.parse import parse_qs, unquote, urlparse
|
|
17
17
|
from requests import Response
|
|
18
18
|
import requests
|
|
19
|
-
|
|
19
|
+
from pathlib import Path
|
|
20
20
|
if url is None:
|
|
21
21
|
typer.echo("❌ Error: URL is required.", err=True)
|
|
22
22
|
return None
|
|
@@ -147,5 +147,6 @@ def download(
|
|
|
147
147
|
|
|
148
148
|
return result_path.resolve()
|
|
149
149
|
|
|
150
|
+
|
|
150
151
|
if __name__ == "__main__":
|
|
151
|
-
|
|
152
|
+
pass
|
|
@@ -2,58 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
|
+
from typing import Optional, Annotated, Literal, TypedDict
|
|
5
6
|
|
|
6
|
-
from typing import Optional
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from typing import Annotated, Literal, TypedDict
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
def path():
|
|
8
|
+
def tui_env(which: Annotated[Literal["PATH", "p", "ENV", "e"], typer.Argument(help="Which environment variable to display.")] = "ENV") -> None:
|
|
12
9
|
"""📚 NAVIGATE PATH variable with TUI"""
|
|
13
10
|
from machineconfig.scripts.python import env_manager as navigator
|
|
14
11
|
from pathlib import Path
|
|
15
|
-
|
|
12
|
+
match which:
|
|
13
|
+
case "PATH" | "p":
|
|
14
|
+
path = Path(navigator.__file__).resolve().parent.joinpath("path_manager_tui.py")
|
|
15
|
+
case "ENV" | "e":
|
|
16
|
+
path = Path(navigator.__file__).resolve().parent.joinpath("env_manager_tui.py")
|
|
16
17
|
from machineconfig.utils.code import run_shell_script, get_uv_command_executing_python_script
|
|
17
18
|
uv_with = ["textual"]
|
|
18
19
|
uv_project_dir = None
|
|
19
20
|
if not Path.home().joinpath("code/machineconfig").exists():
|
|
20
|
-
uv_with.append("machineconfig>=7.
|
|
21
|
+
uv_with.append("machineconfig>=7.83")
|
|
21
22
|
else:
|
|
22
23
|
uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
|
|
23
24
|
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
25
|
|
|
25
26
|
|
|
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
|
-
|
|
27
|
+
def init_project(
|
|
28
|
+
name: Annotated[Optional[str], typer.Option("--name", "-n", help="Name of the project.")]= None,
|
|
29
|
+
tmp_directory: Annotated[bool, typer.Option("--tmp-directory/--no-tmp-directory", "-t/-nt", help="Use a temporary directory for the project initialization.")]= False,
|
|
30
|
+
python: Annotated[Literal["3.13", "3.14"], typer.Option("--python", "-p", help="Python version for the uv virtual environment.")]= "3.13",
|
|
31
|
+
packages: Annotated[Optional[str], typer.Option("--packages", "-p", help="Additional packages to include in the uv virtual environment.")]= None,
|
|
32
|
+
group: Annotated[Optional[str], typer.Option("--group", "-g", help="Group name for the packages.")]= "plot",
|
|
33
|
+
types_packages: Annotated[bool, typer.Option("--types-packages/--no-types-packages", "-T/-NT", help="Include types packages for better type hinting.")]= True,
|
|
34
|
+
linting_debug_packages: Annotated[bool, typer.Option("--linting-debug-packages/--no-linting-debug-packages", "-L/-NL", help="Include linting and debugging packages.")]= True,
|
|
35
|
+
ia_packages: Annotated[bool, typer.Option("--ia-packages/--no-ia-packages", "-I/-NI", help="Include interactive and IA packages.")]= True,
|
|
36
|
+
plot_packages: Annotated[bool, typer.Option("--plot-packages/--no-plot-packages", "-P/-NP", help="Include plotting packages.")]= True,
|
|
37
|
+
data_packages: Annotated[bool, typer.Option("--data-packages/--no-data-packages", "-D/-ND", help="Include data manipulation packages.")]= True,
|
|
38
|
+
) -> None:
|
|
39
|
+
if packages is not None:
|
|
40
|
+
packages_add_line = f"uv add {packages}"
|
|
41
|
+
else:
|
|
42
|
+
packages_add_line = ""
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
if not tmp_directory:
|
|
45
|
+
repo_root = Path.cwd()
|
|
46
|
+
if not (repo_root / "pyproject.toml").exists():
|
|
47
|
+
typer.echo("❌ Error: pyproject.toml not found.", err=True)
|
|
48
|
+
raise typer.Exit(code=1)
|
|
49
|
+
starting_code = ""
|
|
50
|
+
else:
|
|
51
|
+
if name is not None:
|
|
52
|
+
from machineconfig.utils.accessories import randstr
|
|
53
|
+
repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{name}")
|
|
54
|
+
else:
|
|
55
|
+
from machineconfig.utils.accessories import randstr
|
|
56
|
+
repo_root = Path.home().joinpath(f"tmp_results/tmp_projects/{randstr(6)}")
|
|
57
|
+
repo_root.mkdir(parents=True, exist_ok=True)
|
|
58
|
+
print(f"Using temporary directory for project initialization: {repo_root}")
|
|
59
|
+
starting_code = f"""
|
|
60
|
+
cd {repo_root}
|
|
61
|
+
uv init --python {python}
|
|
62
|
+
uv venv
|
|
63
|
+
"""
|
|
64
|
+
print(f"Adding group `{group}` with common data science and plotting packages...")
|
|
65
|
+
total_packages: list[str] = []
|
|
66
|
+
|
|
67
|
+
if types_packages:
|
|
68
|
+
total_packages.append("types-python-dateutil types-pyyaml types-requests types-tqdm types-mysqlclient types-paramiko types-pytz types-sqlalchemy types-toml types-urllib3")
|
|
69
|
+
if linting_debug_packages:
|
|
70
|
+
total_packages.append("mypy pyright ruff pylint pyrefly cleanpy ipdb pudb")
|
|
71
|
+
if ia_packages:
|
|
72
|
+
total_packages.append("ipython ipykernel jupyterlab nbformat marimo")
|
|
73
|
+
if plot_packages:
|
|
74
|
+
total_packages.append("python-magic matplotlib plotly kaleido")
|
|
75
|
+
if data_packages:
|
|
76
|
+
total_packages.append("numpy pandas polars duckdb-engine sqlalchemy psycopg2-binary pyarrow tqdm openpyxl")
|
|
77
|
+
from machineconfig.utils.ve import get_ve_activate_line
|
|
78
|
+
script = f"""
|
|
79
|
+
{starting_code}
|
|
80
|
+
{packages_add_line}
|
|
81
|
+
uv add --group {group} {" ".join(total_packages)}
|
|
82
|
+
{get_ve_activate_line(ve_root=str(repo_root.joinpath(".venv")))}
|
|
83
|
+
ls
|
|
51
84
|
"""
|
|
52
|
-
from machineconfig.utils.code import
|
|
53
|
-
|
|
85
|
+
from machineconfig.utils.code import exit_then_run_shell_script
|
|
86
|
+
exit_then_run_shell_script(script)
|
|
54
87
|
|
|
55
88
|
|
|
56
89
|
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:
|
|
90
|
+
from pathlib import Path
|
|
57
91
|
if path is None:
|
|
58
92
|
root_path = Path.cwd()
|
|
59
93
|
print(f"No path provided. Using current working directory: {root_path}")
|
|
@@ -82,6 +116,13 @@ class MachineSpecs(TypedDict):
|
|
|
82
116
|
system: str
|
|
83
117
|
distro: str
|
|
84
118
|
home_dir: str
|
|
119
|
+
hostname: str
|
|
120
|
+
release: str
|
|
121
|
+
version: str
|
|
122
|
+
machine: str
|
|
123
|
+
processor: str
|
|
124
|
+
python_version: str
|
|
125
|
+
user: str
|
|
85
126
|
|
|
86
127
|
|
|
87
128
|
def get_machine_specs() -> MachineSpecs:
|
|
@@ -90,11 +131,21 @@ def get_machine_specs() -> MachineSpecs:
|
|
|
90
131
|
UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
|
|
91
132
|
command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
92
133
|
import subprocess
|
|
134
|
+
from pathlib import Path
|
|
135
|
+
import socket
|
|
136
|
+
import os
|
|
93
137
|
distro = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
|
|
94
138
|
specs: MachineSpecs = {
|
|
95
139
|
"system": platform.system(),
|
|
96
140
|
"distro": distro,
|
|
97
141
|
"home_dir": str(Path.home()),
|
|
142
|
+
"hostname": socket.gethostname(),
|
|
143
|
+
"release": platform.release(),
|
|
144
|
+
"version": platform.version(),
|
|
145
|
+
"machine": platform.machine(),
|
|
146
|
+
"processor": platform.processor() or "Unknown",
|
|
147
|
+
"python_version": platform.python_version(),
|
|
148
|
+
"user": os.getenv("USER") or os.getenv("USERNAME") or "Unknown",
|
|
98
149
|
}
|
|
99
150
|
print(specs)
|
|
100
151
|
from machineconfig.utils.source_of_truth import CONFIG_ROOT
|
|
@@ -104,3 +155,5 @@ def get_machine_specs() -> MachineSpecs:
|
|
|
104
155
|
path.write_text(json.dumps(specs, indent=4), encoding="utf-8")
|
|
105
156
|
return specs
|
|
106
157
|
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
get_machine_specs()
|
|
@@ -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.installer_cli import
|
|
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,21 +1,70 @@
|
|
|
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
|
+
symantic: Annotated[bool, typer.Option(..., "--symantic", "-s", help="The symantic search of symbols")] = False,
|
|
10
|
+
extension: Annotated[str, typer.Option(..., "--extension", "-e", help="File extension to filter by (e.g., .py, .js)")] = "",
|
|
11
|
+
file: Annotated[bool, typer.Option(..., "--file", "-f", help="File search using fzf")] = False,
|
|
12
|
+
no_dotfiles: Annotated[bool, typer.Option(..., "--no-dotfiles", "-d", help="Exclude dotfiles from search")] = False,
|
|
13
|
+
rga: Annotated[bool, typer.Option(..., "--rga", "-a", help="Use ripgrep-all for searching instead of ripgrep")] = False,
|
|
14
|
+
install_dependencies: Annotated[bool, typer.Option(..., "--install-dependencies", "-p", help="Install required dependencies if missing")] = False
|
|
15
|
+
):
|
|
16
|
+
if install_dependencies:
|
|
17
|
+
from machineconfig.utils.installer_utils.installer_cli import install_if_missing
|
|
18
|
+
install_if_missing("fzf")
|
|
19
|
+
install_if_missing("bat")
|
|
20
|
+
install_if_missing("fd")
|
|
21
|
+
install_if_missing("rg") # ripgrep
|
|
22
|
+
install_if_missing("rga") # ripgrep-all
|
|
23
|
+
# install_if_missing("tree-sitter-cli")
|
|
24
|
+
return
|
|
25
|
+
if symantic:
|
|
26
|
+
script = ""
|
|
27
|
+
for an_ex in extension.split(","):
|
|
28
|
+
script = script + f"""\nparse *.{an_ex} """
|
|
29
|
+
from machineconfig.utils.code import run_shell_script
|
|
30
|
+
run_shell_script(script=script)
|
|
31
|
+
return
|
|
32
|
+
if ast:
|
|
33
|
+
from machineconfig.scripts.python.helpers.ast_search import get_repo_symbols
|
|
34
|
+
symbols = get_repo_symbols(directory)
|
|
35
|
+
from machineconfig.utils.options import choose_from_options
|
|
36
|
+
try:
|
|
37
|
+
res = choose_from_options(options=symbols, msg="Select a symbol to search for:", fzf=True, multi=False)
|
|
38
|
+
from rich import print_json
|
|
39
|
+
import json
|
|
40
|
+
res_json = json.dumps(res, indent=4)
|
|
41
|
+
print_json(res_json)
|
|
42
|
+
return None
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"❌ Error during selection: {e}")
|
|
45
|
+
return None
|
|
46
|
+
if file:
|
|
47
|
+
script = """fzf --ansi --preview-window 'right:60%' --preview 'bat --color=always --style=numbers,grid,header --line-range :300 {}' """
|
|
48
|
+
if no_dotfiles:
|
|
49
|
+
script = "fd | " + script
|
|
50
|
+
from machineconfig.utils.code import run_shell_script
|
|
51
|
+
run_shell_script(script=script)
|
|
52
|
+
return
|
|
6
53
|
from machineconfig.scripts.python.helpers_msearch import FZFG_LINUX_PATH, FZFG_WINDOWS_PATH
|
|
7
54
|
import platform
|
|
8
|
-
if platform.system() == "Linux":
|
|
9
|
-
|
|
55
|
+
if platform.system() == "Linux" or platform.system() == "Darwin":
|
|
56
|
+
script = FZFG_LINUX_PATH.read_text(encoding="utf-8")
|
|
10
57
|
elif platform.system() == "Windows":
|
|
11
|
-
|
|
58
|
+
script = FZFG_WINDOWS_PATH.read_text(encoding="utf-8")
|
|
12
59
|
else:
|
|
13
60
|
raise RuntimeError("Unsupported platform")
|
|
61
|
+
if rga:
|
|
62
|
+
script = script.replace("rg ", "rga ").replace("ripgrep", "ripgrep-all")
|
|
14
63
|
from machineconfig.utils.code import exit_then_run_shell_script
|
|
15
|
-
exit_then_run_shell_script(script=
|
|
64
|
+
exit_then_run_shell_script(script=script, strict=False)
|
|
16
65
|
|
|
17
66
|
|
|
18
67
|
def main():
|
|
19
68
|
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)(
|
|
69
|
+
app.command(name="msearch", help="machineconfig search helper", no_args_is_help=False)(machineconfig_search)
|
|
21
70
|
app()
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
|
|
2
|
+
from typing import cast, Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_all_ipv4_addresses() -> list[tuple[str, str]]:
|
|
6
|
+
import psutil
|
|
7
|
+
import socket
|
|
8
|
+
result: list[tuple[str, str]] = []
|
|
9
|
+
for iface, addrs in psutil.net_if_addrs().items():
|
|
10
|
+
for addr in addrs:
|
|
11
|
+
if addr.family == socket.AF_INET:
|
|
12
|
+
ip = addr.address
|
|
13
|
+
result.append((iface, ip))
|
|
14
|
+
return result
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def select_lan_ipv4(prefer_vpn: bool) -> Optional[str]:
|
|
18
|
+
"""
|
|
19
|
+
Choose the best 'real LAN' IPv4:
|
|
20
|
+
- Excludes loopback/link-local and (by default) VPN/tunnel/container ifaces
|
|
21
|
+
- Prefers physical-looking ifaces (eth/en*/wlan/wl*)
|
|
22
|
+
- Prefers RFC1918 LANs: 192.168/16 > 10/8 > 172.16/12
|
|
23
|
+
- Requires interface is UP
|
|
24
|
+
Set prefer_vpn=True to allow tunnel/VPN ifaces to compete.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import ipaddress
|
|
28
|
+
import re
|
|
29
|
+
from collections.abc import Sequence
|
|
30
|
+
import psutil
|
|
31
|
+
|
|
32
|
+
# Down-rank or exclude: tunnels/VPNs/bridges/containers (add your own if needed)
|
|
33
|
+
VIRTUAL_IFACE_PAT = re.compile(
|
|
34
|
+
r"^(?:lo|loopback|docker\d*|br-.*|veth.*|virbr.*|bridge.*|"
|
|
35
|
+
r"vboxnet.*|vmnet.*|zt.*|ham.*|tailscale.*|wg\d*|utun\d*|llw\d*|awdl\d*|"
|
|
36
|
+
r"tun\d*|tap\d*|cloudflarewarp.*|warp.*)$",
|
|
37
|
+
re.IGNORECASE,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Light preference for names that look like real NICs
|
|
41
|
+
PHYSICAL_IFACE_PAT = re.compile(
|
|
42
|
+
r"^(?:eth\d*|en\d*|enp.*|ens.*|eno.*|wlan\d*|wl.*|.*wifi.*|.*ethernet.*)$",
|
|
43
|
+
re.IGNORECASE,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Known noisy CIDRs to avoid
|
|
47
|
+
NOISY_NETS: list[ipaddress.IPv4Network] = [
|
|
48
|
+
ipaddress.IPv4Network("100.64.0.0/10"), # CGNAT (Tailscale/others)
|
|
49
|
+
ipaddress.IPv4Network("172.17.0.0/16"), # docker0 default
|
|
50
|
+
ipaddress.IPv4Network("172.18.0.0/16"),
|
|
51
|
+
ipaddress.IPv4Network("172.19.0.0/16"),
|
|
52
|
+
ipaddress.IPv4Network("192.168.49.0/24"), # minikube default
|
|
53
|
+
ipaddress.IPv4Network("10.0.2.0/24"), # VirtualBox NAT
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def _in_any(ip: ipaddress.IPv4Address, nets: Sequence[ipaddress.IPv4Network]) -> bool:
|
|
57
|
+
return any(ip in n for n in nets)
|
|
58
|
+
|
|
59
|
+
stats = psutil.net_if_stats()
|
|
60
|
+
best = None
|
|
61
|
+
best_score = -10**9
|
|
62
|
+
import socket
|
|
63
|
+
for iface, addrs in psutil.net_if_addrs().items():
|
|
64
|
+
st = stats.get(iface)
|
|
65
|
+
if not st or not st.isup:
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
for a in addrs:
|
|
69
|
+
if a.family != socket.AF_INET or not a.address:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
ip_str = a.address
|
|
73
|
+
try:
|
|
74
|
+
ip = cast(ipaddress.IPv4Address, ipaddress.ip_address(ip_str))
|
|
75
|
+
except ValueError:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
# Exclude unusable classes
|
|
79
|
+
if ip.is_loopback or ip.is_link_local: # 127.0.0.0/8, 169.254.0.0/16
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
# Hard filter: if it looks virtual and we don't prefer VPNs, skip it
|
|
83
|
+
if not prefer_vpn and VIRTUAL_IFACE_PAT.match(iface):
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
# Hard filter: known noisy subnets (docker, cgnat, etc.)
|
|
87
|
+
if _in_any(ip, NOISY_NETS) and not prefer_vpn:
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
# Base score
|
|
91
|
+
score = 0
|
|
92
|
+
|
|
93
|
+
# Prefer physical-looking names
|
|
94
|
+
if PHYSICAL_IFACE_PAT.match(iface):
|
|
95
|
+
score += 200
|
|
96
|
+
|
|
97
|
+
# Broadcast present usually means L2 LAN (not point-to-point)
|
|
98
|
+
# (psutil puts it on the same entry as .broadcast)
|
|
99
|
+
if getattr(a, "broadcast", None):
|
|
100
|
+
score += 100
|
|
101
|
+
|
|
102
|
+
# Prefer private RFC1918; rank families
|
|
103
|
+
if ip.is_private:
|
|
104
|
+
# Order: 192.168.x.x > 10.x.x.x > 172.16-31.x.x
|
|
105
|
+
ip_net = ipaddress.IPv4Network((ip, 32), strict=False)
|
|
106
|
+
if ipaddress.IPv4Network("192.168.0.0/16").supernet_of(ip_net):
|
|
107
|
+
score += 90
|
|
108
|
+
elif ipaddress.IPv4Network("10.0.0.0/8").supernet_of(ip_net):
|
|
109
|
+
score += 70
|
|
110
|
+
elif ipaddress.IPv4Network("172.16.0.0/12").supernet_of(ip_net):
|
|
111
|
+
score += 50
|
|
112
|
+
else:
|
|
113
|
+
# Public on a NIC is unusual for a home/office LAN
|
|
114
|
+
score -= 50
|
|
115
|
+
|
|
116
|
+
# Slight nudge by interface speed if known (>0 means psutil knows it)
|
|
117
|
+
# (Many tunnels report 0)
|
|
118
|
+
if getattr(st, "speed", 0) > 0:
|
|
119
|
+
score += 20
|
|
120
|
+
|
|
121
|
+
# Deterministic tie-breaker: prefer shorter iface name (eth0 over eth10)
|
|
122
|
+
score -= len(iface) * 0.01
|
|
123
|
+
|
|
124
|
+
if score > best_score:
|
|
125
|
+
best_score = score
|
|
126
|
+
best = ip_str
|
|
127
|
+
|
|
128
|
+
return best
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
print(select_lan_ipv4(False) or "No LAN IPv4 found")
|
|
@@ -137,11 +137,14 @@ def main(pub_path: Annotated[Optional[str], typer.Argument(..., help="Path to th
|
|
|
137
137
|
console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
|
|
138
138
|
from machineconfig.utils.code import run_shell_script
|
|
139
139
|
run_shell_script(script=program)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
|
|
141
|
+
import machineconfig.scripts.python.nw.address as helper
|
|
142
|
+
res = helper.select_lan_ipv4(prefer_vpn=False)
|
|
143
|
+
if res is None:
|
|
144
|
+
console.print(Panel("❌ ERROR: Could not determine local LAN IPv4 address", title="[bold red]Error[/bold red]", border_style="red"))
|
|
145
|
+
raise typer.Exit(code=1)
|
|
146
|
+
local_ip_v4 = res
|
|
147
|
+
|
|
145
148
|
console.print(Panel(f"🌐 This computer is accessible at: {local_ip_v4}", title="[bold green]Network Info[/bold green]", border_style="green"))
|
|
146
149
|
console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
|
|
147
150
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import subprocess
|
|
2
|
-
# from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig
|
|
3
1
|
import typer
|
|
4
2
|
from typing import Annotated
|
|
5
3
|
|
|
@@ -26,6 +24,7 @@ def choose_zellij_session(
|
|
|
26
24
|
typer.Exit()
|
|
27
25
|
return
|
|
28
26
|
cmd = "zellij list-sessions"
|
|
27
|
+
import subprocess
|
|
29
28
|
sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
|
|
30
29
|
# filter out empty lines and keep raw lines (they contain creation info)
|
|
31
30
|
sessions = [s for s in sessions if s.strip()]
|
|
@@ -63,6 +62,7 @@ def choose_zellij_session(
|
|
|
63
62
|
|
|
64
63
|
def get_session_tabs() -> list[tuple[str, str]]:
|
|
65
64
|
cmd = "zellij list-sessions"
|
|
65
|
+
import subprocess
|
|
66
66
|
sessions: list[str] = subprocess.check_output(cmd, shell=True).decode().strip().split("\n")
|
|
67
67
|
sessions = [strip_ansi_codes(s) for s in sessions]
|
|
68
68
|
active_sessions = [s for s in sessions if "EXITED" not in s]
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from machineconfig.scripts.python.helpers_devops.cli_utils import merge_pdfs, compress_pdf
|
|
4
|
-
from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project,
|
|
4
|
+
from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project, tui_env
|
|
5
5
|
from machineconfig.scripts.python.helpers_utils.download import download
|
|
6
6
|
import typer
|
|
7
7
|
from typing import Annotated
|
|
8
8
|
|
|
9
|
+
|
|
9
10
|
def kill_process(
|
|
10
11
|
# name: Annotated[Optional[str], typer.Option(..., "--name", "-n", help="Name of the process to kill")],
|
|
11
12
|
# command: Annotated[str, typer.Option(..., "--command", "-c", help="Match by command line instead of process name")] = "",
|
|
@@ -29,27 +30,27 @@ def upgrade_packages():
|
|
|
29
30
|
|
|
30
31
|
def get_app() -> typer.Typer:
|
|
31
32
|
app = typer.Typer(help="🛠️ utilities operations", no_args_is_help=True, add_help_option=False, add_completion=False)
|
|
32
|
-
app.command(name="kill-process", no_args_is_help=False, help="[k] Choose a process to kill")(kill_process)
|
|
33
|
+
app.command(name="kill-process", no_args_is_help=False, help="⚔️ [k] Choose a process to kill")(kill_process)
|
|
33
34
|
app.command(name="k", no_args_is_help=False, help="Choose a process to kill", hidden=True)(kill_process)
|
|
34
35
|
|
|
35
|
-
app.command("
|
|
36
|
-
app.command("
|
|
36
|
+
app.command("environment", no_args_is_help=False, help="📚 [v] NAVIGATE ENV/PATH variable with TUI")(tui_env)
|
|
37
|
+
app.command("v", no_args_is_help=False, help="NAVIGATE ENV/PATH variable with TUI", hidden=True)(tui_env)
|
|
37
38
|
|
|
38
|
-
app.command(name="upgrade-packages", no_args_is_help=False, help="[up] Upgrade project dependencies.")(upgrade_packages)
|
|
39
|
+
app.command(name="upgrade-packages", no_args_is_help=False, help="⬆️ [up] Upgrade project dependencies.")(upgrade_packages)
|
|
39
40
|
app.command(name="up", no_args_is_help=False, hidden=True)(upgrade_packages)
|
|
40
41
|
|
|
41
|
-
app.command(name="download", no_args_is_help=True, help="[d] Download a file from a URL and optionally decompress it.")(download)
|
|
42
|
+
app.command(name="download", no_args_is_help=True, help="⬇️ [d] Download a file from a URL and optionally decompress it.")(download)
|
|
42
43
|
app.command(name="d", no_args_is_help=True, hidden=True)(download)
|
|
43
|
-
app.command(name="get-machine-specs", no_args_is_help=False, help="[g] Get machine specifications.")(get_machine_specs)
|
|
44
|
+
app.command(name="get-machine-specs", no_args_is_help=False, help="💻 [g] Get machine specifications.")(get_machine_specs)
|
|
44
45
|
app.command(name="g", no_args_is_help=False, hidden=True)(get_machine_specs)
|
|
45
|
-
app.command(name="init-project", no_args_is_help=False, help="[i] Initialize a project with a uv virtual environment and install dev packages.")(init_project)
|
|
46
|
+
app.command(name="init-project", no_args_is_help=False, help="🚀 [i] Initialize a project with a uv virtual environment and install dev packages.")(init_project)
|
|
46
47
|
app.command(name="i", no_args_is_help=False, hidden=True)(init_project)
|
|
47
|
-
app.command(name="edit", no_args_is_help=False, help="[e] Open a file in the default editor.")(edit_file_with_hx)
|
|
48
|
+
app.command(name="edit", no_args_is_help=False, help="✏️ [e] Open a file in the default editor.")(edit_file_with_hx)
|
|
48
49
|
app.command(name="e", no_args_is_help=False, hidden=True)(edit_file_with_hx)
|
|
49
50
|
|
|
50
|
-
app.command(name="pdf-merge", no_args_is_help=True, help="[pm] Merge two PDF files into one.")(merge_pdfs)
|
|
51
|
+
app.command(name="pdf-merge", no_args_is_help=True, help="📄 [pm] Merge two PDF files into one.")(merge_pdfs)
|
|
51
52
|
app.command(name="pm", no_args_is_help=True, hidden=True)(merge_pdfs)
|
|
52
|
-
app.command(name="pdf-compress", no_args_is_help=True, help="[pc] Compress a PDF file.")(compress_pdf)
|
|
53
|
+
app.command(name="pdf-compress", no_args_is_help=True, help="📦 [pc] Compress a PDF file.")(compress_pdf)
|
|
53
54
|
app.command(name="pc", no_args_is_help=True, hidden=True)(compress_pdf)
|
|
54
55
|
|
|
55
56
|
# app.command(name="copy", no_args_is_help=True, help="[c] Copy files or directories.")(copy)
|
|
@@ -7,7 +7,7 @@ $user = ''
|
|
|
7
7
|
$sharePath = ''
|
|
8
8
|
$driveLetter = ''
|
|
9
9
|
|
|
10
|
-
uv run --python 3.14 --with "machineconfig>=7.
|
|
10
|
+
uv run --python 3.14 --with "machineconfig>=7.83" python -m machineconfig.scripts.python.mount_ssh
|
|
11
11
|
|
|
12
12
|
net use T: \\sshfs.kr\$user@$host.local
|
|
13
13
|
# this worked: net use T: \\sshfs\alex@alex-p51s-5.local
|