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
|
@@ -5,11 +5,13 @@ in the event that username@github.com is not mentioned in the remote url.
|
|
|
5
5
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import subprocess
|
|
8
9
|
from rich import print as pprint
|
|
9
|
-
from machineconfig.utils.
|
|
10
|
+
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
10
11
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
11
12
|
from machineconfig.utils.io_save import save_json
|
|
12
13
|
from machineconfig.utils.utils2 import randstr, read_json, read_ini
|
|
14
|
+
from machineconfig.scripts.python.devops_update_repos import run_uv_sync, update_repository
|
|
13
15
|
import argparse
|
|
14
16
|
from dataclasses import dataclass
|
|
15
17
|
from enum import Enum
|
|
@@ -30,37 +32,61 @@ class RepoRecord:
|
|
|
30
32
|
version: dict[str, str]
|
|
31
33
|
|
|
32
34
|
|
|
33
|
-
def git_action(path: PathExtended, action: GitAction, mess: Optional[str] = None, r: bool = False,
|
|
35
|
+
def git_action(path: PathExtended, action: GitAction, mess: Optional[str] = None, r: bool = False, auto_sync: bool = True) -> bool:
|
|
36
|
+
"""Perform git actions using Python instead of shell scripts. Returns True if successful."""
|
|
34
37
|
from git.exc import InvalidGitRepositoryError
|
|
35
38
|
from git.repo import Repo
|
|
39
|
+
|
|
36
40
|
try:
|
|
37
41
|
repo = Repo(str(path), search_parent_directories=False)
|
|
38
42
|
except InvalidGitRepositoryError:
|
|
39
43
|
pprint(f"⚠️ Skipping {path} because it is not a git repository.")
|
|
40
44
|
if r:
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
else:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
results = [git_action(path=sub_path, action=action, mess=mess, r=r, auto_sync=auto_sync) for sub_path in path.search()]
|
|
46
|
+
return all(results) # Return True only if all recursive operations succeeded
|
|
47
|
+
else:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
print(f">>>>>>>>> 🔧{action} - {path}")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
if action == GitAction.commit:
|
|
54
|
+
if mess is None:
|
|
55
|
+
mess = "auto_commit_" + randstr()
|
|
56
|
+
|
|
57
|
+
# Check if there are changes to commit
|
|
58
|
+
if repo.is_dirty() or repo.untracked_files:
|
|
59
|
+
repo.git.add(A=True) # Stage all changes
|
|
60
|
+
repo.index.commit(mess)
|
|
61
|
+
print(f"✅ Committed changes with message: {mess}")
|
|
62
|
+
return True
|
|
63
|
+
else:
|
|
64
|
+
print("ℹ️ No changes to commit")
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
elif action == GitAction.push:
|
|
68
|
+
success = True
|
|
69
|
+
for remote in repo.remotes:
|
|
70
|
+
try:
|
|
71
|
+
print(f"🚀 Pushing to {remote.url}")
|
|
72
|
+
remote.push(repo.active_branch.name)
|
|
73
|
+
print(f"✅ Pushed to {remote.name}")
|
|
74
|
+
except Exception as e:
|
|
75
|
+
print(f"❌ Failed to push to {remote.name}: {e}")
|
|
76
|
+
success = False
|
|
77
|
+
return success
|
|
78
|
+
|
|
79
|
+
elif action == GitAction.pull:
|
|
80
|
+
# Use the enhanced update function with uv sync support
|
|
81
|
+
update_repository(repo, auto_sync=auto_sync)
|
|
82
|
+
print("✅ Pull completed")
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"❌ Error performing {action} on {path}: {e}")
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
return True
|
|
64
90
|
|
|
65
91
|
|
|
66
92
|
def main():
|
|
@@ -68,12 +94,11 @@ def main():
|
|
|
68
94
|
print("📂 Welcome to the Repository Manager")
|
|
69
95
|
print("=" * 50 + "\n")
|
|
70
96
|
|
|
71
|
-
parser = argparse.ArgumentParser(description=
|
|
97
|
+
parser = argparse.ArgumentParser(description="REPO MANAGER")
|
|
72
98
|
# POSITIONAL
|
|
73
99
|
parser.add_argument("directory", help="📁 Folder containing repos to record or a specs JSON file to follow.", default="")
|
|
74
100
|
# FLAGS
|
|
75
101
|
parser.add_argument("--push", help="🚀 Push changes.", action="store_true")
|
|
76
|
-
parser.add_argument("--uv_sync", help="🔄 Sync UV dependencies.", action="store_true")
|
|
77
102
|
parser.add_argument("--pull", help="⬇️ Pull changes.", action="store_true")
|
|
78
103
|
parser.add_argument("--commit", help="💾 Commit changes.", action="store_true")
|
|
79
104
|
parser.add_argument("--all", help="🔄 Pull, commit, and push changes.", action="store_true")
|
|
@@ -82,14 +107,18 @@ def main():
|
|
|
82
107
|
parser.add_argument("--checkout", help="🔀 Check out to versions provided in a JSON file.", action="store_true")
|
|
83
108
|
parser.add_argument("--checkout_to_branch", help="🔀 Check out to the main branch.", action="store_true")
|
|
84
109
|
parser.add_argument("--recursive", "-r", help="🔍 Recursive flag.", action="store_true")
|
|
110
|
+
parser.add_argument("--no-sync", help="🚫 Disable automatic uv sync after pulls.", action="store_true")
|
|
85
111
|
# OPTIONAL
|
|
86
112
|
parser.add_argument("--cloud", "-c", help="☁️ Cloud storage option.", default=None)
|
|
87
113
|
args = parser.parse_args()
|
|
88
114
|
|
|
89
|
-
if args.directory == "":
|
|
90
|
-
|
|
115
|
+
if args.directory == "":
|
|
116
|
+
repos_root = PathExtended.home().joinpath("code") # it is a positional argument, can never be empty.
|
|
117
|
+
else:
|
|
118
|
+
repos_root = PathExtended(args.directory).expanduser().absolute()
|
|
119
|
+
|
|
120
|
+
auto_sync = not args.no_sync # Enable auto sync by default, disable with --no-sync
|
|
91
121
|
|
|
92
|
-
program = ""
|
|
93
122
|
if args.record:
|
|
94
123
|
print("\n📝 Recording repositories...")
|
|
95
124
|
res = record_repos(repos_root=str(repos_root))
|
|
@@ -97,37 +126,56 @@ def main():
|
|
|
97
126
|
save_path = CONFIG_PATH.joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
|
|
98
127
|
save_json(obj=res, path=save_path, indent=4)
|
|
99
128
|
pprint(f"📁 Result saved at {PathExtended(save_path)}")
|
|
100
|
-
if args.cloud is not None:
|
|
101
|
-
|
|
129
|
+
if args.cloud is not None:
|
|
130
|
+
PathExtended(save_path).to_cloud(rel2home=True, cloud=args.cloud)
|
|
131
|
+
print(">>>>>>>>> Finished Recording")
|
|
132
|
+
|
|
102
133
|
elif args.clone or args.checkout or args.checkout_to_branch:
|
|
103
134
|
print("\n📥 Cloning or checking out repositories...")
|
|
104
|
-
|
|
105
|
-
if not repos_root.exists() or repos_root.name !=
|
|
106
|
-
repos_root = CONFIG_PATH.joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
|
|
135
|
+
print(">>>>>>>>> Cloning Repos")
|
|
136
|
+
if not repos_root.exists() or repos_root.name != "repos.json": # Fixed: use name instead of stem
|
|
137
|
+
repos_root = PathExtended(CONFIG_PATH).joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
|
|
107
138
|
if not repos_root.exists():
|
|
108
139
|
if args.cloud is None:
|
|
109
|
-
cloud: str=read_ini(DEFAULTS_PATH)[
|
|
140
|
+
cloud: str = read_ini(DEFAULTS_PATH)["general"]["rclone_config_name"]
|
|
110
141
|
print(f"⚠️ Using default cloud: {cloud}")
|
|
111
142
|
else:
|
|
112
143
|
cloud = args.cloud
|
|
113
144
|
assert cloud is not None, f"Path {repos_root} does not exist and cloud was not passed. You can't clone without one of them."
|
|
114
145
|
repos_root.from_cloud(cloud=cloud, rel2home=True)
|
|
115
|
-
assert (repos_root.exists() and repos_root.name ==
|
|
116
|
-
|
|
146
|
+
assert (repos_root.exists() and repos_root.name == "repos.json") or args.cloud is not None, f"Path {repos_root} does not exist and cloud was not passed. You can't clone without one of them."
|
|
147
|
+
success = install_repos_python(specs_path=str(repos_root), clone=args.clone, checkout_to_recorded_commit=args.checkout, checkout_to_branch=args.checkout_to_branch, auto_sync=auto_sync)
|
|
148
|
+
if success:
|
|
149
|
+
print("✅ Repository operations completed successfully")
|
|
150
|
+
else:
|
|
151
|
+
print("⚠️ Some repository operations encountered issues")
|
|
152
|
+
|
|
117
153
|
elif args.all or args.commit or args.pull or args.push:
|
|
118
154
|
print(f"\n🔄 Performing Git actions on repositories @ `{repos_root}`...")
|
|
155
|
+
overall_success = True
|
|
119
156
|
for a_path in repos_root.search("*"):
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if args.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
157
|
+
print(f"{('Handling ' + str(a_path)).center(80, '-')}")
|
|
158
|
+
path_success = True
|
|
159
|
+
if args.pull or args.all:
|
|
160
|
+
path_success = git_action(path=a_path, action=GitAction.pull, r=args.recursive, auto_sync=auto_sync) and path_success
|
|
161
|
+
if args.commit or args.all:
|
|
162
|
+
path_success = git_action(a_path, action=GitAction.commit, r=args.recursive, auto_sync=auto_sync) and path_success
|
|
163
|
+
if args.push or args.all:
|
|
164
|
+
path_success = git_action(a_path, action=GitAction.push, r=args.recursive, auto_sync=auto_sync) and path_success
|
|
165
|
+
overall_success = overall_success and path_success
|
|
166
|
+
|
|
167
|
+
if overall_success:
|
|
168
|
+
print("✅ All git operations completed successfully")
|
|
169
|
+
else:
|
|
170
|
+
print("⚠️ Some git operations encountered issues")
|
|
171
|
+
else:
|
|
172
|
+
print("❌ No action specified. Try passing --push, --pull, --commit, or --all.")
|
|
126
173
|
|
|
127
174
|
|
|
128
|
-
def record_repos(repos_root: str, r: bool=True) -> list[dict[str, Any]]:
|
|
175
|
+
def record_repos(repos_root: str, r: bool = True) -> list[dict[str, Any]]:
|
|
129
176
|
path_obj = PathExtended(repos_root).expanduser().absolute()
|
|
130
|
-
if path_obj.is_file():
|
|
177
|
+
if path_obj.is_file():
|
|
178
|
+
return []
|
|
131
179
|
search_res = path_obj.search("*", files=False)
|
|
132
180
|
res: list[dict[str, Any]] = []
|
|
133
181
|
for a_search_res in search_res:
|
|
@@ -137,35 +185,145 @@ def record_repos(repos_root: str, r: bool=True) -> list[dict[str, Any]]:
|
|
|
137
185
|
except Exception as e:
|
|
138
186
|
print(f"⚠️ Failed to record {a_search_res}: {e}")
|
|
139
187
|
else:
|
|
140
|
-
if r:
|
|
188
|
+
if r:
|
|
189
|
+
res += record_repos(str(a_search_res), r=r)
|
|
141
190
|
return res
|
|
142
191
|
|
|
143
192
|
|
|
144
|
-
def record_a_repo(path: PathExtended, search_parent_directories: bool=False, preferred_remote: Optional[str] = None):
|
|
193
|
+
def record_a_repo(path: PathExtended, search_parent_directories: bool = False, preferred_remote: Optional[str] = None):
|
|
145
194
|
from git.repo import Repo
|
|
195
|
+
|
|
146
196
|
repo = Repo(path, search_parent_directories=search_parent_directories) # get list of remotes using git python
|
|
147
197
|
repo_root = PathExtended(repo.working_dir).absolute()
|
|
148
198
|
remotes = {remote.name: remote.url for remote in repo.remotes}
|
|
149
199
|
if preferred_remote is not None:
|
|
150
|
-
if preferred_remote in remotes:
|
|
200
|
+
if preferred_remote in remotes:
|
|
201
|
+
remotes = {preferred_remote: remotes[preferred_remote]}
|
|
151
202
|
else:
|
|
152
203
|
print(f"⚠️ `{preferred_remote=}` not found in {remotes}.")
|
|
153
204
|
preferred_remote = None
|
|
154
|
-
try:
|
|
205
|
+
try:
|
|
206
|
+
commit = repo.head.commit.hexsha
|
|
155
207
|
except ValueError: # look at https://github.com/gitpython-developers/GitPython/issues/1016
|
|
156
208
|
print(f"⚠️ Failed to get latest commit of {repo}")
|
|
157
209
|
commit = None
|
|
158
|
-
try:
|
|
210
|
+
try:
|
|
211
|
+
current_branch = repo.head.reference.name # same as repo.active_branch.name
|
|
159
212
|
except TypeError:
|
|
160
213
|
print(f"⁉️ Failed to get current branch of {repo}. It is probably in a detached state.")
|
|
161
214
|
current_branch = None
|
|
162
|
-
res: dict[str, Any] = {"name": repo_root.name, "parent_dir": repo_root.parent.collapseuser().as_posix(),
|
|
163
|
-
"current_branch": current_branch,
|
|
164
|
-
"remotes": remotes, "version": {"branch": current_branch, "commit": commit}}
|
|
215
|
+
res: dict[str, Any] = {"name": repo_root.name, "parent_dir": repo_root.parent.collapseuser().as_posix(), "current_branch": current_branch, "remotes": remotes, "version": {"branch": current_branch, "commit": commit}}
|
|
165
216
|
return res
|
|
166
217
|
|
|
167
218
|
|
|
168
|
-
def
|
|
219
|
+
def install_repos_python(specs_path: str, clone: bool = True, checkout_to_recorded_commit: bool = False, checkout_to_branch: bool = False, editable_install: bool = False, preferred_remote: Optional[str] = None, auto_sync: bool = True) -> bool:
|
|
220
|
+
"""Python-based repository installation with uv sync support. Returns True if all operations succeeded."""
|
|
221
|
+
from git.repo import Repo
|
|
222
|
+
from git.exc import GitCommandError
|
|
223
|
+
|
|
224
|
+
path_obj = PathExtended(specs_path).expanduser().absolute()
|
|
225
|
+
repos: list[dict[str, Any]] = read_json(path_obj)
|
|
226
|
+
overall_success = True
|
|
227
|
+
|
|
228
|
+
for repo in repos:
|
|
229
|
+
repo_success = True
|
|
230
|
+
parent_dir = PathExtended(repo["parent_dir"]).expanduser().absolute()
|
|
231
|
+
parent_dir.mkdir(parents=True, exist_ok=True)
|
|
232
|
+
repo_path = parent_dir / repo["name"]
|
|
233
|
+
|
|
234
|
+
print(f"\n{'Processing ' + repo['name']:.^80}")
|
|
235
|
+
|
|
236
|
+
# Handle cloning and remote setup
|
|
237
|
+
if clone:
|
|
238
|
+
# Select the remote to use for cloning
|
|
239
|
+
if len(repo["remotes"]) == 0:
|
|
240
|
+
print(f"⚠️ No remotes found for {repo['name']}. Skipping clone.")
|
|
241
|
+
repo_success = False
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
remote_name, remote_url = next(iter(repo["remotes"].items())) # Get first remote by default
|
|
245
|
+
if preferred_remote is not None and preferred_remote in repo["remotes"]:
|
|
246
|
+
remote_name = preferred_remote
|
|
247
|
+
remote_url = repo["remotes"][preferred_remote]
|
|
248
|
+
elif preferred_remote is not None:
|
|
249
|
+
print(f"⚠️ `{preferred_remote=}` not found in {repo['remotes']}.")
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
# Clone with the selected remote
|
|
253
|
+
print(f"📥 Cloning {remote_url} to {repo_path}")
|
|
254
|
+
cloned_repo = Repo.clone_from(remote_url, repo_path, origin=remote_name, depth=2)
|
|
255
|
+
print(f"✅ Successfully cloned {repo['name']}")
|
|
256
|
+
|
|
257
|
+
# Add any additional remotes
|
|
258
|
+
for other_remote_name, other_remote_url in repo["remotes"].items():
|
|
259
|
+
if other_remote_name != remote_name:
|
|
260
|
+
try:
|
|
261
|
+
cloned_repo.create_remote(other_remote_name, other_remote_url)
|
|
262
|
+
print(f"✅ Added remote {other_remote_name}")
|
|
263
|
+
except Exception as e:
|
|
264
|
+
print(f"⚠️ Failed to add remote {other_remote_name}: {e}")
|
|
265
|
+
|
|
266
|
+
except GitCommandError as e:
|
|
267
|
+
print(f"❌ Failed to clone {repo['name']}: {e}")
|
|
268
|
+
repo_success = False
|
|
269
|
+
continue
|
|
270
|
+
except Exception as e:
|
|
271
|
+
print(f"❌ Unexpected error cloning {repo['name']}: {e}")
|
|
272
|
+
repo_success = False
|
|
273
|
+
continue
|
|
274
|
+
|
|
275
|
+
# Handle checkout operations (after cloning/if repo exists)
|
|
276
|
+
if repo_path.exists():
|
|
277
|
+
try:
|
|
278
|
+
existing_repo = Repo(repo_path)
|
|
279
|
+
|
|
280
|
+
if checkout_to_recorded_commit:
|
|
281
|
+
commit = repo["version"]["commit"]
|
|
282
|
+
if isinstance(commit, str):
|
|
283
|
+
print(f"🔀 Checking out to commit {commit[:8]}...")
|
|
284
|
+
existing_repo.git.checkout(commit)
|
|
285
|
+
print("✅ Checked out to recorded commit")
|
|
286
|
+
else:
|
|
287
|
+
print(f"⚠️ Skipping {repo['name']} because it doesn't have a commit recorded. Found {commit}")
|
|
288
|
+
|
|
289
|
+
elif checkout_to_branch:
|
|
290
|
+
if repo.get("current_branch"):
|
|
291
|
+
print(f"🔀 Checking out to branch {repo['current_branch']}...")
|
|
292
|
+
existing_repo.git.checkout(repo["current_branch"])
|
|
293
|
+
print("✅ Checked out to recorded branch")
|
|
294
|
+
else:
|
|
295
|
+
print(f"⚠️ No current branch recorded for {repo['name']}")
|
|
296
|
+
|
|
297
|
+
# Handle editable install
|
|
298
|
+
if editable_install:
|
|
299
|
+
pyproject_path = repo_path / "pyproject.toml"
|
|
300
|
+
if pyproject_path.exists():
|
|
301
|
+
print(f"📦 Installing {repo['name']} in editable mode...")
|
|
302
|
+
result = subprocess.run(["uv", "pip", "install", "-e", "."], cwd=repo_path, capture_output=True, text=True)
|
|
303
|
+
if result.returncode == 0:
|
|
304
|
+
print("✅ Editable install completed")
|
|
305
|
+
else:
|
|
306
|
+
print(f"❌ Editable install failed: {result.stderr}")
|
|
307
|
+
repo_success = False
|
|
308
|
+
else:
|
|
309
|
+
print(f"⚠️ No pyproject.toml found in {repo['name']}, skipping editable install")
|
|
310
|
+
|
|
311
|
+
# Run uv sync if auto_sync is enabled and pyproject.toml exists
|
|
312
|
+
if auto_sync and (repo_path / "pyproject.toml").exists():
|
|
313
|
+
sync_success = run_uv_sync(repo_path)
|
|
314
|
+
if not sync_success:
|
|
315
|
+
repo_success = False
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"❌ Error processing existing repository {repo['name']}: {e}")
|
|
319
|
+
repo_success = False
|
|
320
|
+
|
|
321
|
+
overall_success = overall_success and repo_success
|
|
322
|
+
|
|
323
|
+
return overall_success
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def install_repos(specs_path: str, clone: bool = True, checkout_to_recorded_commit: bool = False, checkout_to_branch: bool = False, editable_install: bool = False, preferred_remote: Optional[str] = None):
|
|
169
327
|
program = ""
|
|
170
328
|
path_obj = PathExtended(specs_path).expanduser().absolute()
|
|
171
329
|
repos: list[dict[str, Any]] = read_json(path_obj)
|
|
@@ -197,7 +355,7 @@ def install_repos(specs_path: str, clone: bool=True, checkout_to_recorded_commit
|
|
|
197
355
|
|
|
198
356
|
# Handle checkout operations (after all remotes are set up)
|
|
199
357
|
if checkout_to_recorded_commit:
|
|
200
|
-
commit = repo[
|
|
358
|
+
commit = repo["version"]["commit"]
|
|
201
359
|
if isinstance(commit, str):
|
|
202
360
|
program += f"\ncd {parent_dir.collapseuser().as_posix()}/{repo['name']}; git checkout {commit}"
|
|
203
361
|
else:
|
|
@@ -214,5 +372,5 @@ def install_repos(specs_path: str, clone: bool=True, checkout_to_recorded_commit
|
|
|
214
372
|
return program
|
|
215
373
|
|
|
216
374
|
|
|
217
|
-
if __name__ ==
|
|
375
|
+
if __name__ == "__main__":
|
|
218
376
|
main()
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
slidev
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from machineconfig.utils.
|
|
5
|
+
from machineconfig.utils.source_of_truth import CONFIG_PATH, PROGRAM_PATH
|
|
6
|
+
from machineconfig.utils.code import print_code
|
|
6
7
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
7
8
|
from machineconfig.utils.terminal import Terminal
|
|
8
9
|
import subprocess
|
|
@@ -10,12 +11,13 @@ import platform
|
|
|
10
11
|
|
|
11
12
|
PORT_DEFAULT = 3030
|
|
12
13
|
|
|
13
|
-
SLIDEV_REPO = CONFIG_PATH.joinpath(".cache/slidev")
|
|
14
|
+
SLIDEV_REPO = PathExtended(CONFIG_PATH).joinpath(".cache/slidev")
|
|
14
15
|
if not SLIDEV_REPO.joinpath("components").exists():
|
|
15
16
|
print("📦 Initializing Slidev repository...")
|
|
16
17
|
Terminal(stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE).run(f"cd {SLIDEV_REPO.parent};npm init slidev@latest")
|
|
17
18
|
print("✅ Slidev repository initialized successfully!\n")
|
|
18
19
|
|
|
20
|
+
|
|
19
21
|
def jupyter_to_markdown(file: PathExtended):
|
|
20
22
|
op_dir = file.parent.joinpath("presentation")
|
|
21
23
|
print("📝 Converting Jupyter notebook to markdown...")
|
|
@@ -36,14 +38,15 @@ def jupyter_to_markdown(file: PathExtended):
|
|
|
36
38
|
Terminal().run(cmd, shell="powershell").print()
|
|
37
39
|
|
|
38
40
|
op_file = op_dir.joinpath("slides_raw.md")
|
|
39
|
-
slide_separator =
|
|
40
|
-
md = op_file.read_text(encoding="utf-8").replace(
|
|
41
|
+
slide_separator = "\n\n---\n\n"
|
|
42
|
+
md = op_file.read_text(encoding="utf-8").replace("\n\n\n\n", slide_separator)
|
|
41
43
|
md = slide_separator.join([item for item in md.split(slide_separator) if bool(item.strip())])
|
|
42
44
|
op_file.with_name("slides.md").write_text(md, encoding="utf-8")
|
|
43
45
|
print(f"✅ Conversion completed! Check the results at: {op_dir}\n")
|
|
44
46
|
|
|
45
47
|
return op_dir
|
|
46
48
|
|
|
49
|
+
|
|
47
50
|
def main() -> None:
|
|
48
51
|
import argparse
|
|
49
52
|
|
|
@@ -85,6 +88,7 @@ def main() -> None:
|
|
|
85
88
|
SLIDEV_REPO.joinpath(md_file.name).with_name(name="slides.md", inplace=True, overwrite=True)
|
|
86
89
|
|
|
87
90
|
import socket
|
|
91
|
+
|
|
88
92
|
try:
|
|
89
93
|
local_ip_v4 = socket.gethostbyname(socket.gethostname() + ".local")
|
|
90
94
|
except Exception:
|
|
@@ -100,5 +104,6 @@ def main() -> None:
|
|
|
100
104
|
PROGRAM_PATH.write_text(program, encoding="utf-8")
|
|
101
105
|
print_code(code=program, lexer="bash", desc="Run the following command to start the presentation")
|
|
102
106
|
|
|
103
|
-
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
104
109
|
main()
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
"""Script to start terminals on windows and wsl
|
|
2
|
-
"""
|
|
1
|
+
"""Script to start terminals on windows and wsl"""
|
|
3
2
|
|
|
4
|
-
from machineconfig.utils.
|
|
5
|
-
from machineconfig.utils.options import get_ssh_hosts
|
|
3
|
+
from machineconfig.utils.source_of_truth import PROGRAM_PATH
|
|
4
|
+
from machineconfig.utils.options import display_options, get_ssh_hosts
|
|
6
5
|
import platform
|
|
7
6
|
from itertools import cycle
|
|
8
7
|
from typing import Literal
|
|
@@ -23,15 +22,15 @@ THIS_MACHINE_HOSTNAME = platform.node()
|
|
|
23
22
|
THIS_MACHINE_HOSTNAME_WSL = f"{THIS_MACHINE_HOSTNAME}wsl"
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
def main_windows_and_wsl(window: int, hosts: list[str], orientation: ORIENTATION_TYPE = "vertical", mprocs: bool=False):
|
|
25
|
+
def main_windows_and_wsl(window: int, hosts: list[str], orientation: ORIENTATION_TYPE = "vertical", mprocs: bool = False):
|
|
27
26
|
print("\n🔧 Configuring terminal layout for Windows and WSL...")
|
|
28
27
|
orientation_oposite = "horizontal" if orientation == "vertical" else "vertical"
|
|
29
|
-
orientation_swap
|
|
28
|
+
orientation_swap = "up" if orientation == "horizontal" else "left"
|
|
30
29
|
orientation_opposite_move_focus = "up" if orientation_oposite == "horizontal" else "left"
|
|
31
30
|
orientation_opposite_move_focus_other = "down" if orientation_oposite == "horizontal" else "right"
|
|
32
31
|
sleep = 3
|
|
33
32
|
sep = f"\nsleep {sleep}; wt --window {window}" # or '`;'
|
|
34
|
-
ssh_cmd = "-t 'mprocs'" if mprocs else
|
|
33
|
+
ssh_cmd = "-t 'mprocs'" if mprocs else "" # 'wsl_ssh_windows_port_forwarding.ps1'
|
|
35
34
|
split_per_machine = 1 / len(hosts)
|
|
36
35
|
size = 0.3
|
|
37
36
|
known_hosts = get_ssh_hosts()
|
|
@@ -48,26 +47,33 @@ wt --window {window} --title {hosts[0]} powershell -Command "ssh {host_linux} {s
|
|
|
48
47
|
|
|
49
48
|
elif len(hosts) > 1:
|
|
50
49
|
print("🖥️ Multiple hosts detected. Configuring layout...")
|
|
51
|
-
pane_cmd = f'powershell -Command "ssh {hosts[0]} {ssh_cmd}" ' if hosts[0] != THIS_MACHINE else
|
|
50
|
+
pane_cmd = f'powershell -Command "ssh {hosts[0]} {ssh_cmd}" ' if hosts[0] != THIS_MACHINE else ""
|
|
52
51
|
cmd = f"""wt --window {window} --title {hosts[0]} {pane_cmd} """
|
|
53
52
|
for a_host in hosts[1:]:
|
|
54
|
-
if a_host != THIS_MACHINE:
|
|
55
|
-
|
|
53
|
+
if a_host != THIS_MACHINE:
|
|
54
|
+
pane_cmd = f'powershell -Command "ssh {a_host} {ssh_cmd}" '
|
|
55
|
+
else:
|
|
56
|
+
pane_cmd = "powershell"
|
|
56
57
|
cmd += f"""{sep} split-pane --{orientation_oposite} --title {a_host}Windows --size {split_per_machine} {pane_cmd} """
|
|
57
58
|
for idx, a_host in enumerate(hosts[::-1]):
|
|
58
|
-
if f"{a_host}wsl" not in known_hosts and a_host != THIS_MACHINE:
|
|
59
|
-
|
|
60
|
-
if
|
|
61
|
-
|
|
59
|
+
if f"{a_host}wsl" not in known_hosts and a_host != THIS_MACHINE:
|
|
60
|
+
continue
|
|
61
|
+
pane_cmd = f'powershell -Command "ssh {a_host}wsl"' if a_host != THIS_MACHINE else "wsl"
|
|
62
|
+
if idx == 0:
|
|
63
|
+
tmp = ""
|
|
64
|
+
else:
|
|
65
|
+
tmp = f"move-focus {orientation_opposite_move_focus}" if idx % 2 == 1 else f"move-focus {orientation_opposite_move_focus_other}"
|
|
62
66
|
cmd += f"""{sep} {tmp} split-pane --{orientation} --title {a_host}wsl --size {size} {pane_cmd} """
|
|
63
67
|
cmd += f"""{sep} swap-pane {orientation_swap} """
|
|
64
|
-
else:
|
|
68
|
+
else:
|
|
69
|
+
raise NotImplementedError(f"❌ len(hosts) = {len(hosts)}. Only 1 or 2 hosts are supported.")
|
|
65
70
|
print("✅ Terminal layout configured successfully!\n")
|
|
66
71
|
return cmd
|
|
67
72
|
|
|
68
73
|
|
|
69
74
|
def main():
|
|
70
75
|
import argparse
|
|
76
|
+
|
|
71
77
|
print("\n" + "=" * 50)
|
|
72
78
|
print("🖥️ Welcome to the Terminal Starter Tool")
|
|
73
79
|
print("=" * 50 + "\n")
|
|
@@ -109,5 +115,5 @@ def main():
|
|
|
109
115
|
print("✅ Command saved successfully!\n")
|
|
110
116
|
|
|
111
117
|
|
|
112
|
-
if __name__ ==
|
|
118
|
+
if __name__ == "__main__":
|
|
113
119
|
main()
|