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.
- machineconfig/__init__.py +4 -2
- machineconfig/jobs/python/check_installations.py +8 -6
- machineconfig/jobs/python/checkout_version.py +27 -32
- machineconfig/jobs/python/create_bootable_media.py +1 -1
- machineconfig/jobs/python/python_cargo_build_share.py +2 -2
- machineconfig/jobs/python/tasks.py +2 -2
- machineconfig/jobs/python_custom_installers/gh.py +53 -0
- machineconfig/jobs/python_custom_installers/hx.py +55 -0
- machineconfig/profile/create.py +26 -21
- machineconfig/profile/create_hardlinks.py +101 -0
- machineconfig/profile/shell.py +5 -5
- machineconfig/scripts/python/choose_wezterm_theme.py +96 -0
- machineconfig/scripts/python/cloud_copy.py +24 -17
- machineconfig/scripts/python/cloud_mount.py +20 -10
- machineconfig/scripts/python/cloud_repo_sync.py +109 -56
- machineconfig/scripts/python/cloud_sync.py +73 -68
- machineconfig/scripts/python/croshell.py +23 -14
- machineconfig/scripts/python/devops.py +19 -20
- machineconfig/scripts/python/devops_backup_retrieve.py +19 -10
- machineconfig/scripts/python/devops_devapps_install.py +81 -57
- machineconfig/scripts/python/devops_update_repos.py +5 -5
- machineconfig/scripts/python/dotfile.py +4 -4
- machineconfig/scripts/python/fire_jobs.py +139 -66
- machineconfig/scripts/python/ftpx.py +17 -7
- machineconfig/scripts/python/gh_models.py +53 -0
- machineconfig/scripts/python/mount_nfs.py +1 -1
- machineconfig/scripts/python/mount_nw_drive.py +3 -3
- machineconfig/scripts/python/mount_ssh.py +2 -3
- machineconfig/scripts/python/pomodoro.py +1 -1
- machineconfig/scripts/python/repos.py +26 -23
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +10 -4
- machineconfig/scripts/python/start_terminals.py +6 -5
- machineconfig/scripts/python/wifi_conn.py +34 -42
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +1 -1
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +3 -2
- machineconfig/utils/installer.py +167 -60
- machineconfig/utils/procs.py +2 -2
- machineconfig/utils/scheduling.py +3 -3
- machineconfig/utils/utils.py +137 -56
- machineconfig/utils/ve.py +171 -100
- machineconfig-1.91.dist-info/LICENSE +201 -0
- {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/METADATA +31 -11
- machineconfig-1.91.dist-info/RECORD +69 -0
- {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/WHEEL +1 -1
- machineconfig/jobs/script_installer/azure_data_studio.py +0 -22
- machineconfig/jobs/script_installer/bypass_paywall.py +0 -23
- machineconfig/jobs/script_installer/code.py +0 -34
- machineconfig/jobs/script_installer/docker_desktop.py +0 -41
- machineconfig/jobs/script_installer/ngrok.py +0 -29
- machineconfig/jobs/script_installer/skim.py +0 -21
- machineconfig/jobs/script_installer/wezterm.py +0 -34
- machineconfig-1.8.dist-info/RECORD +0 -70
- /machineconfig/jobs/{script_installer → python_custom_installers}/__init__.py +0 -0
- {machineconfig-1.8.dist-info → machineconfig-1.91.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
"""
|
|
2
|
+
"""
|
|
3
|
+
CC
|
|
3
4
|
"""
|
|
4
5
|
|
|
5
|
-
from crocodile.file_management import P
|
|
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
|
|
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
|
|
66
|
-
target: str
|
|
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=
|
|
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")):
|
|
71
|
-
|
|
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,
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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":
|
|
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
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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 =
|
|
76
|
-
|
|
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
|
-
|
|
85
|
+
repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
|
|
80
86
|
return ""
|
|
81
|
-
|
|
82
|
-
if
|
|
83
|
-
print("=" * 50, '\n', f"WRANING: the remote `{
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
105
|
-
|
|
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
|
-
|
|
116
|
+
repo_remote_root.delete(sure=True)
|
|
110
117
|
from git.remote import Remote
|
|
111
|
-
Remote.remove(
|
|
112
|
-
|
|
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
|
|
121
|
+
print(f"Failed to merge with no errors, keeping local copy of remote at {repo_remote_root} ... ")
|
|
116
122
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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'{
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
8
|
-
from crocodile.core 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
|
|
23
|
-
# target: str
|
|
24
|
-
encrypt: bool
|
|
25
|
-
zip_: bool
|
|
26
|
-
overwrite: bool
|
|
27
|
-
share: bool
|
|
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
|
-
|
|
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
|
|
45
|
-
overwrite: bool
|
|
46
|
-
share: bool
|
|
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
|
|
50
|
-
rel2home: bool
|
|
47
|
+
os_specific: bool=ArgsDefaults.os_specific
|
|
48
|
+
rel2home: bool=ArgsDefaults.rel2home
|
|
51
49
|
|
|
52
|
-
encrypt: bool
|
|
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
|
|
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=
|
|
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,
|
|
135
|
+
assert ES not in target, "Not Implemented here yet."
|
|
122
136
|
path = absolute(target)
|
|
123
|
-
if
|
|
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
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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,
|
|
154
|
+
assert ES not in source, "Not Implemented here yet."
|
|
146
155
|
path = absolute(source)
|
|
147
|
-
if
|
|
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
|
-
|
|
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=
|
|
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(
|
|
184
|
+
raise NotImplementedError("There is no .get_local_path method yet")
|
|
181
185
|
else:
|
|
182
186
|
target_obj = absolute(target)
|
|
183
|
-
if
|
|
184
|
-
if
|
|
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,
|
|
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=
|
|
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(
|
|
202
|
+
raise NotImplementedError("There is no .get_local_path method yet")
|
|
199
203
|
else:
|
|
200
204
|
source_obj = absolute(source)
|
|
201
|
-
if
|
|
202
|
-
if
|
|
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
|
|
230
|
-
target: str
|
|
231
|
-
verbose: bool
|
|
232
|
-
delete: bool
|
|
233
|
-
bisync: bool
|
|
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
|
|