machineconfig 4.97__py3-none-any.whl → 4.99__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/remote/script_execution.py +1 -1
- machineconfig/jobs/installer/custom/gh.py +68 -39
- machineconfig/jobs/installer/custom/hx.py +1 -1
- machineconfig/jobs/installer/custom_dev/bypass_paywall.py +1 -1
- machineconfig/jobs/installer/custom_dev/wezterm.py +68 -35
- machineconfig/jobs/installer/installer_data.json +1 -1
- machineconfig/jobs/installer/package_groups.py +2 -1
- machineconfig/jobs/python/python_ve_symlink.py +1 -1
- machineconfig/profile/create.py +59 -40
- machineconfig/profile/shell.py +1 -1
- machineconfig/scripts/python/cloud_copy.py +1 -1
- machineconfig/scripts/python/cloud_mount.py +1 -1
- machineconfig/scripts/python/cloud_repo_sync.py +1 -1
- machineconfig/scripts/python/croshell.py +1 -1
- machineconfig/scripts/python/devops.py +1 -1
- machineconfig/scripts/python/devops_add_identity.py +1 -1
- machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
- machineconfig/scripts/python/devops_backup_retrieve.py +1 -1
- machineconfig/scripts/python/devops_update_repos.py +135 -65
- machineconfig/scripts/python/dotfile.py +41 -15
- machineconfig/scripts/python/fire_jobs.py +1 -1
- machineconfig/scripts/python/ftpx.py +101 -49
- machineconfig/scripts/python/helpers/helpers2.py +2 -2
- machineconfig/scripts/python/helpers/helpers4.py +1 -1
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +1 -1
- machineconfig/scripts/python/interactive.py +1 -1
- machineconfig/scripts/python/mount_nfs.py +13 -7
- machineconfig/scripts/python/mount_ssh.py +1 -1
- machineconfig/scripts/python/repos.py +7 -6
- machineconfig/scripts/python/repos_helper_action.py +1 -1
- machineconfig/scripts/python/repos_helper_clone.py +4 -4
- machineconfig/scripts/python/repos_helper_record.py +1 -1
- machineconfig/scripts/python/sessions.py +5 -1
- machineconfig/scripts/python/share_terminal.py +14 -7
- machineconfig/scripts/python/start_slidev.py +1 -1
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +1 -1
- machineconfig/utils/code.py +1 -1
- machineconfig/utils/installer.py +1 -1
- machineconfig/{scripts/python/devops_devapps_install.py → utils/installer_utils/installer.py} +2 -3
- machineconfig/utils/installer_utils/installer_abc.py +1 -1
- machineconfig/utils/installer_utils/installer_class.py +1 -1
- machineconfig/utils/links.py +1 -1
- machineconfig/utils/path_extended.py +4 -2
- machineconfig/utils/path_helper.py +1 -1
- machineconfig/utils/scheduler.py +1 -1
- {machineconfig-4.97.dist-info → machineconfig-4.99.dist-info}/METADATA +1 -1
- {machineconfig-4.97.dist-info → machineconfig-4.99.dist-info}/RECORD +54 -55
- machineconfig/cluster/templates/utils.py +0 -51
- /machineconfig/cluster/{templates → remote}/run_cloud.py +0 -0
- /machineconfig/cluster/{templates → remote}/run_cluster.py +0 -0
- /machineconfig/cluster/{templates → remote}/run_remote.py +0 -0
- {machineconfig-4.97.dist-info → machineconfig-4.99.dist-info}/WHEEL +0 -0
- {machineconfig-4.97.dist-info → machineconfig-4.99.dist-info}/entry_points.txt +0 -0
- {machineconfig-4.97.dist-info → machineconfig-4.99.dist-info}/top_level.txt +0 -0
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
"""Update repositories with fancy output"""
|
|
2
2
|
|
|
3
|
-
import git
|
|
4
3
|
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import git
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
from rich.text import Text
|
|
10
|
+
|
|
5
11
|
from machineconfig.scripts.python.repos_helper_update import RepositoryUpdateResult, run_uv_sync, update_repository
|
|
6
|
-
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
7
|
-
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
8
12
|
from machineconfig.utils.io import read_ini
|
|
13
|
+
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
9
17
|
|
|
10
18
|
|
|
11
19
|
def _display_summary(results: list[RepositoryUpdateResult]) -> None:
|
|
12
20
|
"""Display a comprehensive summary of all repository update operations."""
|
|
13
|
-
print("\n" + "=" * 80)
|
|
14
|
-
print("📊 REPOSITORY UPDATE SUMMARY")
|
|
15
|
-
print("=" * 80)
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
console.rule("[bold blue]📊 Repository Update Summary[/bold blue]")
|
|
23
|
+
|
|
18
24
|
total_repos = len(results)
|
|
19
25
|
successful_repos = sum(1 for r in results if r["status"] == "success")
|
|
20
26
|
error_repos = sum(1 for r in results if r["status"] == "error")
|
|
@@ -27,106 +33,161 @@ def _display_summary(results: list[RepositoryUpdateResult]) -> None:
|
|
|
27
33
|
uv_sync_runs = sum(1 for r in results if r["uv_sync_ran"])
|
|
28
34
|
uv_sync_successes = sum(1 for r in results if r["uv_sync_ran"] and r["uv_sync_success"])
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
overview_lines = [
|
|
37
|
+
f"[bold]Total repositories processed:[/] {total_repos}",
|
|
38
|
+
f"✅ Successful updates: {successful_repos}",
|
|
39
|
+
f"❌ Failed updates: {error_repos}",
|
|
40
|
+
f"⏭️ Skipped: {skipped_repos}",
|
|
41
|
+
]
|
|
36
42
|
if auth_failed_repos > 0:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
overview_lines.append(f"🔐 Authentication failed: {auth_failed_repos}")
|
|
44
|
+
|
|
45
|
+
console.print(
|
|
46
|
+
Panel(
|
|
47
|
+
"\n".join(overview_lines),
|
|
48
|
+
title="� Overview",
|
|
49
|
+
border_style="blue",
|
|
50
|
+
padding=(1, 2),
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
changes_lines = [
|
|
55
|
+
f"Repositories with new commits: {repos_with_changes}",
|
|
56
|
+
f"Repositories with dependency changes: {repos_with_dep_changes}",
|
|
57
|
+
f"Repositories with uncommitted changes: {repos_with_uncommitted}",
|
|
58
|
+
]
|
|
59
|
+
console.print(
|
|
60
|
+
Panel(
|
|
61
|
+
"\n".join(changes_lines),
|
|
62
|
+
title="� Changes",
|
|
63
|
+
border_style="magenta",
|
|
64
|
+
padding=(1, 2),
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
uv_sync_lines = [
|
|
69
|
+
f"uv sync operations attempted: {uv_sync_runs}",
|
|
70
|
+
f"uv sync operations successful: {uv_sync_successes}",
|
|
71
|
+
]
|
|
49
72
|
if uv_sync_runs > uv_sync_successes:
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
uv_sync_lines.append(f"uv sync operations failed: {uv_sync_runs - uv_sync_successes}")
|
|
74
|
+
|
|
75
|
+
console.print(
|
|
76
|
+
Panel(
|
|
77
|
+
"\n".join(uv_sync_lines),
|
|
78
|
+
title="📦 uv sync",
|
|
79
|
+
border_style="cyan",
|
|
80
|
+
padding=(1, 2),
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
table = Table(title="📋 Detailed Results", show_lines=True, header_style="bold blue")
|
|
85
|
+
table.add_column("Repository", style="bold")
|
|
86
|
+
table.add_column("Status")
|
|
87
|
+
table.add_column("Details", overflow="fold")
|
|
52
88
|
|
|
53
|
-
# Detailed results per repository
|
|
54
|
-
print("📋 DETAILED RESULTS:")
|
|
55
89
|
for result in results:
|
|
56
90
|
repo_name = Path(result["repo_path"]).name
|
|
57
91
|
status_icon = {"success": "✅", "error": "❌", "skipped": "⏭️", "auth_failed": "🔐"}.get(result["status"], "❓")
|
|
58
|
-
|
|
92
|
+
status_label = result["status"].replace("_", " ").title()
|
|
93
|
+
|
|
94
|
+
detail_lines: list[str] = []
|
|
59
95
|
|
|
60
96
|
if result["status"] == "error" and result["error_message"]:
|
|
61
|
-
|
|
97
|
+
detail_lines.append(f"💥 Error: {result['error_message']}")
|
|
62
98
|
|
|
63
99
|
if result["commits_changed"]:
|
|
64
|
-
|
|
100
|
+
detail_lines.append(f"🔄 Updated: {result['commit_before'][:8]} → {result['commit_after'][:8]}")
|
|
65
101
|
elif result["status"] == "success":
|
|
66
|
-
|
|
102
|
+
detail_lines.append("📍 Already up to date")
|
|
67
103
|
|
|
68
104
|
if result["had_uncommitted_changes"]:
|
|
69
105
|
files_str = ", ".join(result["uncommitted_files"])
|
|
70
|
-
|
|
106
|
+
detail_lines.append(f"⚠️ Uncommitted changes: {files_str}")
|
|
71
107
|
|
|
72
108
|
if result["dependencies_changed"]:
|
|
73
109
|
changes = []
|
|
74
110
|
if result["pyproject_changed"]:
|
|
75
111
|
changes.append("pyproject.toml")
|
|
76
|
-
|
|
112
|
+
if changes:
|
|
113
|
+
detail_lines.append(f"📋 Dependencies changed: {', '.join(changes)}")
|
|
77
114
|
|
|
78
115
|
if result["uv_sync_ran"]:
|
|
79
116
|
sync_status = "✅" if result["uv_sync_success"] else "❌"
|
|
80
|
-
|
|
117
|
+
detail_lines.append(f"📦 uv sync: {sync_status}")
|
|
81
118
|
|
|
82
119
|
if result["is_machineconfig_repo"] and result["permissions_updated"]:
|
|
83
|
-
|
|
120
|
+
detail_lines.append("🛠 Updated permissions for machineconfig files")
|
|
84
121
|
|
|
85
122
|
if result["remotes_processed"]:
|
|
86
|
-
|
|
123
|
+
detail_lines.append(f"📡 Processed remotes: {', '.join(result['remotes_processed'])}")
|
|
87
124
|
if result["remotes_skipped"]:
|
|
88
|
-
|
|
125
|
+
detail_lines.append(f"⏭️ Skipped remotes: {', '.join(result['remotes_skipped'])}")
|
|
126
|
+
|
|
127
|
+
table.add_row(f"{status_icon} {repo_name}", status_label, "\n".join(detail_lines) or "—")
|
|
89
128
|
|
|
90
|
-
print(
|
|
129
|
+
console.print(table)
|
|
91
130
|
|
|
92
|
-
# Final status
|
|
93
131
|
if error_repos == 0 and auth_failed_repos == 0:
|
|
94
|
-
|
|
132
|
+
summary_text = Text("🎉 All repositories processed successfully!", style="green", justify="center")
|
|
133
|
+
border = "green"
|
|
95
134
|
elif successful_repos > 0:
|
|
96
|
-
|
|
135
|
+
summary_text = Text(
|
|
136
|
+
f"⚠️ {successful_repos}/{total_repos} repositories processed successfully",
|
|
137
|
+
style="yellow",
|
|
138
|
+
justify="center",
|
|
139
|
+
)
|
|
140
|
+
border = "yellow"
|
|
97
141
|
else:
|
|
98
|
-
|
|
99
|
-
|
|
142
|
+
summary_text = Text("❌ No repositories were successfully processed", style="red", justify="center")
|
|
143
|
+
border = "red"
|
|
144
|
+
|
|
145
|
+
console.print(Panel(summary_text, title="Summary", border_style=border, padding=(1, 2)))
|
|
100
146
|
|
|
101
147
|
|
|
102
148
|
def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
|
|
103
149
|
"""Main function to update all configured repositories."""
|
|
104
150
|
_ = verbose
|
|
105
|
-
repos: list[
|
|
151
|
+
repos: list[Path] = [Path.home() / "code/machineconfig", Path.home() / "code/crocodile"]
|
|
106
152
|
try:
|
|
107
153
|
tmp = read_ini(DEFAULTS_PATH)["general"]["repos"].split(",")
|
|
108
154
|
if tmp[-1] == "":
|
|
109
155
|
tmp = tmp[:-1]
|
|
110
156
|
for item in tmp:
|
|
111
|
-
item_obj =
|
|
157
|
+
item_obj = Path(item).expanduser()
|
|
112
158
|
if item_obj not in repos:
|
|
113
159
|
repos.append(item_obj)
|
|
114
160
|
except (FileNotFoundError, KeyError, IndexError):
|
|
115
|
-
print(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
161
|
+
console.print(
|
|
162
|
+
Panel(
|
|
163
|
+
"\n".join(
|
|
164
|
+
[
|
|
165
|
+
f"🚫 Configuration error: missing {DEFAULTS_PATH} or the [general] section / repos key.",
|
|
166
|
+
"ℹ️ Using default repositories instead.",
|
|
167
|
+
]
|
|
168
|
+
),
|
|
169
|
+
title="Configuration Missing",
|
|
170
|
+
border_style="red",
|
|
171
|
+
padding=(1, 2),
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
console.print(
|
|
175
|
+
Panel(
|
|
176
|
+
"\n".join(
|
|
177
|
+
[
|
|
178
|
+
"✨ Example configuration:",
|
|
179
|
+
"",
|
|
180
|
+
"[general]",
|
|
181
|
+
"repos = ~/code/repo1,~/code/repo2",
|
|
182
|
+
"rclone_config_name = onedrivePersonal",
|
|
183
|
+
"email_config_name = Yahoo3",
|
|
184
|
+
"to_email = myemail@email.com",
|
|
185
|
+
]
|
|
186
|
+
),
|
|
187
|
+
border_style="cyan",
|
|
188
|
+
padding=(1, 2),
|
|
189
|
+
)
|
|
190
|
+
)
|
|
130
191
|
|
|
131
192
|
# Process repositories
|
|
132
193
|
results: list[RepositoryUpdateResult] = []
|
|
@@ -164,9 +225,18 @@ def main(verbose: bool = True, allow_password_prompt: bool = False) -> None:
|
|
|
164
225
|
"permissions_updated": False,
|
|
165
226
|
}
|
|
166
227
|
results.append(error_result)
|
|
167
|
-
print(
|
|
168
|
-
|
|
169
|
-
|
|
228
|
+
console.print(
|
|
229
|
+
Panel(
|
|
230
|
+
"\n".join(
|
|
231
|
+
[
|
|
232
|
+
f"❌ Repository error: {expanded_path}",
|
|
233
|
+
f"Exception: {ex}",
|
|
234
|
+
]
|
|
235
|
+
),
|
|
236
|
+
border_style="red",
|
|
237
|
+
padding=(1, 2),
|
|
238
|
+
)
|
|
239
|
+
)
|
|
170
240
|
|
|
171
241
|
# Run uv sync for repositories where pyproject.toml changed but sync wasn't run yet
|
|
172
242
|
for repo_path in repos_with_changes:
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
"""Like yadm and dotter."""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
4
|
-
from machineconfig.utils.links import symlink_func
|
|
5
|
-
from machineconfig.utils.source_of_truth import LIBRARY_ROOT, REPO_ROOT
|
|
6
3
|
from typing import Annotated
|
|
4
|
+
|
|
7
5
|
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
|
|
9
|
+
from machineconfig.utils.links import symlink_func
|
|
10
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
11
|
+
from machineconfig.utils.source_of_truth import LIBRARY_ROOT, REPO_ROOT
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
8
15
|
|
|
9
16
|
|
|
10
17
|
def main(
|
|
@@ -30,18 +37,37 @@ def main(
|
|
|
30
37
|
|
|
31
38
|
symlink_func(this=orig_path, to_this=new_path, prioritize_to_this=overwrite)
|
|
32
39
|
|
|
33
|
-
print(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
console.print(
|
|
41
|
+
Panel(
|
|
42
|
+
"\n".join(
|
|
43
|
+
[
|
|
44
|
+
"✅ Symbolic link created successfully!",
|
|
45
|
+
"🔄 Add the following snippet to mapper.toml to persist this mapping:",
|
|
46
|
+
]
|
|
47
|
+
),
|
|
48
|
+
title="Symlink Created",
|
|
49
|
+
border_style="green",
|
|
50
|
+
padding=(1, 2),
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
mapper_snippet = "\n".join(
|
|
55
|
+
[
|
|
56
|
+
f"[bold]📝 Edit configuration file:[/] [cyan]nano {PathExtended(LIBRARY_ROOT)}/symlinks/mapper.toml[/cyan]",
|
|
57
|
+
"",
|
|
58
|
+
f"[{new_path.parent.name}]",
|
|
59
|
+
f"{orig_path.name.split('.')[0]} = {{ this = '{orig_path.collapseuser().as_posix()}', to_this = '{new_path.collapseuser().as_posix()}' }}",
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
console.print(
|
|
64
|
+
Panel(
|
|
65
|
+
mapper_snippet,
|
|
66
|
+
title="Mapper Entry",
|
|
67
|
+
border_style="cyan",
|
|
68
|
+
padding=(1, 2),
|
|
69
|
+
)
|
|
70
|
+
)
|
|
45
71
|
|
|
46
72
|
|
|
47
73
|
def arg_parser() -> None:
|
|
@@ -15,7 +15,7 @@ from machineconfig.utils.ve import get_ve_activate_line, get_ve_path_and_ipython
|
|
|
15
15
|
from machineconfig.utils.options import choose_from_options
|
|
16
16
|
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
17
17
|
|
|
18
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
18
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
19
19
|
from machineconfig.utils.accessories import get_repo_root, randstr
|
|
20
20
|
from machineconfig.scripts.python.fire_jobs_args_helper import FireJobArgs, extract_kwargs, parse_fire_args_from_context
|
|
21
21
|
import platform
|
|
@@ -8,12 +8,18 @@ Currently, the only way to work around this is to predifine the host in ~/.ssh/c
|
|
|
8
8
|
|
|
9
9
|
import typer
|
|
10
10
|
from typing_extensions import Annotated
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
|
|
11
14
|
from machineconfig.utils.ssh import SSH
|
|
12
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
15
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
13
16
|
from machineconfig.scripts.python.helpers.helpers2 import ES
|
|
14
17
|
from machineconfig.utils.accessories import pprint
|
|
15
18
|
|
|
16
19
|
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
def main(
|
|
18
24
|
source: Annotated[str, typer.Argument(help="Source path (machine:path)")],
|
|
19
25
|
target: Annotated[str, typer.Argument(help="Target path (machine:path)")],
|
|
@@ -21,11 +27,19 @@ def main(
|
|
|
21
27
|
zipFirst: Annotated[bool, typer.Option("--zipFirst", "-z", help="Zip before sending.")] = False,
|
|
22
28
|
cloud: Annotated[bool, typer.Option("--cloud", "-c", help="Transfer through the cloud.")] = False,
|
|
23
29
|
) -> None:
|
|
24
|
-
print(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
console.print(
|
|
31
|
+
Panel(
|
|
32
|
+
"\n".join(
|
|
33
|
+
[
|
|
34
|
+
"🚀 FTP File Transfer",
|
|
35
|
+
"📋 Starting transfer process...",
|
|
36
|
+
]
|
|
37
|
+
),
|
|
38
|
+
title="Transfer Initialisation",
|
|
39
|
+
border_style="blue",
|
|
40
|
+
padding=(1, 2),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
29
43
|
|
|
30
44
|
# Initialize variables
|
|
31
45
|
resolved_source: str | None = None
|
|
@@ -94,69 +108,107 @@ def main(
|
|
|
94
108
|
try:
|
|
95
109
|
ssh = SSH(rf"{machine}")
|
|
96
110
|
except AuthenticationException:
|
|
97
|
-
print(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
console.print(
|
|
112
|
+
Panel(
|
|
113
|
+
"\n".join(
|
|
114
|
+
[
|
|
115
|
+
"🔑 Authentication failed. Trying manual authentication...",
|
|
116
|
+
"⚠️ Ensure that the username is provided correctly; only password prompts are handled here.",
|
|
117
|
+
]
|
|
118
|
+
),
|
|
119
|
+
title="Authentication Required",
|
|
120
|
+
border_style="yellow",
|
|
121
|
+
padding=(1, 2),
|
|
122
|
+
)
|
|
123
|
+
)
|
|
105
124
|
import getpass
|
|
106
125
|
|
|
107
126
|
pwd = getpass.getpass()
|
|
108
127
|
ssh = SSH(rf"{machine}", pwd=pwd)
|
|
109
128
|
|
|
110
129
|
if cloud:
|
|
111
|
-
print(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
console.print(
|
|
131
|
+
Panel.fit(
|
|
132
|
+
"☁️ Cloud transfer mode — uploading from remote to cloud...",
|
|
133
|
+
title="Cloud Upload",
|
|
134
|
+
border_style="cyan",
|
|
135
|
+
)
|
|
136
|
+
)
|
|
116
137
|
ssh.run(f"cloud_copy {resolved_source} :^", desc="Uploading from remote to the cloud.").print()
|
|
117
|
-
print(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
console.print(
|
|
139
|
+
Panel.fit(
|
|
140
|
+
"⬇️ Cloud transfer mode — downloading from cloud to local...",
|
|
141
|
+
title="Cloud Download",
|
|
142
|
+
border_style="cyan",
|
|
143
|
+
)
|
|
144
|
+
)
|
|
122
145
|
ssh.run_locally(f"cloud_copy :^ {resolved_target}").print()
|
|
123
146
|
received_file = PathExtended(resolved_target) # type: ignore
|
|
124
147
|
else:
|
|
125
148
|
if source_is_remote:
|
|
126
149
|
assert resolved_source is not None, """
|
|
127
150
|
❌ Path Error: Source must be a remote path (machine:path)"""
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
target_display = resolved_target or "<auto>"
|
|
152
|
+
console.print(
|
|
153
|
+
Panel(
|
|
154
|
+
"\n".join(
|
|
155
|
+
[
|
|
156
|
+
"📥 Transfer Mode: Remote → Local",
|
|
157
|
+
f"Source: [cyan]{resolved_source}[/cyan]",
|
|
158
|
+
f"Target: [cyan]{target_display}[/cyan]",
|
|
159
|
+
f"Options: {'ZIP compression' if zipFirst else 'No compression'}, {'Recursive' if recursive else 'Non-recursive'}",
|
|
160
|
+
]
|
|
161
|
+
),
|
|
162
|
+
title="Transfer Details",
|
|
163
|
+
border_style="cyan",
|
|
164
|
+
padding=(1, 2),
|
|
165
|
+
)
|
|
166
|
+
)
|
|
135
167
|
received_file = ssh.copy_to_here(source=resolved_source, target=resolved_target, z=zipFirst, r=recursive)
|
|
136
168
|
else:
|
|
137
169
|
assert resolved_source is not None, """
|
|
138
170
|
❌ Path Error: Target must be a remote path (machine:path)"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
171
|
+
target_display = resolved_target or "<auto>"
|
|
172
|
+
console.print(
|
|
173
|
+
Panel(
|
|
174
|
+
"\n".join(
|
|
175
|
+
[
|
|
176
|
+
"📤 Transfer Mode: Local → Remote",
|
|
177
|
+
f"Source: [cyan]{resolved_source}[/cyan]",
|
|
178
|
+
f"Target: [cyan]{target_display}[/cyan]",
|
|
179
|
+
f"Options: {'ZIP compression' if zipFirst else 'No compression'}, {'Recursive' if recursive else 'Non-recursive'}",
|
|
180
|
+
]
|
|
181
|
+
),
|
|
182
|
+
title="Transfer Details",
|
|
183
|
+
border_style="cyan",
|
|
184
|
+
padding=(1, 2),
|
|
185
|
+
)
|
|
186
|
+
)
|
|
146
187
|
received_file = ssh.copy_from_here(source=resolved_source, target=resolved_target, z=zipFirst, r=recursive)
|
|
147
188
|
|
|
148
189
|
if source_is_remote and isinstance(received_file, PathExtended):
|
|
149
|
-
print(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
190
|
+
console.print(
|
|
191
|
+
Panel(
|
|
192
|
+
"\n".join(
|
|
193
|
+
[
|
|
194
|
+
"📁 File Received",
|
|
195
|
+
f"Parent: [cyan]{repr(received_file.parent)}[/cyan]",
|
|
196
|
+
f"File: [cyan]{repr(received_file)}[/cyan]",
|
|
197
|
+
]
|
|
198
|
+
),
|
|
199
|
+
title="Transfer Result",
|
|
200
|
+
border_style="green",
|
|
201
|
+
padding=(1, 2),
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
console.print(
|
|
205
|
+
Panel(
|
|
206
|
+
"File transfer process finished successfully",
|
|
207
|
+
title="✅ Transfer Complete",
|
|
208
|
+
border_style="green",
|
|
209
|
+
padding=(1, 2),
|
|
210
|
+
)
|
|
211
|
+
)
|
|
160
212
|
|
|
161
213
|
|
|
162
214
|
def main_from_parser() -> None:
|
|
@@ -104,7 +104,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
104
104
|
if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
|
|
105
105
|
assert ES not in target, f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised."
|
|
106
106
|
target_obj = absolute(target)
|
|
107
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
107
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
108
108
|
|
|
109
109
|
remote_path = PathExtended(target_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
110
110
|
source = f"{cloud}:{remote_path.as_posix()}"
|
|
@@ -124,7 +124,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
124
124
|
if len(target_parts) > 1 and target_parts[1] == ES: # the target path is to be inferred from source.
|
|
125
125
|
assert ES not in source, "You can't use $ in both source and target. Cyclical inference dependency arised."
|
|
126
126
|
source_obj = absolute(source)
|
|
127
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
127
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
128
128
|
|
|
129
129
|
remote_path = PathExtended(source_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
130
130
|
target = f"{cloud}:{remote_path.as_posix()}"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
1
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
2
2
|
from machineconfig.utils.terminal import Response
|
|
3
3
|
from machineconfig.scripts.python.get_zellij_cmd import get_zellij_cmd
|
|
4
4
|
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
@@ -124,7 +124,7 @@ def execute_installations(selected_options: list[str]) -> None:
|
|
|
124
124
|
console.print(Panel("⚡ [bold bright_yellow]CLI APPLICATIONS[/bold bright_yellow]\n[italic]Command-line tools installation[/italic]", border_style="bright_yellow"))
|
|
125
125
|
console.print("🔧 Installing CLI applications", style="bold cyan")
|
|
126
126
|
try:
|
|
127
|
-
from machineconfig.
|
|
127
|
+
from machineconfig.utils.installer_utils.installer import main as devops_devapps_install_main
|
|
128
128
|
devops_devapps_install_main(group=maybe_a_group) # type: ignore
|
|
129
129
|
console.print("✅ CLI applications installed successfully", style="bold green")
|
|
130
130
|
except Exception as e:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""NFS mounting script"""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
4
5
|
from machineconfig.utils.ssh import SSH
|
|
5
6
|
from machineconfig.utils.options import choose_from_options, choose_ssh_host
|
|
6
7
|
|
|
@@ -30,17 +31,22 @@ def main():
|
|
|
30
31
|
print(f"📁 Share Path: {share_path}\n")
|
|
31
32
|
|
|
32
33
|
if platform.system() in ["Linux", "Darwin"]:
|
|
33
|
-
mount_path_1 =
|
|
34
|
-
mount_path_2 =
|
|
34
|
+
mount_path_1 = Path(share_path)
|
|
35
|
+
mount_path_2 = Path.home().joinpath("data/mount_nfs", remote_server)
|
|
35
36
|
if str(mount_path_1).startswith("/home"):
|
|
36
|
-
mount_path_3 =
|
|
37
|
+
mount_path_3 = Path.home().joinpath(*mount_path_1.parts[3:])
|
|
37
38
|
else:
|
|
38
39
|
mount_path_3 = mount_path_2
|
|
39
40
|
|
|
40
41
|
print("🔧 Preparing mount paths...")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
local_mount_point_choice = choose_from_options(
|
|
43
|
+
msg="📂 Choose mount path OR input custom one:",
|
|
44
|
+
options=[mount_path_1, mount_path_2, mount_path_3],
|
|
45
|
+
default=mount_path_2,
|
|
46
|
+
custom_input=True,
|
|
47
|
+
multi=False,
|
|
48
|
+
)
|
|
49
|
+
local_mount_point = Path(local_mount_point_choice).expanduser()
|
|
44
50
|
|
|
45
51
|
txt = f"""
|
|
46
52
|
share_info={share_info}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from platform import system
|
|
4
4
|
import subprocess
|
|
5
5
|
from machineconfig.utils.ssh import SSH
|
|
6
|
-
from machineconfig.utils.path_extended import PathExtended
|
|
6
|
+
from machineconfig.utils.path_extended import PathExtended
|
|
7
7
|
|
|
8
8
|
from machineconfig.utils.options import choose_ssh_host
|
|
9
9
|
|