machineconfig 7.57__py3-none-any.whl → 7.59__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/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 +1 -20
- machineconfig/jobs/installer/custom_dev/wezterm.py +0 -4
- machineconfig/jobs/installer/installer_data.json +57 -23
- machineconfig/jobs/installer/package_groups.py +20 -13
- machineconfig/scripts/linux/wrap_mcfg +1 -1
- machineconfig/scripts/python/croshell.py +4 -4
- machineconfig/scripts/python/devops.py +1 -1
- machineconfig/scripts/python/env_manager/path_manager_tui.py +1 -1
- machineconfig/scripts/python/helpers_croshell/crosh.py +2 -2
- machineconfig/scripts/python/helpers_devops/cli_config_dotfile.py +4 -5
- machineconfig/scripts/python/helpers_devops/cli_self.py +3 -3
- machineconfig/scripts/python/helpers_devops/cli_share_file.py +2 -2
- machineconfig/scripts/python/helpers_devops/cli_share_server.py +1 -1
- machineconfig/scripts/python/helpers_devops/cli_terminal.py +1 -1
- machineconfig/scripts/python/helpers_devops/cli_utils.py +0 -72
- machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py +4 -4
- machineconfig/scripts/python/helpers_fire_command/file_wrangler.py +2 -3
- machineconfig/scripts/python/helpers_fire_command/fire_jobs_route_helper.py +3 -4
- machineconfig/scripts/python/helpers_repos/cloud_repo_sync.py +3 -2
- 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 +151 -0
- machineconfig/scripts/python/helpers_utils/path.py +1 -1
- machineconfig/scripts/python/interactive.py +2 -2
- 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 +2 -3
- machineconfig/scripts/python/utils.py +2 -1
- machineconfig/scripts/windows/mounts/mount_ssh.ps1 +1 -1
- machineconfig/setup_linux/web_shortcuts/interactive.sh +10 -10
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +10 -10
- machineconfig/utils/files/headers.py +2 -2
- machineconfig/utils/installer_utils/installer_class.py +38 -24
- machineconfig/utils/installer_utils/{installer.py → installer_cli.py} +29 -15
- machineconfig/utils/{installer.py → installer_utils/installer_runner.py} +1 -25
- machineconfig/utils/options.py +1 -1
- machineconfig/utils/path_extended.py +2 -2
- machineconfig/utils/path_helper.py +34 -31
- machineconfig/utils/ssh.py +1 -1
- {machineconfig-7.57.dist-info → machineconfig-7.59.dist-info}/METADATA +1 -1
- {machineconfig-7.57.dist-info → machineconfig-7.59.dist-info}/RECORD +54 -53
- machineconfig/jobs/installer/linux_scripts/pgsql.sh +0 -41
- /machineconfig/jobs/installer/linux_scripts/{warp-cli.sh → cloudflare_warp_cli.sh} +0 -0
- /machineconfig/utils/installer_utils/{installer_abc.py → installer_locator_utils.py} +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.59.dist-info}/WHEEL +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.59.dist-info}/entry_points.txt +0 -0
- {machineconfig-7.57.dist-info → machineconfig-7.59.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
import os
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
4
|
import platform
|
|
5
5
|
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ def parse_pyfile(file_path: str):
|
|
|
12
12
|
func_args: list[list[args_spec]] = [[]] # this firt prepopulated dict is for the option 'RUN AS MAIN' which has no args
|
|
13
13
|
import ast
|
|
14
14
|
|
|
15
|
-
parsed_ast = ast.parse(
|
|
15
|
+
parsed_ast = ast.parse(Path(file_path).read_text(encoding="utf-8"))
|
|
16
16
|
functions = [node for node in ast.walk(parsed_ast) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
|
|
17
17
|
module__doc__ = ast.get_docstring(parsed_ast)
|
|
18
18
|
main_option = f"RUN AS MAIN -- {module__doc__ if module__doc__ is not None else 'NoDocs'}"
|
|
@@ -102,7 +102,6 @@ def wrap_import_in_try_except(import_line: str, pyfile: str, repo_root: Optional
|
|
|
102
102
|
print(fr"❌ Failed to import `{pyfile}` as a module: {ex} ")
|
|
103
103
|
print("⚠️ Attempting import with ad-hoc `$PATH` manipulation. DO NOT pickle any objects in this session as correct deserialization cannot be guaranteed.")
|
|
104
104
|
import sys
|
|
105
|
-
from pathlib import Path
|
|
106
105
|
sys.path.append(str(Path(pyfile).parent))
|
|
107
106
|
if repo_root is not None:
|
|
108
107
|
sys.path.append(repo_root)
|
|
@@ -5,11 +5,10 @@ from typing import Optional
|
|
|
5
5
|
import tomllib
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from machineconfig.utils.accessories import randstr
|
|
8
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
9
8
|
from machineconfig.utils.options import choose_from_options
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
def choose_function_or_lines(choice_file:
|
|
11
|
+
def choose_function_or_lines(choice_file: Path, kwargs_dict: dict[str, object]) -> tuple[Optional[str], Path, dict[str, object]]:
|
|
13
12
|
"""
|
|
14
13
|
Choose a function to run from a Python file or lines from a shell script.
|
|
15
14
|
|
|
@@ -46,7 +45,7 @@ def choose_function_or_lines(choice_file: PathExtended, kwargs_dict: dict[str, o
|
|
|
46
45
|
continue
|
|
47
46
|
options.append(line)
|
|
48
47
|
chosen_lines = choose_from_options(msg="Choose a line to run", options=options, fzf=True, multi=True)
|
|
49
|
-
choice_file =
|
|
48
|
+
choice_file = Path.home().joinpath(f"tmp_results/tmp_scripts/shell/{randstr(10)}.sh")
|
|
50
49
|
choice_file.parent.mkdir(parents=True, exist_ok=True)
|
|
51
50
|
choice_file.write_text("\n".join(chosen_lines), encoding="utf-8")
|
|
52
51
|
choice_function = None
|
|
@@ -82,7 +81,7 @@ def get_command_streamlit(choice_file: Path, environment: str, repo_root: Option
|
|
|
82
81
|
port = config["server"]["port"]
|
|
83
82
|
secrets_path = toml_path.with_name("secrets.toml")
|
|
84
83
|
if repo_root is not None:
|
|
85
|
-
secrets_template_path = Path.home().joinpath(f"dotfiles/creds/streamlit/{
|
|
84
|
+
secrets_template_path = Path.home().joinpath(f"dotfiles/creds/streamlit/{Path(repo_root).name}/{choice_file.name}/secrets.toml")
|
|
86
85
|
if environment != "" and not secrets_path.exists() and secrets_template_path.exists():
|
|
87
86
|
secrets_template = tomllib.loads(secrets_template_path.read_text(encoding="utf-8"))
|
|
88
87
|
if environment == "ip":
|
|
@@ -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.59"]
|
|
108
109
|
uv_project_dir = None
|
|
109
110
|
|
|
110
111
|
import tempfile
|
|
@@ -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.59" {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,151 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Optional
|
|
4
|
+
import typer
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def download(
|
|
8
|
+
url: Annotated[Optional[str], typer.Argument(..., help="The URL to download the file from.")] = None,
|
|
9
|
+
decompress: Annotated[bool, typer.Option(..., "--decompress", "-d", help="Decompress the file if it's an archive.")] = False,
|
|
10
|
+
output: Annotated[Optional[str], typer.Option("--output", "-o", help="The output file path.")] = None,
|
|
11
|
+
output_dir: Annotated[Optional[str], typer.Option("--output-dir", help="Directory to place the downloaded file in.")] = None,
|
|
12
|
+
) -> Optional["Path"]:
|
|
13
|
+
|
|
14
|
+
import subprocess
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from urllib.parse import parse_qs, unquote, urlparse
|
|
17
|
+
from requests import Response
|
|
18
|
+
import requests
|
|
19
|
+
|
|
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
|
+
if __name__ == "__main__":
|
|
151
|
+
from pathlib import Path
|
|
@@ -17,7 +17,7 @@ def path():
|
|
|
17
17
|
uv_with = ["textual"]
|
|
18
18
|
uv_project_dir = None
|
|
19
19
|
if not Path.home().joinpath("code/machineconfig").exists():
|
|
20
|
-
uv_with.append("machineconfig>=7.
|
|
20
|
+
uv_with.append("machineconfig>=7.59")
|
|
21
21
|
else:
|
|
22
22
|
uv_project_dir = str(Path.home().joinpath("code/machineconfig"))
|
|
23
23
|
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])
|
|
@@ -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 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,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from platform import system
|
|
4
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich import box
|
|
@@ -26,7 +26,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
26
26
|
results: dict[str, dict[str, str | bool]] = {}
|
|
27
27
|
issues_found: list[str] = []
|
|
28
28
|
|
|
29
|
-
ssh_dir =
|
|
29
|
+
ssh_dir = Path.home().joinpath(".ssh")
|
|
30
30
|
authorized_keys = ssh_dir.joinpath("authorized_keys")
|
|
31
31
|
|
|
32
32
|
console.print(Panel("🔐 Checking SSH directory and authorized_keys...", title="[bold blue]File Permissions[/bold blue]", border_style="blue"))
|
|
@@ -106,7 +106,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
106
106
|
|
|
107
107
|
console.print(Panel("🔌 Checking SSH port and listening status...", title="[bold blue]Network Status[/bold blue]", border_style="blue"))
|
|
108
108
|
|
|
109
|
-
sshd_config_paths = [
|
|
109
|
+
sshd_config_paths = [Path("/etc/ssh/sshd_config"), Path("/etc/sshd_config")]
|
|
110
110
|
sshd_config = None
|
|
111
111
|
for config_path in sshd_config_paths:
|
|
112
112
|
if config_path.exists():
|
|
@@ -236,7 +236,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
236
236
|
|
|
237
237
|
console.print(Panel("🗂️ Checking for problematic files in /etc/...", title="[bold blue]System Files[/bold blue]", border_style="blue"))
|
|
238
238
|
|
|
239
|
-
hosts_deny =
|
|
239
|
+
hosts_deny = Path("/etc/hosts.deny")
|
|
240
240
|
if hosts_deny.exists():
|
|
241
241
|
hosts_deny_content = hosts_deny.read_text(encoding="utf-8")
|
|
242
242
|
active_lines = [line.strip() for line in hosts_deny_content.splitlines() if line.strip() and not line.strip().startswith("#")]
|
|
@@ -252,14 +252,14 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
252
252
|
results["hosts_deny"] = {"status": "ok", "message": "/etc/hosts.deny does not exist", "action": ""}
|
|
253
253
|
console.print(Panel("✅ /etc/hosts.deny not present", title="[bold green]OK[/bold green]", border_style="green"))
|
|
254
254
|
|
|
255
|
-
hosts_allow =
|
|
255
|
+
hosts_allow = Path("/etc/hosts.allow")
|
|
256
256
|
if hosts_allow.exists():
|
|
257
257
|
results["hosts_allow"] = {"status": "ok", "message": "/etc/hosts.allow exists (check if needed)", "action": ""}
|
|
258
258
|
console.print(Panel("ℹ️ /etc/hosts.allow exists\n💡 Ensure it allows SSH if using TCP wrappers", title="[bold blue]Info[/bold blue]", border_style="blue"))
|
|
259
259
|
|
|
260
260
|
console.print(Panel("👤 Checking home directory permissions...", title="[bold blue]User Permissions[/bold blue]", border_style="blue"))
|
|
261
261
|
|
|
262
|
-
home_dir =
|
|
262
|
+
home_dir = Path.home()
|
|
263
263
|
home_stat = os.stat(home_dir)
|
|
264
264
|
home_perms = oct(home_stat.st_mode)[-3:]
|
|
265
265
|
|
|
@@ -294,7 +294,7 @@ def ssh_debug_linux() -> dict[str, dict[str, str | bool]]:
|
|
|
294
294
|
|
|
295
295
|
console.print(Panel("📋 Checking SSH logs for errors...", title="[bold blue]Logs[/bold blue]", border_style="blue"))
|
|
296
296
|
|
|
297
|
-
log_files = [
|
|
297
|
+
log_files = [Path("/var/log/auth.log"), Path("/var/log/secure")]
|
|
298
298
|
log_found = False
|
|
299
299
|
for log_file in log_files:
|
|
300
300
|
if log_file.exists():
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from platform import system
|
|
4
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from rich.console import Console
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich import box
|
|
@@ -26,7 +26,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
26
26
|
results: dict[str, dict[str, str | bool]] = {}
|
|
27
27
|
issues_found: list[str] = []
|
|
28
28
|
|
|
29
|
-
ssh_dir =
|
|
29
|
+
ssh_dir = Path.home().joinpath(".ssh")
|
|
30
30
|
authorized_keys = ssh_dir.joinpath("authorized_keys")
|
|
31
31
|
|
|
32
32
|
console.print(Panel("🔐 Checking SSH directory and authorized_keys...", title="[bold blue]File Permissions[/bold blue]", border_style="blue"))
|
|
@@ -124,7 +124,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
124
124
|
|
|
125
125
|
console.print(Panel("🔌 Checking SSH port and listening status...", title="[bold blue]Network Status[/bold blue]", border_style="blue"))
|
|
126
126
|
|
|
127
|
-
sshd_config_paths = [
|
|
127
|
+
sshd_config_paths = [Path("C:\\ProgramData\\ssh\\sshd_config"), Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "sshd_config")]
|
|
128
128
|
sshd_config = None
|
|
129
129
|
for config_path in sshd_config_paths:
|
|
130
130
|
if config_path.exists():
|
|
@@ -168,7 +168,7 @@ def ssh_debug_windows() -> dict[str, dict[str, str | bool]]:
|
|
|
168
168
|
if admin_authorized_keys_lines:
|
|
169
169
|
console.print(Panel("⚠️ IMPORTANT: Administrators group uses different authorized_keys location\n💡 For admin users, keys should be in: C:\\ProgramData\\ssh\\administrators_authorized_keys\n💡 Not in user's .ssh/authorized_keys!", title="[bold yellow]Admin Users[/bold yellow]", border_style="yellow"))
|
|
170
170
|
|
|
171
|
-
programdata_auth_keys =
|
|
171
|
+
programdata_auth_keys = Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")).joinpath("ssh", "administrators_authorized_keys")
|
|
172
172
|
if programdata_auth_keys.exists():
|
|
173
173
|
console.print(Panel("✅ administrators_authorized_keys file exists", title="[bold green]OK[/bold green]", border_style="green"))
|
|
174
174
|
else:
|
|
@@ -35,15 +35,16 @@ def main(
|
|
|
35
35
|
print("=" * 50 + "\n")
|
|
36
36
|
|
|
37
37
|
path_obj = PathExtended(path).expanduser().absolute()
|
|
38
|
+
relative_home = PathExtended(path_obj.expanduser().absolute().relative_to(Path.home()))
|
|
38
39
|
|
|
39
40
|
if same_file_system:
|
|
40
41
|
print("⚠️ Using a not recommended transfer method! Copying files across the same file system.")
|
|
41
42
|
if system == "Windows": # move files over to WSL
|
|
42
43
|
print("📤 Transferring files from Windows to WSL...")
|
|
43
|
-
path_obj.copy(folder=WSL_FROM_WIN.joinpath(UserName).joinpath(
|
|
44
|
+
path_obj.copy(folder=WSL_FROM_WIN.joinpath(UserName).joinpath(relative_home.parent), overwrite=True) # the following works for files and folders alike.
|
|
44
45
|
else: # move files from WSL to win
|
|
45
46
|
print("📤 Transferring files from WSL to Windows...")
|
|
46
|
-
path_obj.copy(folder=WIN_FROM_WSL.joinpath(UserName).joinpath(
|
|
47
|
+
path_obj.copy(folder=WIN_FROM_WSL.joinpath(UserName).joinpath(relative_home.parent), overwrite=True)
|
|
47
48
|
print("✅ Transfer completed successfully!\n")
|
|
48
49
|
else:
|
|
49
50
|
from machineconfig.utils.ssh import SSH
|
|
@@ -50,19 +50,18 @@ def select_layout(layouts_json_file: Path, selected_layouts_names: Optional[list
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def find_layout_file(layout_path: str, ) -> Path:
|
|
53
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
54
53
|
from machineconfig.utils.path_helper import search_for_files_of_interest
|
|
55
54
|
from machineconfig.utils.options import choose_from_options
|
|
56
55
|
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
57
56
|
path_obj = sanitize_path(layout_path)
|
|
58
57
|
if not path_obj.exists():
|
|
59
|
-
choice_file = match_file_name(sub_string=layout_path, search_root=
|
|
58
|
+
choice_file = match_file_name(sub_string=layout_path, search_root=Path.cwd(), suffixes={".json"})
|
|
60
59
|
elif path_obj.is_dir():
|
|
61
60
|
print(f"🔍 Searching recursively for Python, PowerShell and Shell scripts in directory `{path_obj}`")
|
|
62
61
|
files = search_for_files_of_interest(path_obj, suffixes={".py", ".sh", ".ps1"})
|
|
63
62
|
print(f"🔍 Got #{len(files)} results.")
|
|
64
63
|
choice_file = choose_from_options(multi=False, options=files, fzf=True, msg="Choose one option")
|
|
65
|
-
choice_file =
|
|
64
|
+
choice_file = Path(choice_file).expanduser().absolute()
|
|
66
65
|
else:
|
|
67
66
|
choice_file = path_obj
|
|
68
67
|
return choice_file
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
from machineconfig.scripts.python.helpers_devops.cli_utils import
|
|
3
|
+
from machineconfig.scripts.python.helpers_devops.cli_utils import merge_pdfs, compress_pdf
|
|
4
4
|
from machineconfig.scripts.python.helpers_utils.path import edit_file_with_hx, get_machine_specs, init_project, path
|
|
5
|
+
from machineconfig.scripts.python.helpers_utils.download import download
|
|
5
6
|
import typer
|
|
6
7
|
from typing import Annotated
|
|
7
8
|
|
|
@@ -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.59" 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
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
. <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/uv.sh")
|
|
3
3
|
. <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/linux/wrap_mcfg")
|
|
4
4
|
|
|
5
|
-
alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
6
|
-
alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
7
|
-
alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
8
|
-
alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
9
|
-
alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
10
|
-
alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
11
|
-
alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
12
|
-
alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
13
|
-
alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
14
|
-
alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.
|
|
5
|
+
alias devops='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" devops'
|
|
6
|
+
alias cloud='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" cloud'
|
|
7
|
+
alias agents='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" agents'
|
|
8
|
+
alias sessions='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" sessions'
|
|
9
|
+
alias ftpx='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" ftpx'
|
|
10
|
+
alias fire='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" fire'
|
|
11
|
+
alias croshell='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" croshell'
|
|
12
|
+
alias utils='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" utils'
|
|
13
|
+
alias terminal='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" terminal'
|
|
14
|
+
alias msearch='$HOME/.local/bin/uvx --python 3.14 --from "machineconfig>=7.59" msearch'
|
|
15
15
|
|
|
16
16
|
alias d='wrap_in_shell_script devops'
|
|
17
17
|
alias c='wrap_in_shell_script cloud'
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/uv.ps1").Content
|
|
4
4
|
iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/scripts/windows/wrap_mcfg.ps1").Content
|
|
5
5
|
|
|
6
|
-
function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
7
|
-
function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
8
|
-
function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
9
|
-
function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
10
|
-
function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
11
|
-
function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
12
|
-
function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
13
|
-
function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
14
|
-
function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
15
|
-
function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.
|
|
6
|
+
function devops { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" devops $args }
|
|
7
|
+
function cloud { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" cloud $args }
|
|
8
|
+
function agents { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" agents $args }
|
|
9
|
+
function sessions { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" sessions $args }
|
|
10
|
+
function ftpx { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" ftpx $args }
|
|
11
|
+
function fire { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" fire $args }
|
|
12
|
+
function croshell { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" croshell $args }
|
|
13
|
+
function utils { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" utils $args }
|
|
14
|
+
function terminal { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" terminal $args }
|
|
15
|
+
function msearch { & "$HOME\.local\bin\uvx.exe" --python 3.14 --from "machineconfig>=7.59" msearch $args }
|
|
16
16
|
|
|
17
17
|
function d { wrap_in_shell_script devops @args }
|
|
18
18
|
function c { wrap_in_shell_script cloud @args }
|
|
@@ -25,7 +25,7 @@ def print_header():
|
|
|
25
25
|
table.add_row("Virtual Environment", os.getenv('VIRTUAL_ENV', 'None'))
|
|
26
26
|
table.add_row("Running @", str(Path.cwd()))
|
|
27
27
|
|
|
28
|
-
from machineconfig.utils.
|
|
28
|
+
from machineconfig.utils.installer_utils.installer_runner import get_machineconfig_version
|
|
29
29
|
|
|
30
30
|
console.print(Panel(table, title=f"[bold blue]✨ 🐊 Machineconfig Shell {get_machineconfig_version()} ✨ Made with 🐍 | Built with ❤️[/bold blue]", border_style="blue"))
|
|
31
31
|
def print_logo(logo: str):
|
|
@@ -44,7 +44,7 @@ def print_logo(logo: str):
|
|
|
44
44
|
_default_art = Path(random.choice(glob.glob(str(Path(__file__).parent.joinpath("art", "*")))))
|
|
45
45
|
print(_default_art.read_text())
|
|
46
46
|
elif platform.system() in ["Linux", "Darwin"]: # Explicitly handle both Linux and macOS
|
|
47
|
-
from machineconfig.utils.installer_utils.
|
|
47
|
+
from machineconfig.utils.installer_utils.installer_locator_utils import is_executable_in_path
|
|
48
48
|
avail_cowsay = is_executable_in_path("cowsay")
|
|
49
49
|
avail_lolcat = is_executable_in_path("lolcat")
|
|
50
50
|
avail_boxes = is_executable_in_path("boxes")
|