machineconfig 6.61__py3-none-any.whl → 6.63__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/scripts/python/ftpx.py +1 -1
- machineconfig/scripts/python/helpers_devops/cli_utils.py +26 -1
- machineconfig/scripts/python/utils.py +3 -1
- machineconfig/utils/ssh.py +73 -126
- machineconfig/utils/terminal.py +2 -113
- {machineconfig-6.61.dist-info → machineconfig-6.63.dist-info}/METADATA +1 -1
- {machineconfig-6.61.dist-info → machineconfig-6.63.dist-info}/RECORD +10 -10
- {machineconfig-6.61.dist-info → machineconfig-6.63.dist-info}/WHEEL +0 -0
- {machineconfig-6.61.dist-info → machineconfig-6.63.dist-info}/entry_points.txt +0 -0
- {machineconfig-6.61.dist-info → machineconfig-6.63.dist-info}/top_level.txt +0 -0
|
@@ -183,7 +183,7 @@ def ftpx(
|
|
|
183
183
|
padding=(1, 2),
|
|
184
184
|
)
|
|
185
185
|
)
|
|
186
|
-
received_file = ssh.copy_from_here(source_path=resolved_source,
|
|
186
|
+
received_file = ssh.copy_from_here(source_path=resolved_source, target_rel2home=resolved_target, compress_with_zip=zipFirst, recursive=recursive, overwrite_existing=False)
|
|
187
187
|
|
|
188
188
|
if source_is_remote and isinstance(received_file, PathExtended):
|
|
189
189
|
console.print(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
import typer
|
|
4
|
-
from typing import Annotated, Optional
|
|
4
|
+
from typing import Annotated, Optional, TypedDict
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
import subprocess
|
|
7
7
|
import requests
|
|
@@ -115,3 +115,28 @@ def merge_pdfs(
|
|
|
115
115
|
from machineconfig.utils.code import run_shell_script, get_uv_command_executing_python_script
|
|
116
116
|
uv_command, _py_file = get_uv_command_executing_python_script(python_script=code, uv_with=["pypdf"], uv_project_dir=None)
|
|
117
117
|
run_shell_script(uv_command)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class MachineSpecs(TypedDict):
|
|
121
|
+
system: str
|
|
122
|
+
distro: str
|
|
123
|
+
home_dir: str
|
|
124
|
+
def get_machine_specs() -> MachineSpecs:
|
|
125
|
+
"""Write print and return the local machine specs."""
|
|
126
|
+
import platform
|
|
127
|
+
UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
|
|
128
|
+
command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
129
|
+
import subprocess
|
|
130
|
+
distro = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
|
|
131
|
+
specs: MachineSpecs = {
|
|
132
|
+
"system": platform.system(),
|
|
133
|
+
"distro": distro,
|
|
134
|
+
"home_dir": str(Path.home()),
|
|
135
|
+
}
|
|
136
|
+
print(specs)
|
|
137
|
+
from machineconfig.utils.source_of_truth import CONFIG_ROOT
|
|
138
|
+
path = CONFIG_ROOT.joinpath("machine_specs.json")
|
|
139
|
+
CONFIG_ROOT.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
import json
|
|
141
|
+
path.write_text(json.dumps(specs, indent=4), encoding="utf-8")
|
|
142
|
+
return specs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
from machineconfig.scripts.python.helpers_devops.cli_utils import download, merge_pdfs
|
|
3
|
+
from machineconfig.scripts.python.helpers_devops.cli_utils import download, merge_pdfs, get_machine_specs
|
|
4
4
|
import typer
|
|
5
5
|
|
|
6
6
|
|
|
@@ -11,6 +11,8 @@ def get_app() -> typer.Typer:
|
|
|
11
11
|
app.command(name="d", no_args_is_help=True, hidden=True)(download)
|
|
12
12
|
app.command(name="merge-pdfs", no_args_is_help=True, help="[m] Merge two PDF files into one.")(merge_pdfs)
|
|
13
13
|
app.command(name="m", no_args_is_help=True, hidden=True)(merge_pdfs)
|
|
14
|
+
app.command(name="get-machine-specs", no_args_is_help=False, help="[g] Get machine specifications.")(get_machine_specs)
|
|
15
|
+
app.command(name="g", no_args_is_help=False, hidden=True)(get_machine_specs)
|
|
14
16
|
return app
|
|
15
17
|
|
|
16
18
|
|
machineconfig/utils/ssh.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from typing import Callable, Optional, Any, Union
|
|
1
|
+
from typing import Callable, Optional, Any, Union, cast
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
import platform
|
|
5
|
+
from machineconfig.scripts.python.helpers_devops.cli_utils import MachineSpecs
|
|
5
6
|
import rich.console
|
|
6
|
-
from machineconfig.utils.terminal import Response
|
|
7
|
-
from machineconfig.utils.accessories import pprint
|
|
8
|
-
|
|
7
|
+
from machineconfig.utils.terminal import Response
|
|
8
|
+
from machineconfig.utils.accessories import pprint, randstr
|
|
9
|
+
from machineconfig.utils.meta import lambda_to_python_script
|
|
9
10
|
UV_RUN_CMD = "$HOME/.local/bin/uv run" if platform.system() != "Windows" else """& "$env:USERPROFILE/.local/bin/uv" run"""
|
|
10
11
|
MACHINECONFIG_VERSION = "machineconfig>=6.61"
|
|
11
12
|
DEFAULT_PICKLE_SUBDIR = "tmp_results/tmp_scripts/ssh"
|
|
12
13
|
|
|
13
|
-
|
|
14
14
|
class SSH:
|
|
15
15
|
def __init__(
|
|
16
16
|
self, host: Optional[str], username: Optional[str], hostname: Optional[str], ssh_key_path: Optional[str], password: Optional[str], port: int, enable_compression: bool):
|
|
@@ -22,7 +22,6 @@ class SSH:
|
|
|
22
22
|
self.username: str
|
|
23
23
|
self.port: int = port
|
|
24
24
|
self.proxycommand: Optional[str] = None
|
|
25
|
-
import platform
|
|
26
25
|
import paramiko # type: ignore
|
|
27
26
|
import getpass
|
|
28
27
|
|
|
@@ -109,13 +108,16 @@ class SSH:
|
|
|
109
108
|
def view_bar(self, transferred: int, total: int) -> None:
|
|
110
109
|
if self.progress and self.task is not None:
|
|
111
110
|
self.progress.update(self.task, completed=transferred, total=total)
|
|
112
|
-
|
|
113
111
|
self.tqdm_wrap = RichProgressWrapper
|
|
114
|
-
|
|
115
|
-
self.
|
|
116
|
-
self.
|
|
112
|
+
from machineconfig.scripts.python.helpers_devops.cli_utils import get_machine_specs
|
|
113
|
+
self.local_specs: MachineSpecs = get_machine_specs()
|
|
114
|
+
resp = self.run_shell(command=f"""~/.local/bin/utils get-machine-specs """, verbose_output=False, description="Getting remote machine specs", strict_stderr=False, strict_return_code=False)
|
|
115
|
+
# import json
|
|
116
|
+
json_str = resp.op
|
|
117
|
+
print(f"Remote machine specs JSON: {resp.op}")
|
|
118
|
+
import ast
|
|
119
|
+
self.remote_specs: MachineSpecs = cast(MachineSpecs, ast.literal_eval(json_str))
|
|
117
120
|
self.terminal_responses: list[Response] = []
|
|
118
|
-
self.platform = platform
|
|
119
121
|
|
|
120
122
|
def __enter__(self) -> "SSH":
|
|
121
123
|
return self
|
|
@@ -126,34 +128,11 @@ class SSH:
|
|
|
126
128
|
self.sftp.close()
|
|
127
129
|
self.sftp = None
|
|
128
130
|
self.ssh.close()
|
|
129
|
-
def get_remote_machine(self) -> MACHINE:
|
|
130
|
-
if self._remote_machine is None:
|
|
131
|
-
windows_test1 = self.run_shell(command="$env:OS", verbose_output=False, description="Testing Remote OS Type", strict_stderr=False, strict_return_code=False).op
|
|
132
|
-
windows_test2 = self.run_shell(command="echo %OS%", verbose_output=False, description="Testing Remote OS Type Again", strict_stderr=False, strict_return_code=False).op
|
|
133
|
-
if windows_test1 == "Windows_NT" or windows_test2 == "Windows_NT":
|
|
134
|
-
self._remote_machine = "Windows"
|
|
135
|
-
else:
|
|
136
|
-
self._remote_machine = "Linux"
|
|
137
|
-
return self._remote_machine
|
|
138
|
-
def get_local_distro(self) -> str:
|
|
139
|
-
if self._local_distro is None:
|
|
140
|
-
command = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
141
|
-
import subprocess
|
|
142
|
-
res = subprocess.run(command, shell=True, capture_output=True, text=True).stdout.strip()
|
|
143
|
-
self._local_distro = res
|
|
144
|
-
return res
|
|
145
|
-
return self._local_distro
|
|
146
|
-
def get_remote_distro(self) -> str:
|
|
147
|
-
if self._remote_distro is None:
|
|
148
|
-
command_str = f"""{UV_RUN_CMD} --with distro python -c "import distro; print(distro.name(pretty=True))" """
|
|
149
|
-
res = self.run_shell(command=command_str, verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
|
|
150
|
-
self._remote_distro = res.op_if_successfull_or_default() or ""
|
|
151
|
-
return self._remote_distro
|
|
152
131
|
def restart_computer(self) -> Response:
|
|
153
|
-
return self.run_shell(command="Restart-Computer -Force" if self.
|
|
132
|
+
return self.run_shell(command="Restart-Computer -Force" if self.remote_specs["system"] == "Windows" else "sudo reboot", verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
|
|
154
133
|
def send_ssh_key(self) -> Response:
|
|
155
|
-
self.copy_from_here(source_path=
|
|
156
|
-
if self.
|
|
134
|
+
self.copy_from_here(source_path="~/.ssh/id_rsa.pub", target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
|
|
135
|
+
if self.remote_specs["system"] != "Windows":
|
|
157
136
|
raise RuntimeError("send_ssh_key is only supported for Windows remote machines")
|
|
158
137
|
code_url = "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/refs/heads/main/src/machineconfig/setup_windows/openssh-server_add-sshkey.ps1"
|
|
159
138
|
import urllib.request
|
|
@@ -162,17 +141,17 @@ class SSH:
|
|
|
162
141
|
return self.run_shell(command=code, verbose_output=True, description="", strict_stderr=False, strict_return_code=False)
|
|
163
142
|
|
|
164
143
|
def get_remote_repr(self, add_machine: bool = False) -> str:
|
|
165
|
-
return f"{self.username}@{self.hostname}:{self.port}" + (f" [{self.
|
|
144
|
+
return f"{self.username}@{self.hostname}:{self.port}" + (f" [{self.remote_specs['system']}][{self.remote_specs['distro']}]" if add_machine else "")
|
|
166
145
|
def get_local_repr(self, add_machine: bool = False) -> str:
|
|
167
146
|
import getpass
|
|
168
|
-
return f"{getpass.getuser()}@{
|
|
147
|
+
return f"{getpass.getuser()}@{platform.node()}" + (f" [{platform.system()}][{self.local_specs['distro']}]" if add_machine else "")
|
|
169
148
|
def get_ssh_conn_str(self, command: str) -> str:
|
|
170
149
|
return "ssh " + (f" -i {self.ssh_key_path}" if self.ssh_key_path else "") + self.get_remote_repr(add_machine=False).replace(":", " -p ") + (f" -t {command} " if command != "" else " ")
|
|
171
150
|
def __repr__(self) -> str:
|
|
172
151
|
return f"local {self.get_local_repr(add_machine=True)} >>> SSH TO >>> remote {self.get_remote_repr(add_machine=True)}"
|
|
173
152
|
|
|
174
153
|
def run_locally(self, command: str) -> Response:
|
|
175
|
-
print(f"""💻 [LOCAL EXECUTION] Running command on node: {self.
|
|
154
|
+
print(f"""💻 [LOCAL EXECUTION] Running command on node: {self.local_specs['system']} Command: {command}""")
|
|
176
155
|
res = Response(cmd=command)
|
|
177
156
|
res.output.returncode = os.system(command)
|
|
178
157
|
return res
|
|
@@ -184,15 +163,14 @@ class SSH:
|
|
|
184
163
|
res.print()
|
|
185
164
|
else:
|
|
186
165
|
res.capture().print_if_unsuccessful(desc=description, strict_err=strict_stderr, strict_returncode=strict_return_code, assert_success=False)
|
|
187
|
-
self.terminal_responses.append(res)
|
|
166
|
+
# self.terminal_responses.append(res)
|
|
188
167
|
return res
|
|
189
168
|
def run_py(self, python_code: str, uv_with: Optional[list[str]], uv_project_dir: Optional[str],
|
|
190
169
|
description: str, verbose_output: bool, strict_stderr: bool, strict_return_code: bool) -> Response:
|
|
191
|
-
from machineconfig.utils.accessories import randstr
|
|
192
170
|
py_path = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/runpy_{randstr()}.py")
|
|
193
171
|
py_path.parent.mkdir(parents=True, exist_ok=True)
|
|
194
172
|
py_path.write_text(python_code, encoding="utf-8")
|
|
195
|
-
self.copy_from_here(source_path=py_path,
|
|
173
|
+
self.copy_from_here(source_path=str(py_path), target_rel2home=None, compress_with_zip=False, recursive=False, overwrite_existing=False)
|
|
196
174
|
if uv_with is not None and len(uv_with) > 0:
|
|
197
175
|
with_clause = " --with " + '"' + ",".join(uv_with) + '"'
|
|
198
176
|
else:
|
|
@@ -204,8 +182,7 @@ class SSH:
|
|
|
204
182
|
uv_cmd = f"""{UV_RUN_CMD} {with_clause} python {py_path.relative_to(Path.home())}"""
|
|
205
183
|
return self.run_shell(command=uv_cmd, verbose_output=verbose_output, description=description or f"run_py on {self.get_remote_repr(add_machine=False)}", strict_stderr=strict_stderr, strict_return_code=strict_return_code)
|
|
206
184
|
|
|
207
|
-
def
|
|
208
|
-
from machineconfig.utils.meta import lambda_to_python_script
|
|
185
|
+
def run_lambda_function(self, func: Callable[..., Any], uv_with: Optional[list[str]], uv_project_dir: Optional[str]) -> Response:
|
|
209
186
|
command = lambda_to_python_script(lmb=func, in_global=True)
|
|
210
187
|
return self.run_py(python_code=command, uv_with=uv_with, uv_project_dir=uv_project_dir,
|
|
211
188
|
description=f"run_py_func {func.__name__} on {self.get_remote_repr(add_machine=False)}",
|
|
@@ -218,77 +195,47 @@ class SSH:
|
|
|
218
195
|
local_path.parent.mkdir(parents=True, exist_ok=True)
|
|
219
196
|
self.sftp.get(remotepath=remote_path, localpath=str(local_path))
|
|
220
197
|
|
|
221
|
-
def
|
|
198
|
+
def create_dir(self, path_rel2home: str, overwrite_existing: bool) -> None:
|
|
222
199
|
"""Helper to create a directory on remote machine and return its path."""
|
|
223
|
-
def create_target_dir(
|
|
200
|
+
def create_target_dir(target_rel2home: str, overwrite: bool):
|
|
224
201
|
from pathlib import Path
|
|
225
202
|
import shutil
|
|
226
|
-
|
|
227
|
-
directory_path = Path(target_dir_path).expanduser()
|
|
203
|
+
directory_path = Path(target_rel2home).expanduser()
|
|
228
204
|
if overwrite and directory_path.exists():
|
|
229
205
|
if directory_path.is_dir():
|
|
230
206
|
shutil.rmtree(directory_path)
|
|
231
207
|
else:
|
|
232
208
|
directory_path.unlink()
|
|
233
209
|
directory_path.parent.mkdir(parents=True, exist_ok=True)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
# command = function_to_script(func=create_target_dir, call_with_kwargs={"target_dir_path": Path(target_path).as_posix(), "overwrite": overwrite_existing, "json_output_path": remote_json_output})
|
|
244
|
-
command = lambda_to_python_script(lmb=lambda: create_target_dir(target_dir_path=str(target_path), overwrite=overwrite_existing, json_output_path=remote_json_output), in_global=True)
|
|
245
|
-
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None,
|
|
246
|
-
description=f"Creating target directory `{Path(target_path).parent.as_posix()}` @ {self.get_remote_repr(add_machine=False)}",
|
|
247
|
-
verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
248
|
-
remote_json_path = response.op.strip()
|
|
249
|
-
if not remote_json_path:
|
|
250
|
-
raise RuntimeError(f"Failed to create target directory {target_path} - no response from remote")
|
|
251
|
-
from machineconfig.utils.accessories import randstr
|
|
252
|
-
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
253
|
-
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
254
|
-
import json
|
|
255
|
-
try:
|
|
256
|
-
result = json.loads(local_json.read_text(encoding="utf-8"))
|
|
257
|
-
except (json.JSONDecodeError, FileNotFoundError) as err:
|
|
258
|
-
raise RuntimeError(f"Failed to create target directory {target_path} - invalid JSON response: {err}") from err
|
|
259
|
-
finally:
|
|
260
|
-
if local_json.exists():
|
|
261
|
-
local_json.unlink()
|
|
262
|
-
assert isinstance(result, str), f"Failed to create target directory {target_path} on remote"
|
|
263
|
-
return result
|
|
210
|
+
command = lambda_to_python_script(lmb=lambda: create_target_dir(target_rel2home=path_rel2home, overwrite=overwrite_existing), in_global=True)
|
|
211
|
+
tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
|
|
212
|
+
tmp_py_file.parent.mkdir(parents=True, exist_ok=True)
|
|
213
|
+
tmp_py_file.write_text(command, encoding="utf-8")
|
|
214
|
+
# self.copy_from_here(source_path=str(tmp_py_file), target_rel2home=".tmp_file.py", compress_with_zip=False, recursive=False, overwrite_existing=True)
|
|
215
|
+
assert self.sftp is not None
|
|
216
|
+
tmp_remote_path = ".tmp_pyfile.py"
|
|
217
|
+
self.sftp.put(localpath=str(tmp_py_file), remotepath=str(Path(self.remote_specs["home_dir"]).joinpath(tmp_remote_path)))
|
|
218
|
+
self.run_shell(command=f"""{UV_RUN_CMD} python {tmp_remote_path}""", verbose_output=False, description=f"Creating target dir {path_rel2home}", strict_stderr=True, strict_return_code=True)
|
|
264
219
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if self.sftp is None:
|
|
268
|
-
raise RuntimeError(f"SFTP connection not available for {self.hostname}. Cannot transfer files.")
|
|
269
|
-
|
|
220
|
+
def copy_from_here(self, source_path: str, target_rel2home: Optional[str], compress_with_zip: bool, recursive: bool, overwrite_existing: bool) -> None:
|
|
221
|
+
if self.sftp is None: raise RuntimeError(f"SFTP connection not available for {self.hostname}. Cannot transfer files.")
|
|
270
222
|
source_obj = Path(source_path).expanduser().absolute()
|
|
271
|
-
if not source_obj.exists():
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
if target_path is None:
|
|
275
|
-
try:
|
|
276
|
-
target_path_relative = source_obj.relative_to(Path.home())
|
|
277
|
-
target_path = Path("~") / target_path_relative
|
|
223
|
+
if not source_obj.exists(): raise RuntimeError(f"SSH Error: source `{source_obj}` does not exist!")
|
|
224
|
+
if target_rel2home is None:
|
|
225
|
+
try: target_rel2home = str(source_obj.relative_to(Path.home()))
|
|
278
226
|
except ValueError:
|
|
279
227
|
raise RuntimeError(f"If target is not specified, source must be relative to home directory, but got: {source_obj}")
|
|
280
|
-
|
|
281
228
|
if not compress_with_zip and source_obj.is_dir():
|
|
282
229
|
if not recursive:
|
|
283
230
|
raise RuntimeError(f"SSH Error: source `{source_obj}` is a directory! Set `recursive=True` for recursive sending or `compress_with_zip=True` to zip it first.")
|
|
284
231
|
file_paths_to_upload: list[Path] = [file_path for file_path in source_obj.rglob("*") if file_path.is_file()]
|
|
285
|
-
|
|
232
|
+
self.create_dir(path_rel2home=target_rel2home, overwrite_existing=overwrite_existing)
|
|
286
233
|
for idx, file_path in enumerate(file_paths_to_upload):
|
|
287
234
|
print(f" {idx + 1:03d}. {file_path}")
|
|
288
235
|
for file_path in file_paths_to_upload:
|
|
289
|
-
remote_file_target = Path(
|
|
290
|
-
self.copy_from_here(source_path=file_path,
|
|
291
|
-
return
|
|
236
|
+
remote_file_target = Path(target_rel2home).joinpath(file_path.relative_to(source_obj))
|
|
237
|
+
self.copy_from_here(source_path=str(file_path), target_rel2home=str(remote_file_target), compress_with_zip=False, recursive=False, overwrite_existing=overwrite_existing)
|
|
238
|
+
return None
|
|
292
239
|
if compress_with_zip:
|
|
293
240
|
print("🗜️ ZIPPING ...")
|
|
294
241
|
import shutil
|
|
@@ -298,16 +245,14 @@ class SSH:
|
|
|
298
245
|
else:
|
|
299
246
|
shutil.make_archive(str(zip_path), "zip", source_obj.parent, source_obj.name)
|
|
300
247
|
source_obj = Path(str(zip_path) + ".zip")
|
|
301
|
-
if not
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
remotepath = Path(remotepath_str)
|
|
305
|
-
print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {remotepath.as_posix()}""")
|
|
248
|
+
if not target_rel2home.endswith(".zip"): target_rel2home = target_rel2home + ".zip"
|
|
249
|
+
self.create_dir(path_rel2home=target_rel2home, overwrite_existing=overwrite_existing)
|
|
250
|
+
print(f"""📤 [SFTP UPLOAD] Sending file: {repr(source_obj)} ==> Remote Path: {target_rel2home}""")
|
|
306
251
|
try:
|
|
307
252
|
with self.tqdm_wrap(ascii=True, unit="b", unit_scale=True) as pbar:
|
|
308
253
|
if self.sftp is None: # type: ignore[unreachable]
|
|
309
254
|
raise RuntimeError(f"SFTP connection lost for {self.hostname}")
|
|
310
|
-
self.sftp.put(localpath=str(source_obj), remotepath=
|
|
255
|
+
self.sftp.put(localpath=str(source_obj), remotepath=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), callback=pbar.view_bar)
|
|
311
256
|
except Exception:
|
|
312
257
|
if compress_with_zip and source_obj.exists() and str(source_obj).endswith("_archive.zip"):
|
|
313
258
|
source_obj.unlink()
|
|
@@ -325,11 +270,14 @@ class SSH:
|
|
|
325
270
|
with zipfile.ZipFile(archive_path, "r") as archive_handle:
|
|
326
271
|
archive_handle.extractall(extraction_directory)
|
|
327
272
|
archive_path.unlink()
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
273
|
+
command = lambda_to_python_script(lmb=lambda: unzip_archive(zip_file_path=str(Path(self.remote_specs["home_dir"]).joinpath(target_rel2home)), overwrite_flag=overwrite_existing), in_global=True)
|
|
274
|
+
tmp_py_file = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/create_target_dir_{randstr()}.py")
|
|
275
|
+
tmp_py_file.parent.mkdir(parents=True, exist_ok=True)
|
|
276
|
+
tmp_py_file.write_text(command, encoding="utf-8")
|
|
277
|
+
transferred_py_file = self.copy_from_here(source_path=str(tmp_py_file), target_rel2home=None, compress_with_zip=True, recursive=False, overwrite_existing=True)
|
|
278
|
+
self.run_shell(command=f"""{UV_RUN_CMD} python {transferred_py_file}""", verbose_output=False, description=f"UNZIPPING {target_rel2home}", strict_stderr=True, strict_return_code=True)
|
|
331
279
|
source_obj.unlink()
|
|
332
|
-
return
|
|
280
|
+
return None
|
|
333
281
|
|
|
334
282
|
def _check_remote_is_dir(self, source_path: Union[str, Path]) -> bool:
|
|
335
283
|
"""Helper to check if a remote path is a directory."""
|
|
@@ -342,16 +290,13 @@ class SSH:
|
|
|
342
290
|
json_result_path.write_text(json.dumps(is_directory, indent=2), encoding="utf-8")
|
|
343
291
|
print(json_result_path.as_posix())
|
|
344
292
|
return is_directory
|
|
345
|
-
|
|
346
|
-
from machineconfig.utils.meta import lambda_to_python_script
|
|
347
|
-
from machineconfig.utils.accessories import randstr
|
|
348
293
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
349
294
|
command = lambda_to_python_script(lmb=lambda: check_is_dir(path_to_check=str(source_path), json_output_path=remote_json_output), in_global=True)
|
|
350
295
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description=f"Check if source `{source_path}` is a dir", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
351
296
|
remote_json_path = response.op.strip()
|
|
352
297
|
if not remote_json_path:
|
|
353
298
|
raise RuntimeError(f"Failed to check if {source_path} is directory - no response from remote")
|
|
354
|
-
|
|
299
|
+
|
|
355
300
|
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
356
301
|
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
357
302
|
import json
|
|
@@ -377,15 +322,15 @@ class SSH:
|
|
|
377
322
|
print(json_result_path.as_posix())
|
|
378
323
|
return expanded_path_posix
|
|
379
324
|
|
|
380
|
-
|
|
381
|
-
|
|
325
|
+
|
|
326
|
+
|
|
382
327
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
383
328
|
command = lambda_to_python_script(lmb=lambda: expand_source(path_to_expand=str(source_path), json_output_path=remote_json_output), in_global=True)
|
|
384
329
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description="Resolving source path by expanding user", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
385
330
|
remote_json_path = response.op.strip()
|
|
386
331
|
if not remote_json_path:
|
|
387
332
|
raise RuntimeError(f"Could not resolve source path {source_path} - no response from remote")
|
|
388
|
-
|
|
333
|
+
|
|
389
334
|
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
390
335
|
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
391
336
|
import json
|
|
@@ -426,15 +371,15 @@ class SSH:
|
|
|
426
371
|
print(json_result_path.as_posix())
|
|
427
372
|
return file_paths_list
|
|
428
373
|
|
|
429
|
-
|
|
430
|
-
|
|
374
|
+
|
|
375
|
+
|
|
431
376
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
432
377
|
command = lambda_to_python_script(lmb=lambda: search_files(directory_path=expanded_source, json_output_path=remote_json_output), in_global=True)
|
|
433
378
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description="Searching for files in source", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
434
379
|
remote_json_path = response.op.strip()
|
|
435
380
|
if not remote_json_path:
|
|
436
381
|
raise RuntimeError(f"Could not resolve source path {source} - no response from remote")
|
|
437
|
-
|
|
382
|
+
|
|
438
383
|
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
439
384
|
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
440
385
|
import json
|
|
@@ -464,15 +409,15 @@ class SSH:
|
|
|
464
409
|
except ValueError:
|
|
465
410
|
raise RuntimeError(f"Source path must be relative to home directory: {source_absolute_path}")
|
|
466
411
|
|
|
467
|
-
|
|
468
|
-
|
|
412
|
+
|
|
413
|
+
|
|
469
414
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
470
415
|
command = lambda_to_python_script(lmb=lambda: collapse_to_home_dir(absolute_path=expanded_source, json_output_path=remote_json_output), in_global=True)
|
|
471
416
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description="Finding default target via relative source path", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
472
417
|
remote_json_path_dir = response.op.strip()
|
|
473
418
|
if not remote_json_path_dir:
|
|
474
419
|
raise RuntimeError("Could not resolve target path - no response from remote")
|
|
475
|
-
|
|
420
|
+
|
|
476
421
|
local_json_dir = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
477
422
|
self._simple_sftp_get(remote_path=remote_json_path_dir, local_path=local_json_dir)
|
|
478
423
|
import json
|
|
@@ -516,15 +461,15 @@ class SSH:
|
|
|
516
461
|
print(json_result_path.as_posix())
|
|
517
462
|
return zip_file_path
|
|
518
463
|
|
|
519
|
-
|
|
520
|
-
|
|
464
|
+
|
|
465
|
+
|
|
521
466
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
522
467
|
command = lambda_to_python_script(lmb=lambda: zip_source(path_to_zip=expanded_source, json_output_path=remote_json_output), in_global=True)
|
|
523
468
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description=f"Zipping source file {source}", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
524
469
|
remote_json_path = response.op.strip()
|
|
525
470
|
if not remote_json_path:
|
|
526
471
|
raise RuntimeError(f"Could not zip {source} - no response from remote")
|
|
527
|
-
|
|
472
|
+
|
|
528
473
|
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
529
474
|
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
530
475
|
import json
|
|
@@ -555,15 +500,15 @@ class SSH:
|
|
|
555
500
|
except ValueError:
|
|
556
501
|
raise RuntimeError(f"Source path must be relative to home directory: {source_absolute_path}")
|
|
557
502
|
|
|
558
|
-
|
|
559
|
-
|
|
503
|
+
|
|
504
|
+
|
|
560
505
|
remote_json_output = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/return_{randstr()}.json").as_posix()
|
|
561
506
|
command = lambda_to_python_script(lmb=lambda: collapse_to_home(absolute_path=expanded_source, json_output_path=remote_json_output), in_global=True)
|
|
562
507
|
response = self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description="Finding default target via relative source path", verbose_output=False, strict_stderr=False, strict_return_code=False)
|
|
563
508
|
remote_json_path = response.op.strip()
|
|
564
509
|
if not remote_json_path:
|
|
565
510
|
raise RuntimeError("Could not resolve target path - no response from remote")
|
|
566
|
-
|
|
511
|
+
|
|
567
512
|
local_json = Path.home().joinpath(f"{DEFAULT_PICKLE_SUBDIR}/local_{randstr()}.json")
|
|
568
513
|
self._simple_sftp_get(remote_path=remote_json_path, local_path=local_json)
|
|
569
514
|
import json
|
|
@@ -613,10 +558,12 @@ class SSH:
|
|
|
613
558
|
else:
|
|
614
559
|
file_or_dir_path.unlink()
|
|
615
560
|
|
|
616
|
-
|
|
561
|
+
|
|
617
562
|
command = lambda_to_python_script(lmb=lambda: delete_temp_zip(path_to_delete=expanded_source), in_global=True)
|
|
618
563
|
self.run_py(python_code=command, uv_with=[MACHINECONFIG_VERSION], uv_project_dir=None, description="Cleaning temp zip files @ remote.", verbose_output=False, strict_stderr=True, strict_return_code=True)
|
|
619
564
|
|
|
620
565
|
print("\n")
|
|
621
566
|
return target_obj
|
|
622
567
|
|
|
568
|
+
if __name__ == "__main__":
|
|
569
|
+
ssh = SSH(host="p51s", username=None, hostname=None, ssh_key_path=None, password=None, port=22, enable_compression=False)
|
machineconfig/utils/terminal.py
CHANGED
|
@@ -35,7 +35,8 @@ class Response:
|
|
|
35
35
|
def __call__(self, *args: Any, **kwargs: Any) -> Optional[str]:
|
|
36
36
|
_ = args, kwargs
|
|
37
37
|
return self.op.rstrip() if type(self.op) is str else None
|
|
38
|
-
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return f"Response({self.input=}, {self.output=}, {self.desc=}, {self.op=}, {self.ip=}, {self.err=}, {self.returncode=})"
|
|
39
40
|
@property
|
|
40
41
|
def op(self) -> str:
|
|
41
42
|
return self.output.stdout
|
|
@@ -97,115 +98,3 @@ class Response:
|
|
|
97
98
|
txt = tmp1 + Text(str(self.input), style="white") + tmp2 + Text("\n".join(list_str), style="white")
|
|
98
99
|
con.print(Panel(txt, title=f"🖥️ {self.desc}", subtitle=f"📋 {desc}", width=150, style="bold cyan on black"))
|
|
99
100
|
return self
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
'''
|
|
103
|
-
class Terminal:
|
|
104
|
-
def __init__(self, stdout: Optional[int] = subprocess.PIPE, stderr: Optional[int] = subprocess.PIPE, stdin: Optional[int] = subprocess.PIPE, elevated: bool = False):
|
|
105
|
-
self.machine: str = platform.system()
|
|
106
|
-
self.elevated: bool = elevated
|
|
107
|
-
self.stdout = stdout
|
|
108
|
-
self.stderr = stderr
|
|
109
|
-
self.stdin = stdin
|
|
110
|
-
|
|
111
|
-
# def set_std_system(self): self.stdout = sys.stdout; self.stderr = sys.stderr; self.stdin = sys.stdin
|
|
112
|
-
# def set_std_pipe(self):
|
|
113
|
-
# self.stdout = subprocess.PIPE
|
|
114
|
-
# self.stderr = subprocess.PIPE
|
|
115
|
-
# self.stdin = subprocess.PIPE
|
|
116
|
-
|
|
117
|
-
# def set_std_null(self):
|
|
118
|
-
# self.stdout, self.stderr, self.stdin = subprocess.DEVNULL, subprocess.DEVNULL, subprocess.DEVNULL # Equivalent to `echo 'foo' &> /dev/null`
|
|
119
|
-
|
|
120
|
-
def run(self, *cmds: str, shell: Optional[SHELLS] = "default", check: bool = False, ip: Optional[str] = None) -> Response: # Runs SYSTEM commands like subprocess.run
|
|
121
|
-
"""Blocking operation. Thus, if you start a shell via this method, it will run in the main and won't stop until you exit manually IF stdin is set to sys.stdin, otherwise it will run and close quickly. Other combinations of stdin, stdout can lead to funny behaviour like no output but accept input or opposite.
|
|
122
|
-
* This method is short for: res = subprocess.run("powershell command", capture_output=True, shell=True, text=True) and unlike os.system(cmd), subprocess.run(cmd) gives much more control over the output and input.
|
|
123
|
-
* `shell=True` loads up the profile of the shell called so more specific commands can be run. Importantly, on Windows, the `start` command becomes availalbe and new windows can be launched.
|
|
124
|
-
* `capture_output` prevents the stdout to redirect to the stdout of the script automatically, instead it will be stored in the Response object returned. # `capture_output=True` same as `stdout=subprocess.PIPE, stderr=subprocess.PIPE`"""
|
|
125
|
-
my_list = list(
|
|
126
|
-
cmds
|
|
127
|
-
) # `subprocess.Popen` (process open) is the most general command. Used here to create asynchronous job. `subprocess.run` is a thin wrapper around Popen that makes it wait until it finishes the task. `suprocess.call` is an archaic command for pre-Python-3.5.
|
|
128
|
-
if self.machine == "Windows" and shell in {"powershell", "pwsh"}:
|
|
129
|
-
my_list = [shell, "-Command"] + my_list # alternatively, one can run "cmd"
|
|
130
|
-
if self.elevated is False or self.is_user_admin():
|
|
131
|
-
resp: subprocess.CompletedProcess[str] = subprocess.run(my_list, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=check, input=ip)
|
|
132
|
-
else:
|
|
133
|
-
resp = __import__("ctypes").windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
|
|
134
|
-
return Response.from_completed_process(resp)
|
|
135
|
-
@staticmethod
|
|
136
|
-
def is_user_admin() -> bool: # adopted from: https://stackoverflow.com/questions/19672352/how-to-run-script-with-elevated-privilege-on-windows"""
|
|
137
|
-
if os.name == "nt":
|
|
138
|
-
try:
|
|
139
|
-
return __import__("ctypes").windll.shell32.IsUserAnAdmin()
|
|
140
|
-
except Exception:
|
|
141
|
-
import traceback
|
|
142
|
-
|
|
143
|
-
traceback.print_exc()
|
|
144
|
-
print("Admin check failed, assuming not an admin.")
|
|
145
|
-
return False
|
|
146
|
-
else:
|
|
147
|
-
return os.getuid() == 0 # Check for root on Posix
|
|
148
|
-
'''
|
|
149
|
-
|
|
150
|
-
# def run_shell_script(self, script: str, shell: SHELLS = "default", verbose: bool = False):
|
|
151
|
-
# if self.machine == "Linux":
|
|
152
|
-
# script = "#!/bin/bash" + "\n" + script # `source` is only available in bash.
|
|
153
|
-
# script_file = PathExtended.tmpfile(name="tmp_shell_script", suffix=".ps1" if self.machine == "Windows" else ".sh", folder="tmp_scripts")
|
|
154
|
-
# script_file.write_text(script, newline={"Windows": None, "Linux": "\n"}[self.machine])
|
|
155
|
-
# if shell == "default":
|
|
156
|
-
# if self.machine == "Windows":
|
|
157
|
-
# start_cmd = "powershell" # default shell on Windows is cmd which is not very useful. (./source is not available)
|
|
158
|
-
# full_command: Union[list[str], str] = [start_cmd, str(script_file)] # shell=True will cause this to be a string anyway (with space separation)
|
|
159
|
-
# else:
|
|
160
|
-
# start_cmd = "bash"
|
|
161
|
-
# full_command = f"{start_cmd} {script_file}" # full_command = [start_cmd, str(script_file)]
|
|
162
|
-
# else:
|
|
163
|
-
# full_command = f"{shell} {script_file}" # full_command = [shell, str(tmp_file)]
|
|
164
|
-
# if verbose:
|
|
165
|
-
# desc = "Script to be executed:"
|
|
166
|
-
# if platform.system() == "Windows":
|
|
167
|
-
# lexer = "powershell"
|
|
168
|
-
# elif platform.system() == "Linux":
|
|
169
|
-
# lexer = "sh"
|
|
170
|
-
# elif platform.system() == "Darwin":
|
|
171
|
-
# lexer = "sh" # macOS uses similar shell to Linux
|
|
172
|
-
# else:
|
|
173
|
-
# raise NotImplementedError(f"Platform {platform.system()} not supported.")
|
|
174
|
-
# from rich.console import Console
|
|
175
|
-
# from rich.panel import Panel
|
|
176
|
-
# from rich.syntax import Syntax
|
|
177
|
-
# import rich.progress as pb
|
|
178
|
-
|
|
179
|
-
# console = Console()
|
|
180
|
-
# console.print(Panel(Syntax(code=script, lexer=lexer), title=f"📄 {desc}"), style="bold red")
|
|
181
|
-
# with pb.Progress(transient=True) as progress:
|
|
182
|
-
# _task = progress.add_task(f"Running Script @ {script_file}", total=None)
|
|
183
|
-
# resp = subprocess.run(full_command, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=False)
|
|
184
|
-
# else:
|
|
185
|
-
# resp = subprocess.run(full_command, stderr=self.stderr, stdin=self.stdin, stdout=self.stdout, text=True, shell=True, check=False)
|
|
186
|
-
# return Response.from_completed_process(resp)
|
|
187
|
-
|
|
188
|
-
# def run_py(self, script: str, wdir: OPLike = None, interactive: bool = True, ipython: bool = True, shell: Optional[str] = None, terminal: str = "", new_window: bool = True, header: bool = True): # async run, since sync run is meaningless.
|
|
189
|
-
# script = (Terminal.get_header(wdir=wdir, toolbox=True) if header else "") + script + ("\nDisplayData.set_pandas_auto_width()\n" if terminal in {"wt", "powershell", "pwsh"} else "")
|
|
190
|
-
# py_script = PathExtended.tmpfile(name="tmp_python_script", suffix=".py", folder="tmp_scripts/terminal")
|
|
191
|
-
# py_script.write_text(f"""print(r'''{script}''')""" + "\n" + script)
|
|
192
|
-
# print(f"""🚀 [ASYNC PYTHON SCRIPT] Script URI:
|
|
193
|
-
# {py_script.absolute().as_uri()}""")
|
|
194
|
-
# print("Script to be executed asyncronously: ", py_script.absolute().as_uri())
|
|
195
|
-
# shell_script = f"""
|
|
196
|
-
# {f"cd {wdir}" if wdir is not None else ""}
|
|
197
|
-
# {"ipython" if ipython else "python"} {"-i" if interactive else ""} {py_script}
|
|
198
|
-
# """
|
|
199
|
-
# shell_path = PathExtended.tmpfile(name="tmp_shell_script", suffix=".sh" if self.machine == "Linux" else ".ps1", folder="tmp_scripts/shell")
|
|
200
|
-
# shell_path.write_text(shell_script)
|
|
201
|
-
# if shell is None and self.machine == "Windows":
|
|
202
|
-
# shell = "pwsh"
|
|
203
|
-
# window = "start" if new_window and self.machine == "Windows" else ""
|
|
204
|
-
# os.system(f"{window} {terminal} {shell} {shell_script}")
|
|
205
|
-
|
|
206
|
-
# @staticmethod
|
|
207
|
-
# def run_as_admin(file: PLike, params: Any, wait: bool = False):
|
|
208
|
-
# proce_info = install_n_import(library="win32com", package="pywin32", fromlist=["shell.shell.ShellExecuteEx"]).shell.shell.ShellExecuteEx(lpVerb='runas', lpFile=file, lpParameters=params)
|
|
209
|
-
# # TODO update PATH for this to take effect immediately.
|
|
210
|
-
# if wait: time.sleep(1)
|
|
211
|
-
# return proce_info
|
|
@@ -127,10 +127,10 @@ machineconfig/scripts/python/devops.py,sha256=Lv4d-UlyOREj4VTcu_pxswYo54Mawe3XGe
|
|
|
127
127
|
machineconfig/scripts/python/devops_navigator.py,sha256=5Cm384D4S8_GsvMzTwr0C16D0ktf8_5Mk5bEJncwDO8,237
|
|
128
128
|
machineconfig/scripts/python/entry.py,sha256=liCf186Msa6R-EjcQ0I6K80Km7Wi8qckJB6rXIHeHU0,2488
|
|
129
129
|
machineconfig/scripts/python/fire_jobs.py,sha256=My7sFn1R2vh21uIHGfNppgX99WTEitCFgJ1MSasBUOQ,13597
|
|
130
|
-
machineconfig/scripts/python/ftpx.py,sha256=
|
|
130
|
+
machineconfig/scripts/python/ftpx.py,sha256=A13hL_tDYfcsaK9PkshK-0lrUS6KPhPCtwqWtLSo6IM,9764
|
|
131
131
|
machineconfig/scripts/python/interactive.py,sha256=zt3g6nGKR_Y5A57UnR4Y5-JpLzrpnCOSaqU1bnaikK0,11666
|
|
132
132
|
machineconfig/scripts/python/sessions.py,sha256=UERxO472EDtN7nKHEULbn6G3S5PJIpsDG9Gq3TlByqI,9823
|
|
133
|
-
machineconfig/scripts/python/utils.py,sha256=
|
|
133
|
+
machineconfig/scripts/python/utils.py,sha256=c9HsKG40i5ggwqKuS3O-LCScuFpmxMVKqWpsFHx2dJc,934
|
|
134
134
|
machineconfig/scripts/python/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
135
|
machineconfig/scripts/python/ai/generate_files.py,sha256=VfjKdwgF8O6E4oiRtfWNliibLmmwGe7f9ld6wpOsXTw,14498
|
|
136
136
|
machineconfig/scripts/python/ai/initai.py,sha256=9SZtWOcRuwk8ZU3wHOfPzjInERD79ZTYFY8tVACgza4,2260
|
|
@@ -187,7 +187,7 @@ machineconfig/scripts/python/helpers_devops/cli_repos.py,sha256=Xwkv1adqHZvTfRSP
|
|
|
187
187
|
machineconfig/scripts/python/helpers_devops/cli_self.py,sha256=0-R7YDIQS2cH2n9C7lShFoBRwbytQz3BCuHGQrus7FU,6225
|
|
188
188
|
machineconfig/scripts/python/helpers_devops/cli_share_server.py,sha256=q9pFJ6AxPuygMr3onMNOKEuuQHbVE_6Qoyo7xRT5FX0,4196
|
|
189
189
|
machineconfig/scripts/python/helpers_devops/cli_terminal.py,sha256=k_PzXaiGyE0vXr0Ii1XcJz2A7UvyPJrR31TRWt4RKRI,6019
|
|
190
|
-
machineconfig/scripts/python/helpers_devops/cli_utils.py,sha256=
|
|
190
|
+
machineconfig/scripts/python/helpers_devops/cli_utils.py,sha256=jN_GeZyy-9tAJoBoASsmn9_tI7ZgkgXp7Ijgi2x_ssw,6053
|
|
191
191
|
machineconfig/scripts/python/helpers_devops/devops_backup_retrieve.py,sha256=Dn8luB6QJzxKiiFSC-NMqiYddWZoca3A8eOjMYZDzTc,5598
|
|
192
192
|
machineconfig/scripts/python/helpers_devops/devops_status.py,sha256=PJVPhfhXq8der6Xd-_fjZfnizfM-RGfJApkRGhGBmNo,20525
|
|
193
193
|
machineconfig/scripts/python/helpers_devops/devops_update_repos.py,sha256=kSln8_-Wn7Qu0NaKdt-QTN_bBVyTIAWHH8xVYKK-vCM,10133
|
|
@@ -407,8 +407,8 @@ machineconfig/utils/procs.py,sha256=YPA_vEYQGwPd_o_Lc6nOTBo5BrB1tSs8PJ42XiGpenM,
|
|
|
407
407
|
machineconfig/utils/scheduler.py,sha256=44CASABJg3epccxhAwv2CX7TVgZh6zVy3K4vqHKTuf4,14228
|
|
408
408
|
machineconfig/utils/scheduling.py,sha256=6x5zLA7sY5gohrEtN6zGrXIqNFasMoyBfwLcOjrjiME,11109
|
|
409
409
|
machineconfig/utils/source_of_truth.py,sha256=ZAnCRltiM07ig--P6g9_6nEAvNFC4X4ERFTVcvpIYsE,764
|
|
410
|
-
machineconfig/utils/ssh.py,sha256=
|
|
411
|
-
machineconfig/utils/terminal.py,sha256=
|
|
410
|
+
machineconfig/utils/ssh.py,sha256=Y_n9KKf40VvKAT677B8bMNB0Qs1uIivcFlIQ9xuqo5o,36001
|
|
411
|
+
machineconfig/utils/terminal.py,sha256=VDgsjTjBmMGgZN0YIc0pJ8YksLDrBtiXON1EThy7_is,4264
|
|
412
412
|
machineconfig/utils/tst.py,sha256=6u1GI49NdcpxH2BYGAusNfY5q9G_ytCGVzFM5b6HYpM,674
|
|
413
413
|
machineconfig/utils/upgrade_packages.py,sha256=mSFyKvB3JhHte_x1dtmEgrJZCAXgTUQoaJUSx1OXQ3Y,4145
|
|
414
414
|
machineconfig/utils/ve.py,sha256=L-6PBXnQGXThiwWgheJMQoisAZOZA6SVCbGw2J-GFnI,2414
|
|
@@ -436,8 +436,8 @@ machineconfig/utils/schemas/installer/installer_types.py,sha256=QClRY61QaduBPJoS
|
|
|
436
436
|
machineconfig/utils/schemas/layouts/layout_types.py,sha256=TcqlZdGVoH8htG5fHn1KWXhRdPueAcoyApppZsPAPto,2020
|
|
437
437
|
machineconfig/utils/schemas/repos/repos_types.py,sha256=ECVr-3IVIo8yjmYmVXX2mnDDN1SLSwvQIhx4KDDQHBQ,405
|
|
438
438
|
machineconfig/utils/ssh_utils/utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
439
|
-
machineconfig-6.
|
|
440
|
-
machineconfig-6.
|
|
441
|
-
machineconfig-6.
|
|
442
|
-
machineconfig-6.
|
|
443
|
-
machineconfig-6.
|
|
439
|
+
machineconfig-6.63.dist-info/METADATA,sha256=i_pMCNUzi-v7pZWWUl96i4R5aU1H6JSwVRZr9A3-LbQ,2928
|
|
440
|
+
machineconfig-6.63.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
441
|
+
machineconfig-6.63.dist-info/entry_points.txt,sha256=NTW7hbUlpt5Vx9DdQrONLkYMCuBXpvYh1dt0AtlGxeI,466
|
|
442
|
+
machineconfig-6.63.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
|
|
443
|
+
machineconfig-6.63.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|