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.
- machineconfig/__init__.py +2 -4
- machineconfig/jobs/python/check_installations.py +14 -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/{helix.py → hx.py} +21 -6
- machineconfig/profile/create.py +23 -21
- machineconfig/profile/create_hardlinks.py +101 -0
- machineconfig/profile/shell.py +10 -7
- machineconfig/scripts/python/cloud_copy.py +19 -16
- machineconfig/scripts/python/cloud_repo_sync.py +94 -47
- machineconfig/scripts/python/cloud_sync.py +78 -61
- machineconfig/scripts/python/croshell.py +6 -6
- machineconfig/scripts/python/devops.py +22 -22
- machineconfig/scripts/python/devops_add_ssh_key.py +1 -1
- machineconfig/scripts/python/devops_backup_retrieve.py +19 -10
- machineconfig/scripts/python/devops_devapps_install.py +80 -62
- machineconfig/scripts/python/devops_update_repos.py +5 -5
- machineconfig/scripts/python/dotfile.py +4 -4
- machineconfig/scripts/python/fire_jobs.py +115 -63
- machineconfig/scripts/python/gh_models.py +55 -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 +22 -23
- machineconfig/scripts/python/scheduler.py +1 -1
- machineconfig/scripts/python/start_slidev.py +12 -6
- machineconfig/scripts/python/start_terminals.py +5 -4
- machineconfig/scripts/python/wifi_conn.py +34 -42
- machineconfig/scripts/python/wsl_windows_transfer.py +1 -1
- machineconfig/settings/__init__.py +0 -0
- 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 +86 -41
- machineconfig/utils/procs.py +2 -2
- machineconfig/utils/scheduling.py +3 -3
- machineconfig/utils/utils.py +136 -56
- machineconfig/utils/ve.py +145 -95
- {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/METADATA +160 -155
- machineconfig-1.92.dist-info/RECORD +70 -0
- machineconfig/jobs/python_custom_installers/azuredatastudio.py +0 -36
- machineconfig/jobs/python_custom_installers/bypass_paywall.py +0 -30
- machineconfig/jobs/python_custom_installers/docker_desktop.py +0 -52
- machineconfig/jobs/python_custom_installers/goes.py +0 -35
- machineconfig/jobs/python_custom_installers/lvim.py +0 -48
- machineconfig/jobs/python_custom_installers/ngrok.py +0 -39
- machineconfig/jobs/python_custom_installers/nvim.py +0 -48
- machineconfig/jobs/python_custom_installers/vscode.py +0 -45
- machineconfig/jobs/python_custom_installers/wezterm.py +0 -41
- machineconfig-1.9.dist-info/RECORD +0 -76
- {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/LICENSE +0 -0
- {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/WHEEL +0 -0
- {machineconfig-1.9.dist-info → machineconfig-1.92.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
|
@@ -22,13 +23,14 @@ def get_wt_cmd(wd1: P, wd2: P) -> str:
|
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
def get_zellij_cmd(wd1: P, wd2: P) -> str:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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" """
|
|
32
34
|
]
|
|
33
35
|
return "; ".join(lines)
|
|
34
36
|
|
|
@@ -43,20 +45,23 @@ def args_parser():
|
|
|
43
45
|
|
|
44
46
|
parser.add_argument("--cloud", "-c", help="rclone cloud profile name.", default=None)
|
|
45
47
|
parser.add_argument("--message", "-m", help="Commit Message", default=f"new message {randstr()}")
|
|
46
|
-
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)
|
|
47
49
|
# parser.add_argument("--key", "-k", help="Key for encryption", default=None)
|
|
48
50
|
parser.add_argument("--pwd", "-p", help="Password for encryption", default=None)
|
|
49
|
-
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")
|
|
50
53
|
args = parser.parse_args()
|
|
51
54
|
|
|
52
55
|
# if args.share:
|
|
53
56
|
# from machineconfig.scripts.cloud.dotfiles import put
|
|
54
57
|
# put()
|
|
55
58
|
# return None
|
|
56
|
-
main(cloud=args.cloud, path=args.path, message=args.message,
|
|
59
|
+
main(cloud=args.cloud, path=args.path, message=args.message, action=args.action)
|
|
57
60
|
|
|
58
61
|
|
|
59
|
-
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):
|
|
60
65
|
if cloud is None:
|
|
61
66
|
try:
|
|
62
67
|
cloud_resolved = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
|
|
@@ -67,7 +72,7 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
|
|
|
67
72
|
else: cloud_resolved = cloud
|
|
68
73
|
# repo_root = P(args.repo).expanduser().absolute()
|
|
69
74
|
repo_local_root = P.cwd() if path is None else P(path).expanduser().absolute()
|
|
70
|
-
repo_local_obj =
|
|
75
|
+
repo_local_obj = git.Repo(repo_local_root, search_parent_directories=True)
|
|
71
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.
|
|
72
77
|
CONFIG_PATH.joinpath("remote").create()
|
|
73
78
|
repo_remote_root = CONFIG_PATH.joinpath("remote", repo_local_root.rel2home()) # .delete(sure=True)
|
|
@@ -79,7 +84,7 @@ def main(cloud: Optional[str] = None, path: Optional[str] = None, message: Optio
|
|
|
79
84
|
print("Remote does not exist, creating it and exiting ... ")
|
|
80
85
|
repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
|
|
81
86
|
return ""
|
|
82
|
-
repo_remote_obj =
|
|
87
|
+
repo_remote_obj = git.Repo(repo_remote_root)
|
|
83
88
|
if repo_remote_obj.is_dirty():
|
|
84
89
|
print("=" * 50, '\n', f"WRANING: the remote `{repo_remote_root}` is dirty, please commit or stash changes before proceeding.", '\n', "=" * 50)
|
|
85
90
|
|
|
@@ -102,49 +107,79 @@ echo '-> Fetching originEnc remote.'
|
|
|
102
107
|
git pull originEnc master
|
|
103
108
|
|
|
104
109
|
"""
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
|
|
111
|
+
shell_path = get_shell_script(shell_script=script)
|
|
112
|
+
res = Terminal().run(f". {shell_path}", shell="powershell").capture().print()
|
|
107
113
|
|
|
108
114
|
if res.is_successful(strict_err=True, strict_returcode=True):
|
|
109
115
|
print("\n", "Pull succeeded, removing originEnc, the local copy of remote & pushing merged repo_root to remote ... ")
|
|
110
116
|
repo_remote_root.delete(sure=True)
|
|
111
117
|
from git.remote import Remote
|
|
112
118
|
Remote.remove(repo_local_obj, "originEnc")
|
|
113
|
-
|
|
114
|
-
repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
|
|
119
|
+
repo_local_root.to_cloud(cloud=cloud_resolved, zip=True, encrypt=True, rel2home=True, pwd=pwd, os_specific=False)
|
|
115
120
|
else:
|
|
116
|
-
print(f"Failed to
|
|
117
|
-
|
|
118
|
-
if push:
|
|
119
|
-
if skip_confirmation: resp = "y"
|
|
120
|
-
else: resp = input(f"Would you like to proceed syncing `{repo_local_root}` to `{cloud_resolved}` by pushing local changes to remote and deleting local copy of remote? y/[n] ") or "n"
|
|
121
|
-
else: resp = "n"
|
|
121
|
+
print(f"Failed to merge with no errors, keeping local copy of remote at {repo_remote_root} ... ")
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
program = f"""
|
|
123
|
+
# ================================================================================
|
|
124
|
+
option1 = 'Delete remote copy and push local:'
|
|
125
|
+
program_1_py = f"""
|
|
127
126
|
from machineconfig.scripts.python.cloud_repo_sync import delete_remote_repo_copy_and_push_local as func
|
|
128
|
-
func(remote_repo=r'{repo_remote_root.
|
|
127
|
+
func(remote_repo=r'{repo_remote_root.to_str()}', local_repo=r'{repo_local_root.to_str()}', cloud=r'{cloud_resolved}')
|
|
129
128
|
"""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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"""
|
|
135
134
|
rm -rfd {repo_local_root}
|
|
136
135
|
mv {repo_remote_root} {repo_local_root}
|
|
136
|
+
sudo chmod 600 $HOME/.ssh/*
|
|
137
|
+
sudo chmod 700 $HOME/.ssh
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
shell_file_2 = get_shell_script(shell_script=program_2)
|
|
137
141
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 = f"""
|
|
153
|
+
rm $HOME/dotfiles/creds/rclone/rclone.conf
|
|
154
|
+
cp $HOME/.config/machineconfig/remote/dotfiles/creds/rclone/rclone.conf $HOME/dotfiles/creds/rclone
|
|
155
|
+
cd $HOME/dotfiles
|
|
156
|
+
git commit -am "finished merging"
|
|
157
|
+
. {shell_file_1}
|
|
158
|
+
"""
|
|
159
|
+
shell_file_4 = get_shell_script(shell_script=program_4)
|
|
160
|
+
# ================================================================================
|
|
161
|
+
|
|
162
|
+
print(f"• {option1:75} 👉 {shell_file_1}")
|
|
163
|
+
print(f"• {option2:75} 👉 {shell_file_2}")
|
|
164
|
+
print(f"• {option3:75} 👉 {shell_file_3}")
|
|
165
|
+
print(f"• {option4:75} 👉 {shell_file_4}")
|
|
166
|
+
|
|
167
|
+
match action:
|
|
168
|
+
case "ask":
|
|
169
|
+
choice = choose_one_option(options=[option1, option2, option3, option4], fzf=False)
|
|
170
|
+
if choice == option1: PROGRAM_PATH.write_text(shell_file_1.read_text())
|
|
171
|
+
elif choice == option2: PROGRAM_PATH.write_text(program_2)
|
|
172
|
+
elif choice == option3: PROGRAM_PATH.write_text(shell_file_3.read_text())
|
|
173
|
+
elif choice == option4: PROGRAM_PATH.write_text(program_4)
|
|
174
|
+
else: raise NotImplementedError(f"Choice {choice} not implemented.")
|
|
175
|
+
case "pushLocalMerge":
|
|
176
|
+
PROGRAM_PATH.write_text(shell_file_1.read_text())
|
|
177
|
+
case "overwriteLocal":
|
|
178
|
+
PROGRAM_PATH.write_text(program_2)
|
|
179
|
+
case "InspectRepos":
|
|
180
|
+
PROGRAM_PATH.write_text(shell_file_3.read_text())
|
|
181
|
+
case "RemoveLocalRclone":
|
|
182
|
+
PROGRAM_PATH.write_text(program_4)
|
|
148
183
|
|
|
149
184
|
|
|
150
185
|
def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cloud: str):
|
|
@@ -158,5 +193,17 @@ def delete_remote_repo_copy_and_push_local(remote_repo: str, local_repo: str, cl
|
|
|
158
193
|
repo_root_path.to_cloud(cloud=cloud, zip=True, encrypt=True, rel2home=True, os_specific=False)
|
|
159
194
|
|
|
160
195
|
|
|
196
|
+
def inspect_repos(repo_local_root: str, repo_remote_root: str):
|
|
197
|
+
if platform.system() == "Windows":
|
|
198
|
+
program = get_wt_cmd(wd1=P(repo_local_root), wd2=P(repo_local_root))
|
|
199
|
+
write_shell_script(program=program, execute=True, desc="Inspecting repos ...", preserve_cwd=True, display=True)
|
|
200
|
+
return None
|
|
201
|
+
elif platform.system() == "Linux":
|
|
202
|
+
program = get_zellij_cmd(wd1=P(repo_local_root), wd2=P(repo_remote_root))
|
|
203
|
+
write_shell_script(program=program, execute=True, desc="Inspecting repos ...", preserve_cwd=True, display=True)
|
|
204
|
+
return None
|
|
205
|
+
else: raise NotImplementedError(f"Platform {platform.system()} not implemented.")
|
|
206
|
+
|
|
207
|
+
|
|
161
208
|
if __name__ == "__main__":
|
|
162
209
|
args_parser()
|
|
@@ -4,8 +4,8 @@ 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
|
-
|
|
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
|
|
@@ -22,12 +22,12 @@ ES = "^" # chosen carefully to not mean anything on any shell. `$` was a bad ch
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class ArgsDefaults:
|
|
25
|
-
# source: str
|
|
26
|
-
# target: str
|
|
27
|
-
encrypt: bool
|
|
28
|
-
zip_: bool
|
|
29
|
-
overwrite: bool
|
|
30
|
-
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
|
|
31
31
|
rel2home = False
|
|
32
32
|
root = None
|
|
33
33
|
os_specific = False
|
|
@@ -35,13 +35,13 @@ class ArgsDefaults:
|
|
|
35
35
|
pwd = None
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
@dataclass(config=ConfigDict(extra="forbid", frozen=
|
|
38
|
+
@dataclass(config=ConfigDict(extra="forbid", frozen=False))
|
|
39
39
|
class Args():
|
|
40
40
|
cloud: Optional[str] = None
|
|
41
41
|
|
|
42
|
-
zip: bool
|
|
43
|
-
overwrite: bool
|
|
44
|
-
share: bool
|
|
42
|
+
zip: bool=ArgsDefaults.zip_
|
|
43
|
+
overwrite: bool=ArgsDefaults.overwrite
|
|
44
|
+
share: bool=ArgsDefaults.share
|
|
45
45
|
|
|
46
46
|
root: Optional[str] = ArgsDefaults.root
|
|
47
47
|
os_specific: bool = ArgsDefaults.os_specific
|
|
@@ -70,7 +70,7 @@ def absolute(path: str) -> P:
|
|
|
70
70
|
return obj.absolute()
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
def get_secure_share_cloud_config(interactive: bool
|
|
73
|
+
def get_secure_share_cloud_config(interactive: bool=True) -> Args:
|
|
74
74
|
if os.environ.get("CLOUD_CONFIG_NAME") is not None:
|
|
75
75
|
default_cloud = os.environ.get("CLOUD_CONFIG_NAME")
|
|
76
76
|
assert default_cloud is not None
|
|
@@ -98,7 +98,7 @@ def get_secure_share_cloud_config(interactive: bool = True) -> Args:
|
|
|
98
98
|
pwd=pwd, encrypt=True,
|
|
99
99
|
zip=True, overwrite=True, share=True,
|
|
100
100
|
rel2home=True, root="myshare", os_specific=False,)
|
|
101
|
-
Struct(res.__dict__).print(as_config=True, title=
|
|
101
|
+
Struct(res.__dict__).print(as_config=True, title="⚠️ Using SecureShare cloud config")
|
|
102
102
|
return res
|
|
103
103
|
|
|
104
104
|
|
|
@@ -113,55 +113,71 @@ def find_cloud_config(path: P):
|
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str, str, str]:
|
|
116
|
+
config = args.config
|
|
117
|
+
if config == "ss":
|
|
118
|
+
maybe_config = get_secure_share_cloud_config()
|
|
119
|
+
elif config is not None:
|
|
120
|
+
maybe_config = Args.from_config(absolute(config))
|
|
121
|
+
else:
|
|
122
|
+
maybe_config = None
|
|
123
|
+
|
|
124
|
+
if maybe_config is not None:
|
|
125
|
+
if args.zip == ArgsDefaults.zip_: args.zip = maybe_config.zip
|
|
126
|
+
if args.encrypt == ArgsDefaults.encrypt: args.encrypt = maybe_config.encrypt
|
|
127
|
+
if args.share == ArgsDefaults.share: args.share = maybe_config.share
|
|
128
|
+
if args.root == ArgsDefaults.root: args.root = maybe_config.root
|
|
129
|
+
if args.rel2home == ArgsDefaults.rel2home: args.rel2home = maybe_config.rel2home
|
|
130
|
+
if args.pwd == ArgsDefaults.pwd: args.pwd = maybe_config.pwd
|
|
131
|
+
if args.os_specific == ArgsDefaults.os_specific: args.os_specific = maybe_config.os_specific
|
|
132
|
+
|
|
133
|
+
root = args.root
|
|
134
|
+
rel2home = args.rel2home
|
|
135
|
+
pwd = args.pwd
|
|
136
|
+
encrypt = args.encrypt
|
|
137
|
+
zip_arg = args.zip
|
|
138
|
+
share = args.share
|
|
139
|
+
os_specific = args.os_specific
|
|
140
|
+
|
|
116
141
|
if source.startswith(":"): # default cloud name is omitted cloud_name: # or ES in source
|
|
117
142
|
# At the moment, this cloud.json defaults overrides the args and is activated only when source or target are just ":"
|
|
118
143
|
# consider activating it by a flag, and also not not overriding explicitly passed args options.
|
|
119
|
-
assert ES not in target,
|
|
144
|
+
assert ES not in target, "Not Implemented here yet."
|
|
120
145
|
path = absolute(target)
|
|
121
|
-
if
|
|
122
|
-
maybe_config: Optional[Args] = find_cloud_config(path=path)
|
|
123
|
-
else:
|
|
124
|
-
if args.config == "ss": maybe_config = get_secure_share_cloud_config()
|
|
125
|
-
else: maybe_config = Args.from_config(absolute(args.config))
|
|
146
|
+
if maybe_config is None: maybe_config: Optional[Args] = find_cloud_config(path=path)
|
|
126
147
|
|
|
127
148
|
if maybe_config is None:
|
|
128
|
-
default_cloud: str
|
|
149
|
+
default_cloud: str=Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
|
|
129
150
|
print(f"⚠️ Using default cloud: {default_cloud}")
|
|
130
151
|
source = default_cloud + ":" + source[1:]
|
|
131
152
|
else:
|
|
132
153
|
tmp = maybe_config
|
|
133
154
|
source = f"{tmp.cloud}:" + source[1:]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
# args.jh = 22
|
|
155
|
+
root = tmp.root
|
|
156
|
+
rel2home = tmp.rel2home
|
|
157
|
+
pwd = tmp.pwd
|
|
158
|
+
encrypt = tmp.encrypt
|
|
159
|
+
zip_arg = tmp.zip
|
|
160
|
+
share = tmp.share
|
|
141
161
|
|
|
142
162
|
if target.startswith(":"): # default cloud name is omitted cloud_name: # or ES in target
|
|
143
|
-
assert ES not in source,
|
|
163
|
+
assert ES not in source, "Not Implemented here yet."
|
|
144
164
|
path = absolute(source)
|
|
145
|
-
if
|
|
146
|
-
maybe_config = find_cloud_config(path)
|
|
147
|
-
else:
|
|
148
|
-
if args.config == "ss": maybe_config = get_secure_share_cloud_config()
|
|
149
|
-
else: maybe_config = Args.from_config(absolute(args.config))
|
|
165
|
+
if maybe_config is None: maybe_config = find_cloud_config(path)
|
|
150
166
|
|
|
151
|
-
if maybe_config is
|
|
152
|
-
tmp = maybe_config
|
|
153
|
-
target = f"{tmp.cloud}:" + target[1:]
|
|
154
|
-
args.root = tmp.root
|
|
155
|
-
args.rel2home = tmp.rel2home
|
|
156
|
-
args.pwd = tmp.pwd
|
|
157
|
-
args.encrypt = tmp.encrypt
|
|
158
|
-
args.zip = tmp.zip
|
|
159
|
-
args.share = tmp.share
|
|
160
|
-
|
|
161
|
-
else:
|
|
167
|
+
if maybe_config is None:
|
|
162
168
|
default_cloud = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
|
|
163
169
|
print(f"⚠️ Using default cloud: {default_cloud}")
|
|
164
170
|
target = default_cloud + ":" + target[1:]
|
|
171
|
+
else:
|
|
172
|
+
tmp = maybe_config
|
|
173
|
+
target = f"{tmp.cloud}:" + target[1:]
|
|
174
|
+
root = tmp.root
|
|
175
|
+
rel2home = tmp.rel2home
|
|
176
|
+
pwd = tmp.pwd
|
|
177
|
+
encrypt = tmp.encrypt
|
|
178
|
+
zip_arg = tmp.zip
|
|
179
|
+
share = tmp.share
|
|
180
|
+
|
|
165
181
|
|
|
166
182
|
if ":" in source and (source[1] != ":" if len(source) > 1 else True): # avoid the deceptive case of "C:/"
|
|
167
183
|
source_parts: list[str] = source.split(":")
|
|
@@ -170,37 +186,38 @@ def parse_cloud_source_target(args: Args, source: str, target: str) -> tuple[str
|
|
|
170
186
|
if len(source_parts) > 1 and source_parts[1] == ES: # the source path is to be inferred from target.
|
|
171
187
|
assert ES not in target, f"You can't use expand symbol `{ES}` in both source and target. Cyclical inference dependency arised."
|
|
172
188
|
target_obj = absolute(target)
|
|
173
|
-
remote_path = target_obj.get_remote_path(os_specific=
|
|
189
|
+
remote_path = target_obj.get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
174
190
|
source = f"{cloud}:{remote_path.as_posix()}"
|
|
175
|
-
|
|
176
191
|
else: # source path is mentioned, target? maybe.
|
|
177
192
|
if target == ES: # target path is to be inferred from source.
|
|
178
|
-
raise NotImplementedError(
|
|
193
|
+
raise NotImplementedError("There is no .get_local_path method yet")
|
|
179
194
|
else:
|
|
180
195
|
target_obj = absolute(target)
|
|
181
|
-
if
|
|
182
|
-
|
|
196
|
+
if zip_arg and ".zip" not in source:
|
|
197
|
+
source += ".zip"
|
|
198
|
+
if encrypt and ".enc" not in source:
|
|
199
|
+
source += ".enc"
|
|
183
200
|
|
|
184
201
|
elif ":" in target and (target[1] != ":" if len(target) > 1 else True): # avoid the case of "C:/"
|
|
185
202
|
target_parts: list[str] = target.split(":")
|
|
186
203
|
cloud = target.split(":")[0]
|
|
187
|
-
|
|
188
204
|
if len(target_parts) > 1 and target_parts[1] == ES: # the target path is to be inferred from source.
|
|
189
|
-
assert ES not in source,
|
|
205
|
+
assert ES not in source, "You can't use $ in both source and target. Cyclical inference dependency arised."
|
|
190
206
|
source_obj = absolute(source)
|
|
191
|
-
remote_path = source_obj.get_remote_path(os_specific=
|
|
207
|
+
remote_path = source_obj.get_remote_path(os_specific=os_specific, root=root, rel2home=rel2home, strict=False)
|
|
192
208
|
target = f"{cloud}:{remote_path.as_posix()}"
|
|
193
209
|
else: # target path is mentioned, source? maybe.
|
|
194
210
|
target = str(target)
|
|
195
211
|
if source == ES:
|
|
196
|
-
raise NotImplementedError(
|
|
212
|
+
raise NotImplementedError("There is no .get_local_path method yet")
|
|
197
213
|
else:
|
|
198
214
|
source_obj = absolute(source)
|
|
199
|
-
if
|
|
200
|
-
if
|
|
215
|
+
if zip_arg and ".zip" not in target: target += ".zip"
|
|
216
|
+
if encrypt and ".enc" not in target: target += ".enc"
|
|
201
217
|
else:
|
|
202
218
|
raise ValueError("Either source or target must be a remote path (i.e. machine:path)")
|
|
203
219
|
Struct({"cloud": cloud, "source": str(source), "target": str(target)}).print(as_config=True, title="CLI Resolution")
|
|
220
|
+
_ = pwd, encrypt, zip_arg, share
|
|
204
221
|
return cloud, str(source), str(target)
|
|
205
222
|
|
|
206
223
|
|
|
@@ -224,11 +241,11 @@ def args_parser():
|
|
|
224
241
|
|
|
225
242
|
args = parser.parse_args()
|
|
226
243
|
args_dict = vars(args)
|
|
227
|
-
source: str
|
|
228
|
-
target: str
|
|
229
|
-
verbose: bool
|
|
230
|
-
delete: bool
|
|
231
|
-
bisync: bool
|
|
244
|
+
source: str=args_dict.pop("source")
|
|
245
|
+
target: str=args_dict.pop("target")
|
|
246
|
+
verbose: bool=args_dict.pop("verbose")
|
|
247
|
+
delete: bool=args_dict.pop("delete")
|
|
248
|
+
bisync: bool=args_dict.pop("bisync")
|
|
232
249
|
transfers: int = args_dict.pop("transfers")
|
|
233
250
|
args_obj = Args(**args_dict)
|
|
234
251
|
|
|
@@ -43,7 +43,7 @@ except Exception as e:
|
|
|
43
43
|
return pycode
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def get_read_pyfile_pycode(path: P, as_module: bool, cmd: str
|
|
46
|
+
def get_read_pyfile_pycode(path: P, as_module: bool, cmd: str=""):
|
|
47
47
|
if as_module: pycode = fr"""
|
|
48
48
|
import sys
|
|
49
49
|
sys.path.append(r'{path.parent}')
|
|
@@ -72,8 +72,8 @@ def build_parser():
|
|
|
72
72
|
parser.add_argument("--read", "-r", dest="read", help="read a binary file.", default="")
|
|
73
73
|
parser.add_argument("--file", "-f", dest="file", help="python file path to interpret", default="")
|
|
74
74
|
parser.add_argument("--cmd", "-c", dest="cmd", help="python command to interpret", default="")
|
|
75
|
-
parser.add_argument("--terminal", "-t", dest="terminal", help=
|
|
76
|
-
parser.add_argument("--shell", "-S", dest="shell", help=
|
|
75
|
+
parser.add_argument("--terminal", "-t", dest="terminal", help="specify which terminal to be used. Default console host.", default="") # can choose `wt`
|
|
76
|
+
parser.add_argument("--shell", "-S", dest="shell", help="specify which shell to be used. Defaults to CMD.", default="")
|
|
77
77
|
|
|
78
78
|
args = parser.parse_args()
|
|
79
79
|
# print(f"Crocodile.run: args of the firing command = {args.__dict__}")
|
|
@@ -142,10 +142,10 @@ print_logo(logo="crocodile")
|
|
|
142
142
|
ve = get_ve_profile(P(file)) if args.ve is None else str(args.ve)
|
|
143
143
|
|
|
144
144
|
final_program = f"""
|
|
145
|
-
|
|
146
|
-
. activate_ve {ve}
|
|
145
|
+
. $HOME/scripts/activate_ve '{ve}'
|
|
147
146
|
{interpreter} """
|
|
148
|
-
if interpreter == "ipython":
|
|
147
|
+
if interpreter == "ipython":
|
|
148
|
+
final_program += f"{interactivity} --profile {profile} --no-banner"
|
|
149
149
|
final_program += f" {str(pyfile)}"
|
|
150
150
|
print(f"🔥 sourcing ... {pyfile}")
|
|
151
151
|
# print(f"Running ... {final_program}")
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
"""devops
|
|
5
4
|
"""
|
|
6
|
-
|
|
5
|
+
|
|
7
6
|
from machineconfig.utils.utils import display_options, PROGRAM_PATH, write_shell_script
|
|
8
7
|
from platform import system
|
|
9
8
|
from enum import Enum
|
|
@@ -49,19 +48,19 @@ def main(which: Optional[str] = None):
|
|
|
49
48
|
|
|
50
49
|
elif choice_key == Options.ve.value:
|
|
51
50
|
program = ""
|
|
52
|
-
reply: bool
|
|
53
|
-
if P.cwd().joinpath(".ve.ini").exists():
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
51
|
+
reply: bool=False
|
|
52
|
+
# if P.cwd().joinpath(".ve.ini").exists():
|
|
53
|
+
# reply = input("Detected .ve.ini file. Do you want to use it to build ve? (y/[n]): ") == "y"
|
|
54
|
+
# if reply:
|
|
55
|
+
# from machineconfig.utils.ve import get_ve_install_script_from_specs
|
|
56
|
+
# program_win = get_ve_install_script_from_specs(repo_root=P.cwd().to_str(), system="Windows")
|
|
57
|
+
# program_lin = get_ve_install_script_from_specs(repo_root=P.cwd().to_str(), system="Linux")
|
|
58
|
+
# install_reply = input("Proceed with installation? (y/[n]): ") == "y"
|
|
59
|
+
# if not install_reply: program = ""
|
|
60
|
+
# else:
|
|
61
|
+
# if system() == "Windows": program = program_win
|
|
62
|
+
# elif system() == "Linux": program = program_lin
|
|
63
|
+
# else: raise ValueError(f"Unknown system: {system()}")
|
|
65
64
|
|
|
66
65
|
if not reply:
|
|
67
66
|
from machineconfig.utils.ve import get_ve_install_script
|
|
@@ -92,18 +91,18 @@ def main(which: Optional[str] = None):
|
|
|
92
91
|
program = helper.main()
|
|
93
92
|
|
|
94
93
|
elif choice_key == Options.ssh_setup.value:
|
|
95
|
-
program_windows =
|
|
96
|
-
program_linux =
|
|
94
|
+
program_windows = """Invoke-WebRequest https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/openssh_all.ps1 | Invoke-Expression # https://github.com/thisismygitrepo.keys"""
|
|
95
|
+
program_linux = """curl https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/openssh_all.sh | sudo bash # https://github.com/thisismygitrepo.keys"""
|
|
97
96
|
program = program_linux if system() == "Linux" else program_windows
|
|
98
97
|
|
|
99
98
|
elif choice_key == Options.ssh_setup_wsl.value:
|
|
100
|
-
program =
|
|
99
|
+
program = """curl https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/openssh_wsl.sh | sudo bash"""
|
|
101
100
|
|
|
102
101
|
elif choice_key == Options.backup.value:
|
|
103
|
-
from machineconfig.scripts.python.devops_backup_retrieve import
|
|
102
|
+
from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve as helper
|
|
104
103
|
program = helper(direction="BACKUP")
|
|
105
104
|
elif choice_key == Options.retreive.value:
|
|
106
|
-
from machineconfig.scripts.python.devops_backup_retrieve import
|
|
105
|
+
from machineconfig.scripts.python.devops_backup_retrieve import main_backup_retrieve as helper
|
|
107
106
|
program = helper(direction="RETRIEVE")
|
|
108
107
|
|
|
109
108
|
elif choice_key == Options.scheduler.value:
|
|
@@ -111,8 +110,9 @@ def main(which: Optional[str] = None):
|
|
|
111
110
|
program = helper()
|
|
112
111
|
|
|
113
112
|
else: raise ValueError(f"Unimplemented choice: {choice_key}")
|
|
114
|
-
if program:
|
|
115
|
-
|
|
113
|
+
if program:
|
|
114
|
+
write_shell_script(program=program, display=True, preserve_cwd=True, desc="Shell script prepared by Python.", execute=True if which is not None else False)
|
|
115
|
+
else: write_shell_script(program="echo 'Done.'", display=False, desc="Shell script prepared by Python.", preserve_cwd=True, execute=False) # Python did not return any script to run.
|
|
116
116
|
|
|
117
117
|
|
|
118
118
|
if __name__ == "__main__":
|
|
@@ -38,7 +38,7 @@ def get_add_ssh_key_script(path_to_key: P):
|
|
|
38
38
|
program = LIBRARY_ROOT.joinpath("setup_windows/openssh-server_add-sshkey.ps1")
|
|
39
39
|
program = P(program).expanduser().read_text().replace('$sshfile=""', f'$sshfile="{path_to_key}"')
|
|
40
40
|
|
|
41
|
-
if system() == "Linux": program +=
|
|
41
|
+
if system() == "Linux": program += """
|
|
42
42
|
|
|
43
43
|
sudo chmod 700 ~/.ssh
|
|
44
44
|
sudo chmod 644 ~/.ssh/authorized_keys
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# import subprocess
|
|
6
6
|
from crocodile.file_management import Read, P
|
|
7
|
-
from machineconfig.utils.utils import LIBRARY_ROOT, DEFAULTS_PATH, print_code, choose_cloud_interactively,
|
|
7
|
+
from machineconfig.utils.utils import LIBRARY_ROOT, DEFAULTS_PATH, print_code, choose_cloud_interactively, choose_multiple_options
|
|
8
8
|
from machineconfig.scripts.python.cloud_sync import ES
|
|
9
9
|
from platform import system
|
|
10
10
|
from typing import Any, Literal, Optional
|
|
@@ -13,23 +13,25 @@ from typing import Any, Literal, Optional
|
|
|
13
13
|
OPTIONS = Literal["BACKUP", "RETRIEVE"]
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def
|
|
16
|
+
def main_backup_retrieve(direction: OPTIONS, which: Optional[str] = None):
|
|
17
17
|
try:
|
|
18
18
|
cloud: str = Read.ini(DEFAULTS_PATH)['general']['rclone_config_name']
|
|
19
19
|
print(f"\n{'--' * 50}\n ⚠️ Using default cloud: `{cloud}` ⚠️\n{'--' * 50}\n")
|
|
20
20
|
except (FileNotFoundError, KeyError, IndexError): cloud = choose_cloud_interactively()
|
|
21
21
|
|
|
22
|
-
bu_file: dict[str, Any] = LIBRARY_ROOT.joinpath("profile/backup.toml")
|
|
22
|
+
bu_file: dict[str, Any] = Read.toml(LIBRARY_ROOT.joinpath("profile/backup.toml"))
|
|
23
23
|
if system() == "Linux": bu_file = {key: val for key, val in bu_file.items() if "windows" not in key}
|
|
24
24
|
elif system() == "Windows": bu_file = {key: val for key, val in bu_file.items() if "linux" not in key}
|
|
25
25
|
|
|
26
26
|
if which is None:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
choices = choose_multiple_options(msg=f"WHICH FILE of the following do you want to {direction}?", options=['all'] + list(bu_file.keys()))
|
|
28
|
+
else:
|
|
29
|
+
choices = which.split(",") if isinstance(which, str) else which
|
|
30
30
|
|
|
31
|
-
if
|
|
32
|
-
else:
|
|
31
|
+
if "all" in choices: items = bu_file
|
|
32
|
+
else:
|
|
33
|
+
# items = {choices: bu_file[choices]}
|
|
34
|
+
items = {key: val for key, val in bu_file.items() if key in choices}
|
|
33
35
|
|
|
34
36
|
program = f"""$cloud = "{cloud}:{ES}" \n """ if system() == "Windows" else f"""cloud="{cloud}:{ES}" \n """
|
|
35
37
|
for item_name, item in items.items():
|
|
@@ -42,11 +44,18 @@ def main(direction: OPTIONS, which: Optional[str] = None):
|
|
|
42
44
|
if flags: flags = "-" + flags
|
|
43
45
|
if direction == "BACKUP": program += f"""\ncloud_copy "{P(item['path']).as_posix()}" $cloud {flags}\n"""
|
|
44
46
|
elif direction == "RETRIEVE": program += f"""\ncloud_copy $cloud "{P(item['path']).as_posix()}" {flags}\n"""
|
|
45
|
-
else:
|
|
46
|
-
|
|
47
|
+
else:
|
|
48
|
+
raise RuntimeError(f"Unknown direction: {direction}")
|
|
49
|
+
if item_name == "dotfiles" and system() == "Linux": program += """\nchmod 700 ~/.ssh/*\n"""
|
|
47
50
|
print_code(program, lexer="shell", desc=f"{direction} script")
|
|
48
51
|
return program
|
|
49
52
|
|
|
50
53
|
|
|
54
|
+
def main(direction: OPTIONS, which: Optional[str] = None):
|
|
55
|
+
code = main_backup_retrieve(direction=direction, which=which)
|
|
56
|
+
from machineconfig.utils.utils import write_shell_script
|
|
57
|
+
write_shell_script(program=code, desc="backup_retrieve.sh")
|
|
58
|
+
|
|
59
|
+
|
|
51
60
|
if __name__ == "__main__":
|
|
52
61
|
pass
|