machineconfig 3.2__py3-none-any.whl → 3.5__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/wt_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_local.py +2 -2
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +1 -1
- machineconfig/cluster/sessions_managers/zellij_utils/monitoring_types.py +17 -7
- machineconfig/cluster/templates/utils.py +1 -1
- machineconfig/jobs/linux/msc/cli_agents.sh +18 -2
- machineconfig/jobs/python/python_ve_symlink.py +1 -1
- machineconfig/jobs/python/vscode/api.py +1 -1
- machineconfig/jobs/python/vscode/select_interpreter.py +2 -2
- machineconfig/jobs/python/vscode/sync_code.py +1 -1
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/aider.py +9 -1
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +2 -1
- machineconfig/jobs/python_custom_installers/dev/brave.py +10 -1
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +12 -4
- machineconfig/jobs/python_custom_installers/dev/code.py +10 -3
- machineconfig/jobs/python_custom_installers/dev/cursor.py +2 -1
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/espanso.py +14 -6
- machineconfig/jobs/python_custom_installers/dev/goes.py +10 -1
- machineconfig/jobs/python_custom_installers/dev/lvim.py +9 -1
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +9 -1
- machineconfig/jobs/python_custom_installers/dev/redis.py +2 -2
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +3 -1
- machineconfig/jobs/python_custom_installers/dev/winget.py +2 -1
- machineconfig/jobs/python_custom_installers/docker.py +9 -1
- machineconfig/jobs/python_custom_installers/gh.py +11 -2
- machineconfig/jobs/python_custom_installers/hx.py +10 -9
- machineconfig/jobs/python_custom_installers/warp-cli.py +9 -1
- machineconfig/jobs/python_generic_installers/config.json +601 -412
- machineconfig/jobs/python_generic_installers/config.json.bak +414 -0
- machineconfig/jobs/python_generic_installers/dev/config.json +822 -562
- machineconfig/jobs/python_generic_installers/dev/config.json.bak +565 -0
- machineconfig/jobs/python_linux_installers/archive/config.json +16 -8
- machineconfig/jobs/python_linux_installers/archive/config.json.bak +10 -0
- machineconfig/jobs/python_linux_installers/config.json +134 -99
- machineconfig/jobs/python_linux_installers/config.json.bak +110 -0
- machineconfig/jobs/python_linux_installers/dev/config.json +273 -203
- machineconfig/jobs/python_linux_installers/dev/config.json.bak +206 -0
- machineconfig/jobs/python_windows_installers/config.json +74 -48
- machineconfig/jobs/python_windows_installers/config.json.bak +56 -0
- machineconfig/jobs/python_windows_installers/dev/config.json +3 -2
- machineconfig/jobs/python_windows_installers/dev/config.json.bak +3 -0
- machineconfig/profile/create.py +3 -3
- machineconfig/profile/shell.py +1 -1
- machineconfig/scripts/python/ai/mcinit.py +23 -67
- machineconfig/scripts/python/ai/solutions/__init__.py +0 -0
- machineconfig/scripts/python/ai/solutions/_shared.py +5 -0
- machineconfig/scripts/python/ai/solutions/claude/claude.py +8 -0
- machineconfig/scripts/python/ai/solutions/cline/cline.py +10 -0
- machineconfig/scripts/python/ai/solutions/copilot/github_copilot.py +35 -0
- machineconfig/scripts/python/ai/solutions/copilot/privacy.md +4 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.json +216 -0
- machineconfig/scripts/python/ai/solutions/crush/crush.py +25 -0
- machineconfig/scripts/python/ai/solutions/crush/privacy.md +2 -0
- machineconfig/scripts/python/ai/solutions/cursor/cursors.py +10 -0
- machineconfig/scripts/python/ai/solutions/gemini/gemini.py +14 -0
- machineconfig/scripts/python/ai/solutions/generic.py +41 -0
- machineconfig/scripts/python/ai/solutions/kilocode/privacy.md +3 -0
- machineconfig/scripts/python/ai/solutions/opencode/opencode.json +4 -0
- machineconfig/scripts/python/ai/solutions/opencode/opencode.py +1 -0
- machineconfig/scripts/python/choose_wezterm_theme.py +1 -1
- machineconfig/scripts/python/cloud_copy.py +2 -2
- machineconfig/scripts/python/cloud_mount.py +2 -2
- machineconfig/scripts/python/cloud_repo_sync.py +3 -2
- machineconfig/scripts/python/croshell.py +12 -7
- machineconfig/scripts/python/devops.py +1 -0
- 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 +4 -3
- machineconfig/scripts/python/devops_devapps_install.py +39 -17
- machineconfig/scripts/python/devops_update_repos.py +2 -2
- machineconfig/scripts/python/dotfile.py +1 -1
- machineconfig/scripts/python/fire_agents.py +7 -3
- machineconfig/scripts/python/fire_agents_help_launch.py +2 -2
- machineconfig/scripts/python/fire_jobs.py +8 -8
- machineconfig/scripts/python/fire_jobs_layout_helper.py +2 -2
- machineconfig/scripts/python/ftpx.py +2 -2
- machineconfig/scripts/python/helpers/cloud_helpers.py +2 -1
- machineconfig/scripts/python/helpers/helpers2.py +4 -3
- machineconfig/scripts/python/helpers/helpers4.py +1 -1
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +2 -2
- machineconfig/scripts/python/mount_nfs.py +1 -1
- machineconfig/scripts/python/mount_ssh.py +1 -1
- machineconfig/scripts/python/repos.py +6 -3
- machineconfig/scripts/python/repos_helper_clone.py +121 -0
- machineconfig/scripts/python/repos_helper_record.py +2 -2
- machineconfig/scripts/python/start_slidev.py +1 -1
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/install_nerd_fonts.py +9 -8
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +3 -3
- machineconfig/utils/{utils2.py → accessories.py} +13 -27
- machineconfig/utils/code.py +2 -2
- machineconfig/utils/installer.py +47 -33
- machineconfig/utils/installer_utils/installer_abc.py +2 -5
- machineconfig/utils/installer_utils/installer_class.py +109 -103
- machineconfig/utils/io.py +94 -0
- machineconfig/utils/links.py +2 -2
- machineconfig/utils/notifications.py +0 -9
- machineconfig/utils/{path_reduced.py → path_extended.py} +2 -2
- machineconfig/utils/{path.py → path_helper.py} +1 -1
- machineconfig/utils/procs.py +2 -1
- machineconfig/utils/{utils5.py → scheduler.py} +3 -8
- machineconfig/utils/schemas/installer/installer_types.py +20 -0
- machineconfig/utils/ssh.py +2 -2
- machineconfig/utils/terminal.py +12 -2
- machineconfig/utils/ve.py +2 -16
- {machineconfig-3.2.dist-info → machineconfig-3.5.dist-info}/METADATA +1 -4
- {machineconfig-3.2.dist-info → machineconfig-3.5.dist-info}/RECORD +121 -97
- machineconfig/utils/io_save.py +0 -95
- /machineconfig/scripts/python/ai/{chatmodes → solutions/copilot/chatmodes}/Thinking-Beast-Mode.chatmode.md +0 -0
- /machineconfig/scripts/python/ai/{chatmodes → solutions/copilot/chatmodes}/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +0 -0
- /machineconfig/scripts/python/ai/{chatmodes → solutions/copilot/chatmodes}/deepResearch.chatmode.md +0 -0
- /machineconfig/scripts/python/ai/{instructions → solutions/copilot/instructions}/python/dev.instructions.md +0 -0
- /machineconfig/scripts/python/ai/{prompts → solutions/copilot/prompts}/allLintersAndTypeCheckers.prompt.md +0 -0
- /machineconfig/scripts/python/ai/{prompts → solutions/copilot/prompts}/research-report-skeleton.prompt.md +0 -0
- /machineconfig/scripts/python/ai/{configs/.gemini → solutions/gemini}/settings.json +0 -0
- {machineconfig-3.2.dist-info → machineconfig-3.5.dist-info}/WHEEL +0 -0
- {machineconfig-3.2.dist-info → machineconfig-3.5.dist-info}/entry_points.txt +0 -0
- {machineconfig-3.2.dist-info → machineconfig-3.5.dist-info}/top_level.txt +0 -0
|
@@ -11,17 +11,17 @@ from machineconfig.scripts.python.helpers.helpers4 import search_for_files_of_in
|
|
|
11
11
|
from machineconfig.scripts.python.helpers.helpers4 import convert_kwargs_to_fire_kwargs_str
|
|
12
12
|
from machineconfig.scripts.python.helpers.helpers4 import parse_pyfile
|
|
13
13
|
from machineconfig.scripts.python.helpers.helpers4 import get_import_module_code
|
|
14
|
-
from machineconfig.utils.ve import
|
|
14
|
+
from machineconfig.utils.ve import get_ve_activate_line, get_ve_path_and_ipython_profile
|
|
15
15
|
from machineconfig.utils.options import display_options, choose_one_option
|
|
16
|
-
from machineconfig.utils.
|
|
16
|
+
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
17
17
|
|
|
18
|
-
from machineconfig.utils.
|
|
19
|
-
from machineconfig.utils.
|
|
20
|
-
from machineconfig.utils.utils2 import randstr, read_toml
|
|
18
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
19
|
+
from machineconfig.utils.accessories import get_repo_root, randstr
|
|
21
20
|
from machineconfig.scripts.python.fire_jobs_args_helper import get_args, FireJobArgs, extract_kwargs
|
|
22
21
|
import platform
|
|
23
22
|
from typing import Optional
|
|
24
23
|
from pathlib import Path
|
|
24
|
+
import tomllib
|
|
25
25
|
# import os
|
|
26
26
|
|
|
27
27
|
|
|
@@ -113,7 +113,7 @@ def route(args: FireJobArgs) -> None:
|
|
|
113
113
|
toml_path = toml_path_maybe
|
|
114
114
|
if toml_path is not None:
|
|
115
115
|
print(f"📄 Reading config.toml @ {toml_path}")
|
|
116
|
-
config =
|
|
116
|
+
config = tomllib.loads(toml_path.read_text(encoding="utf-8"))
|
|
117
117
|
if "server" in config:
|
|
118
118
|
if "port" in config["server"]:
|
|
119
119
|
port = config["server"]["port"]
|
|
@@ -121,7 +121,7 @@ def route(args: FireJobArgs) -> None:
|
|
|
121
121
|
if repo_root is not None:
|
|
122
122
|
secrets_template_path = PathExtended.home().joinpath(f"dotfiles/creds/streamlit/{PathExtended(repo_root).name}/{choice_file.name}/secrets.toml")
|
|
123
123
|
if args.environment != "" and not secrets_path.exists() and secrets_template_path.exists():
|
|
124
|
-
secrets_template =
|
|
124
|
+
secrets_template = tomllib.loads(secrets_template_path.read_text(encoding="utf-8"))
|
|
125
125
|
if args.environment == "ip":
|
|
126
126
|
host_url = f"http://{local_ip_v4}:{port}/oauth2callback"
|
|
127
127
|
elif args.environment == "localhost":
|
|
@@ -134,7 +134,7 @@ def route(args: FireJobArgs) -> None:
|
|
|
134
134
|
secrets_template["auth"]["redirect_uri"] = host_url
|
|
135
135
|
secrets_template["auth"]["cookie_secret"] = randstr(35)
|
|
136
136
|
secrets_template["auth"]["auth0"]["redirect_uri"] = host_url
|
|
137
|
-
save_toml(obj=secrets_template, path=secrets_path)
|
|
137
|
+
# save_toml(obj=secrets_template, path=secrets_path)
|
|
138
138
|
except Exception as ex:
|
|
139
139
|
print(ex)
|
|
140
140
|
raise ex
|
|
@@ -3,8 +3,8 @@ from machineconfig.utils.schemas.layouts.layout_types import LayoutConfig, Layou
|
|
|
3
3
|
from typing import Optional, TYPE_CHECKING
|
|
4
4
|
from machineconfig.scripts.python.helpers.helpers4 import search_for_files_of_interest
|
|
5
5
|
from machineconfig.utils.options import choose_one_option
|
|
6
|
-
from machineconfig.utils.
|
|
7
|
-
from machineconfig.utils.
|
|
6
|
+
from machineconfig.utils.path_helper import match_file_name, sanitize_path
|
|
7
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from machineconfig.scripts.python.fire_jobs_args_helper import FireJobArgs
|
|
@@ -8,9 +8,9 @@ Currently, the only way to work around this is to predifine the host in ~/.ssh/c
|
|
|
8
8
|
|
|
9
9
|
import argparse
|
|
10
10
|
from machineconfig.utils.ssh import SSH
|
|
11
|
-
from machineconfig.utils.
|
|
11
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
12
12
|
from machineconfig.scripts.python.helpers.helpers2 import ES
|
|
13
|
-
from machineconfig.utils.
|
|
13
|
+
from machineconfig.utils.accessories import pprint
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def main():
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from machineconfig.utils.
|
|
2
|
+
from machineconfig.utils.io import read_ini, read_json
|
|
3
|
+
from machineconfig.utils.accessories import pprint
|
|
3
4
|
from typing import Optional
|
|
4
5
|
import os
|
|
5
6
|
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from machineconfig.scripts.python.helpers.cloud_helpers import Args, ArgsDefaults, absolute, find_cloud_config, get_secure_share_cloud_config
|
|
2
|
+
from machineconfig.utils.io import read_ini
|
|
2
3
|
from machineconfig.utils.source_of_truth import DEFAULTS_PATH
|
|
3
|
-
from machineconfig.utils.
|
|
4
|
+
from machineconfig.utils.accessories import pprint
|
|
4
5
|
from typing import Optional
|
|
5
6
|
from rich.console import Console
|
|
6
7
|
from rich.panel import Panel
|
|
@@ -103,7 +104,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
103
104
|
if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
|
|
104
105
|
assert ES not in target, f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised."
|
|
105
106
|
target_obj = absolute(target)
|
|
106
|
-
from machineconfig.utils.
|
|
107
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
107
108
|
|
|
108
109
|
remote_path = PathExtended(target_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
109
110
|
source = f"{cloud}:{remote_path.as_posix()}"
|
|
@@ -123,7 +124,7 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
123
124
|
if len(target_parts) > 1 and target_parts[1] == ES: # the target path is to be inferred from source.
|
|
124
125
|
assert ES not in source, "You can't use $ in both source and target. Cyclical inference dependency arised."
|
|
125
126
|
source_obj = absolute(source)
|
|
126
|
-
from machineconfig.utils.
|
|
127
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
127
128
|
|
|
128
129
|
remote_path = PathExtended(source_obj).get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
129
130
|
target = f"{cloud}:{remote_path.as_posix()}"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from machineconfig.utils.
|
|
1
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.terminal import Terminal
|
|
3
3
|
from machineconfig.scripts.python.get_zellij_cmd import get_zellij_cmd
|
|
4
4
|
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
5
|
-
from machineconfig.utils.
|
|
5
|
+
from machineconfig.utils.io import read_ini
|
|
6
6
|
from machineconfig.utils.code import write_shell_script_to_file
|
|
7
7
|
import platform
|
|
8
8
|
from rich.console import Console
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""NFS mounting script"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.
|
|
3
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
4
4
|
from machineconfig.utils.ssh import SSH
|
|
5
5
|
from machineconfig.utils.terminal import Terminal
|
|
6
6
|
from machineconfig.utils.options import display_options, choose_ssh_host
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from platform import system
|
|
4
4
|
from machineconfig.utils.ssh import SSH
|
|
5
5
|
from machineconfig.utils.terminal import Terminal
|
|
6
|
-
from machineconfig.utils.
|
|
6
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
7
7
|
|
|
8
8
|
from machineconfig.utils.options import choose_ssh_host
|
|
9
9
|
|
|
@@ -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
|
+
from machineconfig.utils.io import read_ini
|
|
8
9
|
from machineconfig.utils.source_of_truth import CONFIG_PATH, DEFAULTS_PATH
|
|
9
|
-
from machineconfig.utils.
|
|
10
|
-
from machineconfig.utils.
|
|
10
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
11
|
+
from machineconfig.utils.accessories import randstr
|
|
11
12
|
from machineconfig.scripts.python.repos_helper_update import update_repository
|
|
12
13
|
from machineconfig.scripts.python.repos_helper_record import main as record_repos
|
|
14
|
+
from machineconfig.scripts.python.repos_helper_clone import clone_repos
|
|
13
15
|
|
|
14
16
|
import argparse
|
|
15
17
|
from enum import Enum
|
|
@@ -119,7 +121,7 @@ def main():
|
|
|
119
121
|
elif args.clone or args.checkout or args.checkout_to_branch:
|
|
120
122
|
print("\n📥 Cloning or checking out repositories...")
|
|
121
123
|
print(">>>>>>>>> Cloning Repos")
|
|
122
|
-
if not repos_root.exists() or repos_root.name != "repos.json":
|
|
124
|
+
if not repos_root.exists() or repos_root.name != "repos.json":
|
|
123
125
|
repos_root = PathExtended(CONFIG_PATH).joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
|
|
124
126
|
if not repos_root.exists():
|
|
125
127
|
if args.cloud is None:
|
|
@@ -130,6 +132,7 @@ def main():
|
|
|
130
132
|
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."
|
|
131
133
|
repos_root.from_cloud(cloud=cloud, rel2home=True)
|
|
132
134
|
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."
|
|
135
|
+
clone_repos(spec_path=repos_root, preferred_remote=None, checkout_branch_flag=args.checkout_to_branch, checkout_commit_flag=args.checkout)
|
|
133
136
|
|
|
134
137
|
elif args.all or args.commit or args.pull or args.push:
|
|
135
138
|
print(f"\n🔄 Performing Git actions on repositories @ `{repos_root}`...")
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Optional, cast
|
|
4
|
+
|
|
5
|
+
from git import Repo as GitRepo
|
|
6
|
+
from git.exc import GitCommandError
|
|
7
|
+
from rich import print as pprint
|
|
8
|
+
from rich.progress import BarColumn, MofNCompleteColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
9
|
+
|
|
10
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
11
|
+
from machineconfig.utils.schemas.repos.repos_types import RepoRecordDict, RepoRecordFile, RepoRemote
|
|
12
|
+
from machineconfig.utils.io import read_json
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CloneStatus = Literal["cloned", "skipped", "failed"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def choose_remote(remotes: list[RepoRemote], preferred_remote: Optional[str]) -> Optional[RepoRemote]:
|
|
19
|
+
if preferred_remote is not None:
|
|
20
|
+
for remote in remotes:
|
|
21
|
+
if remote["name"] == preferred_remote:
|
|
22
|
+
return remote
|
|
23
|
+
for remote in remotes:
|
|
24
|
+
if remote["name"] == "origin":
|
|
25
|
+
return remote
|
|
26
|
+
return remotes[0] if len(remotes) > 0 else None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def ensure_destination(parent_dir: str, name: str) -> PathExtended:
|
|
30
|
+
parent_path = PathExtended(parent_dir).expanduser().absolute()
|
|
31
|
+
parent_path.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
return parent_path.joinpath(name)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def checkout_branch(repo: GitRepo, branch: str) -> bool:
|
|
36
|
+
if branch == "DETACHED":
|
|
37
|
+
return False
|
|
38
|
+
current_branch = repo.active_branch.name if not repo.head.is_detached else None
|
|
39
|
+
if current_branch == branch:
|
|
40
|
+
return False
|
|
41
|
+
repo.git.checkout(branch)
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def checkout_commit(repo: GitRepo, commit: str) -> bool:
|
|
46
|
+
if commit in {"", "UNKNOWN"}:
|
|
47
|
+
return False
|
|
48
|
+
current_commit = repo.head.commit.hexsha
|
|
49
|
+
if current_commit == commit:
|
|
50
|
+
return False
|
|
51
|
+
repo.git.checkout(commit)
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def clone_single_repo(repo_spec: RepoRecordDict, preferred_remote: Optional[str], checkout_branch_flag: bool, checkout_commit_flag: bool) -> tuple[CloneStatus, str]:
|
|
56
|
+
destination = ensure_destination(parent_dir=repo_spec["parentDir"], name=repo_spec["name"])
|
|
57
|
+
repo_path = destination.joinpath(".git")
|
|
58
|
+
remotes = repo_spec["remotes"]
|
|
59
|
+
if len(remotes) == 0:
|
|
60
|
+
return ("failed", f"No remotes configured for {destination}")
|
|
61
|
+
remote = choose_remote(remotes=remotes, preferred_remote=preferred_remote)
|
|
62
|
+
if remote is None:
|
|
63
|
+
return ("failed", f"No usable remote for {destination}")
|
|
64
|
+
repo = None
|
|
65
|
+
status: CloneStatus
|
|
66
|
+
message: str
|
|
67
|
+
if destination.exists() and repo_path.exists():
|
|
68
|
+
status = "skipped"
|
|
69
|
+
repo = GitRepo(str(destination))
|
|
70
|
+
message = f"Skipped cloning for {destination}; existing repository reused"
|
|
71
|
+
elif destination.exists() and not repo_path.exists():
|
|
72
|
+
return ("failed", f"Destination exists but is not a git repository: {destination}")
|
|
73
|
+
else:
|
|
74
|
+
try:
|
|
75
|
+
pprint(f"📥 Cloning {repo_spec['name']} from {remote['url']}")
|
|
76
|
+
repo = GitRepo.clone_from(url=remote["url"], to_path=str(destination))
|
|
77
|
+
status = "cloned"
|
|
78
|
+
message = f"Cloned {destination}"
|
|
79
|
+
except Exception as err: # noqa: BLE001
|
|
80
|
+
return ("failed", f"Clone failed for {destination}: {err}")
|
|
81
|
+
assert repo is not None
|
|
82
|
+
checkout_summary: list[str] = []
|
|
83
|
+
try:
|
|
84
|
+
if checkout_branch_flag:
|
|
85
|
+
if checkout_branch(repo=repo, branch=repo_spec["version"]["branch"]):
|
|
86
|
+
checkout_summary.append(f"branch {repo_spec['version']['branch']}")
|
|
87
|
+
if checkout_commit_flag:
|
|
88
|
+
if checkout_commit(repo=repo, commit=repo_spec["version"]["commit"]):
|
|
89
|
+
checkout_summary.append(f"commit {repo_spec['version']['commit'][:8]}")
|
|
90
|
+
except GitCommandError as err:
|
|
91
|
+
return ("failed", f"Checkout failed for {destination}: {err}")
|
|
92
|
+
if len(checkout_summary) > 0:
|
|
93
|
+
message = f"{message} | Checked out {' & '.join(checkout_summary)}"
|
|
94
|
+
return (status, message)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def clone_repos(spec_path: PathExtended, preferred_remote: Optional[str], checkout_branch_flag: bool, checkout_commit_flag: bool) -> list[tuple[CloneStatus, str]]:
|
|
98
|
+
data = cast(RepoRecordFile, read_json(path=spec_path))
|
|
99
|
+
repos = data["repos"]
|
|
100
|
+
results: list[tuple[CloneStatus, str]] = []
|
|
101
|
+
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), BarColumn(), MofNCompleteColumn(), TimeElapsedColumn()) as progress:
|
|
102
|
+
task_id = progress.add_task("Processing repositories...", total=len(repos))
|
|
103
|
+
for repo_spec in repos:
|
|
104
|
+
progress.update(task_id, description=f"Processing {repo_spec['name']}")
|
|
105
|
+
try:
|
|
106
|
+
result = clone_single_repo(repo_spec=repo_spec, preferred_remote=preferred_remote, checkout_branch_flag=checkout_branch_flag, checkout_commit_flag=checkout_commit_flag)
|
|
107
|
+
except Exception as err: # noqa: BLE001
|
|
108
|
+
result = ("failed", f"Unexpected error for {repo_spec['name']}: {err}")
|
|
109
|
+
results.append(result)
|
|
110
|
+
if result[0] == "failed":
|
|
111
|
+
pprint(f"❌ {result[1]}")
|
|
112
|
+
elif result[0] == "cloned":
|
|
113
|
+
pprint(f"✅ {result[1]}")
|
|
114
|
+
else:
|
|
115
|
+
pprint(f"⏭️ {result[1]}")
|
|
116
|
+
progress.update(task_id, advance=1)
|
|
117
|
+
success_count = len([status for status, _ in results if status == "cloned"])
|
|
118
|
+
skip_count = len([status for status, _ in results if status == "skipped"])
|
|
119
|
+
fail_count = len([status for status, _ in results if status == "failed"])
|
|
120
|
+
pprint(f"✅ Cloned: {success_count} | ⏭️ Skipped: {skip_count} | ❌ Failed: {fail_count}")
|
|
121
|
+
return results
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from machineconfig.utils.
|
|
1
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.schemas.repos.repos_types import GitVersionInfo, RepoRecordDict, RepoRemote
|
|
3
3
|
|
|
4
4
|
from machineconfig.utils.schemas.repos.repos_types import RepoRecordFile
|
|
5
5
|
from machineconfig.utils.source_of_truth import CONFIG_PATH
|
|
6
|
-
from machineconfig.utils.
|
|
6
|
+
from machineconfig.utils.io import save_json
|
|
7
7
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
@@ -4,7 +4,7 @@ slidev
|
|
|
4
4
|
|
|
5
5
|
from machineconfig.utils.source_of_truth import CONFIG_PATH
|
|
6
6
|
from machineconfig.utils.code import print_code
|
|
7
|
-
from machineconfig.utils.
|
|
7
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
8
8
|
from machineconfig.utils.terminal import Terminal
|
|
9
9
|
import subprocess
|
|
10
10
|
import platform
|
|
@@ -4,20 +4,21 @@ https://glitchbone.github.io/vscode-base16-term/#/3024
|
|
|
4
4
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from machineconfig.utils.
|
|
7
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
8
8
|
from machineconfig.utils.source_of_truth import LIBRARY_ROOT
|
|
9
9
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
10
10
|
import subprocess
|
|
11
11
|
from typing import Iterable
|
|
12
|
+
from machineconfig.utils.schemas.installer.installer_types import InstallerData
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
nerd_fonts = {
|
|
15
|
-
"
|
|
15
|
+
nerd_fonts: InstallerData = {
|
|
16
|
+
"appName": "Cascadia Code Nerd Font",
|
|
17
|
+
"repoURL": "https://github.com/ryanoasis/nerd-fonts",
|
|
16
18
|
"doc": "Nerd Fonts is a project that patches developer targeted fonts with a high number of glyphs (icons)",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"exe_name": "nerd_fonts",
|
|
19
|
+
"filenameTemplate": {"amd64": {"windows": "CascadiaCode.zip", "linux": "CascadiaCode.zip", "macos": ""}, "arm64": {"windows": "", "linux": "", "macos": ""}},
|
|
20
|
+
"stripVersion": False,
|
|
21
|
+
"exeName": "nerd_fonts",
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
|
|
@@ -64,7 +65,7 @@ def install_nerd_fonts() -> None:
|
|
|
64
65
|
return
|
|
65
66
|
print(f"🔍 Missing fonts detected: {', '.join(missing)}. Proceeding with installation...")
|
|
66
67
|
print("🔍 Downloading Nerd Fonts package...")
|
|
67
|
-
folder, _version_to_be_installed = Installer
|
|
68
|
+
folder, _version_to_be_installed = Installer(installer_data=nerd_fonts).download(version=None)
|
|
68
69
|
|
|
69
70
|
print("🧹 Cleaning up unnecessary files...")
|
|
70
71
|
[p.delete(sure=True) for p in folder.search("*Windows*")]
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Set Windows Terminal Settings"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.
|
|
4
|
-
from machineconfig.utils.
|
|
5
|
-
from machineconfig.utils.
|
|
3
|
+
from machineconfig.utils.accessories import randstr
|
|
4
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
5
|
+
from machineconfig.utils.io import read_json, save_json
|
|
6
6
|
import platform
|
|
7
7
|
|
|
8
8
|
# from uuid import uuid4
|
|
@@ -32,33 +32,6 @@ def split[T](iterable: list[T], every: int = 1, to: Optional[int] = None) -> lis
|
|
|
32
32
|
return list(res)
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
def read_ini(path: "Path", encoding: Optional[str] = None):
|
|
36
|
-
if not Path(path).exists() or Path(path).is_dir():
|
|
37
|
-
raise FileNotFoundError(f"File not found or is a directory: {path}")
|
|
38
|
-
import configparser
|
|
39
|
-
|
|
40
|
-
res = configparser.ConfigParser()
|
|
41
|
-
res.read(filenames=[str(path)], encoding=encoding)
|
|
42
|
-
return res
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def read_json(path: "Path", r: bool = False, **kwargs: Any) -> Any: # return could be list or dict etc
|
|
46
|
-
import json
|
|
47
|
-
try:
|
|
48
|
-
mydict = json.loads(Path(path).read_text(encoding="utf-8"), **kwargs)
|
|
49
|
-
except Exception:
|
|
50
|
-
import pyjson5
|
|
51
|
-
mydict = pyjson5.loads(Path(path).read_text(encoding="utf-8"), **kwargs) # file has C-style comments.
|
|
52
|
-
_ = r
|
|
53
|
-
return mydict
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def read_toml(path: "Path"):
|
|
57
|
-
import tomli
|
|
58
|
-
|
|
59
|
-
return tomli.loads(path.read_text(encoding="utf-8"))
|
|
60
|
-
|
|
61
|
-
|
|
62
35
|
def pprint(obj: dict[Any, Any], title: str) -> None:
|
|
63
36
|
from rich import inspect
|
|
64
37
|
|
|
@@ -84,3 +57,16 @@ def human_friendly_dict(d: dict[str, Any]) -> dict[str, Any]:
|
|
|
84
57
|
else:
|
|
85
58
|
result[k] = v
|
|
86
59
|
return result
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_repo_root(path: Path) -> Optional[Path]:
|
|
63
|
+
from git import Repo, InvalidGitRepositoryError
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
repo = Repo(str(path), search_parent_directories=True)
|
|
67
|
+
root = repo.working_tree_dir
|
|
68
|
+
if root is not None:
|
|
69
|
+
return Path(root)
|
|
70
|
+
except InvalidGitRepositoryError:
|
|
71
|
+
pass
|
|
72
|
+
return None
|
machineconfig/utils/code.py
CHANGED
|
@@ -5,9 +5,9 @@ from rich.console import Console
|
|
|
5
5
|
from rich.panel import Panel
|
|
6
6
|
from rich.syntax import Syntax
|
|
7
7
|
|
|
8
|
-
from machineconfig.utils.
|
|
8
|
+
from machineconfig.utils.accessories import randstr
|
|
9
9
|
from machineconfig.utils.ve import get_ve_activate_line
|
|
10
|
-
from machineconfig.utils.
|
|
10
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def get_shell_script_executing_python_file(python_file: str, func: Optional[str], ve_path: str, strict_execution: bool = True):
|
machineconfig/utils/installer.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"""package manager"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.installer_utils.installer_abc import LINUX_INSTALL_PATH
|
|
3
|
+
from machineconfig.utils.installer_utils.installer_abc import LINUX_INSTALL_PATH
|
|
4
4
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
5
|
+
from machineconfig.utils.schemas.installer.installer_types import APP_INSTALLER_CATEGORY, InstallerData, InstallerDataFiles
|
|
5
6
|
from rich.console import Console
|
|
6
7
|
from rich.panel import Panel # Added import
|
|
7
8
|
|
|
8
|
-
from machineconfig.utils.
|
|
9
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
9
10
|
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT
|
|
10
|
-
from machineconfig.utils.
|
|
11
|
+
from machineconfig.utils.io import read_json
|
|
11
12
|
|
|
12
|
-
# from dataclasses import dataclass
|
|
13
13
|
from typing import Any
|
|
14
14
|
import platform
|
|
15
15
|
from joblib import Parallel, delayed
|
|
@@ -22,21 +22,25 @@ def check_latest():
|
|
|
22
22
|
# installers += get_installers(system=platform.system(), dev=True)
|
|
23
23
|
installers_github = []
|
|
24
24
|
for inst__ in installers:
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
app_name = inst__.installer_data.get("appName", "unknown")
|
|
26
|
+
repo_url = inst__.installer_data.get("repoURL", "")
|
|
27
|
+
if "ntop" in app_name:
|
|
28
|
+
print(f"⏭️ Skipping {app_name} (ntop)")
|
|
27
29
|
continue
|
|
28
|
-
if "github" not in
|
|
29
|
-
print(f"⏭️ Skipping {
|
|
30
|
+
if "github" not in repo_url:
|
|
31
|
+
print(f"⏭️ Skipping {app_name} (not a GitHub release)")
|
|
30
32
|
continue
|
|
31
33
|
installers_github.append(inst__)
|
|
32
34
|
|
|
33
35
|
print(f"\n🔍 Checking {len(installers_github)} GitHub-based installers...\n")
|
|
34
36
|
|
|
35
37
|
def func(inst: Installer):
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
exe_name = inst.installer_data.get("exeName", "unknown")
|
|
39
|
+
repo_url = inst.installer_data.get("repoURL", "")
|
|
40
|
+
print(f"🔎 Checking {exe_name}...")
|
|
41
|
+
_release_url, version_to_be_installed = inst.get_github_release(repo_url=repo_url, version=None)
|
|
42
|
+
verdict, current_ver, new_ver = inst.check_if_installed_already(exe_name=exe_name, version=version_to_be_installed, use_cache=False)
|
|
43
|
+
return exe_name, verdict, current_ver, new_ver
|
|
40
44
|
|
|
41
45
|
print("\n⏳ Processing installers...\n")
|
|
42
46
|
res = [func(inst) for inst in installers_github]
|
|
@@ -90,20 +94,23 @@ def get_installed_cli_apps():
|
|
|
90
94
|
|
|
91
95
|
def get_installers(system: str, dev: bool) -> list[Installer]:
|
|
92
96
|
print(f"\n{'=' * 80}\n🔍 LOADING INSTALLER CONFIGURATIONS 🔍\n{'=' * 80}")
|
|
93
|
-
res_all =
|
|
97
|
+
res_all = get_all_installer_data_files(system=system)
|
|
94
98
|
if not dev:
|
|
95
99
|
print("ℹ️ Excluding development installers...")
|
|
96
100
|
del res_all["CUSTOM_DEV"]
|
|
97
101
|
del res_all["OS_SPECIFIC_DEV"]
|
|
98
102
|
del res_all["OS_GENERIC_DEV"]
|
|
99
|
-
res_final = {}
|
|
100
|
-
for _k, v in res_all.items():
|
|
101
|
-
res_final.update(v)
|
|
102
|
-
print(f"✅ Loaded {len(res_final)} installer configurations\n{'=' * 80}")
|
|
103
|
-
return [Installer.from_dict(d=vd, name=k) for k, vd in res_final.items()]
|
|
104
103
|
|
|
104
|
+
# Flatten the installer data from all categories
|
|
105
|
+
all_installers: list[InstallerData] = []
|
|
106
|
+
for _category, installer_data_files in res_all.items():
|
|
107
|
+
all_installers.extend(installer_data_files["installers"])
|
|
105
108
|
|
|
106
|
-
|
|
109
|
+
print(f"✅ Loaded {len(all_installers)} installer configurations\n{'=' * 80}")
|
|
110
|
+
return [Installer(installer_data=installer_data) for installer_data in all_installers]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_all_installer_data_files(system: str) -> dict[APP_INSTALLER_CATEGORY, InstallerDataFiles]:
|
|
107
114
|
print(f"\n{'=' * 80}\n📂 LOADING CONFIGURATION FILES 📂\n{'=' * 80}")
|
|
108
115
|
|
|
109
116
|
print(f"🔍 Importing OS-specific installers for {system}...")
|
|
@@ -122,18 +129,23 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
|
|
|
122
129
|
path_os_generic_dev = path_os_generic.joinpath("dev")
|
|
123
130
|
|
|
124
131
|
print("📂 Loading configuration files...")
|
|
125
|
-
res_final: dict[
|
|
132
|
+
res_final: dict[APP_INSTALLER_CATEGORY, InstallerDataFiles] = {}
|
|
133
|
+
|
|
126
134
|
print(f"""📄 Loading OS-specific config from: {path_os_specific.joinpath("config.json")}""")
|
|
127
|
-
|
|
135
|
+
os_specific_data = read_json(path=path_os_specific.joinpath("config.json"))
|
|
136
|
+
res_final["OS_SPECIFIC"] = InstallerDataFiles(os_specific_data)
|
|
128
137
|
|
|
129
138
|
print(f"""📄 Loading OS-generic config from: {path_os_generic.joinpath("config.json")}""")
|
|
130
|
-
|
|
139
|
+
os_generic_data = read_json(path=path_os_generic.joinpath("config.json"))
|
|
140
|
+
res_final["OS_GENERIC"] = InstallerDataFiles(os_generic_data)
|
|
131
141
|
|
|
132
142
|
print(f"""📄 Loading OS-specific dev config from: {path_os_specific_dev.joinpath("config.json")}""")
|
|
133
|
-
|
|
143
|
+
os_specific_dev_data = read_json(path=path_os_specific_dev.joinpath("config.json"))
|
|
144
|
+
res_final["OS_SPECIFIC_DEV"] = InstallerDataFiles(os_specific_dev_data)
|
|
134
145
|
|
|
135
146
|
print(f"""📄 Loading OS-generic dev config from: {path_os_generic_dev.joinpath("config.json")}""")
|
|
136
|
-
|
|
147
|
+
os_generic_dev_data = read_json(path=path_os_generic_dev.joinpath("config.json"))
|
|
148
|
+
res_final["OS_GENERIC_DEV"] = InstallerDataFiles(os_generic_dev_data)
|
|
137
149
|
|
|
138
150
|
path_custom_installer = path_os_generic.with_name("python_custom_installers")
|
|
139
151
|
path_custom_installer_dev = path_custom_installer.joinpath("dev")
|
|
@@ -141,29 +153,31 @@ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
|
|
|
141
153
|
print(f"🔍 Loading custom installers from: {path_custom_installer}")
|
|
142
154
|
import runpy
|
|
143
155
|
|
|
144
|
-
|
|
156
|
+
res_custom_installers: list[InstallerData] = []
|
|
145
157
|
for item in path_custom_installer.search("*.py", r=False, not_in=["__init__"]):
|
|
146
158
|
try:
|
|
147
159
|
print(f"📄 Loading custom installer: {item.name}")
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
installer_data: InstallerData = runpy.run_path(str(item), run_name=None)["config_dict"]
|
|
161
|
+
res_custom_installers.append(installer_data)
|
|
150
162
|
except Exception as ex:
|
|
151
163
|
print(f"❌ Failed to load {item}: {ex}")
|
|
152
164
|
|
|
153
165
|
print(f"🔍 Loading custom dev installers from: {path_custom_installer_dev}")
|
|
154
|
-
|
|
166
|
+
res_custom_dev_installers: list[InstallerData] = []
|
|
155
167
|
for item in path_custom_installer_dev.search("*.py", r=False, not_in=["__init__"]):
|
|
156
168
|
try:
|
|
157
169
|
print(f"📄 Loading custom dev installer: {item.name}")
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
installer_data: InstallerData = runpy.run_path(str(item), run_name=None)["config_dict"]
|
|
171
|
+
res_custom_dev_installers.append(installer_data)
|
|
160
172
|
except Exception as ex:
|
|
161
173
|
print(f"❌ Failed to load {item}: {ex}")
|
|
162
174
|
|
|
163
|
-
res_final["CUSTOM"] =
|
|
164
|
-
res_final["CUSTOM_DEV"] =
|
|
175
|
+
res_final["CUSTOM"] = InstallerDataFiles({"version": "1", "installers": res_custom_installers})
|
|
176
|
+
res_final["CUSTOM_DEV"] = InstallerDataFiles({"version": "1", "installers": res_custom_dev_installers})
|
|
165
177
|
|
|
166
|
-
print(
|
|
178
|
+
print(
|
|
179
|
+
f"✅ Configuration loading complete:\n - OS_SPECIFIC: {len(res_final['OS_SPECIFIC']['installers'])} items\n - OS_GENERIC: {len(res_final['OS_GENERIC']['installers'])} items\n - CUSTOM: {len(res_final['CUSTOM']['installers'])} items\n{'=' * 80}"
|
|
180
|
+
)
|
|
167
181
|
return res_final
|
|
168
182
|
|
|
169
183
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
from machineconfig.utils.
|
|
1
|
+
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.source_of_truth import WINDOWS_INSTALL_PATH, LINUX_INSTALL_PATH
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional
|
|
4
4
|
import subprocess
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
CATEGORY: TypeAlias = Literal["OS_SPECIFIC", "OS_GENERIC", "CUSTOM", "OS_SPECIFIC_DEV", "OS_GENERIC_DEV", "CUSTOM_DEV"]
|
|
8
|
-
|
|
9
|
-
|
|
10
7
|
def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optional[str] = None, delete: bool = True, rename_to: Optional[str] = None):
|
|
11
8
|
print(f"\n{'=' * 80}\n🔍 PROCESSING WINDOWS EXECUTABLE 🔍\n{'=' * 80}")
|
|
12
9
|
if exe_name is not None and ".exe" in exe_name:
|