machineconfig 5.34__py3-none-any.whl → 5.36__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/jobs/installer/installer_data.json +17 -0
- machineconfig/jobs/installer/package_groups.py +1 -0
- machineconfig/scripts/python/agents.py +8 -4
- machineconfig/scripts/python/croshell.py +1 -1
- machineconfig/scripts/python/devops_helpers/cli_config.py +10 -1
- machineconfig/scripts/python/devops_helpers/cli_nw.py +19 -20
- machineconfig/scripts/python/devops_helpers/devops_add_ssh_key.py +62 -38
- machineconfig/scripts/python/devops_helpers/themes/__init__.py +0 -0
- machineconfig/scripts/python/devops_helpers/{choose_pwsh_theme.ps1 → themes/choose_pwsh_theme.ps1} +38 -2
- machineconfig/scripts/python/interactive.py +2 -2
- machineconfig/setup_linux/web_shortcuts/interactive.sh +4 -1
- machineconfig/setup_windows/ssh/{openssh-server_add-sshkey.ps1 → add-sshkey.ps1} +0 -3
- machineconfig/setup_windows/ssh/openssh-server.ps1 +0 -2
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +4 -1
- machineconfig/utils/files/dbms.py +76 -177
- {machineconfig-5.34.dist-info → machineconfig-5.36.dist-info}/METADATA +1 -3
- {machineconfig-5.34.dist-info → machineconfig-5.36.dist-info}/RECORD +22 -22
- machineconfig/setup_windows/ssh/openssh_all.ps1 +0 -22
- /machineconfig/scripts/python/devops_helpers/{choose_wezterm_theme.py → themes/choose_wezterm_theme.py} +0 -0
- /machineconfig/setup_windows/ssh/{openssh-server_add_identity.ps1 → add_identity.ps1} +0 -0
- {machineconfig-5.34.dist-info → machineconfig-5.36.dist-info}/WHEEL +0 -0
- {machineconfig-5.34.dist-info → machineconfig-5.36.dist-info}/entry_points.txt +0 -0
- {machineconfig-5.34.dist-info → machineconfig-5.36.dist-info}/top_level.txt +0 -0
|
@@ -2059,6 +2059,23 @@
|
|
|
2059
2059
|
}
|
|
2060
2060
|
}
|
|
2061
2061
|
},
|
|
2062
|
+
{
|
|
2063
|
+
"appName": "qwen-code",
|
|
2064
|
+
"repoURL": "CMD",
|
|
2065
|
+
"doc": "Terminal-based CLI agents and tools for productivity and coding.",
|
|
2066
|
+
"fileNamePattern": {
|
|
2067
|
+
"amd64": {
|
|
2068
|
+
"linux": "npm install -g @qwen-code/qwen-code@latest",
|
|
2069
|
+
"windows": "npm install -g @qwen-code/qwen-code@latest",
|
|
2070
|
+
"macos": "npm install -g @qwen-code/qwen-code@latest"
|
|
2071
|
+
},
|
|
2072
|
+
"arm64": {
|
|
2073
|
+
"linux": "npm install -g @qwen-code/qwen-code@latest",
|
|
2074
|
+
"windows": "npm install -g @qwen-code/qwen-code@latest",
|
|
2075
|
+
"macos": "npm install -g @qwen-code/qwen-code@latest"
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
},
|
|
2062
2079
|
{
|
|
2063
2080
|
"appName": "github-copilot-cli",
|
|
2064
2081
|
"repoURL": "CMD",
|
|
@@ -188,14 +188,18 @@ def init_config():
|
|
|
188
188
|
from machineconfig.scripts.python.ai.initai import add_ai_configs
|
|
189
189
|
add_ai_configs(repo_root=Path.cwd())
|
|
190
190
|
|
|
191
|
+
def generate_files():
|
|
192
|
+
from machineconfig.scripts.python.ai.generate_files import main
|
|
193
|
+
main()
|
|
191
194
|
|
|
192
195
|
def main_from_parser():
|
|
193
196
|
import sys
|
|
194
197
|
agents_app = typer.Typer(help="🤖 AI Agents management subcommands")
|
|
195
|
-
agents_app.command("create", no_args_is_help=True)(create)
|
|
196
|
-
agents_app.command("collect", no_args_is_help=True)(collect)
|
|
197
|
-
agents_app.command("
|
|
198
|
-
agents_app.command("
|
|
198
|
+
agents_app.command("create", no_args_is_help=True, help="Create agents layout file, ready to run.")(create)
|
|
199
|
+
agents_app.command("collect", no_args_is_help=True, help="Collect all agent materials into a single file.")(collect)
|
|
200
|
+
agents_app.command("make-template", no_args_is_help=False, help="Create a template for fire agents")(template)
|
|
201
|
+
agents_app.command("make-config", no_args_is_help=False, help="Initialize AI configurations in the current repository")(init_config)
|
|
202
|
+
agents_app.command("make-todo", no_args_is_help=False, help="Generate a markdown file listing all Python files in the repo")(generate_files)
|
|
199
203
|
if len(sys.argv) == 1:
|
|
200
204
|
agents_app(["--help"])
|
|
201
205
|
else:
|
|
@@ -144,7 +144,7 @@ from pathlib import Path
|
|
|
144
144
|
fire_line = f"uv run --python 3.13 --with machineconfig[plot] {interpreter} {interactivity} {profile} {str(pyfile)}"
|
|
145
145
|
|
|
146
146
|
from machineconfig.utils.code import run_shell_script
|
|
147
|
-
run_shell_script(fire_line, clean_env=
|
|
147
|
+
run_shell_script(fire_line, clean_env=False)
|
|
148
148
|
|
|
149
149
|
|
|
150
150
|
def arg_parser() -> None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from typing import Literal, Annotated, Optional
|
|
4
|
-
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
config_apps = typer.Typer(help="⚙️ Configuration subcommands", no_args_is_help=True)
|
|
@@ -41,3 +41,12 @@ def shell(method: Annotated[Literal["copy", "reference"], typer.Argument(help="C
|
|
|
41
41
|
from machineconfig.profile.shell import create_default_shell_profile
|
|
42
42
|
create_default_shell_profile(method=method)
|
|
43
43
|
|
|
44
|
+
|
|
45
|
+
@config_apps.command(no_args_is_help=False)
|
|
46
|
+
def pwsh_theme():
|
|
47
|
+
"""🔗 Configure your shell profile."""
|
|
48
|
+
import machineconfig.scripts.python.devops_helpers.themes as themes
|
|
49
|
+
file = Path(themes.__file__).parent / "choose_pwsh_theme.ps1"
|
|
50
|
+
import subprocess
|
|
51
|
+
# subprocess.run(["pwsh", "-File", str(file)])
|
|
52
|
+
subprocess.run(["pwsh", "-File", str(file)])
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
import machineconfig.scripts.python.devops_helpers.cli_terminal as cli_terminal
|
|
3
3
|
import typer
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
4
6
|
nw_apps = typer.Typer(help="🔐 Network subcommands", no_args_is_help=True)
|
|
5
7
|
|
|
6
8
|
|
|
@@ -8,32 +10,29 @@ nw_apps.command(name="share-terminal", help="📡 Share terminal via web browser
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
@nw_apps.command()
|
|
11
|
-
def
|
|
12
|
-
"""
|
|
13
|
-
import machineconfig.scripts.python.devops_helpers.devops_add_ssh_key as helper
|
|
14
|
-
helper.main()
|
|
15
|
-
@nw_apps.command()
|
|
16
|
-
def add_identity():
|
|
17
|
-
"""🗝️ SSH add identity (private key) to this machine"""
|
|
18
|
-
import machineconfig.scripts.python.devops_helpers.devops_add_identity as helper
|
|
19
|
-
helper.main()
|
|
20
|
-
@nw_apps.command()
|
|
21
|
-
def connect():
|
|
22
|
-
"""🔐 SSH use key pair to connect two machines"""
|
|
23
|
-
raise NotImplementedError
|
|
24
|
-
|
|
25
|
-
@nw_apps.command()
|
|
26
|
-
def setup():
|
|
27
|
-
"""📡 SSH setup"""
|
|
13
|
+
def install_ssh_server():
|
|
14
|
+
"""📡 SSH install server"""
|
|
28
15
|
import platform
|
|
29
16
|
if platform.system() == "Windows":
|
|
30
17
|
from machineconfig.setup_windows import SSH_SERVER
|
|
31
|
-
program = SSH_SERVER.read_text(encoding="utf-8")
|
|
32
18
|
elif platform.system() == "Linux" or platform.system() == "Darwin":
|
|
33
19
|
from machineconfig.setup_linux import SSH_SERVER
|
|
34
|
-
program = SSH_SERVER.read_text(encoding="utf-8")
|
|
35
20
|
else:
|
|
36
21
|
raise NotImplementedError(f"Platform {platform.system()} is not supported.")
|
|
37
22
|
from machineconfig.utils.code import run_shell_script
|
|
38
|
-
run_shell_script(script=
|
|
23
|
+
run_shell_script(script=SSH_SERVER.read_text(encoding="utf-8"))
|
|
39
24
|
|
|
25
|
+
@nw_apps.command(no_args_is_help=True)
|
|
26
|
+
def add_ssh_key(path: Optional[str] = typer.Option(None, help="Path to the public key file"),
|
|
27
|
+
choose: bool = typer.Option(False, "--choose", "-c", help="Choose from available public keys in ~/.ssh/*.pub"),
|
|
28
|
+
value: bool = typer.Option(False, "--value", "-v", help="Paste the public key content manually"),
|
|
29
|
+
github: Optional[str] = typer.Option(None, "--github", "-g", help="Fetch public keys from a GitHub username")
|
|
30
|
+
):
|
|
31
|
+
"""🔑 SSH add pub key to this machine so its accessible by owner of corresponding private key."""
|
|
32
|
+
import machineconfig.scripts.python.devops_helpers.devops_add_ssh_key as helper
|
|
33
|
+
helper.main(pub_path=path, pub_choose=choose, pub_val=value, from_github=github)
|
|
34
|
+
@nw_apps.command()
|
|
35
|
+
def add_ssh_identity():
|
|
36
|
+
"""🗝️ SSH add identity (private key) to this machine"""
|
|
37
|
+
import machineconfig.scripts.python.devops_helpers.devops_add_identity as helper
|
|
38
|
+
helper.main()
|
|
@@ -7,14 +7,27 @@ from machineconfig.utils.path_extended import PathExtended
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.panel import Panel
|
|
9
9
|
from rich import box # Import box
|
|
10
|
+
from typing import Optional
|
|
11
|
+
import typer
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
console = Console()
|
|
13
15
|
|
|
16
|
+
"""
|
|
17
|
+
if (!$pubkey_string) {
|
|
18
|
+
$pubkey_url = 'https://github.com/thisismygitrepo.keys' # (CHANGE APPROPRIATELY)
|
|
19
|
+
$pubkey_string = (Invoke-WebRequest $pubkey_url).Content
|
|
20
|
+
} else {
|
|
21
|
+
Write-Output "pubkey_string is already defined."
|
|
22
|
+
}
|
|
23
|
+
echo $null >> $HOME/.ssh/authorized_keys # powershell way of touching a file if it doesn't exist
|
|
24
|
+
echo $pubkey_string >> $HOME/.ssh/authorized_keys
|
|
25
|
+
echo $pubkey_string > $HOME/.ssh/pubkey.pub
|
|
26
|
+
"""
|
|
27
|
+
|
|
14
28
|
|
|
15
29
|
def get_add_ssh_key_script(path_to_key: PathExtended):
|
|
16
30
|
console.print(Panel("🔑 SSH KEY CONFIGURATION", title="[bold blue]SSH Setup[/bold blue]"))
|
|
17
|
-
|
|
18
31
|
if system() == "Linux":
|
|
19
32
|
authorized_keys = PathExtended.home().joinpath(".ssh/authorized_keys")
|
|
20
33
|
console.print(Panel(f"🐧 Linux SSH configuration\n📄 Authorized keys file: {authorized_keys}", title="[bold blue]System Info[/bold blue]"))
|
|
@@ -30,7 +43,6 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
|
|
|
30
43
|
keys_text = authorized_keys.read_text(encoding="utf-8").split(split)
|
|
31
44
|
key_count = len([k for k in keys_text if k.strip()])
|
|
32
45
|
console.print(Panel(f"🔍 Current SSH authorization status\n✅ Found {key_count} authorized key(s)", title="[bold blue]Status[/bold blue]"))
|
|
33
|
-
|
|
34
46
|
if path_to_key.read_text(encoding="utf-8") in authorized_keys.read_text(encoding="utf-8"):
|
|
35
47
|
console.print(Panel(f"⚠️ Key already authorized\nKey: {path_to_key.name}\nStatus: Already present in authorized_keys file\nNo action required", title="[bold yellow]Warning[/bold yellow]"))
|
|
36
48
|
program = ""
|
|
@@ -39,7 +51,7 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
|
|
|
39
51
|
if system() == "Linux":
|
|
40
52
|
program = f"cat {path_to_key} >> ~/.ssh/authorized_keys"
|
|
41
53
|
elif system() == "Windows":
|
|
42
|
-
program_path = LIBRARY_ROOT.joinpath("setup_windows/
|
|
54
|
+
program_path = LIBRARY_ROOT.joinpath("setup_windows/add-sshkey.ps1")
|
|
43
55
|
program = program_path.expanduser().read_text(encoding="utf-8")
|
|
44
56
|
place_holder = r'$sshfile = "$env:USERPROFILE\.ssh\pubkey.pub"'
|
|
45
57
|
assert place_holder in program, f"This section performs string manipulation on the script {program_path} to add the key to the authorized_keys file. The script has changed and the string {place_holder} is not found."
|
|
@@ -58,7 +70,6 @@ def get_add_ssh_key_script(path_to_key: PathExtended):
|
|
|
58
70
|
|
|
59
71
|
if system() == "Linux":
|
|
60
72
|
program += """
|
|
61
|
-
|
|
62
73
|
sudo chmod 700 ~/.ssh
|
|
63
74
|
sudo chmod 644 ~/.ssh/authorized_keys
|
|
64
75
|
sudo chmod 644 ~/.ssh/*.pub
|
|
@@ -68,51 +79,64 @@ sudo service ssh --full-restart
|
|
|
68
79
|
return program
|
|
69
80
|
|
|
70
81
|
|
|
71
|
-
def main(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
def main(pub_path: Optional[str] = typer.Argument(None, help="Path to the public key file"),
|
|
83
|
+
pub_choose: bool = typer.Option(False, "--choose", "-c", help="Choose from available public keys in ~/.ssh"),
|
|
84
|
+
pub_val: bool = typer.Option(False, "--paste", "-p", help="Paste the public key content manually"),
|
|
85
|
+
from_github: Optional[str] = typer.Option(None, "--from-github", "-g", help="Fetch public keys from a GitHub username")
|
|
86
|
+
) -> None:
|
|
87
|
+
|
|
88
|
+
if pub_path:
|
|
89
|
+
key_path = PathExtended(pub_path).expanduser().absolute()
|
|
90
|
+
if not key_path.exists():
|
|
91
|
+
console.print(Panel(f"❌ ERROR: Provided key path does not exist\nPath: {key_path}", title="[bold red]Error[/bold red]"))
|
|
92
|
+
raise FileNotFoundError(f"Provided key path does not exist: {key_path}")
|
|
93
|
+
console.print(Panel(f"📄 Using provided public key file: {key_path}", title="[bold blue]Info[/bold blue]"))
|
|
94
|
+
program = get_add_ssh_key_script(key_path)
|
|
95
|
+
from machineconfig.utils.code import run_shell_script
|
|
96
|
+
run_shell_script(script=program)
|
|
97
|
+
console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
|
|
98
|
+
return
|
|
99
|
+
elif pub_choose:
|
|
100
|
+
console.print(Panel("🔐 SSH PUBLIC KEY AUTHORIZATION TOOL", box=box.DOUBLE_EDGE, title_align="left"))
|
|
101
|
+
console.print(Panel("🔍 Searching for public keys...", title="[bold blue]SSH Setup[/bold blue]", border_style="blue"))
|
|
102
|
+
pub_keys = PathExtended.home().joinpath(".ssh").search("*.pub")
|
|
103
|
+
if pub_keys:
|
|
104
|
+
console.print(Panel(f"✅ Found {len(pub_keys)} public key(s)", title="[bold green]Status[/bold green]", border_style="green"))
|
|
105
|
+
else:
|
|
106
|
+
console.print(Panel("⚠️ No public keys found", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
|
|
107
|
+
return
|
|
89
108
|
console.print(Panel(f"🔄 Processing all {len(pub_keys)} public keys...", title="[bold blue]Processing[/bold blue]", border_style="blue"))
|
|
90
109
|
program = "\n\n\n".join([get_add_ssh_key_script(key) for key in pub_keys])
|
|
91
110
|
|
|
92
|
-
elif
|
|
93
|
-
console.print(Panel("📂 Please provide the path to your public key", title="[bold blue]Input Required[/bold blue]", border_style="blue"))
|
|
94
|
-
key_path = PathExtended(input("📋 Path: ")).expanduser().absolute()
|
|
95
|
-
console.print(Panel(f"📄 Using key from path: {key_path}", title="[bold blue]Info[/bold blue]", border_style="blue"))
|
|
96
|
-
program = get_add_ssh_key_script(key_path)
|
|
97
|
-
|
|
98
|
-
elif res == i_paste_option:
|
|
111
|
+
elif pub_val:
|
|
99
112
|
console.print(Panel("📋 Please provide a filename and paste the public key content", title="[bold blue]Input Required[/bold blue]", border_style="blue"))
|
|
100
113
|
key_filename = input("📝 File name (default: my_pasted_key.pub): ") or "my_pasted_key.pub"
|
|
101
114
|
key_path = PathExtended.home().joinpath(f".ssh/{key_filename}")
|
|
102
115
|
key_path.write_text(input("🔑 Paste the public key here: "), encoding="utf-8")
|
|
103
116
|
console.print(Panel(f"💾 Key saved to: {key_path}", title="[bold green]Success[/bold green]", border_style="green"))
|
|
104
117
|
program = get_add_ssh_key_script(key_path)
|
|
105
|
-
|
|
118
|
+
elif from_github:
|
|
119
|
+
console.print(Panel(f"🌐 Fetching public keys from GitHub user: {from_github}", title="[bold blue]GitHub Fetch[/bold blue]", border_style="blue"))
|
|
120
|
+
import requests
|
|
121
|
+
response = requests.get(f"https://api.github.com/users/{from_github}/keys")
|
|
122
|
+
if response.status_code != 200:
|
|
123
|
+
console.print(Panel(f"❌ ERROR: Failed to fetch keys from GitHub user {from_github}\nStatus Code: {response.status_code}", title="[bold red]Error[/bold red]", border_style="red"))
|
|
124
|
+
raise RuntimeError(f"Failed to fetch keys from GitHub user {from_github}: Status Code {response.status_code}")
|
|
125
|
+
keys = response.json()
|
|
126
|
+
if not keys:
|
|
127
|
+
console.print(Panel(f"⚠️ No public keys found for GitHub user: {from_github}", title="[bold yellow]Warning[/bold yellow]", border_style="yellow"))
|
|
128
|
+
return
|
|
129
|
+
console.print(Panel(f"✅ Found {len(keys)} public key(s) for user: {from_github}", title="[bold green]Success[/bold green]", border_style="green"))
|
|
130
|
+
key_path = PathExtended.home().joinpath(f".ssh/{from_github}_github_keys.pub")
|
|
131
|
+
key_path.write_text("\n".join([key["key"] for key in keys]), encoding="utf-8")
|
|
132
|
+
console.print(Panel(f"💾 Keys saved to: {key_path}", title="[bold green]Success[/bold green]", border_style="green"))
|
|
133
|
+
program = get_add_ssh_key_script(key_path)
|
|
106
134
|
else:
|
|
107
|
-
console.print(Panel(
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
console.print(Panel("❌ ERROR: No method provided to add SSH key\nUse --help for options", title="[bold red]Error[/bold red]", border_style="red"))
|
|
136
|
+
raise ValueError("No method provided to add SSH key. Use --help for options.")
|
|
110
137
|
console.print(Panel("🚀 SSH KEY AUTHORIZATION READY\nRun the generated script to apply changes", box=box.DOUBLE_EDGE, title_align="left"))
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
import subprocess
|
|
114
|
-
|
|
115
|
-
subprocess.run(program, shell=True, check=True)
|
|
138
|
+
from machineconfig.utils.code import run_shell_script
|
|
139
|
+
run_shell_script(script=program)
|
|
116
140
|
console.print(Panel("✅ SSH KEY AUTHORIZATION COMPLETED", box=box.DOUBLE_EDGE, title_align="left"))
|
|
117
141
|
|
|
118
142
|
|
|
File without changes
|
machineconfig/scripts/python/devops_helpers/{choose_pwsh_theme.ps1 → themes/choose_pwsh_theme.ps1}
RENAMED
|
@@ -37,8 +37,44 @@ if ($selectedThemeName) {
|
|
|
37
37
|
oh-my-posh init pwsh --config $selectedThemeName | Invoke-Expression
|
|
38
38
|
|
|
39
39
|
Write-Host "`nTheme applied to current session!" -ForegroundColor Green
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
|
|
41
|
+
# Safely update the PowerShell profile
|
|
42
|
+
$profilePath = $PROFILE
|
|
43
|
+
$ompLine = "oh-my-posh init pwsh --config '$selectedThemeName' | Invoke-Expression"
|
|
44
|
+
|
|
45
|
+
# Create profile directory if it doesn't exist
|
|
46
|
+
$profileDir = Split-Path $profilePath -Parent
|
|
47
|
+
if (-not (Test-Path $profileDir)) {
|
|
48
|
+
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
# Read existing profile content or create empty array
|
|
52
|
+
$profileContent = @()
|
|
53
|
+
if (Test-Path $profilePath) {
|
|
54
|
+
$profileContent = Get-Content $profilePath
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Check if oh-my-posh line already exists and replace it, or add it
|
|
58
|
+
$found = $false
|
|
59
|
+
for ($i = 0; $i -lt $profileContent.Count; $i++) {
|
|
60
|
+
if ($profileContent[$i] -match "oh-my-posh init pwsh") {
|
|
61
|
+
$profileContent[$i] = $ompLine
|
|
62
|
+
$found = $true
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (-not $found) {
|
|
68
|
+
# Add the line at the end with a blank line before it
|
|
69
|
+
$profileContent += ""
|
|
70
|
+
$profileContent += $ompLine
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Write back to profile
|
|
74
|
+
$profileContent | Set-Content $profilePath -Encoding UTF8
|
|
75
|
+
|
|
76
|
+
Write-Host "Profile updated successfully!" -ForegroundColor Green
|
|
77
|
+
Write-Host "The theme will be applied automatically in future PowerShell sessions." -ForegroundColor Cyan
|
|
42
78
|
} else {
|
|
43
79
|
Write-Host "`nNo theme selected." -ForegroundColor DarkGray
|
|
44
80
|
}
|
|
@@ -19,7 +19,7 @@ for better user experience with checkbox selections.
|
|
|
19
19
|
|
|
20
20
|
import sys
|
|
21
21
|
from pathlib import Path
|
|
22
|
-
from typing import cast
|
|
22
|
+
# from typing import cast
|
|
23
23
|
import platform
|
|
24
24
|
|
|
25
25
|
import questionary
|
|
@@ -29,7 +29,7 @@ from rich.panel import Panel
|
|
|
29
29
|
from rich.text import Text
|
|
30
30
|
from machineconfig.utils.code import run_shell_script
|
|
31
31
|
|
|
32
|
-
_ = cast
|
|
32
|
+
# _ = cast
|
|
33
33
|
console = Console()
|
|
34
34
|
|
|
35
35
|
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
3
|
. <( curl -sSL "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/uv.sh")
|
|
4
|
-
$HOME/.local/bin/uv run --python 3.13 --with machineconfig devops
|
|
4
|
+
# $HOME/.local/bin/uv run --python 3.13 --with machineconfig devops "$@"
|
|
5
|
+
devops() {
|
|
6
|
+
"$HOME/.local/bin/uv" run --python 3.13 --with machineconfig devops "$@"
|
|
7
|
+
}
|
|
@@ -7,16 +7,13 @@
|
|
|
7
7
|
|
|
8
8
|
$ErrorActionPreference = "Stop"
|
|
9
9
|
$sshd_dir = "$env:ProgramData\ssh"
|
|
10
|
-
|
|
11
10
|
$sshfile = "$env:USERPROFILE\.ssh\pubkey.pub" # this directory is for normal users, not admins.
|
|
12
11
|
# Once they are populated, we can create administrators_authorized_keys
|
|
13
12
|
|
|
14
13
|
Get-Content $sshfile >> "$sshd_dir\administrators_authorized_keys"
|
|
15
|
-
|
|
16
14
|
# set appropirate persmissions for this file
|
|
17
15
|
Set-Location $sshd_dir
|
|
18
16
|
icacls administrators_authorized_keys /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F"
|
|
19
|
-
|
|
20
17
|
# Lastly, enabling public key authentication.
|
|
21
18
|
$sshd_config = "$sshd_dir\sshd_config"
|
|
22
19
|
(Get-Content $sshd_config) -replace '#PubkeyAuthentication', 'PubkeyAuthentication' | Out-File -encoding ASCII $sshd_config
|
|
@@ -20,9 +20,7 @@ Add-WindowsCapability -Online -Name OpenSSH.Client
|
|
|
20
20
|
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
|
21
21
|
|
|
22
22
|
Set-Service -Name sshd -StartupType Automatic
|
|
23
|
-
|
|
24
23
|
#Get-Service -Name ssh-agent | Set-Service -StartupType Automatic
|
|
25
|
-
|
|
26
24
|
#Set-Service -Name ssh-agent -StartupType Automatic
|
|
27
25
|
#Start-Service ssh-agent
|
|
28
26
|
# Starting the service for the first time will populate the directory with config files.
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
iex (iwr "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/uv.ps1").Content
|
|
4
|
-
|
|
4
|
+
function devops {
|
|
5
|
+
& "$HOME\.local\bin\uv.exe" run --python 3.13 --with machineconfig devops $args
|
|
6
|
+
}
|
|
7
|
+
|
|
@@ -3,11 +3,9 @@ from typing import Optional, Any, Callable
|
|
|
3
3
|
|
|
4
4
|
import polars as pl
|
|
5
5
|
|
|
6
|
-
from sqlalchemy.orm import sessionmaker
|
|
7
|
-
from sqlalchemy import text, inspect as inspect__
|
|
8
|
-
from sqlalchemy.engine import Engine
|
|
9
|
-
from sqlalchemy.ext.asyncio import create_async_engine
|
|
10
|
-
from sqlalchemy.engine import Inspector
|
|
6
|
+
from sqlalchemy.orm import sessionmaker
|
|
7
|
+
from sqlalchemy import create_engine, text, inspect as inspect__
|
|
8
|
+
from sqlalchemy.engine import Engine
|
|
11
9
|
from sqlalchemy.sql.schema import MetaData
|
|
12
10
|
from pathlib import Path as P
|
|
13
11
|
|
|
@@ -15,75 +13,23 @@ OPLike = Optional[P] | str | None
|
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
class DBMS:
|
|
18
|
-
def __init__(self, engine: Engine
|
|
16
|
+
def __init__(self, engine: Engine):
|
|
19
17
|
self.eng: Engine = engine
|
|
20
|
-
self.con: Optional[Connection] = None
|
|
21
|
-
self.ses: Optional[Session] = None
|
|
22
|
-
self.insp: Optional[Inspector] = None
|
|
23
|
-
self.meta: Optional[MetaData] = None
|
|
24
|
-
db_path = P(self.eng.url.database) if self.eng.url.database else None
|
|
25
|
-
if db_path and db_path.exists():
|
|
26
|
-
self.path: Optional[P] = db_path
|
|
27
|
-
else: self.path = None
|
|
28
18
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.schema: list[str] = []
|
|
33
|
-
|
|
34
|
-
self.sch_tab: dict[str, list[str]]
|
|
35
|
-
self.sch_vws: dict[str, list[str]]
|
|
36
|
-
self.description: Optional[pl.DataFrame] = None
|
|
37
|
-
# self.tables = None
|
|
38
|
-
# self.views = None
|
|
39
|
-
# self.sch_tab: Optional[Struct] = None
|
|
40
|
-
# self.sch_vws: Optional[Struct] = None
|
|
41
|
-
# if inspect: self.refresh()
|
|
42
|
-
# self.ip_formatter: Optional[Any] = None
|
|
43
|
-
# self.db_specs: Optional[Any] = None
|
|
44
|
-
if self.path is not None:
|
|
45
|
-
if self.path.is_file():
|
|
46
|
-
path_repr = self.path.as_uri()
|
|
47
|
-
else:
|
|
48
|
-
path_repr = self.path
|
|
49
|
-
print(f"Database at {path_repr} is ready.")
|
|
50
|
-
|
|
51
|
-
def refresh(self, sch: Optional[str] = None) -> 'DBMS': # fails if multiple schemas are there and None is specified
|
|
52
|
-
self.con = self.eng.connect()
|
|
53
|
-
self.ses = sessionmaker()(bind=self.eng) # ORM style
|
|
54
|
-
self.meta = MetaData()
|
|
55
|
-
self.meta.reflect(bind=self.eng, schema=sch or self.sch)
|
|
56
|
-
insp = inspect__(subject=self.eng)
|
|
57
|
-
self.insp = insp
|
|
58
|
-
assert self.insp is not None
|
|
59
|
-
self.schema = self.insp.get_schema_names()
|
|
60
|
-
print(f"Inspecting tables of schema `{self.schema}` {self.eng}")
|
|
61
|
-
self.sch_tab = {k: v for k, v in zip(self.schema, [insp.get_table_names(schema=x) for x in self.schema])} # dict(zip(self.schema, self.schema.apply(lambda x: self.insp.get_table_names(schema=x)))) #
|
|
62
|
-
print(f"Inspecting views of schema `{self.schema}` {self.eng}")
|
|
63
|
-
self.sch_vws = {k: v for k, v in zip(self.schema, [insp.get_view_names(schema=x) for x in self.schema])}
|
|
64
|
-
return self
|
|
65
|
-
|
|
66
|
-
@classmethod
|
|
67
|
-
def from_local_db(cls, path: OPLike = None, echo: bool = False, share_across_threads: bool = False, pool_size: int = 5, **kwargs: Any):
|
|
68
|
-
return cls(engine=cls.make_sql_engine(path=path, echo=echo, share_across_threads=share_across_threads, pool_size=pool_size, **kwargs))
|
|
19
|
+
@staticmethod
|
|
20
|
+
def from_local_db(path: OPLike = None, echo: bool = False, share_across_threads: bool = False, pool_size: int = 5, **kwargs: Any):
|
|
21
|
+
return DBMS(engine=DBMS.make_sql_engine(path=path, echo=echo, share_across_threads=share_across_threads, pool_size=pool_size, **kwargs))
|
|
69
22
|
|
|
70
23
|
def __repr__(self): return f"DataBase @ {self.eng}"
|
|
71
|
-
def get_columns(self, table: str, sch: Optional[str] = None):
|
|
72
|
-
assert self.meta is not None
|
|
73
|
-
return self.meta.tables[self._get_table_identifier(table=table, sch=sch)].exported_columns.keys()
|
|
74
24
|
def close(self, sleep: int = 2):
|
|
75
|
-
if self.path:
|
|
76
|
-
print(f"Terminating database `{self.path.as_uri() if self.path.is_file() and 'memory' not in str(self.path) else self.path}`")
|
|
77
|
-
if self.con: self.con.close()
|
|
78
|
-
if self.ses: self.ses.close()
|
|
79
25
|
self.eng.pool.dispose()
|
|
80
26
|
self.eng.dispose()
|
|
81
27
|
time.sleep(sleep)
|
|
82
|
-
|
|
83
|
-
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _get_table_identifier(engine: Engine, table: str, sch: Optional[str]):
|
|
84
30
|
if sch is not None:
|
|
85
31
|
# Handle DuckDB schema names that contain dots (e.g., "klines.main")
|
|
86
|
-
if
|
|
32
|
+
if engine.url.drivername == 'duckdb' and '.' in sch and sch.endswith('.main'):
|
|
87
33
|
# For DuckDB schemas like "klines.main", just use the table name without schema
|
|
88
34
|
return f'"{table}"'
|
|
89
35
|
else:
|
|
@@ -91,83 +37,6 @@ class DBMS:
|
|
|
91
37
|
else:
|
|
92
38
|
return f'"{table}"'
|
|
93
39
|
|
|
94
|
-
@staticmethod
|
|
95
|
-
def make_sql_engine(path: OPLike = None, echo: bool = False, dialect: str = "sqlite", driver: str = ["pysqlite", "DBAPI"][0], pool_size: int = 5, share_across_threads: bool = True, **kwargs: Any):
|
|
96
|
-
"""Establish lazy initialization with database"""
|
|
97
|
-
from sqlalchemy.pool import StaticPool, NullPool
|
|
98
|
-
_ = NullPool
|
|
99
|
-
_ = driver
|
|
100
|
-
if str(path) == "memory":
|
|
101
|
-
print("Linking to in-memory database.")
|
|
102
|
-
if share_across_threads:
|
|
103
|
-
# see: https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads
|
|
104
|
-
return create_engine(url=f"{dialect}+{driver}:///:memory:", echo=echo, future=True, poolclass=StaticPool, connect_args={"check_same_thread": False})
|
|
105
|
-
else:
|
|
106
|
-
return create_engine(url=f"{dialect}+{driver}:///:memory:", echo=echo, future=True, pool_size=pool_size, **kwargs)
|
|
107
|
-
if path is None:
|
|
108
|
-
tmp_dir = P.home().joinpath(".tmp").joinpath("tmp_dbs")
|
|
109
|
-
tmp_dir.mkdir(parents=True, exist_ok=True)
|
|
110
|
-
import tempfile
|
|
111
|
-
with tempfile.NamedTemporaryFile(suffix=".sqlite", dir=str(tmp_dir), delete=False) as tmp_file:
|
|
112
|
-
path = P(tmp_file.name)
|
|
113
|
-
else:
|
|
114
|
-
path = P(path).expanduser().resolve()
|
|
115
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
116
|
-
path_repr = path.as_uri() if path.is_file() else path
|
|
117
|
-
dialect = path.suffix.removeprefix('.')
|
|
118
|
-
print(f"Linking to database at {path_repr}")
|
|
119
|
-
connect_args = kwargs.pop("connect_args", {}) or {}
|
|
120
|
-
try:
|
|
121
|
-
if path.suffix == ".duckdb": # only apply for duckdb files
|
|
122
|
-
# don't overwrite user's explicit setting if already provided
|
|
123
|
-
connect_args.setdefault("read_only", True)
|
|
124
|
-
print(" - Opening DuckDB in read-only mode.")
|
|
125
|
-
except Exception:
|
|
126
|
-
pass
|
|
127
|
-
if pool_size == 0:
|
|
128
|
-
res = create_engine(url=f"{dialect}:///{path}", echo=echo, future=True, poolclass=NullPool, connect_args=connect_args, **kwargs) # echo flag is just a short for the more formal way of logging sql commands.
|
|
129
|
-
else:
|
|
130
|
-
res = create_engine(url=f"{dialect}:///{path}", echo=echo, future=True, pool_size=pool_size, connect_args=connect_args, **kwargs) # echo flag is just a short for the more formal way of logging sql commands.
|
|
131
|
-
return res
|
|
132
|
-
@staticmethod
|
|
133
|
-
def make_sql_async_engine(path: OPLike = None, echo: bool = False, dialect: str = "sqlite", driver: str = "aiosqlite", pool_size: int = 5, share_across_threads: bool = True, **kwargs: Any):
|
|
134
|
-
"""Establish lazy initialization with database"""
|
|
135
|
-
from sqlalchemy.pool import StaticPool, NullPool
|
|
136
|
-
_ = NullPool
|
|
137
|
-
if str(path) == "memory":
|
|
138
|
-
print("Linking to in-memory database.")
|
|
139
|
-
if share_across_threads:
|
|
140
|
-
# see: https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads
|
|
141
|
-
return create_async_engine(url=f"{dialect}+{driver}://", echo=echo, future=True, poolclass=StaticPool, connect_args={"mode": "memory", "cache": "shared"})
|
|
142
|
-
else:
|
|
143
|
-
return create_async_engine(url=f"{dialect}+{driver}:///:memory:", echo=echo, future=True, pool_size=pool_size, **kwargs)
|
|
144
|
-
if path is None:
|
|
145
|
-
tmp_dir = P.home().joinpath(".tmp").joinpath("tmp_dbs")
|
|
146
|
-
tmp_dir.mkdir(parents=True, exist_ok=True)
|
|
147
|
-
import tempfile
|
|
148
|
-
with tempfile.NamedTemporaryFile(suffix=".sqlite", dir=str(tmp_dir), delete=False) as tmp_file:
|
|
149
|
-
path = P(tmp_file.name)
|
|
150
|
-
else:
|
|
151
|
-
path = P(path).expanduser().resolve()
|
|
152
|
-
path.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
-
path_repr = path.as_uri() if path.is_file() else path
|
|
154
|
-
dialect = path.suffix.removeprefix('.')
|
|
155
|
-
print(f"Linking to database at {path_repr}")
|
|
156
|
-
# Add DuckDB-specific read-only flag automatically when pointing to an existing .duckdb file
|
|
157
|
-
connect_args = kwargs.pop("connect_args", {}) or {}
|
|
158
|
-
try:
|
|
159
|
-
if path.suffix == ".duckdb": # only apply for duckdb files
|
|
160
|
-
# don't overwrite user's explicit setting if already provided
|
|
161
|
-
connect_args.setdefault("read_only", True)
|
|
162
|
-
print(" - Opening DuckDB in read-only mode.")
|
|
163
|
-
except Exception:
|
|
164
|
-
pass
|
|
165
|
-
if pool_size == 0:
|
|
166
|
-
res = create_async_engine(url=f"{dialect}+{driver}:///{path}", echo=echo, future=True, poolclass=NullPool, connect_args=connect_args, **kwargs) # echo flag is just a short for the more formal way of logging sql commands.
|
|
167
|
-
else:
|
|
168
|
-
res = create_async_engine(url=f"{dialect}+{driver}:///{path}", echo=echo, future=True, pool_size=pool_size, connect_args=connect_args, **kwargs) # echo flag is just a short for the more formal way of logging sql commands.
|
|
169
|
-
return res
|
|
170
|
-
|
|
171
40
|
# ==================== QUERIES =====================================
|
|
172
41
|
def execute_as_you_go(self, *commands: str, res_func: Callable[[Any], Any] = lambda x: x.all(), df: bool = False):
|
|
173
42
|
with self.eng.begin() as conn:
|
|
@@ -194,17 +63,41 @@ class DBMS:
|
|
|
194
63
|
# return result if not df else pl.DataFrame(result)
|
|
195
64
|
|
|
196
65
|
# ========================== TABLES =====================================
|
|
197
|
-
def
|
|
66
|
+
def insert_dicts(self, table: str, *mydicts: dict[str, Any]) -> None:
|
|
67
|
+
cmd = f"""INSERT INTO {table} VALUES """
|
|
68
|
+
for mydict in mydicts: cmd += f"""({tuple(mydict)}), """
|
|
69
|
+
self.execute_begin_once(cmd)
|
|
70
|
+
|
|
71
|
+
def refresh(self, sch: Optional[str] = None) -> dict[str, Any]:
|
|
72
|
+
con = self.eng.connect()
|
|
73
|
+
ses = sessionmaker()(bind=self.eng)
|
|
74
|
+
meta = MetaData()
|
|
75
|
+
meta.reflect(bind=self.eng, schema=sch)
|
|
76
|
+
insp = inspect__(subject=self.eng)
|
|
77
|
+
schema = insp.get_schema_names()
|
|
78
|
+
sch_tab = {k: v for k, v in zip(schema, [insp.get_table_names(schema=x) for x in schema])}
|
|
79
|
+
sch_vws = {k: v for k, v in zip(schema, [insp.get_view_names(schema=x) for x in schema])}
|
|
80
|
+
return {'con': con, 'ses': ses, 'meta': meta, 'insp': insp, 'schema': schema, 'sch_tab': sch_tab, 'sch_vws': sch_vws}
|
|
81
|
+
|
|
82
|
+
def get_columns(self, table: str, sch: Optional[str] = None) -> list[str]:
|
|
83
|
+
meta = MetaData()
|
|
84
|
+
meta.reflect(bind=self.eng, schema=sch)
|
|
85
|
+
return list(meta.tables[self._get_table_identifier(self.eng, table, sch)].exported_columns.keys())
|
|
86
|
+
|
|
87
|
+
def read_table(self, table: Optional[str] = None, sch: Optional[str] = None, size: int = 5) -> pl.DataFrame:
|
|
88
|
+
insp = inspect__(self.eng)
|
|
89
|
+
schema = insp.get_schema_names()
|
|
90
|
+
sch_tab = {k: v for k, v in zip(schema, [insp.get_table_names(schema=x) for x in schema])}
|
|
198
91
|
if sch is None:
|
|
199
92
|
# First try to find schemas that have tables (excluding system schemas)
|
|
200
93
|
schemas_with_tables = []
|
|
201
|
-
for schema_name in
|
|
94
|
+
for schema_name in schema:
|
|
202
95
|
if schema_name not in ["information_schema", "pg_catalog", "system"]:
|
|
203
|
-
if schema_name in
|
|
96
|
+
if schema_name in sch_tab and len(sch_tab[schema_name]) > 0:
|
|
204
97
|
schemas_with_tables.append(schema_name)
|
|
205
98
|
|
|
206
99
|
if len(schemas_with_tables) == 0:
|
|
207
|
-
raise ValueError(f"No schemas with tables found. Available schemas: {
|
|
100
|
+
raise ValueError(f"No schemas with tables found. Available schemas: {schema}")
|
|
208
101
|
|
|
209
102
|
# Prefer non-"main" schemas if available, otherwise use main
|
|
210
103
|
if len(schemas_with_tables) > 1 and "main" in schemas_with_tables:
|
|
@@ -214,64 +107,71 @@ class DBMS:
|
|
|
214
107
|
print(f"Auto-selected schema: `{sch}` from available schemas: {schemas_with_tables}")
|
|
215
108
|
|
|
216
109
|
if table is None:
|
|
217
|
-
if sch not in
|
|
218
|
-
raise ValueError(f"Schema `{sch}` not found. Available schemas: {list(
|
|
219
|
-
tables =
|
|
110
|
+
if sch not in sch_tab:
|
|
111
|
+
raise ValueError(f"Schema `{sch}` not found. Available schemas: {list(sch_tab.keys())}")
|
|
112
|
+
tables = sch_tab[sch]
|
|
220
113
|
assert len(tables) > 0, f"No tables found in schema `{sch}`"
|
|
221
114
|
import random
|
|
222
115
|
table = random.choice(tables)
|
|
223
116
|
print(f"Reading table `{table}` from schema `{sch}`")
|
|
224
|
-
|
|
117
|
+
with self.eng.connect() as conn:
|
|
225
118
|
try:
|
|
226
|
-
res =
|
|
119
|
+
res = conn.execute(text(f'''SELECT * FROM {self._get_table_identifier(self.eng, table, sch)} '''))
|
|
227
120
|
return pl.DataFrame(res.fetchmany(size))
|
|
228
121
|
except Exception:
|
|
229
122
|
print(f"Error executing query for table `{table}` in schema `{sch}`")
|
|
230
|
-
print(f"Available schemas and tables: {
|
|
123
|
+
print(f"Available schemas and tables: {sch_tab}")
|
|
231
124
|
raise
|
|
232
125
|
|
|
233
|
-
def
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
self.
|
|
237
|
-
|
|
238
|
-
def describe_db(self):
|
|
239
|
-
self.refresh()
|
|
240
|
-
assert self.meta is not None
|
|
126
|
+
def describe_db(self, sch: Optional[str] = None) -> pl.DataFrame:
|
|
127
|
+
meta = MetaData()
|
|
128
|
+
meta.reflect(bind=self.eng, schema=sch)
|
|
129
|
+
ses = sessionmaker()(bind=self.eng)
|
|
241
130
|
res_all = []
|
|
242
|
-
assert self.ses is not None
|
|
243
131
|
from rich.progress import Progress
|
|
244
132
|
with Progress() as progress:
|
|
245
|
-
task = progress.add_task("Inspecting tables", total=len(
|
|
246
|
-
for tbl in
|
|
133
|
+
task = progress.add_task("Inspecting tables", total=len(meta.sorted_tables))
|
|
134
|
+
for tbl in meta.sorted_tables:
|
|
247
135
|
table = tbl.name
|
|
248
|
-
if
|
|
249
|
-
table = f"{
|
|
250
|
-
count =
|
|
136
|
+
if sch is not None:
|
|
137
|
+
table = f"{sch}.{table}"
|
|
138
|
+
count = ses.query(tbl).count()
|
|
251
139
|
res = dict(table=table, count=count, size_mb=count * len(tbl.exported_columns) * 10 / 1e6,
|
|
252
|
-
columns=len(tbl.exported_columns), schema=
|
|
140
|
+
columns=len(tbl.exported_columns), schema=sch)
|
|
253
141
|
res_all.append(res)
|
|
254
142
|
progress.update(task, advance=1)
|
|
255
|
-
|
|
256
|
-
return self.description
|
|
143
|
+
return pl.DataFrame(res_all)
|
|
257
144
|
|
|
258
145
|
def describe_table(self, table: str, sch: Optional[str] = None, dtype: bool = True) -> None:
|
|
259
146
|
print(table.center(100, "="))
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
tbl =
|
|
263
|
-
|
|
264
|
-
count =
|
|
147
|
+
meta = MetaData()
|
|
148
|
+
meta.reflect(bind=self.eng, schema=sch)
|
|
149
|
+
tbl = meta.tables[self._get_table_identifier(self.eng, table, sch)]
|
|
150
|
+
ses = sessionmaker()(bind=self.eng)
|
|
151
|
+
count = ses.query(tbl).count()
|
|
265
152
|
res = dict(name=table, count=count, size_mb=count * len(tbl.exported_columns) * 10 / 1e6)
|
|
266
153
|
from machineconfig.utils.accessories import pprint
|
|
267
154
|
pprint(res, title="TABLE DETAILS")
|
|
268
155
|
dat = self.read_table(table=table, sch=sch, size=2)
|
|
269
|
-
df = dat
|
|
156
|
+
df = dat
|
|
270
157
|
print("SAMPLE:\n", df)
|
|
271
|
-
|
|
272
|
-
if dtype: print("\nDETAILED COLUMNS:\n", pl.DataFrame(
|
|
158
|
+
insp = inspect__(self.eng)
|
|
159
|
+
if dtype: print("\nDETAILED COLUMNS:\n", pl.DataFrame(insp.get_columns(self._get_table_identifier(self.eng, table, sch))))
|
|
273
160
|
print("\n" * 3)
|
|
274
161
|
|
|
162
|
+
@staticmethod
|
|
163
|
+
def make_sql_engine(path: OPLike = None, echo: bool = False, share_across_threads: bool = False, pool_size: int = 5, **kwargs: Any) -> Engine:
|
|
164
|
+
if path is None:
|
|
165
|
+
url = 'sqlite:///:memory:'
|
|
166
|
+
elif isinstance(path, str) and path.startswith(('sqlite://', 'postgresql://', 'mysql://', 'duckdb://')):
|
|
167
|
+
url = path
|
|
168
|
+
else:
|
|
169
|
+
path_str = str(P(path))
|
|
170
|
+
url = f'sqlite:///{path_str}'
|
|
171
|
+
connect_args = {}
|
|
172
|
+
if share_across_threads and 'sqlite' in url:
|
|
173
|
+
connect_args['check_same_thread'] = False
|
|
174
|
+
return create_engine(url, echo=echo, pool_size=pool_size, connect_args=connect_args, **kwargs)
|
|
275
175
|
|
|
276
176
|
DB_TMP_PATH = P.home().joinpath(".tmp").joinpath("tmp_dbs").joinpath("results").joinpath("data.sqlite")
|
|
277
177
|
|
|
@@ -290,7 +190,6 @@ def to_db(table: str, idx: int, idx_max: int, data: Any):
|
|
|
290
190
|
text(insert_row),
|
|
291
191
|
{'time': time_now, 'idx': idx, 'idx_max': idx_max, 'data': data_blob}
|
|
292
192
|
)
|
|
293
|
-
# conn.commit()
|
|
294
193
|
db.close()
|
|
295
194
|
|
|
296
195
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: machineconfig
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.36
|
|
4
4
|
Summary: Dotfiles management package
|
|
5
5
|
Author-email: Alex Al-Saffar <programmer@usa.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -29,8 +29,6 @@ Requires-Dist: questionary>=2.1.1
|
|
|
29
29
|
Requires-Dist: typer>=0.19.2
|
|
30
30
|
Provides-Extra: windows
|
|
31
31
|
Requires-Dist: pywin32; extra == "windows"
|
|
32
|
-
Provides-Extra: docs
|
|
33
|
-
Requires-Dist: pdoc>=15.0.2; extra == "docs"
|
|
34
32
|
Provides-Extra: plot
|
|
35
33
|
Requires-Dist: duckdb-engine>=0.17.0; extra == "plot"
|
|
36
34
|
Requires-Dist: sqlalchemy>=2.0.43; extra == "plot"
|
|
@@ -46,8 +46,8 @@ machineconfig/cluster/templates/cli_trogon.py,sha256=PFWGy8SFYIhT9r3ZV4oIEYfImsQ
|
|
|
46
46
|
machineconfig/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
47
47
|
machineconfig/jobs/installer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
48
|
machineconfig/jobs/installer/check_installations.py,sha256=wOtvWzyJSxbuFueFfcOc4gX_UbTRWv6tWpRcG-3Ml_8,10780
|
|
49
|
-
machineconfig/jobs/installer/installer_data.json,sha256=
|
|
50
|
-
machineconfig/jobs/installer/package_groups.py,sha256=
|
|
49
|
+
machineconfig/jobs/installer/installer_data.json,sha256=py5S8uf0RscDXDZWIsnrFoG2x90zt4XGhpW5w16CGpU,73161
|
|
50
|
+
machineconfig/jobs/installer/package_groups.py,sha256=lS8uG-gGLLodJm4grhGuhAJl7shTWfwKB2wGSD0PHzY,5378
|
|
51
51
|
machineconfig/jobs/installer/custom/gh.py,sha256=gn7TUSrsLx7uqFqj1Z-iYglS0EYBSgtJ9jWHxaJIfXM,4119
|
|
52
52
|
machineconfig/jobs/installer/custom/hx.py,sha256=YQClQXqWtGvon8BLFGf1Fp20JPkHgZeEZ6ebmCJQQfI,5838
|
|
53
53
|
machineconfig/jobs/installer/custom_dev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -127,14 +127,14 @@ machineconfig/scripts/linux/switch_ip,sha256=NQfeKMBSbFY3eP6M-BadD-TQo5qMP96DTp7
|
|
|
127
127
|
machineconfig/scripts/linux/warp-cli.sh,sha256=shFFZ9viet_DSEEHT8kxlGRHoJpO6o85pKYnc3rIkaA,3868
|
|
128
128
|
machineconfig/scripts/linux/z_ls,sha256=ATZtu0ccN3AKvAOxkwLq1xgQjJ3en5byEWJ3Q8afnNg,3340
|
|
129
129
|
machineconfig/scripts/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
130
|
-
machineconfig/scripts/python/agents.py,sha256=
|
|
130
|
+
machineconfig/scripts/python/agents.py,sha256=Bu2_my11RpvgOqUZuY8E0U8rlaKNL1IwBALIH8dYelU,10549
|
|
131
131
|
machineconfig/scripts/python/cloud.py,sha256=kRH9Pt1yEkASFskIVEgRmkidrksdkgv2-bBmjLSxzSo,814
|
|
132
|
-
machineconfig/scripts/python/croshell.py,sha256=
|
|
132
|
+
machineconfig/scripts/python/croshell.py,sha256=VRRf8oyh2DIkIv5sK7eV6thIglXa3tDD_FYlPc0NA2k,6476
|
|
133
133
|
machineconfig/scripts/python/devops.py,sha256=3mG-4RlF9vzcfXO7ISsxwRICQpJRR8JMqxtxKLE70fs,1822
|
|
134
134
|
machineconfig/scripts/python/devops_navigator.py,sha256=iR6HAYt0TA7efV_g6Lr9gPczN6mqvS0V5hShD1x-9R8,29537
|
|
135
135
|
machineconfig/scripts/python/fire_jobs.py,sha256=UxMkQ8WqxmZx_u1gn9LV_Cn4FjbCAtuWUWupZl1UkUI,13486
|
|
136
136
|
machineconfig/scripts/python/ftpx.py,sha256=17oCDB59C9z1RIpEoBTgmv8NFDTqhWzKWew9TqbP8vM,9406
|
|
137
|
-
machineconfig/scripts/python/interactive.py,sha256=
|
|
137
|
+
machineconfig/scripts/python/interactive.py,sha256=Zghe-b2Y6WIDPEhzsO1NDheTv4PAz4357SeL5AXJfh0,11777
|
|
138
138
|
machineconfig/scripts/python/sessions.py,sha256=lO_aTnh7T05XoCa8Ox-ROAWBKMtbo-DZzlFDUqzSipM,8716
|
|
139
139
|
machineconfig/scripts/python/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
140
140
|
machineconfig/scripts/python/ai/generate_files.py,sha256=Vfjgd0skJu-WTgqUxmOVFzaNMfSFBaFmY5oGGVY7MZY,2860
|
|
@@ -177,20 +177,21 @@ machineconfig/scripts/python/croshell_helpers/start_slidev.py,sha256=FAJ1_WkAQ7K
|
|
|
177
177
|
machineconfig/scripts/python/croshell_helpers/viewer.py,sha256=heQNjB9fwn3xxbPgMofhv1Lp6Vtkl76YjjexWWBM0pM,2041
|
|
178
178
|
machineconfig/scripts/python/croshell_helpers/viewer_template.py,sha256=ve3Q1-iKhCLc0VJijKvAeOYp2xaFOeIOC_XW956GWCc,3944
|
|
179
179
|
machineconfig/scripts/python/devops_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
180
|
-
machineconfig/scripts/python/devops_helpers/
|
|
181
|
-
machineconfig/scripts/python/devops_helpers/choose_wezterm_theme.py,sha256=pRXAGe2IpysYshsaF8CKEwHI8EGPtLcM8PtiAqM7vmM,3425
|
|
182
|
-
machineconfig/scripts/python/devops_helpers/cli_config.py,sha256=LObo-YBjpfCB0_0CYQlYBqTtApM1RUOE_H28gEcH7PY,2870
|
|
180
|
+
machineconfig/scripts/python/devops_helpers/cli_config.py,sha256=hoQXU44kCdvYMxLXZErC1xcExRmb1EMgSi7IUDQl7KA,3262
|
|
183
181
|
machineconfig/scripts/python/devops_helpers/cli_config_dotfile.py,sha256=9W9i8Qbs6i2NfTq0knywB3StvE_sHaZYZ0RslTyoVz8,2734
|
|
184
182
|
machineconfig/scripts/python/devops_helpers/cli_data.py,sha256=f_2espL92n6SoNb5sFVMvrK7LA29HzfrFAKhxKaud1M,510
|
|
185
|
-
machineconfig/scripts/python/devops_helpers/cli_nw.py,sha256=
|
|
183
|
+
machineconfig/scripts/python/devops_helpers/cli_nw.py,sha256=S4_6OwH_lZ73BPP6dY_InrB2rmwif6_h6DFLy8hlihY,1821
|
|
186
184
|
machineconfig/scripts/python/devops_helpers/cli_repos.py,sha256=n8UnFjFIgTro-OMf47y5PAdYzqVYCSM6-5P90MvJOpw,9683
|
|
187
185
|
machineconfig/scripts/python/devops_helpers/cli_self.py,sha256=XGWPZVZmwKaDOY_5IYj2l_Ke0ocjhfXP9NK5-nFwFSg,1467
|
|
188
186
|
machineconfig/scripts/python/devops_helpers/cli_terminal.py,sha256=-SNCDrQHBDUZw2cNNrEw3K3owzmZASBjd5deBKB49YY,5358
|
|
189
187
|
machineconfig/scripts/python/devops_helpers/devops_add_identity.py,sha256=wvjNgqsLmqD2SxbNCW_usqfp0LI-TDvcJJKGOWt2oFw,3775
|
|
190
|
-
machineconfig/scripts/python/devops_helpers/devops_add_ssh_key.py,sha256=
|
|
188
|
+
machineconfig/scripts/python/devops_helpers/devops_add_ssh_key.py,sha256=MwyHbpRP9r6DajARCdjEKVNrhEdv_yjd0lXxgEwjwsA,9179
|
|
191
189
|
machineconfig/scripts/python/devops_helpers/devops_backup_retrieve.py,sha256=nK47Rc7gQuDCnkk6_sW1y82gBnDJ9TdHU8XwMPFBK9c,5591
|
|
192
190
|
machineconfig/scripts/python/devops_helpers/devops_status.py,sha256=C1akn6mGteBVV9CiQnUX6H32ehnCgMdCyNgojXVQeqA,23287
|
|
193
191
|
machineconfig/scripts/python/devops_helpers/devops_update_repos.py,sha256=dtBh9mNNJEukyV47Cug98S0hvG9e1U43B0EQSeNtvvs,9394
|
|
192
|
+
machineconfig/scripts/python/devops_helpers/themes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
193
|
+
machineconfig/scripts/python/devops_helpers/themes/choose_pwsh_theme.ps1,sha256=BrJNDHoSK_fHjHwbJqRf3sufSa7SKseBS2kT0qxibJQ,2958
|
|
194
|
+
machineconfig/scripts/python/devops_helpers/themes/choose_wezterm_theme.py,sha256=pRXAGe2IpysYshsaF8CKEwHI8EGPtLcM8PtiAqM7vmM,3425
|
|
194
195
|
machineconfig/scripts/python/helpers_fire/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
195
196
|
machineconfig/scripts/python/helpers_fire/fire_agents_help_launch.py,sha256=RAee8ethEIxyQpRKqe6u9NwIOKWTspAF-nzmPap6a5k,5506
|
|
196
197
|
machineconfig/scripts/python/helpers_fire/fire_agents_help_search.py,sha256=qIfSS_su2YJ1Gb0_lu4cbjlJlYMBw0v52NTGiSrGjk8,2991
|
|
@@ -376,7 +377,7 @@ machineconfig/setup_linux/others/mint_keyboard_shortcuts.sh,sha256=F5dbg0n9RHsKG
|
|
|
376
377
|
machineconfig/setup_linux/ssh/openssh_all.sh,sha256=3dg6HEUFbHQOzLfSAtzK_D_GB8rGCCp_aBnxNdnidVc,824
|
|
377
378
|
machineconfig/setup_linux/ssh/openssh_wsl.sh,sha256=1eeRGrloVB34K5z8yWVUMG5b9pV-WBfHgV9jqXiYgCQ,1398
|
|
378
379
|
machineconfig/setup_linux/web_shortcuts/android.sh,sha256=gzep6bBhK7FCBvGcXK0fdJCtkSfBOftt0aFyDZq_eMs,68
|
|
379
|
-
machineconfig/setup_linux/web_shortcuts/interactive.sh,sha256=
|
|
380
|
+
machineconfig/setup_linux/web_shortcuts/interactive.sh,sha256=BvgzRC0u8CtST8uaguCyK7cFdrRfCTPTPLUqq1KTIXA,299
|
|
380
381
|
machineconfig/setup_windows/__init__.py,sha256=wVpUqoLqXl-_-bRd7gZw_PJ7WZ2GtOqfFMzo_lIwieg,454
|
|
381
382
|
machineconfig/setup_windows/apps.ps1,sha256=G5GqZ9G0aiQr_A-HaahtRdzpaTTdW6n3DRKMZWDTSPc,11214
|
|
382
383
|
machineconfig/setup_windows/machineconfig.ps1,sha256=gIQBOLIh65oUXgSjYMeYeD6lU1Bu80LZ59xqRc3T3BA,918
|
|
@@ -384,11 +385,10 @@ machineconfig/setup_windows/uv.ps1,sha256=mzkFJUQ57dukVQtY7WqAQIVUDMcixnkir8aNM_
|
|
|
384
385
|
machineconfig/setup_windows/others/docker.ps1,sha256=M8NfsSxH8YlmY92J4rSe1xWOwTW8IFrdgb8cI8Riu2E,311
|
|
385
386
|
machineconfig/setup_windows/others/obs.ps1,sha256=2andchcXpxS3rqZjGaMpY5VShxTAKWvw6eCrayjuaLo,30
|
|
386
387
|
machineconfig/setup_windows/others/power_options.ps1,sha256=c7Hn94jBD5GWF29CxMhmNpuM0hgXTQgVJmIRR_7sdcY,182
|
|
387
|
-
machineconfig/setup_windows/ssh/
|
|
388
|
-
machineconfig/setup_windows/ssh/
|
|
389
|
-
machineconfig/setup_windows/ssh/openssh-
|
|
390
|
-
machineconfig/setup_windows/
|
|
391
|
-
machineconfig/setup_windows/web_shortcuts/interactive.ps1,sha256=47KY5sr8Cy3bfzFCszwnGeQpIHpjh7r8_znv3TZhqWY,221
|
|
388
|
+
machineconfig/setup_windows/ssh/add-sshkey.ps1,sha256=qfPdqCpd9KP3VhH4ifsUm1Xvec7c0QVl4Wt8JIAm9HQ,1653
|
|
389
|
+
machineconfig/setup_windows/ssh/add_identity.ps1,sha256=b8ZXpmNUSw3IMYvqSY7ClpdWPG39FS7MefoWnRhWN2U,506
|
|
390
|
+
machineconfig/setup_windows/ssh/openssh-server.ps1,sha256=OMlYQdvuJQNxF5EILLPizB6BZAT3jAmDsv1WcVVxpFQ,2529
|
|
391
|
+
machineconfig/setup_windows/web_shortcuts/interactive.ps1,sha256=6zxr_jgoiMDIyLR82yMnSX9cGkJxgCewyhRr4Y7itJw,235
|
|
392
392
|
machineconfig/setup_windows/wt_and_pwsh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
393
393
|
machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py,sha256=ogxJnwpdcpH7N6dFJu95UCNoGYirZKQho_3X0F_hmXs,6791
|
|
394
394
|
machineconfig/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -414,7 +414,7 @@ machineconfig/utils/ai/generate_file_checklist.py,sha256=ajbmhcBToRugl75c_KZRq2X
|
|
|
414
414
|
machineconfig/utils/cloud/onedrive/setup_oauth.py,sha256=ZTVkqgrwbV_EoPvyT8dyOTUE0ur3BW4sa9o6QYtt5Bo,2341
|
|
415
415
|
machineconfig/utils/cloud/onedrive/transaction.py,sha256=m-aNcnWj_gfZVvJOSpkdIqjZxU_3nXx2CA-qKbQgP3I,26232
|
|
416
416
|
machineconfig/utils/files/ascii_art.py,sha256=cNJaJC07vx94fS44-tzgfbfBeCwXVrgpnWGBLUnfC38,5212
|
|
417
|
-
machineconfig/utils/files/dbms.py,sha256=
|
|
417
|
+
machineconfig/utils/files/dbms.py,sha256=oXDIqWLDiSO2icdeTMgTfd84yq6beDk89FCC8OZ8NcI,11277
|
|
418
418
|
machineconfig/utils/files/headers.py,sha256=F-sudsZ1JyAcmZNO4FdcyhoClbCdb2vMlqceT36zfhE,3717
|
|
419
419
|
machineconfig/utils/files/read.py,sha256=R1bvIIdiFX9N0JyzUISqVfewYFq30cY3z0kqSlKGtuA,4566
|
|
420
420
|
machineconfig/utils/files/ouch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -428,8 +428,8 @@ machineconfig/utils/schemas/fire_agents/fire_agents_input.py,sha256=Xbi59rU35AzR
|
|
|
428
428
|
machineconfig/utils/schemas/installer/installer_types.py,sha256=QClRY61QaduBPJoSpdmTIdgS9LS-RvE-QZ-D260tD3o,1214
|
|
429
429
|
machineconfig/utils/schemas/layouts/layout_types.py,sha256=TcqlZdGVoH8htG5fHn1KWXhRdPueAcoyApppZsPAPto,2020
|
|
430
430
|
machineconfig/utils/schemas/repos/repos_types.py,sha256=ECVr-3IVIo8yjmYmVXX2mnDDN1SLSwvQIhx4KDDQHBQ,405
|
|
431
|
-
machineconfig-5.
|
|
432
|
-
machineconfig-5.
|
|
433
|
-
machineconfig-5.
|
|
434
|
-
machineconfig-5.
|
|
435
|
-
machineconfig-5.
|
|
431
|
+
machineconfig-5.36.dist-info/METADATA,sha256=5H89IL20PMv-QGMrVBhSdigzAE7qvmwnnV93sSIVqyg,3040
|
|
432
|
+
machineconfig-5.36.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
433
|
+
machineconfig-5.36.dist-info/entry_points.txt,sha256=z7b9guivf0GSKUG6b8ALgbDoRg2LuPfkGP_p-PxgX9g,469
|
|
434
|
+
machineconfig-5.36.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
|
|
435
|
+
machineconfig-5.36.dist-info/RECORD,,
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# install server (sshd).
|
|
3
|
-
Invoke-WebRequest https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/openssh-server.ps1 | Invoke-Expression
|
|
4
|
-
|
|
5
|
-
if (!$pubkey_string) {
|
|
6
|
-
$pubkey_url = 'https://github.com/thisismygitrepo.keys' # (CHANGE APPROPRIATELY)
|
|
7
|
-
$pubkey_string = (Invoke-WebRequest $pubkey_url).Content
|
|
8
|
-
} else {
|
|
9
|
-
Write-Output "pubkey_string is already defined."
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
echo $null >> $HOME/.ssh/authorized_keys # powershell way of touching a file if it doesn't exist
|
|
14
|
-
echo $pubkey_string >> $HOME/.ssh/authorized_keys
|
|
15
|
-
echo $pubkey_string > $HOME/.ssh/pubkey.pub
|
|
16
|
-
|
|
17
|
-
Invoke-WebRequest https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/openssh-server_add-sshkey.ps1 | Invoke-Expression
|
|
18
|
-
ipconfig.exe
|
|
19
|
-
|
|
20
|
-
echo "Done"
|
|
21
|
-
echo "USE: ssh $env:USERNAME@$env:COMPUTERNAME -p 22"
|
|
22
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|