machineconfig 2.1__py3-none-any.whl → 2.2__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/archive/create_zellij_template.py +2 -1
- machineconfig/cluster/templates/utils.py +0 -35
- machineconfig/jobs/python/check_installations.py +1 -1
- machineconfig/jobs/python_custom_installers/dev/code.py +0 -13
- machineconfig/jobs/python_generic_installers/config.json +1 -1
- machineconfig/profile/create.py +10 -5
- machineconfig/profile/create_hardlinks.py +3 -1
- machineconfig/profile/shell.py +8 -7
- machineconfig/scripts/__init__.py +0 -2
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/linux/devops +6 -4
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/generate_files.py +14 -15
- machineconfig/scripts/python/ai/mcinit.py +8 -5
- machineconfig/scripts/python/archive/tmate_conn.py +5 -5
- machineconfig/scripts/python/archive/tmate_start.py +7 -7
- machineconfig/scripts/python/choose_wezterm_theme.py +35 -32
- machineconfig/scripts/python/cloud_copy.py +22 -13
- machineconfig/scripts/python/cloud_mount.py +35 -23
- machineconfig/scripts/python/cloud_repo_sync.py +38 -25
- machineconfig/scripts/python/cloud_sync.py +4 -4
- machineconfig/scripts/python/croshell.py +37 -28
- machineconfig/scripts/python/devops.py +45 -27
- machineconfig/scripts/python/devops_add_identity.py +15 -25
- machineconfig/scripts/python/devops_add_ssh_key.py +7 -7
- machineconfig/scripts/python/devops_backup_retrieve.py +17 -15
- machineconfig/scripts/python/devops_devapps_install.py +25 -20
- machineconfig/scripts/python/devops_update_repos.py +142 -57
- machineconfig/scripts/python/dotfile.py +16 -14
- machineconfig/scripts/python/fire_agents.py +24 -17
- machineconfig/scripts/python/fire_jobs.py +91 -55
- machineconfig/scripts/python/ftpx.py +24 -14
- machineconfig/scripts/python/get_zellij_cmd.py +8 -7
- machineconfig/scripts/python/helpers/cloud_helpers.py +33 -28
- machineconfig/scripts/python/helpers/helpers2.py +25 -14
- machineconfig/scripts/python/helpers/helpers4.py +44 -30
- machineconfig/scripts/python/helpers/helpers5.py +1 -1
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +31 -9
- machineconfig/scripts/python/mount_nfs.py +8 -15
- machineconfig/scripts/python/mount_nw_drive.py +10 -5
- machineconfig/scripts/python/mount_ssh.py +8 -6
- machineconfig/scripts/python/repos.py +215 -57
- machineconfig/scripts/python/snapshot.py +0 -1
- machineconfig/scripts/python/start_slidev.py +10 -5
- machineconfig/scripts/python/start_terminals.py +22 -16
- machineconfig/scripts/python/viewer_template.py +0 -1
- machineconfig/scripts/python/wifi_conn.py +49 -75
- machineconfig/scripts/python/wsl_windows_transfer.py +8 -6
- machineconfig/settings/lf/linux/lfrc +1 -0
- machineconfig/setup_linux/web_shortcuts/croshell.sh +5 -0
- machineconfig/setup_linux/web_shortcuts/interactive.sh +1 -1
- machineconfig/setup_linux/web_shortcuts/ssh.sh +0 -4
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -12
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -1
- machineconfig/utils/code.py +3 -3
- machineconfig/utils/installer.py +2 -2
- machineconfig/utils/installer_utils/installer_abc.py +3 -4
- machineconfig/utils/installer_utils/installer_class.py +6 -4
- machineconfig/utils/links.py +103 -33
- machineconfig/utils/notifications.py +52 -38
- machineconfig/utils/options.py +16 -23
- machineconfig/utils/path_reduced.py +239 -205
- machineconfig/utils/procs.py +1 -1
- machineconfig/utils/source_of_truth.py +27 -0
- machineconfig/utils/ssh.py +9 -29
- machineconfig/utils/terminal.py +4 -2
- machineconfig/utils/upgrade_packages.py +91 -0
- machineconfig/utils/utils2.py +1 -2
- machineconfig/utils/utils5.py +23 -11
- machineconfig/utils/ve.py +4 -1
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/METADATA +13 -13
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/RECORD +78 -86
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/generate_files.cpython-313.pyc +0 -0
- machineconfig/scripts/python/ai/__pycache__/mcinit.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/setup_linux/web_shortcuts/all.sh +0 -48
- machineconfig/setup_linux/web_shortcuts/update_system.sh +0 -48
- machineconfig/utils/utils.py +0 -97
- /machineconfig/setup_linux/web_shortcuts/{tmp.sh → android.sh} +0 -0
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/WHEEL +0 -0
- {machineconfig-2.1.dist-info → machineconfig-2.2.dist-info}/top_level.txt +0 -0
|
@@ -4,10 +4,10 @@ from pydantic import ConfigDict
|
|
|
4
4
|
from pydantic.dataclasses import dataclass
|
|
5
5
|
from typing import Optional
|
|
6
6
|
import os
|
|
7
|
-
from machineconfig.utils.
|
|
7
|
+
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.panel import Panel
|
|
10
|
-
from rich import box
|
|
10
|
+
from rich import box # Import box
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
console = Console()
|
|
@@ -16,10 +16,10 @@ console = Console()
|
|
|
16
16
|
class ArgsDefaults:
|
|
17
17
|
# source: str=None
|
|
18
18
|
# target: str=None
|
|
19
|
-
encrypt: bool=False
|
|
20
|
-
zip_: bool=False
|
|
21
|
-
overwrite: bool=False
|
|
22
|
-
share: bool=False
|
|
19
|
+
encrypt: bool = False
|
|
20
|
+
zip_: bool = False
|
|
21
|
+
overwrite: bool = False
|
|
22
|
+
share: bool = False
|
|
23
23
|
rel2home = False
|
|
24
24
|
root = None
|
|
25
25
|
os_specific = False
|
|
@@ -28,12 +28,12 @@ class ArgsDefaults:
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@dataclass(config=ConfigDict(extra="forbid", frozen=False))
|
|
31
|
-
class Args
|
|
31
|
+
class Args:
|
|
32
32
|
cloud: Optional[str] = None
|
|
33
33
|
|
|
34
|
-
zip: bool=ArgsDefaults.zip_
|
|
35
|
-
overwrite: bool=ArgsDefaults.overwrite
|
|
36
|
-
share: bool=ArgsDefaults.share
|
|
34
|
+
zip: bool = ArgsDefaults.zip_
|
|
35
|
+
overwrite: bool = ArgsDefaults.overwrite
|
|
36
|
+
share: bool = ArgsDefaults.share
|
|
37
37
|
|
|
38
38
|
root: Optional[str] = ArgsDefaults.root
|
|
39
39
|
os_specific: bool = ArgsDefaults.os_specific
|
|
@@ -67,15 +67,16 @@ def find_cloud_config(path: Path):
|
|
|
67
67
|
|
|
68
68
|
def absolute(path: str) -> Path:
|
|
69
69
|
obj = Path(path).expanduser()
|
|
70
|
-
if not path.startswith(".") and
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
if not path.startswith(".") and obj.exists():
|
|
71
|
+
return obj
|
|
72
|
+
try_absing = Path.cwd().joinpath(path)
|
|
73
|
+
if try_absing.exists():
|
|
74
|
+
return try_absing
|
|
73
75
|
display_warning(f"Path {path} could not be resolved to absolute path.")
|
|
74
76
|
display_warning("Trying to resolve symlinks (this may result in unintended paths).")
|
|
75
77
|
return obj.absolute()
|
|
76
78
|
|
|
77
79
|
|
|
78
|
-
|
|
79
80
|
def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Args:
|
|
80
81
|
console = Console()
|
|
81
82
|
console.print(Panel("🔐 Secure Share Cloud Configuration", expand=False))
|
|
@@ -88,10 +89,10 @@ def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Ar
|
|
|
88
89
|
console.print(f"☁️ Using cloud from environment: {cloud}")
|
|
89
90
|
else:
|
|
90
91
|
try:
|
|
91
|
-
default_cloud__ = read_ini(DEFAULTS_PATH)[
|
|
92
|
+
default_cloud__ = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
92
93
|
except Exception:
|
|
93
|
-
default_cloud__ =
|
|
94
|
-
if default_cloud__ ==
|
|
94
|
+
default_cloud__ = "No default cloud found."
|
|
95
|
+
if default_cloud__ == "No default cloud found." or interactive:
|
|
95
96
|
# assert default_cloud is not None
|
|
96
97
|
cloud = input(f"☁️ Enter cloud name (default {default_cloud__}): ") or default_cloud__
|
|
97
98
|
else:
|
|
@@ -106,32 +107,36 @@ def get_secure_share_cloud_config(interactive: bool, cloud: Optional[str]) -> Ar
|
|
|
106
107
|
pwd = ""
|
|
107
108
|
default_message = "no default password found"
|
|
108
109
|
pwd = input(f"🔑 Enter encryption password ({default_message}): ") or pwd
|
|
109
|
-
res = Args(cloud=cloud,
|
|
110
|
-
pwd=pwd, encrypt=True,
|
|
111
|
-
zip=True, overwrite=True, share=True,
|
|
112
|
-
rel2home=True, root="myshare", os_specific=False,)
|
|
110
|
+
res = Args(cloud=cloud, pwd=pwd, encrypt=True, zip=True, overwrite=True, share=True, rel2home=True, root="myshare", os_specific=False)
|
|
113
111
|
|
|
114
112
|
display_success("Using SecureShare cloud config")
|
|
115
113
|
pprint(res.__dict__, "SecureShare Config")
|
|
116
114
|
return res
|
|
117
115
|
|
|
116
|
+
|
|
118
117
|
def display_header(title: str):
|
|
119
|
-
console.print(Panel(title, box=box.DOUBLE_EDGE, title_align="left"))
|
|
118
|
+
console.print(Panel(title, box=box.DOUBLE_EDGE, title_align="left")) # Replace print with Panel
|
|
119
|
+
|
|
120
120
|
|
|
121
121
|
def display_subheader(title: str):
|
|
122
|
-
console.print(Panel(title, box=box.ROUNDED, title_align="left"))
|
|
122
|
+
console.print(Panel(title, box=box.ROUNDED, title_align="left")) # Replace print with Panel
|
|
123
|
+
|
|
123
124
|
|
|
124
125
|
def display_content(content: str):
|
|
125
|
-
console.print(Panel(content, box=box.ROUNDED, title_align="left"))
|
|
126
|
+
console.print(Panel(content, box=box.ROUNDED, title_align="left")) # Replace print with Panel
|
|
127
|
+
|
|
126
128
|
|
|
127
129
|
def display_status(status: str):
|
|
128
|
-
console.print(Panel(status, box=box.ROUNDED, title_align="left"))
|
|
130
|
+
console.print(Panel(status, box=box.ROUNDED, title_align="left")) # Replace print with Panel
|
|
131
|
+
|
|
129
132
|
|
|
130
133
|
def display_success(message: str):
|
|
131
|
-
console.print(Panel(message, box=box.ROUNDED, border_style="green", title_align="left"))
|
|
134
|
+
console.print(Panel(message, box=box.ROUNDED, border_style="green", title_align="left")) # Replace print with Panel
|
|
135
|
+
|
|
132
136
|
|
|
133
137
|
def display_warning(message: str):
|
|
134
|
-
console.print(Panel(message, box=box.ROUNDED, border_style="yellow", title_align="left"))
|
|
138
|
+
console.print(Panel(message, box=box.ROUNDED, border_style="yellow", title_align="left")) # Replace print with Panel
|
|
139
|
+
|
|
135
140
|
|
|
136
141
|
def display_error(message: str):
|
|
137
|
-
console.print(Panel(message, box=box.ROUNDED, border_style="red", title_align="left"))
|
|
142
|
+
console.print(Panel(message, box=box.ROUNDED, border_style="red", title_align="left")) # Replace print with Panel
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from machineconfig.scripts.python.helpers.cloud_helpers import Args, ArgsDefaults, absolute, find_cloud_config, get_secure_share_cloud_config
|
|
2
|
-
from machineconfig.utils.
|
|
2
|
+
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
3
3
|
from machineconfig.utils.utils2 import read_ini, pprint
|
|
4
4
|
from typing import Optional
|
|
5
5
|
from rich.console import Console
|
|
@@ -17,7 +17,8 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
17
17
|
if config == "ss":
|
|
18
18
|
cloud_maybe: Optional[str] = target.split(":")[0]
|
|
19
19
|
# if cloud_maybe == "": cloud_maybe = source.split(":")[0]
|
|
20
|
-
if cloud_maybe == "":
|
|
20
|
+
if cloud_maybe == "":
|
|
21
|
+
cloud_maybe = None
|
|
21
22
|
print("cloud_maybe:", cloud_maybe)
|
|
22
23
|
maybe_config = get_secure_share_cloud_config(interactive=True, cloud=cloud_maybe)
|
|
23
24
|
elif config is not None:
|
|
@@ -27,13 +28,20 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
27
28
|
maybe_config = None
|
|
28
29
|
|
|
29
30
|
if maybe_config is not None:
|
|
30
|
-
if args.zip == ArgsDefaults.zip_:
|
|
31
|
-
|
|
32
|
-
if args.
|
|
33
|
-
|
|
34
|
-
if args.
|
|
35
|
-
|
|
36
|
-
if args.
|
|
31
|
+
if args.zip == ArgsDefaults.zip_:
|
|
32
|
+
args.zip = maybe_config.zip
|
|
33
|
+
if args.encrypt == ArgsDefaults.encrypt:
|
|
34
|
+
args.encrypt = maybe_config.encrypt
|
|
35
|
+
if args.share == ArgsDefaults.share:
|
|
36
|
+
args.share = maybe_config.share
|
|
37
|
+
if args.root == ArgsDefaults.root:
|
|
38
|
+
args.root = maybe_config.root
|
|
39
|
+
if args.rel2home == ArgsDefaults.rel2home:
|
|
40
|
+
args.rel2home = maybe_config.rel2home
|
|
41
|
+
if args.pwd == ArgsDefaults.pwd:
|
|
42
|
+
args.pwd = maybe_config.pwd
|
|
43
|
+
if args.os_specific == ArgsDefaults.os_specific:
|
|
44
|
+
args.os_specific = maybe_config.os_specific
|
|
37
45
|
|
|
38
46
|
root = args.root
|
|
39
47
|
rel2home = args.rel2home
|
|
@@ -53,7 +61,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
53
61
|
maybe_config = tmp_maybe_config
|
|
54
62
|
|
|
55
63
|
if maybe_config is None:
|
|
56
|
-
default_cloud: str=read_ini(DEFAULTS_PATH)[
|
|
64
|
+
default_cloud: str = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
57
65
|
console.print(Panel(f"⚠️ No cloud config found. Using default cloud: {default_cloud}", width=150, border_style="yellow"))
|
|
58
66
|
source = default_cloud + ":" + source[1:]
|
|
59
67
|
else:
|
|
@@ -73,7 +81,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
73
81
|
maybe_config = find_cloud_config(path)
|
|
74
82
|
|
|
75
83
|
if maybe_config is None:
|
|
76
|
-
default_cloud = read_ini(DEFAULTS_PATH)[
|
|
84
|
+
default_cloud = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
77
85
|
console.print(Panel(f"⚠️ No cloud config found. Using default cloud: {default_cloud}", width=150, border_style="yellow"))
|
|
78
86
|
target = default_cloud + ":" + target[1:]
|
|
79
87
|
print("target mutated to:", target, f"because of default cloud being {default_cloud}")
|
|
@@ -88,7 +96,6 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
88
96
|
zip_arg = tmp.zip
|
|
89
97
|
share = tmp.share
|
|
90
98
|
|
|
91
|
-
|
|
92
99
|
if ":" in source and (source[1] != ":" if len(source) > 1 else True): # avoid the deceptive case of "C:/"
|
|
93
100
|
source_parts: list[str] = source.split(":")
|
|
94
101
|
cloud = source_parts[0]
|
|
@@ -97,6 +104,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
97
104
|
assert ES not in target, f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised."
|
|
98
105
|
target_obj = absolute(target)
|
|
99
106
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
107
|
+
|
|
100
108
|
remote_path = PathExtended(target_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
101
109
|
source = f"{cloud}:{remote_path.as_posix()}"
|
|
102
110
|
else: # source path is mentioned, target? maybe.
|
|
@@ -116,6 +124,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
116
124
|
assert ES not in source, "You can't use $ in both source and target. Cyclical inference dependency arised."
|
|
117
125
|
source_obj = absolute(source)
|
|
118
126
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
127
|
+
|
|
119
128
|
remote_path = PathExtended(source_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
120
129
|
target = f"{cloud}:{remote_path.as_posix()}"
|
|
121
130
|
else: # target path is mentioned, source? maybe.
|
|
@@ -124,8 +133,10 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
124
133
|
raise NotImplementedError("There is no .get_local_path method yet")
|
|
125
134
|
else:
|
|
126
135
|
source_obj = absolute(source)
|
|
127
|
-
if zip_arg and ".zip" not in target:
|
|
128
|
-
|
|
136
|
+
if zip_arg and ".zip" not in target:
|
|
137
|
+
target += ".zip"
|
|
138
|
+
if encrypt and ".enc" not in target:
|
|
139
|
+
target += ".enc"
|
|
129
140
|
else:
|
|
130
141
|
console.print(Panel("❌ ERROR: Invalid path configuration\nEither source or target must be a remote path (i.e. machine:path)", title="[bold red]Error[/bold red]", border_style="red"))
|
|
131
142
|
raise ValueError(f"Either source or target must be a remote path (i.e. machine:path)\nGot: source: `{source}`, target: `{target}`")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
1
|
from typing import Any, Callable, Optional
|
|
3
2
|
import inspect
|
|
4
3
|
import os
|
|
4
|
+
|
|
5
5
|
# import argparse
|
|
6
6
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
7
7
|
# from machineconfig.utils.utils import choose_ssh_host
|
|
@@ -27,7 +27,7 @@ def convert_kwargs_to_fire_kwargs_str(kwargs: dict[str, Any]) -> str:
|
|
|
27
27
|
# https://google.github.io/python-fire/guide/
|
|
28
28
|
# https://github.com/google/python-fire/blob/master/docs/guide.md#argument-parsing
|
|
29
29
|
if not kwargs: # empty dict
|
|
30
|
-
kwargs_str =
|
|
30
|
+
kwargs_str = ""
|
|
31
31
|
else:
|
|
32
32
|
# For fire module, all keyword arguments should be passed as --key value pairs
|
|
33
33
|
tmp_list: list[str] = []
|
|
@@ -40,40 +40,48 @@ def convert_kwargs_to_fire_kwargs_str(kwargs: dict[str, Any]) -> str:
|
|
|
40
40
|
def parse_pyfile(file_path: str):
|
|
41
41
|
print(f"🔍 Loading {file_path} ...")
|
|
42
42
|
from typing import NamedTuple
|
|
43
|
+
|
|
43
44
|
args_spec = NamedTuple("args_spec", [("name", str), ("type", str), ("default", Optional[str])])
|
|
44
45
|
func_args: list[list[args_spec]] = [[]] # this firt prepopulated dict is for the option 'RUN AS MAIN' which has no args
|
|
45
46
|
|
|
46
47
|
import ast
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
for node in ast.walk(parsed_ast)
|
|
51
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
|
|
52
|
-
]
|
|
48
|
+
|
|
49
|
+
parsed_ast = ast.parse(PathExtended(file_path).read_text(encoding="utf-8"))
|
|
50
|
+
functions = [node for node in ast.walk(parsed_ast) if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))]
|
|
53
51
|
module__doc__ = ast.get_docstring(parsed_ast)
|
|
54
52
|
main_option = f"RUN AS MAIN -- {module__doc__ if module__doc__ is not None else 'NoDocs'}"
|
|
55
53
|
options = [main_option]
|
|
56
54
|
for function in functions:
|
|
57
|
-
if function.name.startswith(
|
|
58
|
-
|
|
59
|
-
if any(arg.arg ==
|
|
55
|
+
if function.name.startswith("__") and function.name.endswith("__"):
|
|
56
|
+
continue
|
|
57
|
+
if any(arg.arg == "self" for arg in function.args.args):
|
|
58
|
+
continue
|
|
59
|
+
if any(arg.arg == "self" for arg in function.args.args):
|
|
60
|
+
continue
|
|
60
61
|
doc_string_tmp: str | None = ast.get_docstring(function)
|
|
61
|
-
if doc_string_tmp is None:
|
|
62
|
-
|
|
62
|
+
if doc_string_tmp is None:
|
|
63
|
+
doc_string = "NoDocs"
|
|
64
|
+
else:
|
|
65
|
+
doc_string = doc_string_tmp.replace("\n", " ")
|
|
63
66
|
options.append(f"{function.name} -- {', '.join([arg.arg for arg in function.args.args])} -- {doc_string}")
|
|
64
67
|
tmp = []
|
|
65
68
|
for idx, arg in enumerate(function.args.args):
|
|
66
69
|
if arg.annotation is not None:
|
|
67
|
-
try:
|
|
70
|
+
try:
|
|
71
|
+
type_ = arg.annotation.__dict__["id"]
|
|
68
72
|
except KeyError as ke:
|
|
69
73
|
type_ = "Any" # e.g. a callable object
|
|
70
74
|
_ = ke
|
|
71
|
-
else:
|
|
75
|
+
else:
|
|
76
|
+
type_ = "Any"
|
|
72
77
|
default_tmp = function.args.defaults[idx] if idx < len(function.args.defaults) else None
|
|
73
|
-
if default_tmp is None:
|
|
78
|
+
if default_tmp is None:
|
|
79
|
+
default = None
|
|
74
80
|
else:
|
|
75
|
-
if hasattr(default_tmp, "__dict__"):
|
|
76
|
-
|
|
81
|
+
if hasattr(default_tmp, "__dict__"):
|
|
82
|
+
default = default_tmp.__dict__.get("value", None)
|
|
83
|
+
else:
|
|
84
|
+
default = None
|
|
77
85
|
tmp.append(args_spec(name=arg.arg, type=type_, default=default))
|
|
78
86
|
func_args.append(tmp)
|
|
79
87
|
return options, func_args
|
|
@@ -85,26 +93,32 @@ def interactively_run_function(func: Callable[[Any], Any]):
|
|
|
85
93
|
args = []
|
|
86
94
|
kwargs = {}
|
|
87
95
|
for param in params:
|
|
88
|
-
if param.annotation is not inspect.Parameter.empty:
|
|
89
|
-
|
|
96
|
+
if param.annotation is not inspect.Parameter.empty:
|
|
97
|
+
hint = f" ({param.annotation.__name__})"
|
|
98
|
+
else:
|
|
99
|
+
hint = ""
|
|
90
100
|
if param.default is not inspect.Parameter.empty:
|
|
91
101
|
default = param.default
|
|
92
102
|
value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) (default = {default}) : ")
|
|
93
|
-
if value == "":
|
|
94
|
-
|
|
103
|
+
if value == "":
|
|
104
|
+
value = default
|
|
105
|
+
else:
|
|
106
|
+
value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) : ")
|
|
95
107
|
try:
|
|
96
108
|
if param.annotation is not inspect.Parameter.empty:
|
|
97
109
|
value = param.annotation
|
|
98
110
|
except (TypeError, ValueError) as err:
|
|
99
111
|
raise ValueError(f"Invalid input: {value} is not of type {param.annotation}") from err
|
|
100
|
-
if param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
101
|
-
|
|
112
|
+
if param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
113
|
+
kwargs[param.name] = value
|
|
114
|
+
else:
|
|
115
|
+
args.append((param.name, value))
|
|
102
116
|
args_to_kwargs = dict(args)
|
|
103
117
|
return args_to_kwargs, kwargs
|
|
104
118
|
|
|
105
119
|
|
|
106
120
|
def get_attrs_recursively(obj: Any):
|
|
107
|
-
if hasattr(obj,
|
|
121
|
+
if hasattr(obj, "__dict__"):
|
|
108
122
|
res = {}
|
|
109
123
|
for k, v in obj.__dict__.items():
|
|
110
124
|
res[k] = get_attrs_recursively(v)
|
|
@@ -127,8 +141,8 @@ def get_attrs_recursively(obj: Any):
|
|
|
127
141
|
|
|
128
142
|
|
|
129
143
|
def find_repo_root_path(start_path: str) -> Optional[str]:
|
|
130
|
-
root_files = [
|
|
131
|
-
path: str=start_path
|
|
144
|
+
root_files = ["setup.py", "pyproject.toml", ".git"]
|
|
145
|
+
path: str = start_path
|
|
132
146
|
trials = 0
|
|
133
147
|
root_path = os.path.abspath(os.sep)
|
|
134
148
|
while path != root_path and trials < 20:
|
|
@@ -144,12 +158,12 @@ def find_repo_root_path(start_path: str) -> Optional[str]:
|
|
|
144
158
|
def get_import_module_code(module_path: str):
|
|
145
159
|
root_path = find_repo_root_path(module_path)
|
|
146
160
|
if root_path is None: # just make a desperate attempt to import it
|
|
147
|
-
module_name = module_path.lstrip(os.sep).replace(os.sep,
|
|
161
|
+
module_name = module_path.lstrip(os.sep).replace(os.sep, ".")
|
|
148
162
|
if module_name.endswith(".py"):
|
|
149
163
|
module_name = module_name[:-3]
|
|
150
164
|
else:
|
|
151
|
-
relative_path = module_path.replace(root_path,
|
|
152
|
-
module_name = relative_path.lstrip(os.sep).replace(os.sep,
|
|
165
|
+
relative_path = module_path.replace(root_path, "")
|
|
166
|
+
module_name = relative_path.lstrip(os.sep).replace(os.sep, ".")
|
|
153
167
|
if module_name.endswith(".py"):
|
|
154
168
|
module_name = module_name[:-3]
|
|
155
169
|
# module_name = module_name.replace("src.", "").replace("myresources.", "").replace("resources.", "").replace("source.", "")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.terminal import Terminal
|
|
3
3
|
from machineconfig.scripts.python.get_zellij_cmd import get_zellij_cmd
|
|
4
|
-
from machineconfig.utils.
|
|
4
|
+
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
5
5
|
from machineconfig.utils.utils2 import read_ini
|
|
6
6
|
from machineconfig.utils.code import write_shell_script_to_file
|
|
7
7
|
import platform
|
|
@@ -19,10 +19,12 @@ def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cl
|
|
|
19
19
|
print("🧹 Removed temporary remote copy")
|
|
20
20
|
from git.remote import Remote
|
|
21
21
|
from git.repo import Repo
|
|
22
|
+
|
|
22
23
|
try:
|
|
23
24
|
Remote.remove(Repo(repo_root_path), "originEnc")
|
|
24
25
|
console.print(Panel("🔗 Removed originEnc remote reference", border_style="blue"))
|
|
25
|
-
except Exception:
|
|
26
|
+
except Exception:
|
|
27
|
+
pass # type: ignore
|
|
26
28
|
console.print(Panel("📈 Deleting remote repository copy and pushing local changes", width=150, border_style="blue"))
|
|
27
29
|
|
|
28
30
|
repo_root_path.to_cloud(cloud=cloud, zip=True, encrypt=True, rel2home=True, os_specific=False)
|
|
@@ -37,7 +39,7 @@ def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cl
|
|
|
37
39
|
def get_wt_cmd(wd1: PathExtended, wd2: PathExtended) -> str:
|
|
38
40
|
lines = [
|
|
39
41
|
f"""wt --window 0 new-tab --profile pwsh --title "gitdiff" --tabColor `#3b04d1 --startingDirectory {wd1} ` --colorScheme "Solarized Dark" """,
|
|
40
|
-
f"""split-pane --horizontal --profile pwsh --startingDirectory {wd2} --size 0.5 --colorScheme "Tango Dark" -- pwsh -Interactive """
|
|
42
|
+
f"""split-pane --horizontal --profile pwsh --startingDirectory {wd2} --size 0.5 --colorScheme "Tango Dark" -- pwsh -Interactive """,
|
|
41
43
|
]
|
|
42
44
|
return " `; ".join(lines)
|
|
43
45
|
|
|
@@ -53,24 +55,24 @@ def inspect_repos(repo_local_root: str, repo_remote_root: str):
|
|
|
53
55
|
program = get_zellij_cmd(wd1=PathExtended(repo_local_root), wd2=PathExtended(repo_remote_root))
|
|
54
56
|
write_shell_script_to_file(shell_script=program)
|
|
55
57
|
return None
|
|
56
|
-
else:
|
|
58
|
+
else:
|
|
59
|
+
raise NotImplementedError(f"Platform {platform.system()} not implemented.")
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
def fetch_dotfiles():
|
|
60
63
|
console.print(Panel("📁 Fetching Dotfiles", title="[bold blue]Dotfiles[/bold blue]", border_style="blue"))
|
|
61
64
|
|
|
62
|
-
cloud_resolved = read_ini(DEFAULTS_PATH)[
|
|
65
|
+
cloud_resolved = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
63
66
|
console.print(Panel(f"⚠️ Using default cloud: `{cloud_resolved}` from {DEFAULTS_PATH}", width=150, border_style="yellow"))
|
|
64
67
|
|
|
65
68
|
dotfiles_local = PathExtended.home().joinpath("dotfiles")
|
|
66
69
|
CONFIG_PATH.joinpath("remote").mkdir(parents=True, exist_ok=True)
|
|
67
|
-
dotfiles_remote = CONFIG_PATH.joinpath("remote", dotfiles_local.rel2home())
|
|
70
|
+
dotfiles_remote = PathExtended(CONFIG_PATH).joinpath("remote", dotfiles_local.rel2home())
|
|
68
71
|
remote_path = dotfiles_local.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
|
|
69
72
|
|
|
70
73
|
console.print(Panel("📥 Downloading dotfiles from cloud...", width=150, border_style="blue"))
|
|
71
74
|
|
|
72
|
-
dotfiles_remote.from_cloud(remotepath=remote_path, cloud=cloud_resolved,
|
|
73
|
-
unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=None)
|
|
75
|
+
dotfiles_remote.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=None)
|
|
74
76
|
|
|
75
77
|
console.print(Panel("🗑️ Removing old dotfiles and replacing with cloud version...", width=150, border_style="blue"))
|
|
76
78
|
|
|
@@ -80,7 +82,8 @@ def fetch_dotfiles():
|
|
|
80
82
|
# rm -rf {dotfiles_local}
|
|
81
83
|
# mv {dotfiles_remote} {dotfiles_local}
|
|
82
84
|
"""
|
|
83
|
-
if platform.system() == "Linux":
|
|
85
|
+
if platform.system() == "Linux":
|
|
86
|
+
script += """
|
|
84
87
|
sudo chmod 600 $HOME/.ssh/*
|
|
85
88
|
sudo chmod 700 $HOME/.ssh
|
|
86
89
|
sudo chmod +x $HOME/dotfiles/scripts/linux -R
|
|
@@ -90,3 +93,22 @@ sudo chmod +x $HOME/dotfiles/scripts/linux -R
|
|
|
90
93
|
|
|
91
94
|
console.print(Panel("✅ Dotfiles successfully fetched and installed", title="[bold green]Dotfiles[/bold green]", border_style="green"))
|
|
92
95
|
|
|
96
|
+
|
|
97
|
+
def check_dotfiles_version_is_beyond(commit_dtm: str, update: bool) -> bool:
|
|
98
|
+
dotfiles_path = str(PathExtended.home().joinpath("dotfiles"))
|
|
99
|
+
from git import Repo
|
|
100
|
+
|
|
101
|
+
repo = Repo(path=dotfiles_path)
|
|
102
|
+
last_commit = repo.head.commit
|
|
103
|
+
dtm = last_commit.committed_datetime
|
|
104
|
+
from datetime import datetime # make it tz unaware
|
|
105
|
+
|
|
106
|
+
dtm = datetime(dtm.year, dtm.month, dtm.day, dtm.hour, dtm.minute, dtm.second)
|
|
107
|
+
res = dtm > datetime.fromisoformat(commit_dtm)
|
|
108
|
+
if res is False and update is True:
|
|
109
|
+
console = Console()
|
|
110
|
+
console.print(Panel(f"🔄 UPDATE REQUIRED | Updating dotfiles because {dtm} < {datetime.fromisoformat(commit_dtm)}", border_style="bold blue", expand=False))
|
|
111
|
+
from machineconfig.scripts.python.cloud_repo_sync import main
|
|
112
|
+
|
|
113
|
+
main(cloud=None, path=dotfiles_path)
|
|
114
|
+
return res
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
"""NFS mounting script
|
|
2
|
-
"""
|
|
1
|
+
"""NFS mounting script"""
|
|
3
2
|
|
|
4
3
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
5
4
|
from machineconfig.utils.ssh import SSH
|
|
6
5
|
from machineconfig.utils.terminal import Terminal
|
|
7
|
-
from machineconfig.utils.
|
|
6
|
+
from machineconfig.utils.options import display_options, choose_ssh_host
|
|
7
|
+
from machineconfig.utils.source_of_truth import PROGRAM_PATH
|
|
8
8
|
import platform
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
def main():
|
|
11
12
|
print("\n" + "=" * 50)
|
|
12
13
|
print("🚀 Starting NFS Mounting Process")
|
|
@@ -19,11 +20,7 @@ def main():
|
|
|
19
20
|
assert isinstance(tmp, str)
|
|
20
21
|
ssh = SSH(tmp)
|
|
21
22
|
default = f"{ssh.hostname}:{ssh.run('echo $HOME').op}/data/share_nfs"
|
|
22
|
-
share_info = display_options(
|
|
23
|
-
"📂 Choose a share path:",
|
|
24
|
-
options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run("cat /etc/exports").op.split("\n") if not item.startswith("#")] + [default],
|
|
25
|
-
default=default
|
|
26
|
-
)
|
|
23
|
+
share_info = display_options("📂 Choose a share path:", options=[f"{ssh.hostname}:{item.split(' ')[0]}" for item in ssh.run("cat /etc/exports").op.split("\n") if not item.startswith("#")] + [default], default=default)
|
|
27
24
|
assert isinstance(share_info, str), f"❌ share_info must be a string. Got {type(share_info)}"
|
|
28
25
|
|
|
29
26
|
remote_server = share_info.split(":")[0]
|
|
@@ -41,12 +38,7 @@ def main():
|
|
|
41
38
|
mount_path_3 = mount_path_2
|
|
42
39
|
|
|
43
40
|
print("🔧 Preparing mount paths...")
|
|
44
|
-
local_mount_point = display_options(
|
|
45
|
-
msg="📂 Choose mount path OR input custom one:",
|
|
46
|
-
options=[mount_path_1, mount_path_2, mount_path_3],
|
|
47
|
-
default=mount_path_2,
|
|
48
|
-
custom_input=True
|
|
49
|
-
)
|
|
41
|
+
local_mount_point = display_options(msg="📂 Choose mount path OR input custom one:", options=[mount_path_1, mount_path_2, mount_path_3], default=mount_path_2, custom_input=True)
|
|
50
42
|
assert isinstance(local_mount_point, PathExtended), f"❌ local_mount_point must be a pathlib.Path. Got {type(local_mount_point)}"
|
|
51
43
|
local_mount_point = PathExtended(local_mount_point).expanduser()
|
|
52
44
|
|
|
@@ -79,5 +71,6 @@ $driveLetter = "{driver_letter}"
|
|
|
79
71
|
|
|
80
72
|
print("🎉 NFS Mounting Process Completed Successfully!\n")
|
|
81
73
|
|
|
82
|
-
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
83
76
|
main()
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from machineconfig.utils.
|
|
1
|
+
from machineconfig.utils.source_of_truth import PROGRAM_PATH
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
import platform
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def main():
|
|
6
7
|
print("\n" + "=" * 50)
|
|
7
8
|
print("🚀 Welcome to the Windows Network Drive Mounting Wizard")
|
|
@@ -12,7 +13,7 @@ def main():
|
|
|
12
13
|
|
|
13
14
|
mount_point_input = input(f"📂 Enter the mount point directory (e.g., /mnt/network) [Default: ~/data/mount_nw/{machine_name}]: ")
|
|
14
15
|
if mount_point_input == "":
|
|
15
|
-
mount_point = Path.home().joinpath(
|
|
16
|
+
mount_point = Path.home().joinpath(rf"data/mount_nw/{machine_name}")
|
|
16
17
|
else:
|
|
17
18
|
mount_point = Path(mount_point_input).expanduser()
|
|
18
19
|
|
|
@@ -24,13 +25,16 @@ def main():
|
|
|
24
25
|
|
|
25
26
|
if platform.system() in ["Linux", "Darwin"]:
|
|
26
27
|
print("\n🔧 Saving configuration for Linux...")
|
|
27
|
-
PROGRAM_PATH.write_text(
|
|
28
|
+
PROGRAM_PATH.write_text(
|
|
29
|
+
f"""
|
|
28
30
|
drive_location='{drive_location}'
|
|
29
31
|
mount_point='{mount_point}'
|
|
30
32
|
username='{username}'
|
|
31
33
|
password='{password}'
|
|
32
34
|
|
|
33
|
-
""",
|
|
35
|
+
""",
|
|
36
|
+
encoding="utf-8",
|
|
37
|
+
)
|
|
34
38
|
print("✅ Configuration saved successfully!\n")
|
|
35
39
|
|
|
36
40
|
elif platform.system() == "Windows":
|
|
@@ -39,5 +43,6 @@ password='{password}'
|
|
|
39
43
|
|
|
40
44
|
print("🎉 Windows Network Drive Mounting Process Completed!\n")
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
43
48
|
main()
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
"""Mount a remote SSHFS share on a local directory
|
|
2
|
-
"""
|
|
1
|
+
"""Mount a remote SSHFS share on a local directory"""
|
|
3
2
|
|
|
4
3
|
from platform import system
|
|
5
4
|
from machineconfig.utils.ssh import SSH
|
|
6
5
|
from machineconfig.utils.terminal import Terminal
|
|
7
6
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
8
|
-
from machineconfig.utils.
|
|
7
|
+
from machineconfig.utils.source_of_truth import PROGRAM_PATH
|
|
8
|
+
from machineconfig.utils.options import choose_ssh_host
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
def main():
|
|
11
12
|
print("\n" + "=" * 50)
|
|
@@ -33,7 +34,7 @@ def main():
|
|
|
33
34
|
|
|
34
35
|
mount_point = input(f"📂 Enter the mount point directory (e.g., /mnt/network) [Default: ~/data/mount_ssh/{ssh.hostname}]: ")
|
|
35
36
|
if mount_point == "":
|
|
36
|
-
mount_point = PathExtended.home().joinpath(
|
|
37
|
+
mount_point = PathExtended.home().joinpath(rf"data/mount_ssh/{ssh.hostname}")
|
|
37
38
|
|
|
38
39
|
print(f"\n📁 Mount Point: {mount_point}")
|
|
39
40
|
|
|
@@ -43,7 +44,7 @@ sshfs alex@:/media/dbhdd /media/dbhdd\
|
|
|
43
44
|
"""
|
|
44
45
|
print("\n🔧 Preparing SSHFS mount command for Linux...")
|
|
45
46
|
elif system() == "Windows":
|
|
46
|
-
txt =
|
|
47
|
+
txt = rf"""
|
|
47
48
|
net use {driver_letter} {share_info}
|
|
48
49
|
fusermount -u /mnt/dbhdd
|
|
49
50
|
"""
|
|
@@ -56,5 +57,6 @@ fusermount -u /mnt/dbhdd
|
|
|
56
57
|
|
|
57
58
|
print("🎉 SSHFS Mounting Process Completed!\n")
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
60
62
|
main()
|