machineconfig 4.94__py3-none-any.whl → 4.97__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/ffile.py +4 -0
- machineconfig/jobs/installer/custom/gh.py +2 -15
- machineconfig/jobs/installer/custom_dev/alacritty.py +41 -26
- machineconfig/jobs/installer/custom_dev/brave.py +42 -28
- machineconfig/jobs/installer/custom_dev/bypass_paywall.py +30 -19
- machineconfig/jobs/installer/custom_dev/code.py +29 -20
- machineconfig/jobs/installer/custom_dev/espanso.py +64 -41
- machineconfig/jobs/installer/custom_dev/goes.py +41 -36
- machineconfig/jobs/installer/custom_dev/lvim.py +49 -33
- machineconfig/jobs/installer/custom_dev/nerdfont.py +71 -47
- machineconfig/jobs/installer/custom_dev/nerfont_windows_helper.py +25 -10
- machineconfig/jobs/installer/custom_dev/redis.py +51 -33
- machineconfig/jobs/installer/installer_data.json +17 -0
- machineconfig/jobs/installer/package_groups.py +271 -102
- machineconfig/jobs/python/python_cargo_build_share.py +0 -1
- machineconfig/jobs/python/python_ve_symlink.py +23 -15
- machineconfig/jobs/python/vscode/api.py +16 -8
- machineconfig/jobs/python/vscode/sync_code.py +42 -27
- machineconfig/scripts/python/cloud_repo_sync.py +8 -4
- machineconfig/scripts/python/devops_devapps_install.py +34 -26
- machineconfig/scripts/python/fire_jobs_args_helper.py +9 -0
- machineconfig/scripts/python/helpers/helpers4.py +2 -68
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +4 -2
- machineconfig/scripts/python/interactive.py +24 -23
- machineconfig/scripts/python/mount_nfs.py +3 -6
- machineconfig/scripts/python/mount_ssh.py +3 -4
- machineconfig/scripts/python/sessions.py +10 -9
- machineconfig/scripts/python/start_slidev.py +14 -4
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +36 -22
- machineconfig/utils/code.py +11 -2
- machineconfig/utils/installer.py +15 -9
- machineconfig/utils/path_extended.py +49 -7
- machineconfig/utils/ssh.py +22 -12
- machineconfig/utils/terminal.py +66 -92
- {machineconfig-4.94.dist-info → machineconfig-4.97.dist-info}/METADATA +1 -1
- {machineconfig-4.94.dist-info → machineconfig-4.97.dist-info}/RECORD +39 -38
- {machineconfig-4.94.dist-info → machineconfig-4.97.dist-info}/WHEEL +0 -0
- {machineconfig-4.94.dist-info → machineconfig-4.97.dist-info}/entry_points.txt +0 -0
- {machineconfig-4.94.dist-info → machineconfig-4.97.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,7 @@ from rich.console import Console
|
|
|
6
6
|
from rich.panel import Panel
|
|
7
7
|
from rich.table import Table
|
|
8
8
|
from typing import Optional, cast, get_args
|
|
9
|
-
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUPS
|
|
9
|
+
from machineconfig.jobs.installer.package_groups import PACKAGE_GROUPS, PACKAGE_GROUP2NAMES
|
|
10
10
|
|
|
11
11
|
console = Console()
|
|
12
12
|
|
|
@@ -63,7 +63,7 @@ def main(
|
|
|
63
63
|
if which is not None:
|
|
64
64
|
return install_clis(clis_names=[x.strip() for x in which.split(",") if x.strip() != ""])
|
|
65
65
|
if group is not None:
|
|
66
|
-
return
|
|
66
|
+
return install_group(package_group=group)
|
|
67
67
|
if interactive:
|
|
68
68
|
return install_interactively()
|
|
69
69
|
typer.echo("❌ You must provide either --which, --group, or --interactive/-ia option.")
|
|
@@ -78,21 +78,32 @@ def install_interactively():
|
|
|
78
78
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
79
79
|
from machineconfig.utils.installer import get_installers
|
|
80
80
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
81
|
-
installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=
|
|
81
|
+
installers = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=None)
|
|
82
82
|
installer_options = []
|
|
83
83
|
for x in installers:
|
|
84
84
|
installer_options.append(Installer(installer_data=x).get_description())
|
|
85
|
-
|
|
86
|
-
options
|
|
85
|
+
|
|
86
|
+
# Build category options and maintain a mapping from display text to actual category name
|
|
87
|
+
category_display_to_name: dict[str, str] = {}
|
|
88
|
+
for group_name, group_values in PACKAGE_GROUP2NAMES.items():
|
|
89
|
+
display = f"📦 {group_name:<20}" + " -- " + f"{'|'.join(group_values):<60}"
|
|
90
|
+
category_display_to_name[display] = group_name
|
|
91
|
+
|
|
92
|
+
options_system = get_installers_system_groups()
|
|
93
|
+
for item in options_system:
|
|
94
|
+
display = f"📦 {item['appName']:<20} -- {item['doc']:<60}"
|
|
95
|
+
category_display_to_name[display] = item['appName']
|
|
96
|
+
|
|
97
|
+
options = list(category_display_to_name.keys()) + ["─" * 50] + installer_options
|
|
87
98
|
program_names = choose_from_options(multi=True, msg="Categories are prefixed with 📦", options=options, header="🚀 CHOOSE DEV APP OR CATEGORY", default="📦 essentials", fzf=True)
|
|
88
99
|
installation_messages: list[str] = []
|
|
89
100
|
for _an_idx, a_program_name in enumerate(program_names):
|
|
90
101
|
if a_program_name.startswith("─"): # 50 dashes separator
|
|
91
102
|
continue
|
|
92
103
|
if a_program_name.startswith("📦 "):
|
|
93
|
-
category_name = a_program_name
|
|
94
|
-
if category_name
|
|
95
|
-
|
|
104
|
+
category_name = category_display_to_name.get(a_program_name)
|
|
105
|
+
if category_name:
|
|
106
|
+
install_group(package_group=cast(PACKAGE_GROUPS, category_name))
|
|
96
107
|
else:
|
|
97
108
|
installer_idx = installer_options.index(a_program_name)
|
|
98
109
|
an_installer_data = installers[installer_idx]
|
|
@@ -103,27 +114,24 @@ def install_interactively():
|
|
|
103
114
|
console.print(panel)
|
|
104
115
|
|
|
105
116
|
|
|
106
|
-
def
|
|
117
|
+
def install_group(package_group: PACKAGE_GROUPS):
|
|
107
118
|
panel = Panel(f"[bold yellow]Installing programs from category: [green]{package_group}[/green][/bold yellow]", title="[bold blue]📦 Category Installation[/bold blue]", border_style="blue", padding=(1, 2))
|
|
108
119
|
console.print(panel)
|
|
109
|
-
from machineconfig.utils.installer import get_installers,
|
|
120
|
+
from machineconfig.utils.installer import get_installers, install_bulk
|
|
110
121
|
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
program = an_item["fileNamePattern"][get_normalized_arch()][get_os_name()]
|
|
125
|
-
if program is not None:
|
|
126
|
-
run_script(program)
|
|
122
|
+
if package_group in PACKAGE_GROUP2NAMES:
|
|
123
|
+
installers_ = get_installers(os=get_os_name(), arch=get_normalized_arch(), which_cats=[package_group])
|
|
124
|
+
install_bulk(installers_data=installers_)
|
|
125
|
+
else:
|
|
126
|
+
options_system = get_installers_system_groups()
|
|
127
|
+
from machineconfig.utils.schemas.installer.installer_types import get_normalized_arch, get_os_name
|
|
128
|
+
from machineconfig.utils.code import run_script
|
|
129
|
+
for an_item in options_system:
|
|
130
|
+
if an_item["appName"] == package_group:
|
|
131
|
+
program = an_item["fileNamePattern"][get_normalized_arch()][get_os_name()]
|
|
132
|
+
if program is not None:
|
|
133
|
+
run_script(program)
|
|
134
|
+
break
|
|
127
135
|
|
|
128
136
|
|
|
129
137
|
def choose_from_system_package_groups(options_system: dict[str, tuple[str, str]]) -> str:
|
|
@@ -44,6 +44,10 @@ def extract_kwargs(args: FireJobArgs) -> dict[str, object]:
|
|
|
44
44
|
|
|
45
45
|
# Look for Fire-style arguments in sys.argv
|
|
46
46
|
for arg in sys.argv:
|
|
47
|
+
# Skip the -- separator
|
|
48
|
+
if arg == '--':
|
|
49
|
+
continue
|
|
50
|
+
|
|
47
51
|
# Match patterns like --key=value or --key value (but we'll focus on --key=value)
|
|
48
52
|
if arg.startswith('--') and '=' in arg:
|
|
49
53
|
key, value = arg[2:].split('=', 1) # Remove -- prefix and split on first =
|
|
@@ -53,6 +57,11 @@ def extract_kwargs(args: FireJobArgs) -> dict[str, object]:
|
|
|
53
57
|
elif arg.startswith('--') and '=' not in arg:
|
|
54
58
|
# Handle boolean flags like --debug
|
|
55
59
|
key = arg[2:] # Remove -- prefix
|
|
60
|
+
|
|
61
|
+
# Skip empty key (this would happen if someone just used '--')
|
|
62
|
+
if not key:
|
|
63
|
+
continue
|
|
64
|
+
|
|
56
65
|
# Check if next argument exists and doesn't start with --
|
|
57
66
|
arg_index = sys.argv.index(arg)
|
|
58
67
|
if arg_index + 1 < len(sys.argv) and not sys.argv[arg_index + 1].startswith('--'):
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from typing import Any, Callable, Optional
|
|
2
|
-
import inspect
|
|
3
|
-
import os
|
|
4
1
|
|
|
2
|
+
from typing import Optional
|
|
3
|
+
import os
|
|
5
4
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
6
5
|
|
|
7
6
|
|
|
@@ -21,19 +20,6 @@ def search_for_files_of_interest(path_obj: PathExtended):
|
|
|
21
20
|
return files
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
def convert_kwargs_to_fire_kwargs_str(kwargs: dict[str, Any]) -> str:
|
|
25
|
-
# https://google.github.io/python-fire/guide/
|
|
26
|
-
# https://github.com/google/python-fire/blob/master/docs/guide.md#argument-parsing
|
|
27
|
-
if not kwargs: # empty dict
|
|
28
|
-
kwargs_str = ""
|
|
29
|
-
else:
|
|
30
|
-
# For fire module, all keyword arguments should be passed as --key value pairs
|
|
31
|
-
tmp_list: list[str] = []
|
|
32
|
-
for k, v in kwargs.items():
|
|
33
|
-
tmp_list.append(f"--{k} {v}")
|
|
34
|
-
kwargs_str = " " + " ".join(tmp_list) + " "
|
|
35
|
-
return kwargs_str
|
|
36
|
-
|
|
37
23
|
|
|
38
24
|
def parse_pyfile(file_path: str):
|
|
39
25
|
print(f"🔍 Loading {file_path} ...")
|
|
@@ -84,58 +70,6 @@ def parse_pyfile(file_path: str):
|
|
|
84
70
|
return options, func_args
|
|
85
71
|
|
|
86
72
|
|
|
87
|
-
def interactively_run_function(func: Callable[[Any], Any]):
|
|
88
|
-
sig = inspect.signature(func)
|
|
89
|
-
params = list(sig.parameters.values())
|
|
90
|
-
args = []
|
|
91
|
-
kwargs = {}
|
|
92
|
-
for param in params:
|
|
93
|
-
if param.annotation is not inspect.Parameter.empty:
|
|
94
|
-
hint = f" ({param.annotation.__name__})"
|
|
95
|
-
else:
|
|
96
|
-
hint = ""
|
|
97
|
-
if param.default is not inspect.Parameter.empty:
|
|
98
|
-
default = param.default
|
|
99
|
-
value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) (default = {default}) : ")
|
|
100
|
-
if value == "":
|
|
101
|
-
value = default
|
|
102
|
-
else:
|
|
103
|
-
value = input(f"Please enter a value for argument `{param.name}` (type = {hint}) : ")
|
|
104
|
-
try:
|
|
105
|
-
if param.annotation is not inspect.Parameter.empty:
|
|
106
|
-
value = param.annotation
|
|
107
|
-
except (TypeError, ValueError) as err:
|
|
108
|
-
raise ValueError(f"Invalid input: {value} is not of type {param.annotation}") from err
|
|
109
|
-
if param.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
110
|
-
kwargs[param.name] = value
|
|
111
|
-
else:
|
|
112
|
-
args.append((param.name, value))
|
|
113
|
-
args_to_kwargs = dict(args)
|
|
114
|
-
return args_to_kwargs, kwargs
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def get_attrs_recursively(obj: Any):
|
|
118
|
-
if hasattr(obj, "__dict__"):
|
|
119
|
-
res = {}
|
|
120
|
-
for k, v in obj.__dict__.items():
|
|
121
|
-
res[k] = get_attrs_recursively(v)
|
|
122
|
-
return res
|
|
123
|
-
return obj
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# def run_on_remote(func_file: str, args: argparse.Namespace):
|
|
127
|
-
# host = choose_ssh_host(multi=False)
|
|
128
|
-
# assert isinstance(host, str), f"host must be a string. Got {type(host)}"
|
|
129
|
-
# from machineconfig.cluster.remote_machine import RemoteMachine, RemoteMachineConfig
|
|
130
|
-
# config = RemoteMachineConfig(copy_repo=True, update_repo=False, update_essential_repos=True,
|
|
131
|
-
# notify_upon_completion=True, ssh_params=dict(host=host),
|
|
132
|
-
# # to_email=None, email_config_name='enaut',
|
|
133
|
-
# data=[],
|
|
134
|
-
# ipython=False, interactive=args.interactive, pdb=False, pudb=args.debug, wrap_in_try_except=False,
|
|
135
|
-
# transfer_method="sftp")
|
|
136
|
-
# m = RemoteMachine(func=func_file, func_kwargs=None, config=config)
|
|
137
|
-
# m.run()
|
|
138
|
-
|
|
139
73
|
|
|
140
74
|
def find_repo_root_path(start_path: str) -> Optional[str]:
|
|
141
75
|
root_files = ["setup.py", "pyproject.toml", ".git"]
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
|
-
from machineconfig.utils.terminal import
|
|
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
|
|
5
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
|
+
import subprocess
|
|
8
9
|
from rich.console import Console
|
|
9
10
|
from rich.panel import Panel
|
|
10
11
|
|
|
@@ -89,7 +90,8 @@ sudo chmod 700 $HOME/.ssh
|
|
|
89
90
|
sudo chmod +x $HOME/dotfiles/scripts/linux -R
|
|
90
91
|
"""
|
|
91
92
|
shell_path = write_shell_script_to_file(shell_script=script)
|
|
92
|
-
|
|
93
|
+
completed = subprocess.run(f". {shell_path}", capture_output=True, check=False, text=True, shell=True)
|
|
94
|
+
Response.from_completed_process(completed).capture().print()
|
|
93
95
|
|
|
94
96
|
console.print(Panel("✅ Dotfiles successfully fetched and installed", title="[bold green]Dotfiles[/bold green]", border_style="green"))
|
|
95
97
|
|
|
@@ -17,7 +17,6 @@ for better user experience with checkbox selections.
|
|
|
17
17
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
import subprocess
|
|
21
20
|
import sys
|
|
22
21
|
from pathlib import Path
|
|
23
22
|
from platform import system
|
|
@@ -28,21 +27,12 @@ from questionary import Choice
|
|
|
28
27
|
from rich.console import Console
|
|
29
28
|
from rich.panel import Panel
|
|
30
29
|
from rich.text import Text
|
|
31
|
-
|
|
30
|
+
from machineconfig.utils.code import run_script as run_command
|
|
32
31
|
|
|
33
32
|
_ = cast
|
|
34
33
|
console = Console()
|
|
35
34
|
|
|
36
35
|
|
|
37
|
-
def run_command(command: str, description: str) -> bool:
|
|
38
|
-
"""Execute a shell command and return success status."""
|
|
39
|
-
console.print(f"\n🔧 {description}", style="bold cyan")
|
|
40
|
-
try:
|
|
41
|
-
result = subprocess.run(command, shell=True, check=True, capture_output=False)
|
|
42
|
-
return result.returncode == 0
|
|
43
|
-
except subprocess.CalledProcessError as e:
|
|
44
|
-
console.print(f"❌ Error executing command: {e}", style="bold red")
|
|
45
|
-
return False
|
|
46
36
|
def display_header() -> None:
|
|
47
37
|
"""Display the script header."""
|
|
48
38
|
header_text = Text("MACHINE CONFIGURATION", style="bold magenta")
|
|
@@ -111,6 +101,7 @@ def get_installation_choices() -> list[str]:
|
|
|
111
101
|
Choice(value="TerminalEyeCandy", title="🎨 Install ASCII Art Libraries - Terminal visualization tools", checked=False),
|
|
112
102
|
Choice(value="install_repos", title="🐍 Install Repos - Set up Python environment and repositories permanently.", checked=False),
|
|
113
103
|
Choice(value="install_ssh_server", title="🔒 Install SSH Server - Set up remote access", checked=False),
|
|
104
|
+
Choice(value="install_shell_profile", title="🐚 Configure Shell Profile - Source machineconfig shell initialization", checked=False),
|
|
114
105
|
Choice(value="create_symlinks", title="🔗 Create Symlinks - Set up configuration symlinks (finish dotfiles transfer first)", checked=False),
|
|
115
106
|
Choice(value="retrieve_repositories", title="📚 Retrieve Repositories - Clone repositories to ~/code", checked=False),
|
|
116
107
|
Choice(value="retrieve_data", title="💾 Retrieve Data - Backup restoration", checked=False),
|
|
@@ -124,36 +115,35 @@ def get_installation_choices() -> list[str]:
|
|
|
124
115
|
|
|
125
116
|
def execute_installations(selected_options: list[str]) -> None:
|
|
126
117
|
"""Execute the selected installation options."""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
if system() == "Windows":
|
|
119
|
+
run_command("$HOME/.local/bin/uv.exe self update")
|
|
120
|
+
else:
|
|
121
|
+
run_command("$HOME/.local/bin/uv self update")
|
|
131
122
|
for maybe_a_group in selected_options:
|
|
132
123
|
if maybe_a_group in ("ESSENTIAL", "DEV", "ESSENTIAL_SYSTEM", "DEV_SYSTEM", "TerminalEyeCandy"):
|
|
133
124
|
console.print(Panel("⚡ [bold bright_yellow]CLI APPLICATIONS[/bold bright_yellow]\n[italic]Command-line tools installation[/italic]", border_style="bright_yellow"))
|
|
134
125
|
console.print("🔧 Installing CLI applications", style="bold cyan")
|
|
135
126
|
try:
|
|
136
127
|
from machineconfig.scripts.python.devops_devapps_install import main as devops_devapps_install_main
|
|
137
|
-
# maybe_a_group = cast(PA, maybe_a_group)
|
|
138
128
|
devops_devapps_install_main(group=maybe_a_group) # type: ignore
|
|
139
129
|
console.print("✅ CLI applications installed successfully", style="bold green")
|
|
140
130
|
except Exception as e:
|
|
141
131
|
console.print(f"❌ Error installing CLI applications: {e}", style="bold red")
|
|
142
|
-
run_command(". $HOME/.bashrc"
|
|
132
|
+
run_command(". $HOME/.bashrc")
|
|
143
133
|
|
|
144
134
|
if "upgrade_system" in selected_options:
|
|
145
135
|
if system() == "Windows":
|
|
146
136
|
console.print("❌ System upgrade is not applicable on Windows via this script.", style="bold red")
|
|
147
137
|
elif system() == "Linux":
|
|
148
138
|
console.print(Panel("🔄 [bold magenta]SYSTEM UPDATE[/bold magenta]\n[italic]Package management[/italic]", border_style="magenta"))
|
|
149
|
-
run_command("sudo nala upgrade -y"
|
|
139
|
+
run_command("sudo nala upgrade -y")
|
|
150
140
|
else:
|
|
151
141
|
console.print(f"❌ System upgrade not supported on {system()}.", style="bold red")
|
|
152
142
|
if "install_repos" in selected_options:
|
|
153
143
|
console.print(Panel("🐍 [bold green]PYTHON ENVIRONMENT[/bold green]\n[italic]Virtual environment setup[/italic]", border_style="green"))
|
|
154
144
|
from machineconfig import setup_linux as module
|
|
155
145
|
script = Path(module.__file__).parent / "repos.sh"
|
|
156
|
-
run_command(f"bash {script}"
|
|
146
|
+
run_command(f"bash {script}")
|
|
157
147
|
|
|
158
148
|
if "install_ssh_server" in selected_options:
|
|
159
149
|
console.print(Panel("🔒 [bold red]SSH SERVER[/bold red]\n[italic]Remote access setup[/italic]", border_style="red"))
|
|
@@ -162,9 +152,20 @@ def execute_installations(selected_options: list[str]) -> None:
|
|
|
162
152
|
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
|
|
163
153
|
Start-Service sshd
|
|
164
154
|
Set-Service -Name sshd -StartupType 'Automatic'"""
|
|
165
|
-
run_command(f'powershell -Command "{powershell_script}"'
|
|
155
|
+
run_command(f'powershell -Command "{powershell_script}"')
|
|
166
156
|
else:
|
|
167
|
-
run_command("sudo nala install openssh-server -y"
|
|
157
|
+
run_command("sudo nala install openssh-server -y")
|
|
158
|
+
|
|
159
|
+
if "install_shell_profile" in selected_options:
|
|
160
|
+
console.print(Panel("🐚 [bold green]SHELL PROFILE[/bold green]\n[italic]Shell configuration setup[/italic]", border_style="green"))
|
|
161
|
+
console.print("🔧 Configuring shell profile", style="bold cyan")
|
|
162
|
+
try:
|
|
163
|
+
from machineconfig.profile.create import main_profile
|
|
164
|
+
|
|
165
|
+
main_profile()
|
|
166
|
+
console.print("✅ Shell profile configured successfully", style="bold green")
|
|
167
|
+
except Exception as e:
|
|
168
|
+
console.print(f"❌ Error configuring shell profile: {e}", style="bold red")
|
|
168
169
|
|
|
169
170
|
if "create_symlinks" in selected_options:
|
|
170
171
|
display_dotfiles_instructions()
|
|
@@ -178,8 +179,8 @@ Set-Service -Name sshd -StartupType 'Automatic'"""
|
|
|
178
179
|
console.print("✅ Symlinks created successfully", style="bold green")
|
|
179
180
|
except Exception as e:
|
|
180
181
|
console.print(f"❌ Error creating symlinks: {e}", style="bold red")
|
|
181
|
-
run_command("sudo chmod 600 $HOME/.ssh/*"
|
|
182
|
-
run_command("sudo chmod 700 $HOME/.ssh"
|
|
182
|
+
run_command("sudo chmod 600 $HOME/.ssh/*")
|
|
183
|
+
run_command("sudo chmod 700 $HOME/.ssh")
|
|
183
184
|
else:
|
|
184
185
|
console.print("⏭️ Skipping symlink creation - finish dotfiles transfer first", style="yellow")
|
|
185
186
|
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
4
4
|
from machineconfig.utils.ssh import SSH
|
|
5
|
-
from machineconfig.utils.terminal import Terminal
|
|
6
5
|
from machineconfig.utils.options import choose_from_options, choose_ssh_host
|
|
7
6
|
|
|
8
7
|
import platform
|
|
8
|
+
import subprocess
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def main():
|
|
@@ -49,15 +49,14 @@ share_path={share_path}
|
|
|
49
49
|
local_mount_point={local_mount_point}
|
|
50
50
|
"""
|
|
51
51
|
# PROGRAM_PATH.write_text(txt)
|
|
52
|
-
import subprocess
|
|
53
|
-
|
|
54
52
|
subprocess.run(txt, shell=True, check=True)
|
|
55
53
|
|
|
56
54
|
print("✅ Mount paths prepared successfully!\n")
|
|
57
55
|
|
|
58
56
|
elif platform.system() == "Windows":
|
|
59
57
|
print("\n🔍 Checking existing drives...")
|
|
60
|
-
|
|
58
|
+
completed = subprocess.run(["powershell", "-Command", "Get-PSDrive -PSProvider 'FileSystem'"], capture_output=True, check=False, text=True)
|
|
59
|
+
print((completed.stdout or "").strip())
|
|
61
60
|
driver_letter = input(r"🖥️ Choose driver letter (e.g., Z:\\) [Avoid already used ones]: ") or "Z:\\"
|
|
62
61
|
txt = f"""
|
|
63
62
|
$server = "{remote_server}"
|
|
@@ -65,8 +64,6 @@ $sharePath = "{share_path}"
|
|
|
65
64
|
$driveLetter = "{driver_letter}"
|
|
66
65
|
"""
|
|
67
66
|
# PROGRAM_PATH.write_text(txt)
|
|
68
|
-
import subprocess
|
|
69
|
-
|
|
70
67
|
subprocess.run(txt, shell=True, check=True)
|
|
71
68
|
print("✅ Drive letter selected and configuration saved!\n")
|
|
72
69
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Mount a remote SSHFS share on a local directory"""
|
|
2
2
|
|
|
3
3
|
from platform import system
|
|
4
|
+
import subprocess
|
|
4
5
|
from machineconfig.utils.ssh import SSH
|
|
5
|
-
from machineconfig.utils.terminal import Terminal
|
|
6
6
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
7
7
|
|
|
8
8
|
from machineconfig.utils.options import choose_ssh_host
|
|
@@ -27,7 +27,8 @@ def main():
|
|
|
27
27
|
|
|
28
28
|
if system() == "Windows":
|
|
29
29
|
print("\n🔍 Checking existing drives...")
|
|
30
|
-
|
|
30
|
+
completed = subprocess.run(["powershell", "-Command", "net use"], capture_output=True, check=False, text=True)
|
|
31
|
+
print((completed.stdout or "").strip())
|
|
31
32
|
driver_letter = input(r"🖥️ Choose driver letter (e.g., Z:\\) [Avoid already used ones]: ") or "Z:\\"
|
|
32
33
|
else:
|
|
33
34
|
driver_letter = None
|
|
@@ -53,8 +54,6 @@ fusermount -u /mnt/dbhdd
|
|
|
53
54
|
raise ValueError(f"❌ Not implemented for this system: {system()}")
|
|
54
55
|
|
|
55
56
|
# PROGRAM_PATH.write_text(txt, encoding="utf-8")
|
|
56
|
-
import subprocess
|
|
57
|
-
|
|
58
57
|
subprocess.run(txt, shell=True, check=True)
|
|
59
58
|
print("✅ Configuration saved successfully!\n")
|
|
60
59
|
|
|
@@ -50,7 +50,7 @@ def select_layout(layouts_json_file: Path, selected_layouts_names: Optional[list
|
|
|
50
50
|
return layouts_chosen
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def
|
|
53
|
+
def find_layout_file(layout_path: str, ) -> Path:
|
|
54
54
|
from machineconfig.utils.path_extended import PathExtended
|
|
55
55
|
from machineconfig.scripts.python.helpers.helpers4 import search_for_files_of_interest
|
|
56
56
|
from machineconfig.utils.options import choose_from_options
|
|
@@ -66,23 +66,24 @@ def handle_layout_args(layout_path: str, select_layouts: Optional[str], select_i
|
|
|
66
66
|
choice_file = PathExtended(choice_file)
|
|
67
67
|
else:
|
|
68
68
|
choice_file = path_obj
|
|
69
|
-
|
|
70
|
-
return selected_layouts
|
|
69
|
+
return choice_file
|
|
71
70
|
|
|
72
71
|
|
|
73
72
|
def launch(layout_path: str = typer.Argument(..., help="Path to the layout.json file"),
|
|
74
73
|
max_tabs: int = typer.Option(10, help="A Sanity checker that throws an error if any layout exceeds the maximum number of tabs to launch."),
|
|
75
74
|
max_layouts: int = typer.Option(10, help="A Sanity checker that throws an error if the total number of layouts exceeds this number."),
|
|
76
75
|
sleep_inbetween: float = typer.Option(1.0, help="Sleep time in seconds between launching layouts"),
|
|
77
|
-
monitor: bool = typer.Option(False, help="Monitor the layout sessions for completion"),
|
|
78
|
-
parallel: bool = typer.Option(False, help="Launch multiple layouts in parallel"),
|
|
79
|
-
kill_upon_completion: bool = typer.Option(False, help="Kill session(s) upon completion (only relevant if monitor flag is set)"),
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
monitor: bool = typer.Option(False, "--monitor", "-m", help="Monitor the layout sessions for completion"),
|
|
77
|
+
parallel: bool = typer.Option(False, "--parallel", "-p", help="Launch multiple layouts in parallel"),
|
|
78
|
+
kill_upon_completion: bool = typer.Option(False, "--kill-upon-completion", "-k", help="Kill session(s) upon completion (only relevant if monitor flag is set)"),
|
|
79
|
+
choose: Optional[str] = typer.Option(None, "--choose", "-c", help="Comma separated names of layouts to be selected from the layout file passed"),
|
|
80
|
+
choose_interactively: bool = typer.Option(False, "--choose-interactively", "-ia", help="Select layouts interactively")
|
|
82
81
|
):
|
|
83
82
|
"""
|
|
83
|
+
Launch terminal sessions based on a layout configuration file.
|
|
84
84
|
"""
|
|
85
|
-
|
|
85
|
+
layout_path_resolved = find_layout_file(layout_path=layout_path)
|
|
86
|
+
layouts_selected = select_layout(layouts_json_file=layout_path_resolved, selected_layouts_names=choose.split(",") if choose else None, select_interactively=choose_interactively)
|
|
86
87
|
|
|
87
88
|
# ============= Basic sanity checks =============
|
|
88
89
|
if len(layouts_selected) > max_layouts:
|
|
@@ -5,7 +5,7 @@ slidev
|
|
|
5
5
|
from machineconfig.utils.source_of_truth import CONFIG_PATH
|
|
6
6
|
from machineconfig.utils.code import print_code
|
|
7
7
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
8
|
-
from machineconfig.utils.terminal import
|
|
8
|
+
from machineconfig.utils.terminal import Response
|
|
9
9
|
from typing import Annotated, Optional
|
|
10
10
|
import typer
|
|
11
11
|
import subprocess
|
|
@@ -16,10 +16,20 @@ PORT_DEFAULT = 3030
|
|
|
16
16
|
SLIDEV_REPO = PathExtended(CONFIG_PATH).joinpath(".cache/slidev")
|
|
17
17
|
if not SLIDEV_REPO.joinpath("components").exists():
|
|
18
18
|
print("📦 Initializing Slidev repository...")
|
|
19
|
-
|
|
19
|
+
subprocess.run(f"cd {SLIDEV_REPO.parent};npm init slidev@latest", check=False, shell=True, text=True)
|
|
20
20
|
print("✅ Slidev repository initialized successfully!\n")
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _execute_with_shell(command: str) -> Response:
|
|
24
|
+
if platform.system() == "Windows":
|
|
25
|
+
completed = subprocess.run(["powershell", "-Command", command], capture_output=True, check=False, text=True)
|
|
26
|
+
else:
|
|
27
|
+
completed = subprocess.run(command, capture_output=True, check=False, text=True, shell=True)
|
|
28
|
+
response = Response.from_completed_process(completed)
|
|
29
|
+
response.print()
|
|
30
|
+
return response
|
|
31
|
+
|
|
32
|
+
|
|
23
33
|
def jupyter_to_markdown(file: PathExtended):
|
|
24
34
|
op_dir = file.parent.joinpath("presentation")
|
|
25
35
|
print("📝 Converting Jupyter notebook to markdown...")
|
|
@@ -35,9 +45,9 @@ def jupyter_to_markdown(file: PathExtended):
|
|
|
35
45
|
# for key, value in resources['outputs'].items():
|
|
36
46
|
|
|
37
47
|
cmd = f"jupyter nbconvert --to markdown --no-prompt --no-input --output-dir {op_dir} --output slides_raw.md {file}"
|
|
38
|
-
|
|
48
|
+
_execute_with_shell(cmd)
|
|
39
49
|
cmd = f"jupyter nbconvert --to html --no-prompt --no-input --output-dir {op_dir} {file}"
|
|
40
|
-
|
|
50
|
+
_execute_with_shell(cmd)
|
|
41
51
|
|
|
42
52
|
op_file = op_dir.joinpath("slides_raw.md")
|
|
43
53
|
slide_separator = "\n\n---\n\n"
|
|
@@ -8,6 +8,7 @@ import platform
|
|
|
8
8
|
# from uuid import uuid4
|
|
9
9
|
import os
|
|
10
10
|
from typing import Any
|
|
11
|
+
from rich import box
|
|
11
12
|
from rich.console import Console
|
|
12
13
|
from rich.panel import Panel
|
|
13
14
|
|
|
@@ -28,34 +29,41 @@ system = platform.system() # Linux or Windows
|
|
|
28
29
|
assert system == "Windows", "This script is only for Windows."
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
def render_banner(message: str, title: str, border_style: str, box_style: box.Box) -> None:
|
|
33
|
+
console.print(Panel.fit(message, title=title, border_style=border_style, box=box_style, padding=(1, 4)))
|
|
34
|
+
|
|
35
|
+
|
|
31
36
|
class TerminalSettings(object):
|
|
32
37
|
def __init__(self):
|
|
33
38
|
# Grabbing Terminal Settings file:
|
|
34
|
-
print(
|
|
39
|
+
console.print()
|
|
40
|
+
render_banner("🔍 INITIALIZING TERMINAL SETTINGS 🔍", "Windows Terminal", "cyan", box.DOUBLE)
|
|
41
|
+
console.print()
|
|
35
42
|
tmp = os.getenv("LOCALAPPDATA")
|
|
36
43
|
if not isinstance(tmp, str):
|
|
37
|
-
print("❌ ERROR: Could not find LOCALAPPDATA environment variable!")
|
|
44
|
+
console.print("❌ ERROR: Could not find LOCALAPPDATA environment variable!")
|
|
38
45
|
raise ValueError("Could not find LOCALAPPDATA environment variable.")
|
|
39
46
|
self.path = PathExtended(tmp).joinpath(r"Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json")
|
|
40
47
|
backup_name = f".orig_{randstr()}"
|
|
41
|
-
print(f"📝 Creating backup of original settings as {backup_name}...")
|
|
48
|
+
console.print(f"📝 Creating backup of original settings as {backup_name}...")
|
|
42
49
|
self.path.copy(append=backup_name)
|
|
43
|
-
print(f"📂 Loading Windows Terminal settings from: {self.path}")
|
|
50
|
+
console.print(f"📂 Loading Windows Terminal settings from: {self.path}")
|
|
44
51
|
self.dat: dict[str, Any] = read_json(self.path)
|
|
45
52
|
# Use a plain Python list for profiles
|
|
46
53
|
self.profs = list(self.dat["profiles"]["list"])
|
|
47
|
-
console
|
|
48
|
-
console.print(Panel(f"✅ Successfully loaded {len(self.profs)} profiles", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue"))
|
|
54
|
+
console.print(Panel(f"✅ Successfully loaded {len(self.profs)} profiles", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue", box=box.ROUNDED))
|
|
49
55
|
|
|
50
56
|
def save_terminal_settings(self):
|
|
51
|
-
print(
|
|
57
|
+
console.print()
|
|
58
|
+
console.print(f"💾 Saving terminal settings to: {self.path}")
|
|
52
59
|
self.dat["profiles"]["list"] = list(self.profs)
|
|
53
60
|
save_json(obj=self.dat, path=self.path, indent=5)
|
|
54
|
-
console.print(Panel("✅ Settings saved successfully!", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue"))
|
|
61
|
+
console.print(Panel("✅ Settings saved successfully!", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue", box=box.ROUNDED))
|
|
55
62
|
|
|
56
63
|
# ========================= Terminal Settings =========================================
|
|
57
64
|
def update_default_settings(self):
|
|
58
|
-
print(
|
|
65
|
+
console.print()
|
|
66
|
+
console.print("⚙️ Updating default terminal settings...")
|
|
59
67
|
# Changing start up settings:
|
|
60
68
|
self.dat["startOnUserLogin"] = True
|
|
61
69
|
self.dat["launchMode"] = "fullscreen"
|
|
@@ -64,12 +72,13 @@ class TerminalSettings(object):
|
|
|
64
72
|
self.dat["copyOnSelect"] = True
|
|
65
73
|
self.dat["profiles"]["defaults"]["padding"] = "0"
|
|
66
74
|
self.dat["profiles"]["defaults"]["useAcrylic"] = False
|
|
67
|
-
console.print(Panel("✅ Default settings updated", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue"))
|
|
75
|
+
console.print(Panel("✅ Default settings updated", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue", box=box.ROUNDED))
|
|
68
76
|
|
|
69
77
|
# 1- Customizing Powershell========================================================
|
|
70
78
|
# as opposed to Windows Powershell
|
|
71
79
|
def customize_powershell(self, nerd_font: bool = True):
|
|
72
|
-
print(
|
|
80
|
+
console.print()
|
|
81
|
+
console.print("🛠️ Customizing PowerShell profile...")
|
|
73
82
|
pwsh: dict[str, Any] = dict(
|
|
74
83
|
name="PowerShell",
|
|
75
84
|
commandline="pwsh",
|
|
@@ -79,45 +88,50 @@ class TerminalSettings(object):
|
|
|
79
88
|
startingDirectory="%USERPROFILE%", # "%USERPROFILE%", # None: inherent from parent process.
|
|
80
89
|
)
|
|
81
90
|
if nerd_font:
|
|
82
|
-
print("🔤 Setting PowerShell font to CaskaydiaCove Nerd Font...")
|
|
91
|
+
console.print("🔤 Setting PowerShell font to CaskaydiaCove Nerd Font...")
|
|
83
92
|
pwsh["font"] = dict(face="CaskaydiaCove Nerd Font") # because oh-my-posh uses glyphs from this font.
|
|
84
93
|
|
|
85
94
|
for idx, item in enumerate(self.profs):
|
|
86
95
|
if item["name"] == "PowerShell":
|
|
87
96
|
self.profs[idx].update(pwsh)
|
|
88
|
-
console.print(Panel("✅ PowerShell profile customized successfully", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue"))
|
|
97
|
+
console.print(Panel("✅ PowerShell profile customized successfully", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue", box=box.ROUNDED))
|
|
89
98
|
break
|
|
90
99
|
else:
|
|
91
|
-
console.print(Panel("❌ Couldn't customize PowerShell because profile not found, try to install it first.", title="[bold red]Terminal Settings[/bold red]", border_style="red"))
|
|
100
|
+
console.print(Panel("❌ Couldn't customize PowerShell because profile not found, try to install it first.", title="[bold red]Terminal Settings[/bold red]", border_style="red", box=box.ROUNDED))
|
|
92
101
|
|
|
93
102
|
def make_powershell_default_profile(self):
|
|
94
|
-
print(
|
|
103
|
+
console.print()
|
|
104
|
+
console.print("🌟 Setting PowerShell as the default profile...")
|
|
95
105
|
for profile in self.profs:
|
|
96
106
|
if profile["name"] == "PowerShell":
|
|
97
107
|
self.dat["defaultProfile"] = profile["guid"]
|
|
98
|
-
console.print(Panel("✅ PowerShell is now the default profile!", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue"))
|
|
108
|
+
console.print(Panel("✅ PowerShell is now the default profile!", title="[bold blue]Terminal Settings[/bold blue]", border_style="blue", box=box.ROUNDED))
|
|
99
109
|
break
|
|
100
110
|
else:
|
|
101
|
-
console.print(Panel("❌ PowerShell profile was not found in the list of profiles and therefore was not made the default.", title="[bold red]Terminal Settings[/bold red]", border_style="red"))
|
|
111
|
+
console.print(Panel("❌ PowerShell profile was not found in the list of profiles and therefore was not made the default.", title="[bold red]Terminal Settings[/bold red]", border_style="red", box=box.ROUNDED))
|
|
102
112
|
|
|
103
113
|
|
|
104
114
|
def main():
|
|
105
|
-
print(
|
|
115
|
+
console.print()
|
|
116
|
+
render_banner("🖥️ WINDOWS TERMINAL SETUP 🖥️", "Windows Terminal", "cyan", box.DOUBLE)
|
|
117
|
+
console.print()
|
|
106
118
|
shell = {"powershell": "pwsh.exe", "Windows Powershell": "powershell.exe"}["powershell"].split(".exe", maxsplit=1)[0]
|
|
107
119
|
if shell == "pwsh":
|
|
108
|
-
print("🚀 Starting Windows Terminal configuration with PowerShell...")
|
|
120
|
+
console.print("🚀 Starting Windows Terminal configuration with PowerShell...")
|
|
109
121
|
ts = TerminalSettings()
|
|
110
122
|
ts.update_default_settings()
|
|
111
123
|
ts.customize_powershell(nerd_font=True)
|
|
112
124
|
ts.make_powershell_default_profile()
|
|
113
|
-
print("⌨️ Adding keyboard shortcut for pane zoom (ctrl+shift+z)...")
|
|
125
|
+
console.print("⌨️ Adding keyboard shortcut for pane zoom (ctrl+shift+z)...")
|
|
114
126
|
ts.dat["actions"].append({"command": "togglePaneZoom", "keys": "ctrl+shift+z"})
|
|
115
127
|
|
|
116
128
|
ts.save_terminal_settings()
|
|
117
|
-
print(
|
|
129
|
+
console.print()
|
|
130
|
+
render_banner("✨ WINDOWS TERMINAL SETUP COMPLETE ✨", "Windows Terminal", "green", box.DOUBLE)
|
|
131
|
+
console.print()
|
|
118
132
|
else:
|
|
119
133
|
error_msg = "❌ ERROR: Only PowerShell is supported, not Windows PowerShell!"
|
|
120
|
-
print(error_msg)
|
|
134
|
+
console.print(error_msg)
|
|
121
135
|
raise NotImplementedError(error_msg)
|
|
122
136
|
|
|
123
137
|
|