machineconfig 1.7__py3-none-any.whl → 1.9__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/__init__.py +4 -2
- machineconfig/jobs/python/check_installations.py +38 -32
- machineconfig/jobs/python/create_bootable_media.py +4 -4
- machineconfig/jobs/python/create_zellij_template.py +3 -2
- machineconfig/jobs/python/python_cargo_build_share.py +14 -9
- machineconfig/jobs/python/python_ve_symlink.py +6 -6
- machineconfig/jobs/python_custom_installers/azuredatastudio.py +36 -0
- machineconfig/jobs/python_custom_installers/bypass_paywall.py +30 -0
- machineconfig/jobs/{python_linux_installers/dev → python_custom_installers}/docker_desktop.py +15 -4
- machineconfig/jobs/python_custom_installers/gh.py +53 -0
- machineconfig/jobs/python_custom_installers/goes.py +35 -0
- machineconfig/jobs/python_custom_installers/helix.py +43 -0
- machineconfig/jobs/python_custom_installers/lvim.py +48 -0
- machineconfig/jobs/python_custom_installers/ngrok.py +39 -0
- machineconfig/jobs/python_custom_installers/nvim.py +48 -0
- machineconfig/jobs/python_custom_installers/vscode.py +45 -0
- machineconfig/jobs/python_custom_installers/wezterm.py +41 -0
- machineconfig/profile/create.py +12 -7
- machineconfig/profile/shell.py +15 -13
- machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
- machineconfig/scripts/python/cloud_copy.py +18 -13
- machineconfig/scripts/python/cloud_mount.py +41 -15
- machineconfig/scripts/python/cloud_repo_sync.py +57 -31
- machineconfig/scripts/python/cloud_sync.py +13 -15
- machineconfig/scripts/python/croshell.py +19 -10
- machineconfig/scripts/python/devops.py +22 -6
- machineconfig/scripts/python/devops_add_identity.py +7 -6
- machineconfig/scripts/python/devops_add_ssh_key.py +10 -9
- machineconfig/scripts/python/devops_backup_retrieve.py +5 -5
- machineconfig/scripts/python/devops_devapps_install.py +24 -19
- machineconfig/scripts/python/devops_update_repos.py +5 -4
- machineconfig/scripts/python/dotfile.py +8 -4
- machineconfig/scripts/python/fire_jobs.py +165 -55
- machineconfig/scripts/python/ftpx.py +18 -8
- machineconfig/scripts/python/mount_nfs.py +13 -10
- machineconfig/scripts/python/mount_nw_drive.py +4 -3
- machineconfig/scripts/python/mount_ssh.py +8 -5
- machineconfig/scripts/python/repos.py +26 -21
- machineconfig/scripts/python/snapshot.py +2 -2
- machineconfig/scripts/python/start_slidev.py +104 -0
- machineconfig/scripts/python/start_terminals.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +20 -34
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +11 -12
- machineconfig/utils/installer.py +177 -217
- machineconfig/utils/scheduling.py +1 -1
- machineconfig/utils/utils.py +107 -54
- machineconfig/utils/ve.py +120 -16
- machineconfig-1.9.dist-info/LICENSE +201 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/METADATA +155 -140
- machineconfig-1.9.dist-info/RECORD +76 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/WHEEL +1 -1
- machineconfig/jobs/python_generic_installers/archive/gopass.py +0 -18
- machineconfig/jobs/python_generic_installers/archive/nvim.py +0 -20
- machineconfig/jobs/python_generic_installers/archive/opencommit.py +0 -25
- machineconfig/jobs/python_generic_installers/archive/strongbox.py +0 -33
- machineconfig/jobs/python_generic_installers/dev/__init__.py +0 -0
- machineconfig/jobs/python_linux_installers/archive/__init__.py +0 -0
- machineconfig/jobs/python_linux_installers/archive/bandwhich.py +0 -14
- machineconfig/jobs/python_linux_installers/archive/ranger.py +0 -19
- machineconfig/jobs/python_linux_installers/dev/azure_data_studio.py +0 -21
- machineconfig/jobs/python_linux_installers/dev/bytehound.py +0 -20
- machineconfig/jobs/python_linux_installers/dev/nnn.py +0 -22
- machineconfig/jobs/python_windows_installers/archive/ntop.py +0 -21
- machineconfig/jobs/python_windows_installers/dev/bypass_paywall.py +0 -22
- machineconfig/jobs/python_windows_installers/dev/obs_background_removal_plugin.py +0 -22
- machineconfig/scripts/python/choose_ohmybash_theme.py +0 -31
- machineconfig/scripts/python/choose_ohmyposh_theme.py +0 -57
- machineconfig/utils/pandas_type.py +0 -37
- machineconfig/utils/to_exe.py +0 -7
- machineconfig-1.7.dist-info/RECORD +0 -81
- /machineconfig/jobs/{python_generic_installers/archive → python_custom_installers}/__init__.py +0 -0
- {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/top_level.txt +0 -0
machineconfig/utils/utils.py
CHANGED
|
@@ -13,7 +13,8 @@ from rich.panel import Panel
|
|
|
13
13
|
from rich.console import Console
|
|
14
14
|
from rich.syntax import Syntax
|
|
15
15
|
import platform
|
|
16
|
-
|
|
16
|
+
import subprocess
|
|
17
|
+
from typing import Optional, Union, TypeVar, Iterable
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
LIBRARY_ROOT = P(machineconfig.__file__).resolve().parent # .replace(P.home().str.lower(), P.home().str)
|
|
@@ -35,21 +36,20 @@ def choose_cloud_interactively() -> str:
|
|
|
35
36
|
tmp = Terminal().run("rclone listremotes").op_if_successfull_or_default(strict_returcode=False)
|
|
36
37
|
# consider this: remotes = Read.ini(P.home().joinpath(".config/rclone/rclone.conf")).sections()
|
|
37
38
|
if isinstance(tmp, str):
|
|
38
|
-
remotes = L(tmp.splitlines()).apply(lambda x: x.replace(":", ""))
|
|
39
|
+
remotes: list[str] = L(tmp.splitlines()).apply(lambda x: x.replace(":", "")).list
|
|
39
40
|
|
|
40
41
|
else: raise ValueError(f"Got {tmp} from rclone listremotes")
|
|
41
42
|
if len(remotes) == 0:
|
|
42
43
|
raise RuntimeError(f"You don't have remotes. Configure your rclone first to get cloud services access.")
|
|
43
|
-
cloud =
|
|
44
|
-
assert isinstance(cloud, str)
|
|
44
|
+
cloud: str = choose_one_option(msg="WHICH CLOUD?", options=list(remotes), default=remotes[0], fzf=True)
|
|
45
45
|
return cloud
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def sanitize_path(a_path: P):
|
|
48
|
+
def sanitize_path(a_path: P) -> P:
|
|
49
49
|
path = P(a_path)
|
|
50
50
|
if path.as_posix().startswith("/home"):
|
|
51
51
|
if platform.system() == "Windows": # path copied from Linux to Windows
|
|
52
|
-
path = P.home().joinpath(*path.parts[
|
|
52
|
+
path = P.home().joinpath(*path.parts[3:]) # exlcude /home/username
|
|
53
53
|
assert path.exists(), f"File not found: {path}"
|
|
54
54
|
print(f"\n{'--' * 50}\n🔗 Mapped `{a_path}` ➡️ `{path}`\n{'--' * 50}\n")
|
|
55
55
|
elif platform.system() == "Linux" and P.home().as_posix() not in path.as_posix(): # copied from Linux to Linux with different username
|
|
@@ -69,24 +69,56 @@ def sanitize_path(a_path: P):
|
|
|
69
69
|
return path.expanduser().absolute()
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
def match_file_name(sub_string: str):
|
|
72
|
+
def match_file_name(sub_string: str, search_root: Optional[P] = None) -> P:
|
|
73
73
|
"""Look up current directory for file name that matches the passed substring."""
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
root = search_root if search_root is not None else P.cwd()
|
|
75
|
+
print(f"Searching for {sub_string} in {root}")
|
|
76
|
+
search_results = root.absolute().search(f"*{sub_string}*.py", r=True)
|
|
76
77
|
if len(search_results) == 1:
|
|
77
78
|
path_obj = search_results.list[0]
|
|
78
79
|
elif len(search_results) > 1:
|
|
79
|
-
choice =
|
|
80
|
-
assert not isinstance(choice, list)
|
|
80
|
+
choice = choose_one_option(msg=f"Search results are ambiguous or non-existent", options=search_results.list, fzf=True)
|
|
81
81
|
path_obj = P(choice)
|
|
82
82
|
else:
|
|
83
|
+
# let's do a final retry with sub_string.small()
|
|
84
|
+
sub_string_small = sub_string.lower()
|
|
85
|
+
if sub_string_small != sub_string:
|
|
86
|
+
return match_file_name(sub_string=sub_string_small)
|
|
87
|
+
from git.repo import Repo
|
|
88
|
+
from git.exc import InvalidGitRepositoryError
|
|
89
|
+
try:
|
|
90
|
+
repo = Repo(root, search_parent_directories=True)
|
|
91
|
+
repo_root_dir = P(repo.working_dir)
|
|
92
|
+
if repo_root_dir != root: # may be user is in a subdirectory of the repo root, try with root dir.
|
|
93
|
+
return match_file_name(sub_string=sub_string, search_root=repo_root_dir)
|
|
94
|
+
else:
|
|
95
|
+
root = repo_root_dir
|
|
96
|
+
except InvalidGitRepositoryError:
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
if check_tool_exists("fzf"):
|
|
100
|
+
try:
|
|
101
|
+
search_res = subprocess.run(f"cd '{root}'; fzf --filter={sub_string}", stdout=subprocess.PIPE, text=True, check=True, shell=True).stdout.split("\n")[:-1]
|
|
102
|
+
except subprocess.CalledProcessError as cpe:
|
|
103
|
+
print(f"Failed at fzf search with {sub_string} in {root}.\n{cpe}")
|
|
104
|
+
msg = f"\n{'--' * 50}\n💥 Path {sub_string} does not exist. No search results\n{'--' * 50}\n"
|
|
105
|
+
raise FileNotFoundError(msg) from cpe
|
|
106
|
+
if len(search_res) == 1: return root.joinpath(search_res[0])
|
|
107
|
+
else:
|
|
108
|
+
try:
|
|
109
|
+
res = subprocess.run(f"cd '{root}'; fzf --query={sub_string}", check=True, stdout=subprocess.PIPE, text=True, shell=True).stdout.strip()
|
|
110
|
+
except subprocess.CalledProcessError as cpe:
|
|
111
|
+
print(f"Failed at fzf search with {sub_string} in {root}. {cpe}")
|
|
112
|
+
msg = f"\n{'--' * 50}\n💥 Path {sub_string} does not exist. No search results\n{'--' * 50}\n"
|
|
113
|
+
raise FileNotFoundError(msg) from cpe
|
|
114
|
+
return root.joinpath(res)
|
|
83
115
|
msg = f"\n{'--' * 50}\n💥 Path {sub_string} does not exist. No search results\n{'--' * 50}\n"
|
|
84
116
|
raise FileNotFoundError(msg)
|
|
85
117
|
print(f"\n{'--' * 50}\n🔗 Matched `{sub_string}` ➡️ `{path_obj}`\n{'--' * 50}\n")
|
|
86
118
|
return path_obj
|
|
87
119
|
|
|
88
120
|
|
|
89
|
-
def choose_one_option(options:
|
|
121
|
+
def choose_one_option(options: Iterable[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "",
|
|
90
122
|
default: Optional[T] = None, fzf: bool = False, custom_input: bool = False) -> T:
|
|
91
123
|
choice_key = display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt,
|
|
92
124
|
default=default, fzf=fzf, multi=False, custom_input=custom_input)
|
|
@@ -94,7 +126,7 @@ def choose_one_option(options: list[T], header: str = "", tail: str = "", prompt
|
|
|
94
126
|
return choice_key
|
|
95
127
|
|
|
96
128
|
|
|
97
|
-
def choose_multiple_options(options:
|
|
129
|
+
def choose_multiple_options(options: Iterable[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "",
|
|
98
130
|
default: Optional[T] = None, custom_input: bool = False) -> list[T]:
|
|
99
131
|
choice_key = display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt,
|
|
100
132
|
default=default, fzf=True, multi=True,
|
|
@@ -103,23 +135,30 @@ def choose_multiple_options(options: list[T], header: str = "", tail: str = "",
|
|
|
103
135
|
return [choice_key]
|
|
104
136
|
|
|
105
137
|
|
|
106
|
-
def display_options(msg: str, options:
|
|
138
|
+
def display_options(msg: str, options: Iterable[T], header: str = "", tail: str = "", prompt: str = "",
|
|
107
139
|
default: Optional[T] = None, fzf: bool = False, multi: bool = False, custom_input: bool = False) -> Union[T, list[T]]:
|
|
108
140
|
# TODO: replace with https://github.com/tmbo/questionary # also see https://github.com/charmbracelet/gum
|
|
109
141
|
tool_name = "fzf"
|
|
142
|
+
options_strings: list[str] = [str(x) for x in options]
|
|
143
|
+
default_string = str(default) if default is not None else None
|
|
110
144
|
if fzf and check_tool_exists(tool_name):
|
|
111
145
|
install_n_import("pyfzf")
|
|
112
146
|
from pyfzf.pyfzf import FzfPrompt
|
|
113
147
|
fzf_prompt = FzfPrompt()
|
|
114
148
|
nl = "\n"
|
|
115
|
-
|
|
149
|
+
choice_string_multi: list[str] = fzf_prompt.prompt(choices=options_strings, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" ') # --border-label={msg.replace(nl, ' ')}")
|
|
116
150
|
# --border=rounded doens't work on older versions of fzf installed at Ubuntu 20.04
|
|
117
151
|
if not multi:
|
|
118
|
-
try:
|
|
152
|
+
try:
|
|
153
|
+
choice_one_string = choice_string_multi[0]
|
|
154
|
+
choice_idx = options_strings.index(choice_one_string)
|
|
155
|
+
return list(options)[choice_idx]
|
|
119
156
|
except IndexError as ie:
|
|
120
|
-
print(f"{options=}, {
|
|
121
|
-
print(
|
|
157
|
+
print(f"{options=}, {choice_string_multi=}")
|
|
158
|
+
print(choice_string_multi)
|
|
122
159
|
raise ie
|
|
160
|
+
choice_idx_s = [options_strings.index(x) for x in choice_string_multi]
|
|
161
|
+
return [list(options)[x] for x in choice_idx_s]
|
|
123
162
|
else:
|
|
124
163
|
console = Console()
|
|
125
164
|
if default is not None:
|
|
@@ -134,29 +173,34 @@ def display_options(msg: str, options: list[T], header: str = "", tail: str = ""
|
|
|
134
173
|
if default is not None:
|
|
135
174
|
choice_string = input(f"{prompt}\nEnter option number or hit enter for default choice: ")
|
|
136
175
|
else: choice_string = input(f"{prompt}\nEnter option number: ")
|
|
176
|
+
|
|
137
177
|
if choice_string == "":
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
178
|
+
if default_string is None:
|
|
179
|
+
print(f"Default option not available!")
|
|
180
|
+
return display_options(msg=msg, options=options, header=header, tail=tail, prompt=prompt, default=default, fzf=fzf, multi=multi, custom_input=custom_input)
|
|
181
|
+
choice_idx = options_strings.index(default_string)
|
|
182
|
+
assert default is not None, f"🧨 Default option not available!"
|
|
183
|
+
choice_one: T = default
|
|
141
184
|
else:
|
|
142
185
|
try:
|
|
143
|
-
choice_idx = int(choice_string)
|
|
144
|
-
|
|
186
|
+
choice_idx = int(choice_string, base=10)
|
|
187
|
+
choice_one = list(options)[choice_idx]
|
|
145
188
|
except IndexError as ie: # i.e. converting to integer was successful but indexing failed.
|
|
146
|
-
if choice_string in
|
|
147
|
-
choice_idx =
|
|
148
|
-
|
|
189
|
+
if choice_string in options_strings: # string input
|
|
190
|
+
choice_idx = options_strings.index(choice_one) # type: ignore #TODO: fix this
|
|
191
|
+
choice_one = list(options)[choice_idx]
|
|
149
192
|
elif custom_input: return str(choice_string) # type: ignore #TODO: fix this
|
|
150
193
|
else: raise ValueError(f"Unknown choice. {choice_string}") from ie
|
|
151
194
|
except TypeError as te: # int(choice_string) failed due to # either the number is invalid, or the input is custom.
|
|
152
|
-
if choice_string in
|
|
153
|
-
choice_idx =
|
|
154
|
-
|
|
155
|
-
elif custom_input:
|
|
195
|
+
if choice_string in options_strings: # string input
|
|
196
|
+
choice_idx = options_strings.index(choice_one) # type: ignore #TODO: fix this
|
|
197
|
+
choice_one = list(options)[choice_idx]
|
|
198
|
+
elif custom_input:
|
|
199
|
+
return choice_string # type: ignore #TODO: fix this
|
|
156
200
|
else: raise ValueError(f"Unknown choice. {choice_string}") from te
|
|
157
|
-
print(f"{choice_idx}: {
|
|
158
|
-
if multi:
|
|
159
|
-
return
|
|
201
|
+
print(f"{choice_idx}: {choice_one}", f"<<<<-------- CHOICE MADE")
|
|
202
|
+
if multi: return [choice_one]
|
|
203
|
+
return choice_one
|
|
160
204
|
|
|
161
205
|
|
|
162
206
|
def symlink(this: P, to_this: P, prioritize_to_this: bool = True):
|
|
@@ -180,7 +224,8 @@ def symlink(this: P, to_this: P, prioritize_to_this: bool = True):
|
|
|
180
224
|
if not to_this.exists(): to_this.touch() # we have to touch it (file) or create it (folder)
|
|
181
225
|
if platform.system() == "Windows": _ = install_n_import("win32api", "pywin32") # this is crucial for windows to pop up the concent window in case python was not run as admin.
|
|
182
226
|
try:
|
|
183
|
-
|
|
227
|
+
# print(f"Linking {this} ➡️ {to_this}")
|
|
228
|
+
P(this).symlink_to(target=to_this, verbose=True, overwrite=True)
|
|
184
229
|
except Exception as ex: print(f"Failed at linking {this} ➡️ {to_this}.\nReason: {ex}")
|
|
185
230
|
|
|
186
231
|
|
|
@@ -244,30 +289,39 @@ def print_code(code: str, lexer: str, desc: str = ""):
|
|
|
244
289
|
console.print(Panel(Syntax(code=code, lexer=lexer), title=desc), style="bold red")
|
|
245
290
|
|
|
246
291
|
|
|
247
|
-
def get_latest_version(url: str) -> None:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
292
|
+
# def get_latest_version(url: str) -> None:
|
|
293
|
+
# # not yet used, consider, using it.
|
|
294
|
+
# import requests
|
|
295
|
+
# import json
|
|
296
|
+
# url = f"https://api.github.com/repos/{url.split('github.com/')[1]}/releases/latest"
|
|
297
|
+
# # Replace {owner} and {repo} with the actual owner and repository name
|
|
298
|
+
# response = requests.get(url, timeout=10)
|
|
299
|
+
# if response.status_code == 200:
|
|
300
|
+
# data = json.loads(response.text)
|
|
301
|
+
# latest_version = data["tag_name"]
|
|
302
|
+
# print("Latest release version:", latest_version)
|
|
303
|
+
# else: print("Error:", response.status_code)
|
|
304
|
+
|
|
259
305
|
|
|
306
|
+
def check_tool_exists(tool_name: str, install_script: Optional[str] = None) -> bool:
|
|
307
|
+
"""This is the CLI equivalent of `install_n_import` function of crocodile. """
|
|
308
|
+
if platform.system() == "Windows":
|
|
309
|
+
tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
260
310
|
|
|
261
|
-
def check_tool_exists(tool_name: str) -> bool:
|
|
262
|
-
if platform.system() == "Windows": tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
263
311
|
if platform.system() == "Windows": cmd = "where.exe"
|
|
264
312
|
elif platform.system() == "Linux": cmd = "which"
|
|
265
313
|
else: raise NotImplementedError(f"platform {platform.system()} not implemented")
|
|
266
|
-
|
|
314
|
+
|
|
267
315
|
try:
|
|
268
|
-
subprocess.check_output([cmd, tool_name])
|
|
269
|
-
|
|
270
|
-
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
316
|
+
_tmp = subprocess.check_output([cmd, tool_name], stderr=subprocess.DEVNULL)
|
|
317
|
+
res: bool = True
|
|
318
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
319
|
+
res = False
|
|
320
|
+
if res is False and install_script is not None:
|
|
321
|
+
print(f"Installing {tool_name} ...")
|
|
322
|
+
Terminal().run(install_script, shell="powershell").print()
|
|
323
|
+
return check_tool_exists(tool_name=tool_name, install_script=None)
|
|
324
|
+
return res
|
|
271
325
|
|
|
272
326
|
|
|
273
327
|
def get_ssh_hosts() -> list[str]:
|
|
@@ -285,8 +339,7 @@ def check_dotfiles_version_is_beyond(commit_dtm: str, update: bool = False):
|
|
|
285
339
|
repo = Repo(path=dotfiles_path)
|
|
286
340
|
last_commit = repo.head.commit
|
|
287
341
|
dtm = last_commit.committed_datetime
|
|
288
|
-
# make it tz unaware
|
|
289
|
-
from datetime import datetime
|
|
342
|
+
from datetime import datetime # make it tz unaware
|
|
290
343
|
dtm = datetime(dtm.year, dtm.month, dtm.day, dtm.hour, dtm.minute, dtm.second)
|
|
291
344
|
res = dtm > datetime.fromisoformat(commit_dtm)
|
|
292
345
|
if res is False and update is True:
|
machineconfig/utils/ve.py
CHANGED
|
@@ -2,13 +2,28 @@
|
|
|
2
2
|
"""python and ve installation related utils
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from crocodile.file_management import P, Struct, modify_text, List
|
|
5
|
+
from crocodile.file_management import P, Struct, modify_text, List, Read, Save
|
|
6
6
|
from machineconfig.utils.utils import LIBRARY_ROOT
|
|
7
7
|
import platform
|
|
8
|
+
from dataclasses import dataclass
|
|
8
9
|
from typing import Optional, Literal
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
@dataclass
|
|
13
|
+
class VE_Specs:
|
|
14
|
+
ve_name: str
|
|
15
|
+
py_version: str
|
|
16
|
+
ipy_profile: str
|
|
17
|
+
os: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class VE_INI:
|
|
22
|
+
specs: VE_Specs
|
|
23
|
+
|
|
24
|
+
|
|
11
25
|
def get_ipython_profile(init_path: P):
|
|
26
|
+
"""Relies on .ipy_profile"""
|
|
12
27
|
a_path = init_path
|
|
13
28
|
ipy_profile: str = "default"
|
|
14
29
|
idx = len(a_path.parts)
|
|
@@ -25,6 +40,7 @@ def get_ipython_profile(init_path: P):
|
|
|
25
40
|
|
|
26
41
|
|
|
27
42
|
def get_ve_profile(init_path: P, strict: bool = False):
|
|
43
|
+
"""Relies on .ve_path"""
|
|
28
44
|
ve = ""
|
|
29
45
|
tmp = init_path
|
|
30
46
|
for _ in init_path.parents:
|
|
@@ -37,6 +53,23 @@ def get_ve_profile(init_path: P, strict: bool = False):
|
|
|
37
53
|
return ve
|
|
38
54
|
|
|
39
55
|
|
|
56
|
+
def get_ve_name_and_ipython_profile(init_path: P):
|
|
57
|
+
ve_name = "ve"
|
|
58
|
+
ipy_profile = "default"
|
|
59
|
+
tmp = init_path
|
|
60
|
+
for _ in init_path.parents:
|
|
61
|
+
if tmp.joinpath(".ve.ini").exists():
|
|
62
|
+
ini = Read.ini(tmp.joinpath(".ve.ini"))
|
|
63
|
+
ve_name = ini["specs"]["ve_name"]
|
|
64
|
+
# py_version = ini["specs"]["py_version"]
|
|
65
|
+
ipy_profile = ini["specs"]["ipy_profile"]
|
|
66
|
+
print(f"✅ Using Virtual Environment: {ve_name}")
|
|
67
|
+
print(f"✅ Using IPython profile: {ipy_profile}")
|
|
68
|
+
break
|
|
69
|
+
tmp = tmp.parent
|
|
70
|
+
return ve_name, ipy_profile
|
|
71
|
+
|
|
72
|
+
|
|
40
73
|
def get_current_ve():
|
|
41
74
|
import sys
|
|
42
75
|
path = P(sys.executable) # something like ~\\venvs\\ve\\Scripts\\python.exe'
|
|
@@ -47,12 +80,12 @@ def get_current_ve():
|
|
|
47
80
|
def get_installed_interpreters() -> list[P]:
|
|
48
81
|
system = platform.system()
|
|
49
82
|
if system == "Windows":
|
|
50
|
-
tmp: list[P] = P.get_env().PATH.search("python.exe").reduce().list[1:]
|
|
51
|
-
List(tmp).print()
|
|
83
|
+
tmp: list[P] = P.get_env().PATH.search("python.exe").reduce(func=lambda x, y: x+y).list[1:]
|
|
52
84
|
else:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
85
|
+
items: List[P] = P.get_env().PATH.search("python3*").reduce(lambda x, y: x+y)
|
|
86
|
+
tmp = list(set(items.filter(lambda x: not x.is_symlink() and "-" not in x)))
|
|
87
|
+
List(tmp).print()
|
|
88
|
+
return list(set([P(x) for x in tmp]))
|
|
56
89
|
|
|
57
90
|
|
|
58
91
|
def get_ve_specs(ve_path: P) -> dict[str, str]:
|
|
@@ -67,7 +100,7 @@ def get_ve_specs(ve_path: P) -> dict[str, str]:
|
|
|
67
100
|
|
|
68
101
|
def get_ve_install_script(ve_name: Optional[str] = None, py_version: Optional[str] = None, install_crocodile_and_machineconfig: Optional[bool] = None,
|
|
69
102
|
delete_if_exists: bool = True,
|
|
70
|
-
system: Optional[Literal["Windows", "Linux"]] = None):
|
|
103
|
+
system: Optional[Literal["Windows", "Linux"]] = None) -> str:
|
|
71
104
|
from rich.console import Console
|
|
72
105
|
if system is None:
|
|
73
106
|
system_: str = platform.system()
|
|
@@ -114,26 +147,97 @@ def get_ve_install_script(ve_name: Optional[str] = None, py_version: Optional[st
|
|
|
114
147
|
text = LIBRARY_ROOT.joinpath(f"setup_{system_.lower()}/repos.{'ps1' if system_ == 'Windows' else 'sh'}").read_text()
|
|
115
148
|
text = modify_text(txt_raw=text, txt_search="ve_name=", txt_alt=f"{variable_prefix}ve_name='{ve_name}'", replace_line=True)
|
|
116
149
|
scripts += text
|
|
150
|
+
|
|
151
|
+
# ve_ini_specs = VE_Specs(ve_name=ve_name, py_version=dotted_py_version, ipy_profile="default", os=system_)
|
|
152
|
+
# ve_ini = VE_INI(specs=ve_ini_specs)
|
|
117
153
|
return scripts
|
|
118
154
|
|
|
119
155
|
|
|
120
|
-
def
|
|
156
|
+
def get_ve_install_script_from_specs(repo_root: str, system: Literal["Windows", "Linux"]):
|
|
157
|
+
ini_file = P(repo_root).joinpath(".ve.ini")
|
|
158
|
+
assert ini_file.exists(), f"File {ini_file} does not exist."
|
|
159
|
+
ini = Read.ini(ini_file)
|
|
160
|
+
ve_name = ini["specs"]["ve_name"]
|
|
161
|
+
py_version = ini["specs"]["py_version"]
|
|
162
|
+
ipy_profile = ini["specs"]["ipy_profile"]
|
|
163
|
+
|
|
164
|
+
# for backward compatibility:
|
|
165
|
+
ini_file.with_name(".ve_path").write_text(f"~/venvs/{ve_name}")
|
|
166
|
+
ini_file.with_name(".ipy_profile").write_text(ipy_profile)
|
|
167
|
+
|
|
168
|
+
vscode_settings = P(repo_root).joinpath(".vscode/settings.json")
|
|
169
|
+
if vscode_settings.exists():
|
|
170
|
+
settings = Read.json(vscode_settings)
|
|
171
|
+
else:
|
|
172
|
+
settings = {}
|
|
173
|
+
if system == "Windows":
|
|
174
|
+
settings["python.defaultInterpreterPath"] = f"~/venvs/{ve_name}/Scripts/python.exe"
|
|
175
|
+
elif system == "Linux":
|
|
176
|
+
settings["python.defaultInterpreterPath"] = f"~/venvs/{ve_name}/bin/python"
|
|
177
|
+
pass
|
|
178
|
+
else:
|
|
179
|
+
raise NotImplementedError(f"System {system} not supported.")
|
|
180
|
+
Save.json(obj=settings, path=vscode_settings, indent=4)
|
|
181
|
+
|
|
182
|
+
subpath = "versions/init"
|
|
183
|
+
base_path = P(repo_root).joinpath(subpath).create()
|
|
184
|
+
if system == "Windows":
|
|
185
|
+
script = get_ps1_install_template(ve_name=ve_name, py_version=py_version)
|
|
186
|
+
base_path.joinpath("install_ve.ps1").write_text(script)
|
|
187
|
+
elif system == "Linux":
|
|
188
|
+
script = get_bash_install_template(ve_name=ve_name, py_version=py_version)
|
|
189
|
+
base_path.joinpath("install_ve.sh").write_text(script)
|
|
190
|
+
else:
|
|
191
|
+
raise NotImplementedError(f"System {system} not supported.")
|
|
192
|
+
|
|
193
|
+
base_path.joinpath("install_requirements.ps1").write_text(get_install_requirements_template(repo_root=P(repo_root), requirements_subpath=subpath))
|
|
194
|
+
base_path.joinpath("install_requirements.sh").write_text(get_install_requirements_template(repo_root=P(repo_root), requirements_subpath=subpath))
|
|
195
|
+
|
|
196
|
+
# vscode:
|
|
197
|
+
if not system == "Windows": # symlinks on windows require admin rights.
|
|
198
|
+
P(repo_root).joinpath(".venv").symlink_to(target=P.home().joinpath("venvs", ve_name), strict=False)
|
|
199
|
+
# set strict to False since ve doesn't exist yet.
|
|
200
|
+
|
|
201
|
+
return script
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_ps1_install_template(ve_name: str, py_version: str):
|
|
121
205
|
template = f"""
|
|
122
206
|
$ve_name = '{ve_name}'
|
|
123
207
|
$py_version = '{py_version}' # type: ignore
|
|
124
|
-
(Invoke-WebRequest bit.ly/cfgvewindows).Content | Invoke-Expression
|
|
208
|
+
(Invoke-WebRequest https://bit.ly/cfgvewindows).Content | Invoke-Expression
|
|
125
209
|
. $HOME/scripts/activate_ve $ve_name
|
|
126
|
-
cd {req_root}
|
|
127
|
-
pip install -r requirements_{platform.system().lower()}.txt
|
|
128
210
|
"""
|
|
129
211
|
return template
|
|
130
|
-
def get_bash_install_template(ve_name: str,
|
|
212
|
+
def get_bash_install_template(ve_name: str, py_version: str):
|
|
131
213
|
template = f"""
|
|
132
214
|
export ve_name='{ve_name}'
|
|
133
215
|
export py_version='{py_version}' # type: ignore
|
|
134
|
-
curl -L bit.ly/cfgvelinux | bash
|
|
135
|
-
. activate_ve $ve_name
|
|
136
|
-
cd {req_root}
|
|
137
|
-
pip install -r requirements_{platform.system().lower()}.txt
|
|
216
|
+
curl -L https://bit.ly/cfgvelinux | bash
|
|
217
|
+
. $HOME/scripts/activate_ve $ve_name
|
|
138
218
|
"""
|
|
139
219
|
return template
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def get_install_requirements_template(repo_root: P, requirements_subpath: str):
|
|
223
|
+
return f"""
|
|
224
|
+
# This is a template that is meant to be modified manually to install requirements.txt and editable packages.
|
|
225
|
+
# one can dispense with this and install libraries manually and on adhoc-basis and then use version_checkout utility.
|
|
226
|
+
|
|
227
|
+
set -e # exit on error, you don't want to install reqiurements in wrong environment.
|
|
228
|
+
cd $HOME/{repo_root.rel2home().as_posix()}
|
|
229
|
+
. $HOME/scripts/activate_ve
|
|
230
|
+
pip install -r {requirements_subpath}/requirements.txt
|
|
231
|
+
pip install -e .
|
|
232
|
+
|
|
233
|
+
# cd ~/code; git clone https://github.com/thisismygitrepo/crocodile.git --origin origin
|
|
234
|
+
# cd ~/code/crocodile; git remote set-url origin https://github.com/thisismygitrepo/crocodile.git
|
|
235
|
+
# cd ~/code/crocodile; git remote add origin https://github.com/thisismygitrepo/crocodile.git
|
|
236
|
+
# cd ~/code/crocodile; pip install -e .
|
|
237
|
+
|
|
238
|
+
# cd ~/code; git clone https://github.com/thisismygitrepo/machineconfig --origin origin
|
|
239
|
+
# cd ~/code/machineconfig; git remote set-url origin https://github.com/thisismygitrepo/machineconfig
|
|
240
|
+
# cd ~/code/machineconfig; git remote add origin https://github.com/thisismygitrepo/machineconfig
|
|
241
|
+
# cd ~/code/machineconfig; pip install -e .
|
|
242
|
+
|
|
243
|
+
"""
|