machineconfig 1.8__py3-none-any.whl → 1.91__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 (56) hide show
  1. machineconfig/__init__.py +4 -2
  2. machineconfig/jobs/python/check_installations.py +8 -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/gh.py +53 -0
  8. machineconfig/jobs/python_custom_installers/hx.py +55 -0
  9. machineconfig/profile/create.py +26 -21
  10. machineconfig/profile/create_hardlinks.py +101 -0
  11. machineconfig/profile/shell.py +5 -5
  12. machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
  13. machineconfig/scripts/python/cloud_copy.py +24 -17
  14. machineconfig/scripts/python/cloud_mount.py +20 -10
  15. machineconfig/scripts/python/cloud_repo_sync.py +109 -56
  16. machineconfig/scripts/python/cloud_sync.py +73 -68
  17. machineconfig/scripts/python/croshell.py +23 -14
  18. machineconfig/scripts/python/devops.py +19 -20
  19. machineconfig/scripts/python/devops_backup_retrieve.py +19 -10
  20. machineconfig/scripts/python/devops_devapps_install.py +81 -57
  21. machineconfig/scripts/python/devops_update_repos.py +5 -5
  22. machineconfig/scripts/python/dotfile.py +4 -4
  23. machineconfig/scripts/python/fire_jobs.py +139 -66
  24. machineconfig/scripts/python/ftpx.py +17 -7
  25. machineconfig/scripts/python/gh_models.py +53 -0
  26. machineconfig/scripts/python/mount_nfs.py +1 -1
  27. machineconfig/scripts/python/mount_nw_drive.py +3 -3
  28. machineconfig/scripts/python/mount_ssh.py +2 -3
  29. machineconfig/scripts/python/pomodoro.py +1 -1
  30. machineconfig/scripts/python/repos.py +26 -23
  31. machineconfig/scripts/python/scheduler.py +1 -1
  32. machineconfig/scripts/python/start_slidev.py +10 -4
  33. machineconfig/scripts/python/start_terminals.py +6 -5
  34. machineconfig/scripts/python/wifi_conn.py +34 -42
  35. machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
  36. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +1 -1
  37. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +3 -2
  38. machineconfig/utils/installer.py +167 -60
  39. machineconfig/utils/procs.py +2 -2
  40. machineconfig/utils/scheduling.py +3 -3
  41. machineconfig/utils/utils.py +137 -56
  42. machineconfig/utils/ve.py +171 -100
  43. machineconfig-1.91.dist-info/LICENSE +201 -0
  44. {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/METADATA +31 -11
  45. machineconfig-1.91.dist-info/RECORD +69 -0
  46. {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/WHEEL +1 -1
  47. machineconfig/jobs/script_installer/azure_data_studio.py +0 -22
  48. machineconfig/jobs/script_installer/bypass_paywall.py +0 -23
  49. machineconfig/jobs/script_installer/code.py +0 -34
  50. machineconfig/jobs/script_installer/docker_desktop.py +0 -41
  51. machineconfig/jobs/script_installer/ngrok.py +0 -29
  52. machineconfig/jobs/script_installer/skim.py +0 -21
  53. machineconfig/jobs/script_installer/wezterm.py +0 -34
  54. machineconfig-1.8.dist-info/RECORD +0 -70
  55. /machineconfig/jobs/{script_installer → python_custom_installers}/__init__.py +0 -0
  56. {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
1
 
2
- """CC
2
+ """
3
+ CC
3
4
  """
4
5
 
5
- from crocodile.file_management import P, Struct
6
+ from crocodile.file_management import P
7
+ from crocodile.core import Struct
6
8
  from crocodile.meta import RepeatUntilNoException
7
9
  import getpass
8
10
  from machineconfig.scripts.python.cloud_sync import parse_cloud_source_target, ArgsDefaults, Args
@@ -13,22 +15,20 @@ import os
13
15
  from typing import Optional
14
16
 
15
17
 
16
- @RepeatUntilNoException()
17
- def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] = None):
18
+ @RepeatUntilNoException(retry=3, sleep=1)
19
+ def get_securely_shared_file(url: Optional[str] = None, folder: Optional[str] = None) -> None:
18
20
  folder_obj = P.cwd() if folder is None else P(folder)
19
21
 
20
22
  if os.environ.get("DECRYPTION_PASSWORD") is not None:
21
- pwd: str = str(os.environ.get("DECRYPTION_PASSWORD"))
23
+ pwd: str=str(os.environ.get("DECRYPTION_PASSWORD"))
22
24
  else:
23
25
  pwd = getpass.getpass(prompt="Enter decryption password: ")
24
-
25
26
  if url is None:
26
27
  if os.environ.get("SHARE_URL") is not None:
27
28
  url = os.environ.get("SHARE_URL")
28
29
  assert url is not None
29
30
  else:
30
31
  url = input("Enter share url: ")
31
-
32
32
  from rich.progress import Progress
33
33
  with Progress(transient=True) as progress:
34
34
  _task = progress.add_task("Downloading ... ", total=None)
@@ -57,28 +57,32 @@ def arg_parser() -> None:
57
57
  parser.add_argument("--pwd", "-p", help="Password for encryption", type=str, default=ArgsDefaults.pwd)
58
58
  parser.add_argument("--encrypt", "-e", help="Decrypt after receiving.", action="store_true", default=ArgsDefaults.encrypt)
59
59
  parser.add_argument("--zip", "-z", help="unzip after receiving.", action="store_true", default=ArgsDefaults.zip_)
60
+ parser.add_argument("--os_specific", "-o", help="choose path specific for this OS.", action="store_true", default=ArgsDefaults.os_specific)
60
61
 
61
62
  parser.add_argument("--config", "-c", help="path to cloud.json file.", default=None)
62
63
 
63
64
  args = parser.parse_args()
64
65
  args_dict = vars(args)
65
- source: str = args_dict.pop("source")
66
- target: str = args_dict.pop("target")
66
+ source: str=args_dict.pop("source")
67
+ target: str=args_dict.pop("target")
67
68
  args_obj = Args(**args_dict)
68
- Struct(args_obj.__dict__).print(as_config=True, title=f"CLI config")
69
+ Struct(args_obj.__dict__).print(as_config=True, title="CLI config")
69
70
 
70
- if args_obj.config == "ss" and (source.startswith("http") or source.startswith("bit.ly")): return get_securely_shared_file(url=source, folder=target)
71
- if args_obj.rel2home is True and args_obj.root is None: args_obj.root = "myhome"
71
+ if args_obj.config == "ss" and (source.startswith("http") or source.startswith("bit.ly")):
72
+ return get_securely_shared_file(url=source, folder=target)
73
+ if args_obj.rel2home is True and args_obj.root is None:
74
+ args_obj.root = "myhome"
72
75
 
76
+ # print(f"source: {source}")
73
77
  cloud, source, target = parse_cloud_source_target(args=args_obj, source=source, target=target)
74
78
 
75
- assert args_obj.key is None, f"Key is not supported yet."
79
+ assert args_obj.key is None, "Key is not supported yet."
76
80
  if cloud in source:
77
81
  P(target).from_cloud(cloud=cloud, remotepath=source.replace(cloud + ":", ""),
78
- unzip=args_obj.zip, decrypt=args_obj.encrypt, pwd=args_obj.pwd,
79
- overwrite=args_obj.overwrite,
80
- rel2home=args_obj.rel2home, os_specific=args_obj.os_specific, root=args_obj.root, strict=False,
81
- )
82
+ unzip=args_obj.zip, decrypt=args_obj.encrypt, pwd=args_obj.pwd,
83
+ overwrite=args_obj.overwrite,
84
+ rel2home=args_obj.rel2home, os_specific=args_obj.os_specific, root=args_obj.root, strict=False,
85
+ )
82
86
  elif cloud in target:
83
87
  res = P(source).to_cloud(cloud=cloud, remotepath=target.replace(cloud + ":", ""),
84
88
  zip=args_obj.zip, encrypt=args_obj.encrypt, pwd=args_obj.pwd,
@@ -86,6 +90,9 @@ def arg_parser() -> None:
86
90
  share=args_obj.share)
87
91
  if args_obj.share:
88
92
  print(res.as_url_str())
93
+ if P(source).is_dir(): share_url_path = P(source).joinpath(".share_url")
94
+ else: share_url_path = P(source).with_suffix(".share_url")
95
+ share_url_path.write_text(res.as_url_str())
89
96
  else: raise ValueError(f"Cloud `{cloud}` not found in source or target.")
90
97
 
91
98
 
@@ -43,20 +43,26 @@ mprocs "echo 'see {DEFAULT_MOUNT}/{cloud} for the mounted cloud'; rclone about {
43
43
 
44
44
 
45
45
  def mount(cloud: Optional[str], network: Optional[str], destination: Optional[str]) -> None:
46
-
47
46
  config = get_rclone_config()
48
47
  if cloud is None:
49
48
  res = choose_one_option(msg="which cloud", options=config.sections(), header="CLOUD MOUNT", default=None)
50
49
  if type(res) is str: cloud = res
51
50
  else: raise ValueError("no cloud selected")
52
51
 
53
-
54
52
  if network is None:
55
- if destination is None: mount_loc = P(DEFAULT_MOUNT).expanduser().joinpath(cloud)
56
- else: mount_loc = P(destination)
57
-
58
- if platform.system() == "Windows": mount_loc.parent.create()
59
- elif platform.system() == "Linux": mount_loc.create()
53
+ if destination is None:
54
+ mount_loc = P(DEFAULT_MOUNT).expanduser().joinpath(cloud)
55
+ else:
56
+ mount_loc = P(destination)
57
+
58
+ if platform.system() == "Windows":
59
+ mount_loc.parent.create()
60
+ elif platform.system() == "Linux":
61
+ try: mount_loc.create()
62
+ except (FileExistsError, OSError) as err:
63
+ # We need a umount command here.
64
+ print(err)
65
+ pass
60
66
  else: raise ValueError("unsupported platform")
61
67
 
62
68
  elif network and platform.system() == "Windows": mount_loc = "X: --network-mode"
@@ -65,7 +71,9 @@ def mount(cloud: Optional[str], network: Optional[str], destination: Optional[st
65
71
  mount_cmd = f"rclone mount {cloud}: {mount_loc} --vfs-cache-mode full --file-perms=0777"
66
72
 
67
73
  # txt = get_mprocs_mount_txt(cloud, mount_cmd)
68
- if platform.system() == "Windows": txt = f"""
74
+ if platform.system() == "Windows":
75
+
76
+ txt = f"""
69
77
  wt --window 0 --profile "Windows PowerShell" --startingDirectory "$HOME/data/rclone" `; split-pane --horizontal --profile "Command Prompt" --size 0.2 powershell -Command "{mount_cmd}" `; split-pane --vertical --profile "Windows PowerShell" --size 0.2 powershell -NoExit -Command "rclone about {cloud}:" `; move-focus up
70
78
  """
71
79
  elif platform.system() == "Linux": txt = f"""
@@ -74,8 +82,10 @@ ZJ_SESSIONS=$(zellij list-sessions)
74
82
 
75
83
  if [[ "${{ZJ_SESSIONS}}" != *"(current)"* ]]; then
76
84
  echo "Not inside a zellij session ..."
77
- echo '{mount_cmd}'
78
- exit 1
85
+ echo '{mount_cmd} --daemon'
86
+ # exit 1
87
+
88
+ {mount_cmd} --daemon
79
89
  fi
80
90
 
81
91
  zellij run --direction down --name rclone -- {mount_cmd}
@@ -1,14 +1,15 @@
1
1
 
2
2
  """utils"""
3
3
 
4
-
5
- from machineconfig.utils.utils import CONFIG_PATH, DEFAULTS_PATH, write_shell_script, get_shell_file_executing_python_script
6
- from crocodile.file_management import P, Read, install_n_import
4
+ import git
5
+ from crocodile.file_management import P, Read
7
6
  from crocodile.core import randstr
8
7
  from crocodile.meta import Terminal
8
+
9
+ from machineconfig.utils.utils import CONFIG_PATH, DEFAULTS_PATH, PROGRAM_PATH, write_shell_script, get_shell_file_executing_python_script, get_shell_script, choose_one_option
9
10
  import argparse
10
11
  import platform
11
- from typing import Optional
12
+ from typing import Optional, Literal
12
13
  # import sys
13
14
  # import subprocess
14
15
 
@@ -20,14 +21,16 @@ def get_wt_cmd(wd1: P, wd2: P) -> str:
20
21
  ]
21
22
  return " `; ".join(lines)
22
23
 
24
+
23
25
  def get_zellij_cmd(wd1: P, wd2: P) -> str:
24
- lines = [f""" zellij action new-tab --name gitdiff""",
25
- f"""zellij action new-pane --direction down --name local --cwd ./data """,
26
- f"""zellij action write-chars "cd '{wd1}'; git status" """,
27
- f"""zellij action move-focus up; zellij action close-pane """,
28
- f"""zellij action new-pane --direction down --name remote --cwd code """,
29
- f"""zellij action write-chars "cd '{wd2}' """,
30
- f"""git status" """
26
+ _ = wd1, wd2
27
+ lines = [""" zellij action new-tab --name gitdiff""",
28
+ """zellij action new-pane --direction down --name local --cwd ./data """,
29
+ """zellij action write-chars "cd '{wd1}'; git status" """,
30
+ """zellij action move-focus up; zellij action close-pane """,
31
+ """zellij action new-pane --direction down --name remote --cwd code """,
32
+ """zellij action write-chars "cd '{wd2}' """,
33
+ """git status" """
31
34
  ]
32
35
  return "; ".join(lines)
33
36
 
@@ -42,20 +45,23 @@ def args_parser():
42
45
 
43
46
  parser.add_argument("--cloud", "-c", help="rclone cloud profile name.", default=None)
44
47
  parser.add_argument("--message", "-m", help="Commit Message", default=f"new message {randstr()}")
45
- parser.add_argument("--skip_confirmation", "-s", help="Skip confirmation.", action="store_true", default=False)
48
+ # parser.add_argument("--skip_confirmation", "-s", help="Skip confirmation.", action="store_true", default=False)
46
49
  # parser.add_argument("--key", "-k", help="Key for encryption", default=None)
47
50
  parser.add_argument("--pwd", "-p", help="Password for encryption", default=None)
48
- parser.add_argument("--no_push", "-u", help="push to reomte.", action="store_true") # default is False
51
+ # parser.add_argument("--no_push", "-u", help="push to reomte.", action="store_true") # default is False
52
+ parser.add_argument("--action", "-a", help="Action to take if merge fails.", choices=["ask", "pushLocalMerge", "overwriteLocal", "InspectRepos", "RemoveLocalRclone"], default="ask")
49
53
  args = parser.parse_args()
50
54
 
51
55
  # if args.share:
52
56
  # from machineconfig.scripts.cloud.dotfiles import put
53
57
  # put()
54
58
  # return None
55
- main(cloud=args.cloud, path=args.path, message=args.message, skip_confirmation=args.skip_confirmation, pwd=args.pwd, push=not args.no_push)
59
+ main(cloud=args.cloud, path=args.path, message=args.message, action=args.action)
56
60
 
57
61
 
58
- def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optional[str] = None, skip_confirmation: bool = False, pwd: Optional[str] = None, push: bool = True):
62
+ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optional[str] = None,
63
+ action: Literal["ask", "pushLocalMerge", "overwriteLocal", "InspectRepos", "RemoveLocalRclone"] = "ask",
64
+ pwd: Optional[str] = None):
59
65
  if cloud is None:
60
66
  try:
61
67
  cloud_resolved = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
@@ -65,78 +71,112 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
65
71
  return ""
66
72
  else: cloud_resolved = cloud
67
73
  # repo_root = P(args.repo).expanduser().absolute()
68
- repo_root = P.cwd() if path is None else P(path).expanduser().absolute()
69
- repo_obj = install_n_import("git", "gitpython").Repo(repo_root, search_parent_directories=True)
70
- repo_root = P(repo_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
74
+ repo_local_root = P.cwd() if path is None else P(path).expanduser().absolute()
75
+ repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
76
+ repo_local_root = P(repo_local_obj.working_dir) # cwd might have been in a sub directory of repo_root, so its better to redefine it.
71
77
  CONFIG_PATH.joinpath("remote").create()
72
- repo_sync_root = CONFIG_PATH.joinpath("remote", repo_root.rel2home()) # .delete(sure=True)
78
+ repo_remote_root = CONFIG_PATH.joinpath("remote", repo_local_root.rel2home()) # .delete(sure=True)
73
79
  try:
74
80
  print("\n", "=============================== Downloading Remote Repo ====================================")
75
- remote_path = repo_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
76
- repo_sync_root.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=pwd)
81
+ remote_path = repo_local_root.get_remote_path(rel2home=True, os_specific=False, root="myhome") + ".zip.enc"
82
+ repo_remote_root.from_cloud(remotepath=remote_path, cloud=cloud_resolved, unzip=True, decrypt=True, rel2home=True, os_specific=False, pwd=pwd)
77
83
  except AssertionError:
78
84
  print("Remote does not exist, creating it and exiting ... ")
79
- repo_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
85
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
80
86
  return ""
81
- repo_sync_obj = install_n_import("git", "gitpython").Repo(repo_sync_root)
82
- if repo_sync_obj.is_dirty():
83
- print("=" * 50, '\n', f"WRANING: the remote `{repo_sync_root}` is dirty, please commit or stash changes before proceeding.", '\n', "=" * 50)
87
+ repo_remote_obj = git.Repo(repo_remote_root)
88
+ if repo_remote_obj.is_dirty():
89
+ print("=" * 50, '\n', f"WRANING: the remote `{repo_remote_root}` is dirty, please commit or stash changes before proceeding.", '\n', "=" * 50)
84
90
 
85
91
  script = f"""
86
92
  echo ""
87
93
  echo "=============================== Committing Local Changes ==================================="
88
- cd {repo_root}
94
+ cd {repo_local_root}
89
95
  git status
90
96
  git add .
91
97
  git commit -am "{message}"
92
98
  echo ""
93
99
  echo ""
94
100
  echo "=============================== Pulling Latest From Remote ================================"
95
- cd {repo_root}
101
+ cd {repo_local_root}
96
102
  echo '-> Trying to removing originEnc remote from local repo if it exists.'
97
103
  git remote remove originEnc
98
104
  echo '-> Adding originEnc remote to local repo'
99
- git remote add originEnc {repo_sync_root}
105
+ git remote add originEnc {repo_remote_root}
100
106
  echo '-> Fetching originEnc remote.'
101
107
  git pull originEnc master
102
108
 
103
109
  """
104
- suffix = '.ps1' if platform.system() == 'Windows' else '.sh'
105
- res = Terminal().run(f". {P.tmpfile(suffix=suffix).write_text(script)}", shell="powershell").capture().print()
110
+
111
+ shell_path = get_shell_script(shell_script=script)
112
+ res = Terminal().run(f". {shell_path}", shell="powershell").capture().print()
106
113
 
107
114
  if res.is_successful(strict_err=True, strict_returcode=True):
108
115
  print("\n", "Pull succeeded, removing originEnc, the local copy of remote & pushing merged repo_root to remote ... ")
109
- repo_sync_root.delete(sure=True)
116
+ repo_remote_root.delete(sure=True)
110
117
  from git.remote import Remote
111
- Remote.remove(repo_obj, "originEnc")
112
- if push:
113
- repo_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
118
+ Remote.remove(repo_local_obj, "originEnc")
119
+ repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
114
120
  else:
115
- print(f"Failed to pull, keeping local copy of remote at {repo_sync_root} ... ")
121
+ print(f"Failed to merge with no errors, keeping local copy of remote at {repo_remote_root} ... ")
116
122
 
117
- if push:
118
- if skip_confirmation: resp = "y"
119
- else: resp = input(f"Would you like to proceed syncing `{repo_root}` to `{cloud_resolved}` by pushing local changes to remote and deleting local copy of remote? y/[n] ") or "n"
120
- else: resp = "n"
121
-
122
- if resp.lower() == "y":
123
- delete_remote_repo_copy_and_push_local(remote_repo=repo_sync_root.str, local_repo=repo_root.str, cloud=cloud_resolved)
124
- else:
125
- program = f"""
123
+ # ================================================================================
124
+ option1 = 'Delete remote copy and push local:'
125
+ program_1_py = f"""
126
126
  from machineconfig.scripts.python.cloud_repo_sync import delete_remote_repo_copy_and_push_local as func
127
- func(remote_repo=r'{repo_sync_root.str}', local_repo=r'{repo_root.str}', cloud=r'{cloud_resolved}')
127
+ func(remote_repo=r'{repo_remote_root.to_str()}', local_repo=r'{repo_local_root.to_str()}', cloud=r'{cloud_resolved}')
128
+ """
129
+ shell_file_1 = get_shell_file_executing_python_script(python_script=program_1_py, ve_name="ve")
130
+ # ================================================================================
131
+
132
+ option2 = 'Delete local repo and replace it with remote copy:'
133
+ program_2 = f"""
134
+ rm -rfd {repo_local_root}
135
+ mv {repo_remote_root} {repo_local_root}
136
+ sudo chmod 600 ~/.ssh/*
137
+ sudo chmod 700 ~/.ssh
138
+ """
139
+
140
+ shell_file_2 = get_shell_script(shell_script=program_2)
141
+
142
+ # ================================================================================
143
+ option3 = 'Inspect repos:'
144
+ program_3_py = f"""
145
+ from machineconfig.scripts.python.cloud_repo_sync import inspect_repos as func
146
+ func(repo_local_root=r'{repo_local_root.to_str()}', repo_remote_root=r'{repo_remote_root.to_str()}')
147
+ """
148
+ shell_file_3 = get_shell_file_executing_python_script(python_script=program_3_py, ve_name="ve")
149
+ # ================================================================================
150
+
151
+ option4 = 'Remove problematic rclone file from repo and replace with remote:'
152
+ program_4 = """
153
+ rm ~/dotfiles/creds/rclone/rclone.conf
154
+ cp ~/.config/machineconfig/remote/dotfiles/creds/rclone/rclone.conf ~/dotfiles/creds/rclone
128
155
  """
129
- shell_file = get_shell_file_executing_python_script(python_script=program)
130
- print(f"When ready, use this snippet: \n. {shell_file}")
131
- if platform.system() == "Windows":
132
- program = get_wt_cmd(wd1=repo_root, wd2=repo_sync_root)
133
- write_shell_script(program=program, execute=True)
134
- return None
135
- elif platform.system() == "Linux":
136
- program = get_zellij_cmd(wd1=repo_root, wd2=repo_sync_root)
137
- write_shell_script(program=program, execute=True)
138
- return None
139
- else: raise NotImplementedError(f"Platform {platform.system()} not implemented.")
156
+ shell_file_4 = get_shell_script(shell_script=program_4)
157
+ # ================================================================================
158
+
159
+ print(f"• {option1:75} 👉 {shell_file_1}")
160
+ print(f"• {option2:75} 👉 {shell_file_2}")
161
+ print(f"• {option3:75} 👉 {shell_file_3}")
162
+ print(f"• {option4:75} 👉 {shell_file_4}")
163
+
164
+ match action:
165
+ case "ask":
166
+ choice = choose_one_option(options=[option1, option2, option3, option4])
167
+ if choice == option1: PROGRAM_PATH.write_text(shell_file_1.read_text())
168
+ elif choice == option2: PROGRAM_PATH.write_text(program_2)
169
+ elif choice == option3: PROGRAM_PATH.write_text(shell_file_3.read_text())
170
+ elif choice == option4: PROGRAM_PATH.write_text(program_4)
171
+ else: raise NotImplementedError(f"Choice {choice} not implemented.")
172
+ case "pushLocalMerge":
173
+ PROGRAM_PATH.write_text(shell_file_1.read_text())
174
+ case "overwriteLocal":
175
+ PROGRAM_PATH.write_text(program_2)
176
+ case "InspectRepos":
177
+ PROGRAM_PATH.write_text(shell_file_3.read_text())
178
+ case "RemoveLocalRclone":
179
+ PROGRAM_PATH.write_text(program_4)
140
180
 
141
181
 
142
182
  def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cloud: str):
@@ -150,5 +190,18 @@ def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cl
150
190
  repo_root_path.to_cloud(cloud=cloud, zip=True, encrypt=True, rel2home=True, os_specific=False)
151
191
 
152
192
 
193
+ def inspect_repos(repo_local_root: str, repo_remote_root: str):
194
+ if platform.system() == "Windows":
195
+ program = get_wt_cmd(wd1=P(repo_local_root), wd2=P(repo_local_root))
196
+ write_shell_script(program=program, execute=True)
197
+ return None
198
+ elif platform.system() == "Linux":
199
+ program = get_zellij_cmd(wd1=P(repo_local_root), wd2=P(repo_remote_root))
200
+ write_shell_script(program=program, execute=True)
201
+ return None
202
+ else: raise NotImplementedError(f"Platform {platform.system()} not implemented.")
203
+
204
+
205
+
153
206
  if __name__ == "__main__":
154
207
  args_parser()
@@ -4,27 +4,30 @@ TODO: use tap typed-argument-parser to parse args
4
4
  TODO: use typer to make clis
5
5
  """
6
6
 
7
- from crocodile.file_management import P, Read, Struct
8
- from crocodile.core import install_n_import
7
+ from crocodile.file_management import P, Read
8
+ from crocodile.core import Struct
9
9
  from machineconfig.utils.utils import PROGRAM_PATH, DEFAULTS_PATH
10
10
  from machineconfig.scripts.python.cloud_mount import get_mprocs_mount_txt
11
11
  import argparse
12
12
  import os
13
13
  from typing import Optional
14
14
  # from dataclasses import dataclass
15
+ # install_n_import("pydantic")
15
16
  # from tap import Tap
17
+ from pydantic.dataclasses import dataclass
18
+ from pydantic import ConfigDict
16
19
 
17
20
 
18
21
  ES = "^" # chosen carefully to not mean anything on any shell. `$` was a bad choice.
19
22
 
20
23
 
21
24
  class ArgsDefaults:
22
- # source: str = None
23
- # target: str = None
24
- encrypt: bool = False
25
- zip_: bool = False
26
- overwrite: bool = False
27
- share: bool = False
25
+ # source: str=None
26
+ # target: str=None
27
+ encrypt: bool=False
28
+ zip_: bool=False
29
+ overwrite: bool=False
30
+ share: bool=False
28
31
  rel2home = False
29
32
  root = None
30
33
  os_specific = False
@@ -32,24 +35,19 @@ class ArgsDefaults:
32
35
  pwd = None
33
36
 
34
37
 
35
- install_n_import("pydantic")
36
- from pydantic.dataclasses import dataclass # type: ignore # ruffle: ignore
37
- from pydantic import ConfigDict
38
-
39
-
40
- @dataclass(config=ConfigDict(extra="forbid", frozen=True))
38
+ @dataclass(config=ConfigDict(extra="forbid", frozen=False))
41
39
  class Args():
42
40
  cloud: Optional[str] = None
43
41
 
44
- zip: bool = ArgsDefaults.zip_
45
- overwrite: bool = ArgsDefaults.overwrite
46
- share: bool = ArgsDefaults.share
42
+ zip: bool=ArgsDefaults.zip_
43
+ overwrite: bool=ArgsDefaults.overwrite
44
+ share: bool=ArgsDefaults.share
47
45
 
48
46
  root: Optional[str] = ArgsDefaults.root
49
- os_specific: bool = ArgsDefaults.os_specific
50
- rel2home: bool = ArgsDefaults.rel2home
47
+ os_specific: bool=ArgsDefaults.os_specific
48
+ rel2home: bool=ArgsDefaults.rel2home
51
49
 
52
- encrypt: bool = ArgsDefaults.encrypt
50
+ encrypt: bool=ArgsDefaults.encrypt
53
51
  key: Optional[str] = ArgsDefaults.key
54
52
  pwd: Optional[str] = ArgsDefaults.pwd
55
53
 
@@ -72,7 +70,7 @@ def absolute(path: str) -> P:
72
70
  return obj.absolute()
73
71
 
74
72
 
75
- def get_secure_share_cloud_config(interactive: bool = True) -> Args:
73
+ def get_secure_share_cloud_config(interactive: bool=True) -> Args:
76
74
  if os.environ.get("CLOUD_CONFIG_NAME") is not None:
77
75
  default_cloud = os.environ.get("CLOUD_CONFIG_NAME")
78
76
  assert default_cloud is not None
@@ -100,7 +98,7 @@ def get_secure_share_cloud_config(interactive: bool = True) -> Args:
100
98
  pwd=pwd, encrypt=True,
101
99
  zip=True, overwrite=True, share=True,
102
100
  rel2home=True, root="myshare", os_specific=False,)
103
- Struct(res.__dict__).print(as_config=True, title=f"⚠️ Using SecureShare cloud config")
101
+ Struct(res.__dict__).print(as_config=True, title="⚠️ Using SecureShare cloud config")
104
102
  return res
105
103
 
106
104
 
@@ -115,55 +113,62 @@ def find_cloud_config(path: P):
115
113
 
116
114
 
117
115
  def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str, str, str]:
116
+ config = args.config
117
+ root = args.root
118
+ rel2home = args.rel2home
119
+ pwd = args.pwd
120
+ encrypt = args.encrypt
121
+ zip_arg = args.zip
122
+ share = args.share
123
+ os_specific = args.os_specific
124
+
125
+ if config == "ss":
126
+ maybe_config = get_secure_share_cloud_config()
127
+ elif config is not None:
128
+ maybe_config = Args.from_config(absolute(config))
129
+ else:
130
+ maybe_config = None
131
+
118
132
  if source.startswith(":"): # default cloud name is omitted cloud_name: # or ES in source
119
133
  # At the moment, this cloud.json defaults overrides the args and is activated only when source or target are just ":"
120
134
  # consider activating it by a flag, and also not not overriding explicitly passed args options.
121
- assert ES not in target, f"Not Implemented here yet."
135
+ assert ES not in target, "Not Implemented here yet."
122
136
  path = absolute(target)
123
- if args.config is None:
124
- maybe_config: Optional[Args] = find_cloud_config(path=path)
125
- else:
126
- if args.config == "ss": maybe_config = get_secure_share_cloud_config()
127
- else: maybe_config = Args.from_config(absolute(args.config))
137
+ if maybe_config is None: maybe_config: Optional[Args] = find_cloud_config(path=path)
128
138
 
129
139
  if maybe_config is None:
130
- default_cloud: str = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
140
+ default_cloud: str=Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
131
141
  print(f"⚠️ Using default cloud: {default_cloud}")
132
142
  source = default_cloud + ":" + source[1:]
133
143
  else:
134
144
  tmp = maybe_config
135
145
  source = f"{tmp.cloud}:" + source[1:]
136
- args.root = tmp.root
137
- args.rel2home = tmp.rel2home
138
- args.pwd = tmp.pwd
139
- args.encrypt = tmp.encrypt
140
- args.zip = tmp.zip
141
- args.share = tmp.share
142
- # args.jh = 22
146
+ root = tmp.root
147
+ rel2home = tmp.rel2home
148
+ pwd = tmp.pwd
149
+ encrypt = tmp.encrypt
150
+ zip_arg = tmp.zip
151
+ share = tmp.share
143
152
 
144
153
  if target.startswith(":"): # default cloud name is omitted cloud_name: # or ES in target
145
- assert ES not in source, f"Not Implemented here yet."
154
+ assert ES not in source, "Not Implemented here yet."
146
155
  path = absolute(source)
147
- if args.config is None:
148
- maybe_config = find_cloud_config(path)
149
- else:
150
- if args.config == "ss": maybe_config = get_secure_share_cloud_config()
151
- else: maybe_config = Args.from_config(absolute(args.config))
152
-
153
- if maybe_config is not None:
154
- tmp = maybe_config
155
- target = f"{tmp.cloud}:" + target[1:]
156
- args.root = tmp.root
157
- args.rel2home = tmp.rel2home
158
- args.pwd = tmp.pwd
159
- args.encrypt = tmp.encrypt
160
- args.zip = tmp.zip
161
- args.share = tmp.share
156
+ if maybe_config is None: maybe_config = find_cloud_config(path)
162
157
 
163
- else:
158
+ if maybe_config is None:
164
159
  default_cloud = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
165
160
  print(f"⚠️ Using default cloud: {default_cloud}")
166
161
  target = default_cloud + ":" + target[1:]
162
+ else:
163
+ tmp = maybe_config
164
+ target = f"{tmp.cloud}:" + target[1:]
165
+ root = tmp.root
166
+ rel2home = tmp.rel2home
167
+ pwd = tmp.pwd
168
+ encrypt = tmp.encrypt
169
+ zip_arg = tmp.zip
170
+ share = tmp.share
171
+
167
172
 
168
173
  if ":" in source and (source[1] != ":" if len(source) > 1 else True): # avoid the deceptive case of "C:/"
169
174
  source_parts: list[str] = source.split(":")
@@ -172,37 +177,37 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
172
177
  if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
173
178
  assert ES not in target, f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised."
174
179
  target_obj = absolute(target)
175
- remote_path = target_obj.get_remote_path(os_specific=args.os_specific, root=args.root, rel2home=args.rel2home, strict=False)
180
+ remote_path = target_obj.get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
176
181
  source = f"{cloud}:{remote_path.as_posix()}"
177
-
178
182
  else: # source path is mentioned, target? maybe.
179
183
  if target == ES: # target path is to be inferred from source.
180
- raise NotImplementedError(f"There is no .get_local_path method yet")
184
+ raise NotImplementedError("There is no .get_local_path method yet")
181
185
  else:
182
186
  target_obj = absolute(target)
183
- if args.zip and ".zip" not in source: source += ".zip"
184
- if args.encrypt and ".enc" not in source: source += ".enc"
187
+ if zip_arg and ".zip" not in source: source += ".zip"
188
+ if encrypt and ".enc" not in source: source += ".enc"
185
189
 
186
190
  elif ":" in target and (target[1] != ":" if len(target) > 1 else True): # avoid the case of "C:/"
187
191
  target_parts: list[str] = target.split(":")
188
192
  cloud = target.split(":")[0]
189
193
 
190
194
  if len(target_parts) > 1 and target_parts[1] == ES: # the target path is to be inferred from source.
191
- assert ES not in source, f"You can't use $ in both source and target. Cyclical inference dependency arised."
195
+ assert ES not in source, "You can't use $ in both source and target. Cyclical inference dependency arised."
192
196
  source_obj = absolute(source)
193
- remote_path = source_obj.get_remote_path(os_specific=args.os_specific, root=args.root, rel2home=args.rel2home, strict=False)
197
+ remote_path = source_obj.get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
194
198
  target = f"{cloud}:{remote_path.as_posix()}"
195
199
  else: # target path is mentioned, source? maybe.
196
200
  target = str(target)
197
201
  if source == ES:
198
- raise NotImplementedError(f"There is no .get_local_path method yet")
202
+ raise NotImplementedError("There is no .get_local_path method yet")
199
203
  else:
200
204
  source_obj = absolute(source)
201
- if args.zip and ".zip" not in target: target += ".zip"
202
- if args.encrypt and ".enc" not in target: target += ".enc"
205
+ if zip_arg and ".zip" not in target: target += ".zip"
206
+ if encrypt and ".enc" not in target: target += ".enc"
203
207
  else:
204
208
  raise ValueError("Either source or target must be a remote path (i.e. machine:path)")
205
209
  Struct({"cloud": cloud, "source": str(source), "target": str(target)}).print(as_config=True, title="CLI Resolution")
210
+ _ = pwd, encrypt, zip_arg, share
206
211
  return cloud, str(source), str(target)
207
212
 
208
213
 
@@ -226,11 +231,11 @@ def args_parser():
226
231
 
227
232
  args = parser.parse_args()
228
233
  args_dict = vars(args)
229
- source: str = args_dict.pop("source")
230
- target: str = args_dict.pop("target")
231
- verbose: bool = args_dict.pop("verbose")
232
- delete: bool = args_dict.pop("delete")
233
- bisync: bool = args_dict.pop("bisync")
234
+ source: str=args_dict.pop("source")
235
+ target: str=args_dict.pop("target")
236
+ verbose: bool=args_dict.pop("verbose")
237
+ delete: bool=args_dict.pop("delete")
238
+ bisync: bool=args_dict.pop("bisync")
234
239
  transfers: int = args_dict.pop("transfers")
235
240
  args_obj = Args(**args_dict)
236
241