machineconfig 1.9__py3-none-any.whl → 1.92__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 (55) hide show
  1. machineconfig/__init__.py +2 -4
  2. machineconfig/jobs/python/check_installations.py +14 -6
  3. machineconfig/jobs/python/checkout_version.py +27 -32
  4. machineconfig/jobs/python/create_bootable_media.py +1 -1
  5. machineconfig/jobs/python/python_cargo_build_share.py +2 -2
  6. machineconfig/jobs/python/tasks.py +2 -2
  7. machineconfig/jobs/python_custom_installers/{helix.py → hx.py} +21 -6
  8. machineconfig/profile/create.py +23 -21
  9. machineconfig/profile/create_hardlinks.py +101 -0
  10. machineconfig/profile/shell.py +10 -7
  11. machineconfig/scripts/python/cloud_copy.py +19 -16
  12. machineconfig/scripts/python/cloud_repo_sync.py +94 -47
  13. machineconfig/scripts/python/cloud_sync.py +78 -61
  14. machineconfig/scripts/python/croshell.py +6 -6
  15. machineconfig/scripts/python/devops.py +22 -22
  16. machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
  17. machineconfig/scripts/python/devops_backup_retrieve.py +19 -10
  18. machineconfig/scripts/python/devops_devapps_install.py +80 -62
  19. machineconfig/scripts/python/devops_update_repos.py +5 -5
  20. machineconfig/scripts/python/dotfile.py +4 -4
  21. machineconfig/scripts/python/fire_jobs.py +115 -63
  22. machineconfig/scripts/python/gh_models.py +55 -0
  23. machineconfig/scripts/python/mount_nfs.py +1 -1
  24. machineconfig/scripts/python/mount_nw_drive.py +3 -3
  25. machineconfig/scripts/python/mount_ssh.py +2 -3
  26. machineconfig/scripts/python/pomodoro.py +1 -1
  27. machineconfig/scripts/python/repos.py +22 -23
  28. machineconfig/scripts/python/scheduler.py +1 -1
  29. machineconfig/scripts/python/start_slidev.py +12 -6
  30. machineconfig/scripts/python/start_terminals.py +5 -4
  31. machineconfig/scripts/python/wifi_conn.py +34 -42
  32. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  33. machineconfig/settings/__init__.py +0 -0
  34. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +1 -1
  35. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +3 -2
  36. machineconfig/utils/installer.py +86 -41
  37. machineconfig/utils/procs.py +2 -2
  38. machineconfig/utils/scheduling.py +3 -3
  39. machineconfig/utils/utils.py +136 -56
  40. machineconfig/utils/ve.py +145 -95
  41. {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/METADATA +160 -155
  42. machineconfig-1.92.dist-info/RECORD +70 -0
  43. machineconfig/jobs/python_custom_installers/azuredatastudio.py +0 -36
  44. machineconfig/jobs/python_custom_installers/bypass_paywall.py +0 -30
  45. machineconfig/jobs/python_custom_installers/docker_desktop.py +0 -52
  46. machineconfig/jobs/python_custom_installers/goes.py +0 -35
  47. machineconfig/jobs/python_custom_installers/lvim.py +0 -48
  48. machineconfig/jobs/python_custom_installers/ngrok.py +0 -39
  49. machineconfig/jobs/python_custom_installers/nvim.py +0 -48
  50. machineconfig/jobs/python_custom_installers/vscode.py +0 -45
  51. machineconfig/jobs/python_custom_installers/wezterm.py +0 -41
  52. machineconfig-1.9.dist-info/RECORD +0 -76
  53. {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/LICENSE +0 -0
  54. {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/WHEEL +0 -0
  55. {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@ def pomodoro(work: int = 25, rest: int = 5, repeats: int = 4):
16
16
  while (diff := rest - ((datetime.now() - start).seconds / 60)) > 0: logger.critical(f"Keep Resting. Time Left: {round(diff)} minutes"); time.sleep(60 * 1)
17
17
  def speak(txt: str):
18
18
  install_n_import("gtts").gTTS(txt, lang='en', tld='com.au').save(tmp := P.tmpfile(suffix=".mp3")); time.sleep(0.5)
19
- pyglet = install_n_import("pyglet"); pyglet.resource.path = [tmp.parent.str]; pyglet.resource.reindex(); pyglet.resource.media(tmp.name).play()
19
+ pyglet = install_n_import("pyglet"); pyglet.resource.path = [tmp.parent.to_str()]; pyglet.resource.reindex(); pyglet.resource.media(tmp.name).play()
20
20
  def beep(duration: int = 1, frequency: int = 3000):
21
21
  try: import winsound
22
22
  except ImportError: __import__("os").system(f'beep -f {frequency} -l {1000 * duration}')
@@ -8,7 +8,7 @@ in the event that username@github.com is not mentioned in the remote url.
8
8
 
9
9
  from rich import print as pprint
10
10
  from machineconfig.utils.utils import write_shell_script, CONFIG_PATH, DEFAULTS_PATH
11
- from crocodile.file_management import P, install_n_import, Read, Save
11
+ from crocodile.file_management import P, Read, Save
12
12
  from crocodile.core import randstr
13
13
  import argparse
14
14
  from dataclasses import dataclass
@@ -31,7 +31,7 @@ class RepoRecord:
31
31
  version: dict[str, str]
32
32
 
33
33
 
34
- def git_action(path: P, action: GitAction, mess: Optional[str] = None, r: bool = False) -> str:
34
+ def git_action(path: P, action: GitAction, mess: Optional[str] = None, r: bool=False) -> str:
35
35
  from git.exc import InvalidGitRepositoryError
36
36
  from git.repo import Repo
37
37
  try:
@@ -57,7 +57,7 @@ git add .; git commit -am "{mess}"
57
57
  action_name = "pull" if action == GitAction.pull else "push"
58
58
  cmds = [f'echo "pulling from {remote.url}" ; git {action_name} {remote.name} {repo.active_branch.name}' for remote in repo.remotes]
59
59
  program += '\n' + '\n'.join(cmds) + '\n'
60
- program = program + f'''
60
+ program = program + '''
61
61
  echo ""; echo ""
62
62
  '''
63
63
  return program
@@ -68,41 +68,40 @@ def main():
68
68
  # POSITIONAL
69
69
  parser.add_argument("directory", help="folder containing repos to record a json out of OR a specs json file to follow.", default="")
70
70
  # FLAGS
71
- parser.add_argument("--push", help=f"push", action="store_true")
72
- parser.add_argument("--pull", help=f"pull", action="store_true")
73
- parser.add_argument("--commit", help=f"commit", action="store_true")
74
- parser.add_argument("--all", help=f"pull, commit and push", action="store_true")
75
- parser.add_argument("--record", help=f"record respos", action="store_true")
76
- parser.add_argument("--clone", help=f"clone repos from record", action="store_true")
77
- parser.add_argument("--checkout", help=f"Check out to versions prvided in this json file", action="store_true")
71
+ parser.add_argument("--push", help="push", action="store_true")
72
+ parser.add_argument("--pull", help="pull", action="store_true")
73
+ parser.add_argument("--commit", help="commit", action="store_true")
74
+ parser.add_argument("--all", help="pull, commit and push", action="store_true")
75
+ parser.add_argument("--record", help="record respos", action="store_true")
76
+ parser.add_argument("--clone", help="clone repos from record", action="store_true")
77
+ parser.add_argument("--checkout", help="Check out to versions prvided in this json file", action="store_true")
78
78
  parser.add_argument("--checkout_to_branch", help="Checkout to the main branch", action="store_true")
79
- parser.add_argument("--recursive", "-r", help=f"recursive flag", action="store_true")
79
+ parser.add_argument("--recursive", "-r", help="recursive flag", action="store_true")
80
80
  # OPTIONAL
81
- parser.add_argument("--cloud", "-c", help=f"cloud", default=None)
81
+ parser.add_argument("--cloud", "-c", help="cloud", default=None)
82
82
  args = parser.parse_args()
83
83
 
84
84
  if args.directory == "": repos_root = P.home().joinpath("code") # it is a positional argument, can never be empty.
85
85
  else: repos_root = P(args.directory).expanduser().absolute()
86
- _ = install_n_import("git", "gitpython")
87
86
 
88
87
  program = ""
89
88
  if args.record:
90
89
  res = record_repos(repos_root=str(repos_root))
91
- pprint(f"Recorded repositories:\n", res)
90
+ pprint("Recorded repositories:\n", res)
92
91
  save_path = CONFIG_PATH.joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
93
92
  # Save.pickle(obj=res, path=save_path)
94
- Save.json(obj=res, path=save_path)
93
+ Save.json(obj=res, path=save_path, indent=4)
95
94
  pprint(f"Result pickled at {P(save_path)}")
96
95
  if args.cloud is not None: P(save_path).to_cloud(rel2home=True, cloud=args.cloud)
97
- program += f"""\necho '>>>>>>>>> Finished Recording'\n"""
96
+ program += """\necho '>>>>>>>>> Finished Recording'\n"""
98
97
  elif args.clone or args.checkout or args.checkout_to_branch:
99
98
  # preferred_remote = input("Enter preferred remote to use (default: None): ") or ""
100
- program += f"""\necho '>>>>>>>>> Cloning Repos'\n"""
99
+ program += """\necho '>>>>>>>>> Cloning Repos'\n"""
101
100
  if not repos_root.exists() or repos_root.stem != 'repos.json': # user didn't pass absolute path to pickle file, but rather expected it to be in the default save location
102
101
  repos_root = CONFIG_PATH.joinpath("repos").joinpath(repos_root.rel2home()).joinpath("repos.json")
103
102
  if not repos_root.exists():
104
103
  if args.cloud is None:
105
- cloud: str = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
104
+ cloud: str=Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
106
105
  print(f"⚠️ Using default cloud: {cloud}")
107
106
  else:
108
107
  cloud = args.cloud
@@ -119,10 +118,10 @@ def main():
119
118
  if args.commit or args.all: program += git_action(a_path, action=GitAction.commit, r=args.recursive)
120
119
  if args.push or args.all: program += git_action(a_path, action=GitAction.push, r=args.recursive)
121
120
  else: program = "echo 'no action specified, try to pass --push, --pull, --commit or --all'"
122
- write_shell_script(program, "Script to update repos")
121
+ write_shell_script(program=program, desc="Script to update repos", preserve_cwd=True)
123
122
 
124
123
 
125
- def record_repos(repos_root: str, r: bool = True) -> list[dict[str, Any]]:
124
+ def record_repos(repos_root: str, r: bool=True) -> list[dict[str, Any]]:
126
125
  path_obj = P(repos_root).expanduser().absolute()
127
126
  if path_obj.is_file(): return []
128
127
  search_res = path_obj.search("*", files=False)
@@ -135,7 +134,7 @@ def record_repos(repos_root: str, r: bool = True) -> list[dict[str, Any]]:
135
134
  return res
136
135
 
137
136
 
138
- def record_a_repo(path: P, search_parent_directories: bool = False, preferred_remote: Optional[str] = None):
137
+ def record_a_repo(path: P, search_parent_directories: bool=False, preferred_remote: Optional[str] = None):
139
138
  from git.repo import Repo
140
139
  repo = Repo(path, search_parent_directories=search_parent_directories) # get list of remotes using git python
141
140
  repo_root = P(repo.working_dir).absolute()
@@ -160,7 +159,7 @@ def record_a_repo(path: P, search_parent_directories: bool = False, preferred_re
160
159
  return res
161
160
 
162
161
 
163
- def install_repos(specs_path: str, clone: bool = True, checkout_to_recorded_commit: bool = False, checkout_to_branch: bool = False, editable_install: bool = False, preferred_remote: Optional[str] = None):
162
+ def install_repos(specs_path: str, clone: bool=True, checkout_to_recorded_commit: bool=False, checkout_to_branch: bool=False, editable_install: bool=False, preferred_remote: Optional[str] = None):
164
163
  program = ""
165
164
  path_obj = P(specs_path).expanduser().absolute()
166
165
  repos: list[dict[str, Any]] = Read.json(path_obj)
@@ -189,7 +188,7 @@ def install_repos(specs_path: str, clone: bool = True, checkout_to_recorded_comm
189
188
  program += f"\ncd {parent_dir.collapseuser().as_posix()}/{repo['name']}; git checkout {repo['current_branch']}"
190
189
  break
191
190
  if editable_install and idx == 0:
192
- program += f"\ncd {parent_dir.collapseuser().as_posix()}/{repo['name']}; pip install -e ."
191
+ program += f"\ncd {parent_dir.collapseuser().as_posix()}/{repo['name']}; uv pip install -e ."
193
192
  program += "\n"
194
193
  pprint(program)
195
194
  return program
@@ -45,7 +45,7 @@ def main_parse():
45
45
  print(f"✅ Task {task_name} created in {task_root}. Head there and edit the config.ini file & task.py file.")
46
46
  return None
47
47
 
48
- main(root=root.str, ignore_conditions=args.ignore_conditions)
48
+ main(root=root.to_str(), ignore_conditions=args.ignore_conditions)
49
49
 
50
50
 
51
51
  if __name__ == "__main__":
@@ -16,7 +16,7 @@ PORT_DEFAULT = 3030
16
16
  SLIDEV_REPO = CONFIG_PATH.joinpath(".cache/slidev")
17
17
  if not SLIDEV_REPO.joinpath("components").exists():
18
18
  # assert slidev is installed first
19
- Terminal(stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE).run(f"cd {SLIDEV_REPO.parent};npm init slidev")
19
+ Terminal(stderr=subprocess.PIPE, stdin=subprocess.PIPE, stdout=subprocess.PIPE).run(f"cd {SLIDEV_REPO.parent};npm init slidev@latest")
20
20
 
21
21
 
22
22
  def jupyter_to_markdown(file: P):
@@ -55,10 +55,11 @@ def main() -> None:
55
55
  parser = argparse.ArgumentParser()
56
56
  parser.add_argument("-d", "--directory", default=None, help="Directory of the report")
57
57
  parser.add_argument("-j", "--jupyter-file", default=None, help="Jupyter notebook file to convert to slides. If not provided, slides.md is used.")
58
- parser.add_argument("--port", default=PORT_DEFAULT, help=f"Port to serve the report, default to {PORT_DEFAULT}")
58
+ # parser.add_argument("--port", default=PORT_DEFAULT, help=f"Port to serve the report, default to {PORT_DEFAULT}")
59
59
  args = parser.parse_args()
60
60
 
61
- port = args.port
61
+ # port = args.port
62
+ port = PORT_DEFAULT
62
63
 
63
64
  if args.jupyter_file is not None:
64
65
  report_dir = jupyter_to_markdown(P(args.jupyter_file))
@@ -89,15 +90,20 @@ def main() -> None:
89
90
  import socket
90
91
  try: local_ip_v4 = socket.gethostbyname(socket.gethostname() + ".local") # without .local, in linux machines, '/etc/hosts' file content, you have an IP address mapping with '127.0.1.1' to your hostname
91
92
  except Exception:
92
- print(f"Warning: Could not get local_ip_v4. This is probably because you are running a WSL instance") # TODO find a way to get the local_ip_v4 in WSL
93
+ print("Warning: Could not get local_ip_v4. This is probably because you are running a WSL instance") # TODO find a way to get the local_ip_v4 in WSL
93
94
  local_ip_v4 = socket.gethostbyname(socket.gethostname())
94
95
 
95
96
  print(f"Presentation is served at http://{platform.node()}:{port}")
96
97
  print(f"Presentation is served at http://localhost:{port}")
97
98
  print(f"Presentation is served at http://{local_ip_v4}:{port}")
98
- program: str = f"cd {SLIDEV_REPO}; slidev --port {port} --remote 0.0.0.0; cd {P.cwd()}"
99
+ # This version requires a globally installed cli of slidev, which is not recommended.
100
+ # program: str=f"cd {SLIDEV_REPO}; slidev --port {port} --remote 0.0.0.0; cd {P.cwd()}"
101
+
102
+ # The recommended approach is do `npm init slidev@latest` in the directory where you want to create the presentation
103
+ # Then you can do the following:
104
+ program = "npm run dev slides.md -- --remote"
99
105
  PROGRAM_PATH.write_text(program)
100
- print_code(program, lexer="bash")
106
+ print_code(code=program, lexer="bash", desc="Run the following command to start the presentation")
101
107
 
102
108
 
103
109
  if __name__ == '__main__':
@@ -2,7 +2,7 @@
2
2
  """Script to start terminals on windows and wsl
3
3
  """
4
4
 
5
- from machineconfig.utils.utils import PROGRAM_PATH, display_options, install_n_import, get_ssh_hosts, platform
5
+ from machineconfig.utils.utils import PROGRAM_PATH, display_options, get_ssh_hosts, platform
6
6
  from itertools import cycle
7
7
  from typing import Literal
8
8
 
@@ -22,14 +22,14 @@ THIS_MACHINE_HOSTNAME = platform.node()
22
22
  THIS_MACHINE_HOSTNAME_WSL = f"{THIS_MACHINE_HOSTNAME}wsl"
23
23
 
24
24
 
25
- def main_windows_and_wsl(window: int, hosts: list[str], orientation: ORIENTATION_TYPE = "vertical", mprocs: bool = False):
25
+ def main_windows_and_wsl(window: int, hosts: list[str], orientation: ORIENTATION_TYPE = "vertical", mprocs: bool=False):
26
26
  orientation_oposite = "horizontal" if orientation == "vertical" else "vertical"
27
27
  orientation_swap = "up" if orientation == "horizontal" else "left"
28
28
  orientation_opposite_move_focus = "up" if orientation_oposite == "horizontal" else "left"
29
29
  orientation_opposite_move_focus_other = "down" if orientation_oposite == "horizontal" else "right"
30
30
  sleep = 3
31
31
  sep = f"\nsleep {sleep}; wt --window {window}" # or '`;'
32
- ssh_cmd = f"-t 'mprocs'" if mprocs else '' # 'wsl_ssh_windows_port_forwarding.ps1'
32
+ ssh_cmd = "-t 'mprocs'" if mprocs else '' # 'wsl_ssh_windows_port_forwarding.ps1'
33
33
  split_per_machine = 1 / len(hosts)
34
34
  size = 0.3
35
35
  known_hosts = get_ssh_hosts()
@@ -89,7 +89,8 @@ def main():
89
89
  cmd = main_windows_and_wsl(window=args.window, hosts=hosts, orientation="vertical" if args.vertical else "horizontal")
90
90
 
91
91
  print(cmd)
92
- install_n_import("clipboard").copy(cmd)
92
+ # import clipboard
93
+ # install_n_import("clipboard").copy(cmd)
93
94
  PROGRAM_PATH.write_text(cmd)
94
95
 
95
96
 
@@ -1,50 +1,44 @@
1
-
2
1
  """Wifi connect
2
+
3
+ sudo apt-get install network-manager
4
+
3
5
  """
4
6
 
5
7
  import argparse
6
8
  import configparser
7
9
  from pathlib import Path
8
- # import random
9
- # import string
10
10
  import os
11
-
11
+ import platform
12
+ import subprocess
12
13
 
13
14
  def create_new_connection(name: str, ssid: str, password: str):
14
- config = """<?xml version=\"1.0\"?>
15
- <WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
16
- <name>""" + name + """</name>
17
- <SSIDConfig>
18
- <SSID>
19
- <name>""" + ssid + """</name>
20
- </SSID>
21
- </SSIDConfig>
22
- <connectionType>ESS</connectionType>
23
- <connectionMode>auto</connectionMode>
24
- <MSM>
25
- <security>
26
- <authEncryption>
27
- <authentication>WPA2PSK</authentication>
28
- <encryption>AES</encryption>
29
- <useOneX>false</useOneX>
30
- </authEncryption>
31
- <sharedKey>
32
- <keyType>passPhrase</keyType>
33
- <protected>false</protected>
34
- <keyMaterial>""" + password + """</keyMaterial>
35
- </sharedKey>
36
- </security>
37
- </MSM>
38
- </WLANProfile>"""
39
- command = "netsh wlan add profile filename=\"" + name + ".xml\"" + " interface=Wi-Fi"
40
- with open(name + ".xml", mode='w', encoding="utf-8") as file: file.write(config)
41
- os.system(command)
42
-
15
+ if platform.system() == "Windows":
16
+ config = """<?xml version=\"1.0\"?>
17
+ // ...existing XML config...
18
+ """
19
+ command = "netsh wlan add profile filename=\"" + name + ".xml\"" + " interface=Wi-Fi"
20
+ with open(name + ".xml", mode='w', encoding="utf-8") as file:
21
+ file.write(config)
22
+ os.system(command)
23
+ elif platform.system() == "Linux":
24
+ # Use nmcli to add/update connection
25
+ command = f"nmcli connection add type wifi con-name '{name}' ssid '{ssid}' wifi-sec.key-mgmt wpa-psk wifi-sec.psk '{password}'"
26
+ subprocess.run(command, shell=True, check=True)
43
27
 
44
28
  def connect(name: str, ssid: str):
45
- command = "netsh wlan connect name=\"" + name + "\" ssid=\"" + ssid + "\" interface=Wi-Fi"
46
- os.system(command)
47
- def display_available_networks(): os.system("netsh wlan show networks interface=Wi-Fi") #
29
+ if platform.system() == "Windows":
30
+ command = "netsh wlan connect name=\"" + name + "\" ssid=\"" + ssid + "\" interface=Wi-Fi"
31
+ os.system(command)
32
+ elif platform.system() == "Linux":
33
+ command = f"nmcli connection up '{name}'"
34
+ subprocess.run(command, shell=True, check=True)
35
+
36
+
37
+ def display_available_networks():
38
+ if platform.system() == "Windows":
39
+ os.system("netsh wlan show networks interface=Wi-Fi")
40
+ elif platform.system() == "Linux":
41
+ subprocess.run("nmcli device wifi list", shell=True, check=True)
48
42
 
49
43
 
50
44
  def main():
@@ -52,20 +46,18 @@ def main():
52
46
  creds.read(Path.home().joinpath('dotfiles/machineconfig/setup/wifi.ini'))
53
47
 
54
48
  parser = argparse.ArgumentParser(description='Wifi Connector')
55
- parser.add_argument('-n', "--ssid", help=f"SSID of Wifi", default='MyPhoneHotSpot')
49
+ parser.add_argument('-n', "--ssid", help="SSID of Wifi", default='MyPhoneHotSpot')
56
50
 
57
51
  args = parser.parse_args()
58
52
  ssid = creds[args.ssid]['SSID']
59
- # pwd = creds[args.ssid]['pwd']
53
+ password = creds[args.ssid]['pwd'] # You'll need the password for Linux connections
60
54
 
61
- # displayAvailableNetworks()
62
- # createNewConnection(name, name, password)
55
+ # Create and connect to the network
56
+ create_new_connection(ssid, ssid, password)
63
57
  connect(ssid, ssid)
64
58
 
65
59
 
66
60
  def get_current_wifi_name() -> str:
67
- import subprocess
68
- import platform
69
61
  if platform.system() == "Windows":
70
62
  try:
71
63
  cmd_output = subprocess.check_output(["netsh", "wlan", "show", "interface"], shell=True).decode("utf-8")
@@ -28,7 +28,7 @@ Otherwise, a flag must be raised to indicate the direction.""")
28
28
  path = P(args.path).expanduser().absolute()
29
29
 
30
30
  if args.same_file_system:
31
- print(f"💥 Using a not recommended transfer method! Copying same files across different file systems.")
31
+ print("💥 Using a not recommended transfer method! Copying same files across different file systems.")
32
32
  if system == "Windows": # move files over to WSL
33
33
  path.copy(folder=WSL_FROM_WIN.joinpath(UserName).joinpath(path.rel2home().parent), overwrite=True) # the following works for files and folders alike.
34
34
  else: # move files from WSL to win
File without changes
@@ -32,7 +32,7 @@ def install_nerd_fonts():
32
32
  folder.search("*readme*").apply(lambda p: p.delete(sure=True))
33
33
  folder.search("*LICENSE*").apply(lambda p: p.delete(sure=True))
34
34
  file = P.tmpfile(suffix=".ps1").write_text(LIBRARY_ROOT.joinpath("setup_windows/wt_and_pwsh/install_fonts.ps1").read_text().replace(r".\fonts-to-be-installed", str(folder)))
35
- subprocess.run(rf"powershell.exe -executionpolicy Bypass -nologo -noninteractive -File {file.str}", check=True)
35
+ subprocess.run(rf"powershell.exe -executionpolicy Bypass -nologo -noninteractive -File {file.to_str()}", check=True)
36
36
  folder.delete(sure=True)
37
37
 
38
38
 
@@ -52,7 +52,7 @@ class TerminalSettings(object):
52
52
 
53
53
  # 1- Customizing Powershell========================================================
54
54
  # as opposed to Windows Powershell
55
- def customize_powershell(self, nerd_font: bool = True):
55
+ def customize_powershell(self, nerd_font: bool=True):
56
56
  pwsh: dict[str, Any] = dict(name="PowerShell",
57
57
  commandline="pwsh",
58
58
  hidden=False,
@@ -65,7 +65,8 @@ class TerminalSettings(object):
65
65
  if item["name"] == "PowerShell":
66
66
  self.profs.list[idx].update(pwsh)
67
67
  break
68
- else: print(f"Couldn't customize powershell because profile not found, try to install it first.")
68
+ else:
69
+ print("Couldn't customize powershell because profile not found, try to install it first.")
69
70
 
70
71
  def make_powershell_default_profile(self):
71
72
  for profile in self.profs:
@@ -3,22 +3,25 @@
3
3
  """
4
4
  from rich.console import Console
5
5
 
6
- from crocodile.file_management import P, List as L, Read, Struct
6
+ from crocodile.file_management import P, List as L, Read
7
+ from crocodile.core import Struct
7
8
  from crocodile.meta import Terminal
8
9
  from machineconfig.utils.utils import INSTALL_VERSION_ROOT, INSTALL_TMP_DIR, LIBRARY_ROOT, check_tool_exists
9
10
 
10
11
  # from dataclasses import dataclass
11
- from typing import Optional, Any
12
+ from typing import Optional, Any, TypeAlias, Literal
12
13
  import platform
13
14
  # import os
14
15
 
15
16
 
16
- LINUX_INSTALL_PATH = '/usr/local/bin'
17
- WINDOWS_INSTALL_PATH = '~/AppData/Local/Microsoft/WindowsApps'
17
+ # LINUX_INSTALL_PATH = '/usr/local/bin'
18
+ LINUX_INSTALL_PATH = '~/.local/bin'
18
19
 
20
+ WINDOWS_INSTALL_PATH = P.home().joinpath("AppData/Local/Microsoft/WindowsApps").__str__()
21
+ CATEGORY: TypeAlias = Literal["OS_SPECIFIC", "OS_GENERIC", "CUSTOM", "OS_SPECIFIC_DEV", "OS_GENERIC_DEV", "CUSTOM_DEV"]
19
22
 
20
- def find_move_delete_windows(downloaded_file_path: P, exe_name: Optional[str] = None, delete: bool = True, rename_to: Optional[str] = None):
21
- """Moves executable to {WINDOWS_INSTALL_PATH}"""
23
+
24
+ def find_move_delete_windows(downloaded_file_path: P, exe_name: Optional[str] = None, delete: bool=True, rename_to: Optional[str] = None):
22
25
  if exe_name is not None and ".exe" in exe_name: exe_name = exe_name.replace(".exe", "")
23
26
  if downloaded_file_path.is_file():
24
27
  exe = downloaded_file_path
@@ -59,26 +62,30 @@ def find_move_delete_linux(downloaded: P, tool_name: str, delete: Optional[bool]
59
62
  print(f"MOVING file `{repr(exe)}` to '{LINUX_INSTALL_PATH}'")
60
63
  exe.chmod(0o777)
61
64
  # exe.move(folder=LINUX_INSTALL_PATH, overwrite=False)
62
- Terminal().run(f"sudo mv {exe} {LINUX_INSTALL_PATH}/").print_if_unsuccessful(desc=f"MOVING executable `{exe}` to {LINUX_INSTALL_PATH}", strict_err=True, strict_returncode=True)
65
+ if "/usr" in LINUX_INSTALL_PATH:
66
+ Terminal().run(f"sudo mv {exe} {LINUX_INSTALL_PATH}/").print_if_unsuccessful(desc=f"MOVING executable `{exe}` to {LINUX_INSTALL_PATH}", strict_err=True, strict_returncode=True)
67
+ else:
68
+ exe.move(folder=LINUX_INSTALL_PATH, overwrite=True)
63
69
  if delete: downloaded.delete(sure=True)
64
70
  exe_new_location = P(LINUX_INSTALL_PATH).joinpath(exe.name)
65
71
  return exe_new_location
66
72
 
67
73
 
68
74
  class Installer:
69
- def __init__(self, repo_url: str, name: str, doc: str, filename_template_windows_amd_64: str, filename_template_linux_amd_64: str, strip_v: bool, exe_name: str):
70
- self.repo_url = repo_url
71
- self.name = name
72
- self.doc = doc
73
- self.filename_template_windows_amd_64 = filename_template_windows_amd_64
74
- self.filename_template_linux_amd_64 = filename_template_linux_amd_64
75
- self.strip_v = strip_v
76
- self.exe_name = exe_name
75
+ def __init__(self, repo_url: str, name: str, doc: str, filename_template_windows_amd_64: str, filename_template_linux_amd_64: str,
76
+ strip_v: bool, exe_name: str):
77
+ self.repo_url: str=repo_url
78
+ self.name: str=name
79
+ self.doc: str=doc
80
+ self.filename_template_windows_amd_64: str=filename_template_windows_amd_64
81
+ self.filename_template_linux_amd_64: str=filename_template_linux_amd_64
82
+ self.strip_v: bool=strip_v
83
+ self.exe_name: str=exe_name
77
84
  def __repr__(self) -> str: return f"Installer of {self.repo_url}"
78
85
  def get_description(self):
79
86
  # old_version_cli = Terminal().run(f"{self.exe_name} --version").op.replace("\n", "")
80
87
  # old_version_cli = os.system(f"{self.exe_name} --version").replace("\n", "")
81
- old_version_cli = check_tool_exists(tool_name=self.exe_name)
88
+ old_version_cli: bool=check_tool_exists(tool_name=self.exe_name)
82
89
  old_version_cli_str = "✅" if old_version_cli else "❌"
83
90
  # name_version = f"{self.exe_name} {old_version_cli_str}"
84
91
  return f"{self.exe_name:<12} {old_version_cli_str} {self.doc}"
@@ -102,34 +109,41 @@ class Installer:
102
109
 
103
110
  def install_robust(self, version: Optional[str]):
104
111
  try:
112
+
105
113
  old_version_cli = Terminal().run(f"{self.exe_name} --version").op.replace("\n", "")
106
114
  self.install(version=version)
107
115
  new_version_cli = Terminal().run(f"{self.exe_name} --version").op.replace("\n", "")
108
- if old_version_cli == new_version_cli: return f"📦️ 😑 {self.exe_name}, same version: {old_version_cli}"
109
- else: return f"📦️ 🤩 {self.exe_name} updated from {old_version_cli} === to ===> {new_version_cli}"
116
+
117
+ if old_version_cli == new_version_cli: return f"""echo "📦️ 😑 {self.exe_name}, same version: {old_version_cli}" """
118
+ else: return f"""echo "📦️ 🤩 {self.exe_name} updated from {old_version_cli} ➡️ TO ➡️ {new_version_cli}" """
119
+
110
120
  except Exception as ex:
111
121
  print(ex)
112
- return f"📦️ Failed at {self.exe_name} with {ex}"
122
+ return f"""echo "📦️ Failed at `{self.name}` with {ex}" """
113
123
 
114
124
  def install(self, version: Optional[str]):
115
125
  if self.repo_url == "CUSTOM":
126
+
116
127
  import machineconfig.jobs.python_custom_installers as python_custom_installers
117
128
  installer_path = P(python_custom_installers.__file__).parent.joinpath(self.exe_name + ".py")
129
+ if not installer_path.exists():
130
+ installer_path = P(python_custom_installers.__file__).parent.joinpath("dev", self.exe_name + ".py")
131
+
118
132
  import runpy
119
- program = runpy.run_path(str(installer_path), run_name=None)['main'](version=version)
120
- Terminal().run_script(script=program, shell="default").print(desc="Running custom installer", capture=True)
133
+ print(f"Executing func `main` from `{installer_path}`to get the program to run")
134
+ program: str = runpy.run_path(str(installer_path), run_name=None)['main'](version=version)
135
+ # print(program)
136
+ Terminal(stdin=None, stdout=None, stderr=None).run_script(script=program, shell="default").print(desc="Running custom installer", capture=True)
121
137
  # import subprocess
122
138
  # subprocess.run(program, shell=True, check=True)
123
139
  version_to_be_installed = str(version)
124
- elif "npm " in self.repo_url:
125
- Terminal().run(self.repo_url, shell="default").print_if_unsuccessful(desc="npm install", strict_err=True, strict_returncode=True)
126
- version_to_be_installed = "npmLatest"
127
- elif "pip " in self.repo_url:
128
- Terminal().run(self.repo_url, shell="default").print_if_unsuccessful(desc="pip install", strict_err=True, strict_returncode=True)
129
- version_to_be_installed = "pipLatest"
140
+ elif "npm " in self.repo_url or "pip " in self.repo_url or "winget " in self.repo_url:
141
+ desc = self.repo_url.split(" ", maxsplit=1)[0] + "installation"
142
+ version_to_be_installed = self.repo_url.split(" ", maxsplit=1)[0] + "Latest"
143
+ Terminal().run(self.repo_url, shell="default").print_if_unsuccessful(desc=desc, strict_err=True, strict_returncode=True)
130
144
  else:
131
145
  downloaded, version_to_be_installed = self.download(version=version)
132
- if downloaded.str.endswith(".deb"):
146
+ if downloaded.to_str().endswith(".deb"):
133
147
  assert platform.system() == "Linux"
134
148
  Terminal().run(f"sudo apt install -y {downloaded}").print_if_unsuccessful(desc="Installing .deb", strict_err=True, strict_returncode=True)
135
149
  downloaded.delete(sure=True)
@@ -161,16 +175,17 @@ class Installer:
161
175
  else: raise NotImplementedError(f"📦️ System {platform.system()} not implemented")
162
176
  version_to_be_installed = "predefined_url"
163
177
  else:
164
- release_url, version_to_be_installed = self.get_github_release(repo_url=self.repo_url, version=version)
178
+ release_url, version_to_be_installed = Installer.get_github_release(repo_url=self.repo_url, version=version)
165
179
  print(f"📦️ Version to be installed: {version_to_be_installed}")
166
180
  print(f"📦️ Release URL: {release_url}")
167
181
  version_to_be_installed_stripped = version_to_be_installed.replace("v", "") if self.strip_v else version_to_be_installed
182
+ version_to_be_installed_stripped = version_to_be_installed_stripped.replace("ipinfo-", "")
168
183
  if platform.system() == "Windows":
169
184
  file_name = self.filename_template_windows_amd_64.format(version_to_be_installed_stripped)
170
185
  elif platform.system() == "Linux":
171
186
  file_name = self.filename_template_linux_amd_64.format(version_to_be_installed_stripped)
172
187
  else: raise NotImplementedError(f"📦️ System {platform.system()} not implemented")
173
- print(f"📦️ File name", file_name)
188
+ print("📦️ File name", file_name)
174
189
  download_link = release_url.joinpath(file_name)
175
190
  print(f"📦️ Downloading {self.name}: ", download_link.as_url_str())
176
191
  downloaded = download_link.download(folder=INSTALL_TMP_DIR).decompress()
@@ -262,36 +277,66 @@ def get_installed_cli_apps():
262
277
 
263
278
 
264
279
  def get_installers(system: str, dev: bool) -> list[Installer]:
280
+ res_all = get_all_dicts(system=system)
281
+ if not dev:
282
+ del res_all["CUSTOM_DEV"]
283
+ del res_all["OS_SPECIFIC_DEV"]
284
+ del res_all["OS_GENERIC_DEV"]
285
+ res_final = {}
286
+ for _k, v in res_all.items():
287
+ res_final.update(v)
288
+ return [Installer.from_dict(d=vd, name=k) for k, vd in res_final.items()]
289
+
290
+
291
+ def get_all_dicts(system: str) -> dict[CATEGORY, dict[str, dict[str, Any]]]:
265
292
  if system == "Windows": import machineconfig.jobs.python_windows_installers as os_specific_installer
266
293
  else: import machineconfig.jobs.python_linux_installers as os_specific_installer
294
+
267
295
  import machineconfig.jobs.python_generic_installers as generic_installer
268
296
  path_os_specific = P(os_specific_installer.__file__).parent
269
297
  path_os_generic = P(generic_installer.__file__).parent
298
+
299
+ path_os_specific_dev = path_os_specific.joinpath("dev")
300
+ path_os_generic_dev = path_os_generic.joinpath("dev")
301
+
302
+ res_final: dict[CATEGORY, dict[str, dict[str, Any]]] = {}
303
+ res_final["OS_SPECIFIC"] = Read.json(path=path_os_specific.joinpath("config.json"))
304
+ res_final["OS_GENERIC"] = Read.json(path=path_os_generic.joinpath("config.json"))
305
+ res_final["OS_SPECIFIC_DEV"] = Read.json(path=path_os_specific_dev.joinpath("config.json"))
306
+ res_final["OS_GENERIC_DEV"] = Read.json(path=path_os_generic_dev.joinpath("config.json"))
307
+
270
308
  path_custom_installer = path_os_generic.with_name("python_custom_installers")
271
- if dev:
272
- path_os_specific = path_os_specific.joinpath("dev")
273
- path_os_generic = path_os_generic.joinpath("dev")
274
- res1: dict[str, Any] = Read.json(path=path_os_specific.joinpath("config.json"))
275
- res2: dict[str, Any] = Read.json(path=path_os_generic.joinpath("config.json"))
276
- res2.update(res1)
309
+ path_custom_installer_dev = path_custom_installer.joinpath("dev")
310
+
277
311
  import runpy
278
- for item in path_custom_installer.search("*.py", r=False):
312
+ res_custom: dict[str, dict[str, Any]] = {}
313
+ for item in path_custom_installer.search("*.py", r=False, not_in=["__init__"]):
314
+ try:
315
+ config_dict = runpy.run_path(str(item), run_name=None)['config_dict']
316
+ res_custom[item.stem] = config_dict
317
+ except Exception as ex:
318
+ print(f"Failed to load {item}: {ex}")
319
+
320
+ res_custom_dev: dict[str, dict[str, Any]] = {}
321
+ for item in path_custom_installer_dev.search("*.py", r=False, not_in=["__init__"]):
279
322
  try:
280
323
  config_dict = runpy.run_path(str(item), run_name=None)['config_dict']
281
- res2[item.stem] = config_dict
324
+ res_custom_dev[item.stem] = config_dict
282
325
  except Exception as ex:
283
326
  print(f"Failed to load {item}: {ex}")
284
327
 
285
- return [Installer.from_dict(d=vd, name=k) for k, vd in res2.items()]
328
+ res_final["CUSTOM"] = res_custom
329
+ res_final["CUSTOM_DEV"] = res_custom_dev
330
+ return res_final
286
331
 
287
332
 
288
- def install_all(installers: L[Installer], safe: bool = False, jobs: int = 10, fresh: bool = False):
333
+ def install_all(installers: L[Installer], safe: bool=False, jobs: int = 10, fresh: bool=False):
289
334
  if fresh: INSTALL_VERSION_ROOT.delete(sure=True)
290
335
  if safe:
291
336
  from machineconfig.jobs.python.check_installations import APP_SUMMARY_PATH
292
337
  apps_dir = APP_SUMMARY_PATH.readit()
293
338
  if platform.system().lower() == "windows":
294
- apps_dir.search("*").apply(lambda app: app.move(folder=P.get_env().WindowsApps))
339
+ apps_dir.search("*").apply(lambda app: app.move(folder=P.get_env().WindowsPaths().WindowsApps))
295
340
  elif platform.system().lower() == "linux":
296
341
  Terminal().run(f"sudo mv {apps_dir.as_posix()}/* {LINUX_INSTALL_PATH}/").print_if_unsuccessful(desc=f"MOVING executable to {LINUX_INSTALL_PATH}", strict_err=True, strict_returncode=True)
297
342
  else: raise NotImplementedError(f"I don't know this system {platform.system()}")
@@ -60,7 +60,7 @@ class ProcessManager:
60
60
  self.kill(pids=sub_df.pid.to_list())
61
61
  return
62
62
  kill_by_index = input("🔫 Kill by index? 1 4 ... /[n] ")
63
- if kill_by_index != "":
63
+ if kill_by_index != "" and kill_by_index != "n":
64
64
  indices = [int(val) for val in kill_by_index.split(" ")]
65
65
  sub_sub_df = sub_df.iloc[indices]
66
66
  for idx2, row in sub_sub_df.iterrows():
@@ -93,7 +93,7 @@ class ProcessManager:
93
93
  print(f'💀 Killed process with pid {pid} and name {proc.name()}. It lived {get_age(proc.create_time())}. RIP 🪦💐')
94
94
  except psutil.NoSuchProcess: print(f'No process with pid {pid} found')
95
95
  for command in commands:
96
- rows = self.df[self.df['command'].str.contains(command)]
96
+ rows = self.df[self.df['command'].to_str().contains(command)]
97
97
  if len(rows) > 0:
98
98
  for _idx, a_row in rows.iterrows():
99
99
  psutil.Process(a_row.pid).kill()