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.

Files changed (72) hide show
  1. machineconfig/__init__.py +4 -2
  2. machineconfig/jobs/python/check_installations.py +38 -32
  3. machineconfig/jobs/python/create_bootable_media.py +4 -4
  4. machineconfig/jobs/python/create_zellij_template.py +3 -2
  5. machineconfig/jobs/python/python_cargo_build_share.py +14 -9
  6. machineconfig/jobs/python/python_ve_symlink.py +6 -6
  7. machineconfig/jobs/python_custom_installers/azuredatastudio.py +36 -0
  8. machineconfig/jobs/python_custom_installers/bypass_paywall.py +30 -0
  9. machineconfig/jobs/{python_linux_installers/dev → python_custom_installers}/docker_desktop.py +15 -4
  10. machineconfig/jobs/python_custom_installers/gh.py +53 -0
  11. machineconfig/jobs/python_custom_installers/goes.py +35 -0
  12. machineconfig/jobs/python_custom_installers/helix.py +43 -0
  13. machineconfig/jobs/python_custom_installers/lvim.py +48 -0
  14. machineconfig/jobs/python_custom_installers/ngrok.py +39 -0
  15. machineconfig/jobs/python_custom_installers/nvim.py +48 -0
  16. machineconfig/jobs/python_custom_installers/vscode.py +45 -0
  17. machineconfig/jobs/python_custom_installers/wezterm.py +41 -0
  18. machineconfig/profile/create.py +12 -7
  19. machineconfig/profile/shell.py +15 -13
  20. machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
  21. machineconfig/scripts/python/cloud_copy.py +18 -13
  22. machineconfig/scripts/python/cloud_mount.py +41 -15
  23. machineconfig/scripts/python/cloud_repo_sync.py +57 -31
  24. machineconfig/scripts/python/cloud_sync.py +13 -15
  25. machineconfig/scripts/python/croshell.py +19 -10
  26. machineconfig/scripts/python/devops.py +22 -6
  27. machineconfig/scripts/python/devops_add_identity.py +7 -6
  28. machineconfig/scripts/python/devops_add_ssh_key.py +10 -9
  29. machineconfig/scripts/python/devops_backup_retrieve.py +5 -5
  30. machineconfig/scripts/python/devops_devapps_install.py +24 -19
  31. machineconfig/scripts/python/devops_update_repos.py +5 -4
  32. machineconfig/scripts/python/dotfile.py +8 -4
  33. machineconfig/scripts/python/fire_jobs.py +165 -55
  34. machineconfig/scripts/python/ftpx.py +18 -8
  35. machineconfig/scripts/python/mount_nfs.py +13 -10
  36. machineconfig/scripts/python/mount_nw_drive.py +4 -3
  37. machineconfig/scripts/python/mount_ssh.py +8 -5
  38. machineconfig/scripts/python/repos.py +26 -21
  39. machineconfig/scripts/python/snapshot.py +2 -2
  40. machineconfig/scripts/python/start_slidev.py +104 -0
  41. machineconfig/scripts/python/start_terminals.py +1 -1
  42. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +20 -34
  43. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +11 -12
  44. machineconfig/utils/installer.py +177 -217
  45. machineconfig/utils/scheduling.py +1 -1
  46. machineconfig/utils/utils.py +107 -54
  47. machineconfig/utils/ve.py +120 -16
  48. machineconfig-1.9.dist-info/LICENSE +201 -0
  49. {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/METADATA +155 -140
  50. machineconfig-1.9.dist-info/RECORD +76 -0
  51. {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/WHEEL +1 -1
  52. machineconfig/jobs/python_generic_installers/archive/gopass.py +0 -18
  53. machineconfig/jobs/python_generic_installers/archive/nvim.py +0 -20
  54. machineconfig/jobs/python_generic_installers/archive/opencommit.py +0 -25
  55. machineconfig/jobs/python_generic_installers/archive/strongbox.py +0 -33
  56. machineconfig/jobs/python_generic_installers/dev/__init__.py +0 -0
  57. machineconfig/jobs/python_linux_installers/archive/__init__.py +0 -0
  58. machineconfig/jobs/python_linux_installers/archive/bandwhich.py +0 -14
  59. machineconfig/jobs/python_linux_installers/archive/ranger.py +0 -19
  60. machineconfig/jobs/python_linux_installers/dev/azure_data_studio.py +0 -21
  61. machineconfig/jobs/python_linux_installers/dev/bytehound.py +0 -20
  62. machineconfig/jobs/python_linux_installers/dev/nnn.py +0 -22
  63. machineconfig/jobs/python_windows_installers/archive/ntop.py +0 -21
  64. machineconfig/jobs/python_windows_installers/dev/bypass_paywall.py +0 -22
  65. machineconfig/jobs/python_windows_installers/dev/obs_background_removal_plugin.py +0 -22
  66. machineconfig/scripts/python/choose_ohmybash_theme.py +0 -31
  67. machineconfig/scripts/python/choose_ohmyposh_theme.py +0 -57
  68. machineconfig/utils/pandas_type.py +0 -37
  69. machineconfig/utils/to_exe.py +0 -7
  70. machineconfig-1.7.dist-info/RECORD +0 -81
  71. /machineconfig/jobs/{python_generic_installers/archive → python_custom_installers}/__init__.py +0 -0
  72. {machineconfig-1.7.dist-info → machineconfig-1.9.dist-info}/top_level.txt +0 -0
@@ -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
- from typing import Optional, Union, TypeVar
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 = display_options(msg="WHICH CLOUD?", options=list(remotes), default=remotes[0], fzf=True)
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[2:]) # exlcude /home/username
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
- print(f"Searching for {sub_string} in {P.cwd()}")
75
- search_results = P.cwd().absolute().search(f"*{sub_string}*.py", r=True)
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 = display_options(msg=f"Search results are ambiguous or non-existent", options=search_results.list, fzf=True, multi=False)
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: list[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "",
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: list[T], header: str = "", tail: str = "", prompt: str = "", msg: str = "",
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: list[T], header: str = "", tail: str = "", prompt: str = "",
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
- choice_key = fzf_prompt.prompt(choices=options, fzf_options=("--multi" if multi else "") + f' --prompt "{prompt.replace(nl, " ")}" ') # --border-label={msg.replace(nl, ' ')}")
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: choice_key = choice_key[0]
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=}, {choice_key=}")
121
- print(choice_key)
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
- assert default is not None, f"Default option not available!"
139
- choice_idx = options.index(default)
140
- choice_key = default
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
- choice_key = options[choice_idx]
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 options: # string input
147
- choice_idx = options.index(choice_key) # type: ignore #TODO: fix this
148
- choice_key = options[choice_idx]
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 options: # string input
153
- choice_idx = options.index(choice_key) # type: ignore #TODO: fix this
154
- choice_key = options[choice_idx]
155
- elif custom_input: return str(choice_string) # type: ignore #TODO: fix this
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}: {choice_key}", f"<<<<-------- CHOICE MADE")
158
- if multi: choice_key = [choice_key]
159
- return choice_key
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
- P(this).symlink_to(to_this, verbose=True, overwrite=True)
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
- # not yet used, consider, using it.
249
- import requests
250
- import json
251
- url = f"https://api.github.com/repos/{url.split('github.com/')[1]}/releases/latest"
252
- # Replace {owner} and {repo} with the actual owner and repository name
253
- response = requests.get(url, timeout=10)
254
- if response.status_code == 200:
255
- data = json.loads(response.text)
256
- latest_version = data["tag_name"]
257
- print("Latest release version:", latest_version)
258
- else: print("Error:", response.status_code)
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
- import subprocess
314
+
267
315
  try:
268
- subprocess.check_output([cmd, tool_name])
269
- return True
270
- except (subprocess.CalledProcessError, FileNotFoundError): return False
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
- tmp = list(set(List(P.get_env().PATH.search("python3*").reduce()).filter(lambda x: not x.is_symlink() and "-" not in x))) # type: ignore
54
- List(tmp).print()
55
- return [P(x) for x in tmp]
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 get_ps1_install_template(ve_name: str, req_root: str, py_version: str):
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, req_root: str, py_version: str = "3.11"):
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
+ """