machineconfig 3.97__py3-none-any.whl → 3.99__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/scripts/python/devops.py +13 -63
- machineconfig/scripts/python/devops_devapps_install.py +21 -82
- machineconfig/scripts/python/interactive.py +149 -138
- machineconfig/scripts/python/repos.py +0 -1
- machineconfig/scripts/python/share_terminal.py +30 -11
- machineconfig/setup_linux/__init__.py +0 -0
- machineconfig/setup_linux/web_shortcuts/interactive.sh +2 -51
- machineconfig/setup_windows/web_shortcuts/interactive.ps1 +2 -181
- machineconfig/utils/installer.py +5 -6
- machineconfig/utils/installer_utils/installer_abc.py +134 -2
- machineconfig/utils/installer_utils/installer_class.py +1 -41
- machineconfig/utils/options.py +3 -16
- {machineconfig-3.97.dist-info → machineconfig-3.99.dist-info}/METADATA +1 -1
- {machineconfig-3.97.dist-info → machineconfig-3.99.dist-info}/RECORD +17 -16
- {machineconfig-3.97.dist-info → machineconfig-3.99.dist-info}/entry_points.txt +1 -4
- {machineconfig-3.97.dist-info → machineconfig-3.99.dist-info}/WHEEL +0 -0
- {machineconfig-3.97.dist-info → machineconfig-3.99.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Optional
|
|
4
|
+
from typing import Optional, Annotated
|
|
5
5
|
import typer
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.panel import Panel
|
|
@@ -43,12 +43,33 @@ def display_terminal_url(local_ip_v4: str, port: int) -> None:
|
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
# Print with extra spacing and attention-grabbing elements
|
|
46
|
-
console.print("\n" + "🔥" * 60 + "\n", style="bright_red bold")
|
|
46
|
+
# console.print("\n" + "🔥" * 60 + "\n", style="bright_red bold")
|
|
47
47
|
console.print(panel)
|
|
48
|
-
console.print("🔥" * 60 + "\n", style="bright_red bold")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def
|
|
48
|
+
# console.print("🔥" * 60 + "\n", style="bright_red bold")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def install_ttyd():
|
|
52
|
+
# uv run --python 3.13 --with machineconfig devops install ttyd
|
|
53
|
+
from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
|
|
54
|
+
exists = check_tool_exists("ttyd")
|
|
55
|
+
if exists:
|
|
56
|
+
print("✅ ttyd is already installed.")
|
|
57
|
+
return
|
|
58
|
+
print("⏳ ttyd not found. Installing...")
|
|
59
|
+
from machineconfig.scripts.python.devops_devapps_install import main
|
|
60
|
+
main(which="ttyd")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def main(
|
|
65
|
+
port: Annotated[Optional[int], typer.Option("--port", "-p", help="Port to run the terminal server on (default: 7681)")] = None,
|
|
66
|
+
username: Annotated[Optional[str], typer.Option("--username", "-u", help="Username for terminal access (default: current user)")] = None,
|
|
67
|
+
password: Annotated[Optional[str], typer.Option("--password", "-w", help="Password for terminal access (default: from ~/dotfiles/creds/passwords/quick_password)")] = None
|
|
68
|
+
) -> None:
|
|
69
|
+
install_ttyd()
|
|
70
|
+
if username is None:
|
|
71
|
+
import getpass
|
|
72
|
+
username = getpass.getuser()
|
|
52
73
|
if password is None:
|
|
53
74
|
pwd_path = Path.home().joinpath("dotfiles/creds/passwords/quick_password")
|
|
54
75
|
if pwd_path.exists():
|
|
@@ -67,11 +88,9 @@ def main(port: Optional[int]=None, password: Optional[str]=None) -> None:
|
|
|
67
88
|
|
|
68
89
|
# Display the flashy terminal announcement
|
|
69
90
|
display_terminal_url(local_ip_v4, port)
|
|
70
|
-
|
|
71
|
-
code = f"""
|
|
72
|
-
|
|
73
|
-
uv run --python 3.13 --with machineconfig install -ttyd
|
|
74
|
-
ttyd --writable -t enableSixel=true --port {port} --credential "$USER:{password}" -t 'theme={"background": "black"}' bash
|
|
91
|
+
|
|
92
|
+
code = f"""#!/bin/bash
|
|
93
|
+
ttyd --writable -t enableSixel=true --port {port} --credential "{username}:{password}" -t 'theme={{"background": "black"}}' bash
|
|
75
94
|
"""
|
|
76
95
|
import subprocess
|
|
77
96
|
subprocess.run(code, shell=True, check=True)
|
|
File without changes
|
|
@@ -2,57 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
echo """
|
|
4
4
|
=======================================================================
|
|
5
|
-
📦 MACHINE CONFIGURATION | Interactive Installation Script
|
|
5
|
+
📦 MACHINE CONFIGURATION | Interactive Installation Script. Installing uv then getting started.
|
|
6
6
|
======================================================================="""
|
|
7
7
|
|
|
8
8
|
curl https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_linux/ve.sh | bash
|
|
9
|
-
$HOME/.local/bin/uv run --python 3.13 --with machineconfig ia
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
echo """#=======================================================================
|
|
15
|
-
📂 DOTFILES MIGRATION | Configuration transfer options
|
|
16
|
-
#=======================================================================
|
|
17
|
-
|
|
18
|
-
🖱️ Method 1: USING MOUSE WITHOUT KB OR BROWSER SHARE
|
|
19
|
-
On original machine, run:
|
|
20
|
-
cd ~/dotfiles/creds/msc
|
|
21
|
-
easy-sharing . --password rew --username al
|
|
22
|
-
Then open brave on new machine to get MouseWithoutBorders password
|
|
23
|
-
|
|
24
|
-
🔐 Method 2: USING SSH
|
|
25
|
-
FROM REMOTE, RUN:
|
|
26
|
-
fptx ~/dotfiles $USER@$(hostname):^ -z
|
|
27
|
-
# OR, using IP address if router has not yet found the hostname:
|
|
28
|
-
fptx ~/dotfiles $USER@$(hostname -I | awk '{print $1}'):^ -z
|
|
29
|
-
|
|
30
|
-
☁️ Method 3: USING INTERNET SECURE SHARE
|
|
31
|
-
cd ~
|
|
32
|
-
cloud_copy SHARE_URL . --config ss
|
|
33
|
-
(requires symlinks to be created first)
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
echo """#=======================================================================
|
|
37
|
-
📂 DOTFILES STATUS | Configuration files check
|
|
38
|
-
#=======================================================================
|
|
39
|
-
"""
|
|
40
|
-
read -p "📂 Have you finished copying dotfiles? [y]/n? " choice
|
|
41
|
-
|
|
42
|
-
echo """#=======================================================================
|
|
43
|
-
🔗 SYMLINK CREATION | Configuration setup
|
|
44
|
-
#=======================================================================
|
|
45
|
-
"""
|
|
46
|
-
read -p "🔗 Create Symlinks (finish dotfiles transfer first) [y]/n? " choice
|
|
47
|
-
choice=${choice:-y}
|
|
48
|
-
if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
|
|
49
|
-
echo """ 🔧 Creating symlinks and setting permissions...
|
|
50
|
-
"""
|
|
51
|
-
uv run --python 3.13 --with machineconfig python -m fire machineconfig.profile.create main_symlinks --choice=all
|
|
52
|
-
sudo chmod 600 $HOME/.ssh/*
|
|
53
|
-
sudo chmod 700 $HOME/.ssh
|
|
54
|
-
else
|
|
55
|
-
echo """ ⏭️ Skipping symlink creation
|
|
56
|
-
"""
|
|
57
|
-
fi
|
|
58
|
-
|
|
9
|
+
$HOME/.local/bin/uv run --python 3.13 --with machineconfig devops ia
|
|
@@ -3,189 +3,10 @@ Write-Host "
|
|
|
3
3
|
📦 Machine Configuration Installation Script
|
|
4
4
|
============================================="
|
|
5
5
|
|
|
6
|
-
Write-Host "ℹ️ If you have execution policy issues, run:
|
|
7
|
-
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser"
|
|
8
|
-
Write-Host "💡 To accept all prompts automatically, run: `$yesAll = `$true`n"
|
|
6
|
+
Write-Host "ℹ️ If you have execution policy issues, run: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser"
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
Write-Host "🔄 Setting up Python environment..."
|
|
12
8
|
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/ve.ps1" -OutFile "ve.ps1"
|
|
13
9
|
.\ve.ps1
|
|
14
10
|
rm ve.ps1
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/repos.ps1" -OutFile "repos.ps1"
|
|
18
|
-
.\repos.ps1
|
|
19
|
-
rm repos.ps1
|
|
20
|
-
|
|
21
|
-
Write-Host "`n📂 ============================================
|
|
22
|
-
🔄 DOTFILES MIGRATION OPTIONS
|
|
23
|
-
============================================="
|
|
24
|
-
|
|
25
|
-
Write-Host "🖱️ Method 1: USING MOUSE WITHOUT KB OR BROWSER SHARE
|
|
26
|
-
On original machine, run:
|
|
27
|
-
cd ~/dotfiles/creds/msc
|
|
28
|
-
easy-sharing . --password rew
|
|
29
|
-
Then open browser on new machine to get MouseWithoutBorders password"
|
|
30
|
-
|
|
31
|
-
Write-Host "`n🔐 Method 2: USING SSH
|
|
32
|
-
FROM REMOTE, RUN:
|
|
33
|
-
ftpx ~/dotfiles `$(hostname):^ -z"
|
|
34
|
-
|
|
35
|
-
Write-Host "`n💻 For WSL:
|
|
36
|
-
wsl_server.ps1
|
|
37
|
-
ftpx ~/dotfiles `$env:USERNAME@localhost:2222 -z
|
|
38
|
-
# OR:
|
|
39
|
-
New-Item -ItemType SymbolicLink -Path `$env:USERPROFILE\dotfiles -Target \\wsl`$\Ubuntu\home\`$env:USERNAME\dotfiles"
|
|
40
|
-
|
|
41
|
-
Write-Host "`n☁️ Method 3: USING INTERNET SECURE SHARE
|
|
42
|
-
cd ~
|
|
43
|
-
cloud_copy SHARE_URL . --config ss
|
|
44
|
-
(requires symlinks to be created first)"
|
|
45
|
-
|
|
46
|
-
Write-Host "`n----------------------------------------"
|
|
47
|
-
if (-not $yesAll) {
|
|
48
|
-
$choice = Read-Host "🔒 Install SSH Server [y]/n"
|
|
49
|
-
} else {
|
|
50
|
-
$choice = "y"
|
|
51
|
-
}
|
|
52
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
53
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
54
|
-
Write-Host "`n🔧 Installing and configuring SSH server..."
|
|
55
|
-
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
|
|
56
|
-
Start-Service sshd
|
|
57
|
-
Set-Service -Name sshd -StartupType 'Automatic'
|
|
58
|
-
Write-Host "✅ SSH Server installed and configured successfully!"
|
|
59
|
-
} else {
|
|
60
|
-
Write-Host "⏭️ Skipping SSH server installation"
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
Write-Host "`n✨ ===========================================
|
|
64
|
-
🎉 Installation Complete!
|
|
65
|
-
============================================="
|
|
66
|
-
|
|
67
|
-
# Confirm copying finished
|
|
68
|
-
if (-not $yesAll) {
|
|
69
|
-
$choice = Read-Host "Did you finish copying [y]/n ? "
|
|
70
|
-
} else {
|
|
71
|
-
$choice = "y"
|
|
72
|
-
}
|
|
73
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
74
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
75
|
-
Write-Host "Proceeding..."
|
|
76
|
-
} else {
|
|
77
|
-
Write-Host "Installation aborted."
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
# Create Symlinks
|
|
81
|
-
if (-not $yesAll) {
|
|
82
|
-
$createLinksChoice = Read-Host "Create (Sym/Hard)links (finish dotfiles transfer first) [y]/n ? "
|
|
83
|
-
} else {
|
|
84
|
-
$createLinksChoice = "y"
|
|
85
|
-
}
|
|
86
|
-
if ([string]::IsNullOrEmpty($createLinksChoice)) { $createLinksChoice = "y" }
|
|
87
|
-
|
|
88
|
-
if ($createLinksChoice -eq "y" -or $createLinksChoice -eq "Y") {
|
|
89
|
-
if (-not $yesAll) {
|
|
90
|
-
$linkTypeChoice = Read-Host "Create Symlinks (s) or Hardlinks (h) [s]/h ? "
|
|
91
|
-
} else {
|
|
92
|
-
$linkTypeChoice = "h"
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
. ~\code\machineconfig\.venv\Scripts\Activate.ps1
|
|
96
|
-
|
|
97
|
-
if ($linkTypeChoice -eq "s" -or $linkTypeChoice -eq "S") {
|
|
98
|
-
python -m fire machineconfig.profile.create main --choice=all
|
|
99
|
-
} elseif ($linkTypeChoice -eq "h" -or $linkTypeChoice -eq "H") {
|
|
100
|
-
# python -m fire machineconfig.profile.create_hardlinks main --choice=all
|
|
101
|
-
python -m fire machineconfig.profile.create main --choice=all
|
|
102
|
-
} else {
|
|
103
|
-
Write-Host "Invalid choice for link type. Installation aborted."
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
# icacls "~\.ssh\*" /inheritance:r /grant:r "$($env:USERNAME):(F)"
|
|
107
|
-
# icacls "~\.ssh" /inheritance:r /grant:r "$($env:USERNAME):(F)"
|
|
108
|
-
} else {
|
|
109
|
-
Write-Host "Installation aborted."
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
# Install CLI Apps
|
|
113
|
-
if (-not $yesAll) {
|
|
114
|
-
$choice = Read-Host "Install CLI Apps [y]/n ? "
|
|
115
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
116
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
117
|
-
. ~\code\machineconfig\src\machineconfig\setup_windows\devapps.ps1
|
|
118
|
-
} else {
|
|
119
|
-
Write-Host "Installation aborted."
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
uv run --python 3.13 --with machineconfig python -m fire machineconfig.scripts.python.devops_devapps_install main --which=essentials
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# Retrieve Repos
|
|
128
|
-
if (-not $yesAll) {
|
|
129
|
-
$choice = Read-Host "Retrieve Repos at ~/code [y]/n ? "
|
|
130
|
-
} else {
|
|
131
|
-
$choice = "y"
|
|
132
|
-
}
|
|
133
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
134
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
135
|
-
repos ~\code --clone --cloud odg1
|
|
136
|
-
} else {
|
|
137
|
-
Write-Host "Installation aborted."
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
# Retrieve Data
|
|
141
|
-
if (-not $yesAll) {
|
|
142
|
-
$choice = Read-Host "Retrieve data [y]/n ? "
|
|
143
|
-
} else {
|
|
144
|
-
$choice = "y"
|
|
145
|
-
}
|
|
146
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
147
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
148
|
-
uv run --python 3.13 --with machineconfig python -m fire machineconfig.scripts.python.devops_backup_retrieve main --direction=RETRIEVE
|
|
149
|
-
} else {
|
|
150
|
-
Write-Host "Installation aborted."
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# Install Brave, WezTerm, and VSCode
|
|
155
|
-
if (-not $yesAll) {
|
|
156
|
-
$choice = Read-Host "Install Brave+WindowsTerminal+WezTerm+VSCode [y]/n ? "
|
|
157
|
-
} else {
|
|
158
|
-
$choice = "y"
|
|
159
|
-
}
|
|
160
|
-
if ([string]::IsNullOrEmpty($choice)) { $choice = "y" }
|
|
161
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
162
|
-
winget install --no-upgrade --name "Windows Terminal" --Id "Microsoft.WindowsTerminal" --source winget --scope user --accept-package-agreements --accept-source-agreements # Terminal is is installed by default on W 11
|
|
163
|
-
winget install --no-upgrade --name "Powershell" --Id "Microsoft.PowerShell" --source winget --scope user --accept-package-agreements --accept-source-agreements # powershell require admin
|
|
164
|
-
winget install --no-upgrade --name "Brave" --Id "Brave.Brave" --source winget --scope user --accept-package-agreements --accept-source-agreements
|
|
165
|
-
winget install --no-upgrade --name "Microsoft Visual Studio Code" --Id "Microsoft.VisualStudioCode" --source winget --scope user --accept-package-agreements --accept-source-agreements
|
|
166
|
-
uv run --python 3.13 --with machineconfig python -m fire machineconfig.setup_windows.wt_and_pwsh.install_nerd_fonts main
|
|
167
|
-
uv run --python 3.13 --with machineconfig python -m fire machineconfig.setup_windows.wt_and_pwsh.set_wt_settings main
|
|
168
|
-
} else {
|
|
169
|
-
Write-Host "Installation aborted."
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
# Install Apps
|
|
174
|
-
if (-not $yesAll) {
|
|
175
|
-
$choice = Read-Host "Install Windows Apps [y]/n ? "
|
|
176
|
-
} else {
|
|
177
|
-
$choice = "y"
|
|
178
|
-
}
|
|
179
|
-
if ($choice -eq "y" -or $choice -eq "Y") {
|
|
180
|
-
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/thisismygitrepo/machineconfig/main/src/machineconfig/setup_windows/apps.ps1" -OutFile "apps.ps1"
|
|
181
|
-
.\apps.ps1
|
|
182
|
-
} else {
|
|
183
|
-
Write-Host "Installation aborted."
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
# Instructions for Thunderbird profile restoration
|
|
187
|
-
# Run this after installing Thunderbird and starting it and shutting it down but before downloading backup
|
|
188
|
-
# cd ~/AppData/Roaming/ThunderBird/Profiles
|
|
189
|
-
# $res = Get-ChildItem
|
|
190
|
-
# $name = $res[0].Name
|
|
191
|
-
# Move-Item $backup_folder $name
|
|
12
|
+
uv run --python 3.13 --with machineconfig devops ia
|
machineconfig/utils/installer.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"""package manager"""
|
|
2
2
|
|
|
3
|
-
from machineconfig.utils.installer_utils.installer_abc import
|
|
3
|
+
from machineconfig.utils.installer_utils.installer_abc import check_if_installed_already
|
|
4
4
|
from machineconfig.utils.installer_utils.installer_class import Installer
|
|
5
5
|
from machineconfig.utils.schemas.installer.installer_types import APP_INSTALLER_CATEGORY, InstallerData, InstallerDataFiles, get_normalized_arch, get_os_name, OPERATING_SYSTEMS, CPU_ARCHITECTURES
|
|
6
6
|
from rich.console import Console
|
|
7
|
-
from rich.panel import Panel
|
|
7
|
+
from rich.panel import Panel
|
|
8
8
|
|
|
9
9
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
10
|
-
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT
|
|
10
|
+
from machineconfig.utils.source_of_truth import INSTALL_VERSION_ROOT, LINUX_INSTALL_PATH
|
|
11
11
|
from machineconfig.utils.io import read_json
|
|
12
12
|
|
|
13
13
|
from typing import Any
|
|
@@ -38,7 +38,7 @@ def check_latest():
|
|
|
38
38
|
repo_url = inst.installer_data.get("repoURL", "")
|
|
39
39
|
print(f"🔎 Checking {exe_name}...")
|
|
40
40
|
_release_url, version_to_be_installed = inst.get_github_release(repo_url=repo_url, version=None)
|
|
41
|
-
verdict, current_ver, new_ver =
|
|
41
|
+
verdict, current_ver, new_ver = check_if_installed_already(exe_name=exe_name, version=version_to_be_installed, use_cache=False)
|
|
42
42
|
return exe_name, verdict, current_ver, new_ver
|
|
43
43
|
|
|
44
44
|
print("\n⏳ Processing installers...\n")
|
|
@@ -188,5 +188,4 @@ def install_all(installers: list[Installer], safe: bool = False, jobs: int = 10,
|
|
|
188
188
|
print("\n" * 2)
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
pass
|
|
191
|
+
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
|
|
1
2
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
|
-
from machineconfig.utils.source_of_truth import WINDOWS_INSTALL_PATH, LINUX_INSTALL_PATH
|
|
3
|
-
|
|
3
|
+
from machineconfig.utils.source_of_truth import WINDOWS_INSTALL_PATH, LINUX_INSTALL_PATH, INSTALL_VERSION_ROOT
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Optional
|
|
4
7
|
import subprocess
|
|
8
|
+
import platform
|
|
5
9
|
|
|
6
10
|
|
|
7
11
|
def find_move_delete_windows(downloaded_file_path: PathExtended, exe_name: Optional[str] = None, delete: bool = True, rename_to: Optional[str] = None):
|
|
@@ -106,3 +110,131 @@ def find_move_delete_linux(downloaded: PathExtended, tool_name: str, delete: Opt
|
|
|
106
110
|
exe_new_location = PathExtended(LINUX_INSTALL_PATH).joinpath(exe.name)
|
|
107
111
|
print(f"✅ Executable installed at: {exe_new_location}\n{'=' * 80}")
|
|
108
112
|
return exe_new_location
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def check_tool_exists(tool_name: str) -> bool:
|
|
116
|
+
if platform.system() == "Windows":
|
|
117
|
+
tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
118
|
+
res1 = any([Path(WINDOWS_INSTALL_PATH).joinpath(tool_name).is_file(), Path.home().joinpath("AppData/Roaming/npm").joinpath(tool_name).is_file()])
|
|
119
|
+
tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
120
|
+
res2 = any([Path(WINDOWS_INSTALL_PATH).joinpath(tool_name).is_file(), Path.home().joinpath("AppData/Roaming/npm").joinpath(tool_name).is_file()])
|
|
121
|
+
return res1 or res2
|
|
122
|
+
elif platform.system() in ["Linux", "Darwin"]:
|
|
123
|
+
root_path = Path(LINUX_INSTALL_PATH)
|
|
124
|
+
return any([Path("/usr/local/bin").joinpath(tool_name).is_file(), Path("/usr/bin").joinpath(tool_name).is_file(), root_path.joinpath(tool_name).is_file()])
|
|
125
|
+
else:
|
|
126
|
+
raise NotImplementedError(f"platform {platform.system()} not implemented")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def check_if_installed_already(exe_name: str, version: Optional[str], use_cache: bool) -> tuple[str, str, str]:
|
|
130
|
+
print(f"\n{'=' * 80}\n🔍 CHECKING INSTALLATION STATUS: {exe_name} 🔍\n{'=' * 80}")
|
|
131
|
+
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
132
|
+
tmp_path = INSTALL_VERSION_ROOT.joinpath(exe_name)
|
|
133
|
+
|
|
134
|
+
if use_cache:
|
|
135
|
+
print("🗂️ Using cached version information...")
|
|
136
|
+
if tmp_path.exists():
|
|
137
|
+
existing_version = tmp_path.read_text(encoding="utf-8").rstrip()
|
|
138
|
+
print(f"📄 Found cached version: {existing_version}")
|
|
139
|
+
else:
|
|
140
|
+
existing_version = None
|
|
141
|
+
print("ℹ️ No cached version information found")
|
|
142
|
+
else:
|
|
143
|
+
print("🔍 Checking installed version directly...")
|
|
144
|
+
result = subprocess.run([exe_name, "--version"], check=False, capture_output=True, text=True)
|
|
145
|
+
if result.stdout.strip() == "":
|
|
146
|
+
existing_version = None
|
|
147
|
+
print("ℹ️ Could not detect installed version")
|
|
148
|
+
else:
|
|
149
|
+
existing_version = result.stdout.strip()
|
|
150
|
+
print(f"📄 Detected installed version: {existing_version}")
|
|
151
|
+
|
|
152
|
+
if existing_version is not None and version is not None:
|
|
153
|
+
if existing_version == version:
|
|
154
|
+
print(f"✅ {exe_name} is up to date (version {version})")
|
|
155
|
+
print(f"📂 Version information stored at: {INSTALL_VERSION_ROOT}")
|
|
156
|
+
return ("✅ Up to date", version.strip(), version.strip())
|
|
157
|
+
else:
|
|
158
|
+
print(f"🔄 {exe_name} needs update: {existing_version.rstrip()} → {version}")
|
|
159
|
+
tmp_path.write_text(version, encoding="utf-8")
|
|
160
|
+
return ("❌ Outdated", existing_version.strip(), version.strip())
|
|
161
|
+
else:
|
|
162
|
+
print(f"📦 {exe_name} is not installed. Will install version: {version}")
|
|
163
|
+
# tmp_path.write_text(version, encoding="utf-8")
|
|
164
|
+
|
|
165
|
+
print(f"{'=' * 80}")
|
|
166
|
+
return ("⚠️ NotInstalled", "None", version or "unknown")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def parse_apps_installer_linux(txt: str) -> dict[str, Any]:
|
|
170
|
+
"""Parse Linux shell installation scripts into logical chunks.
|
|
171
|
+
|
|
172
|
+
Supports two formats:
|
|
173
|
+
1. Legacy format with 'yes '' | sed 3q; echo "----------------------------- installing' delimiter
|
|
174
|
+
2. New format with # --BLOCK:<name>-- comment signatures
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
dict[str, str]: Dictionary mapping block/section names to their installation scripts
|
|
178
|
+
"""
|
|
179
|
+
# Try new block format first
|
|
180
|
+
if "# --BLOCK:" in txt:
|
|
181
|
+
import re
|
|
182
|
+
# Split by block signatures: # --BLOCK:<name>--
|
|
183
|
+
blocks = re.split(r'# --BLOCK:([^-]+)--', txt)
|
|
184
|
+
res: dict[str, str] = {}
|
|
185
|
+
|
|
186
|
+
# Process blocks in pairs (block_name, block_content)
|
|
187
|
+
for i in range(1, len(blocks), 2):
|
|
188
|
+
if i + 1 < len(blocks):
|
|
189
|
+
block_name = blocks[i].strip()
|
|
190
|
+
block_content = blocks[i + 1].strip()
|
|
191
|
+
if block_content:
|
|
192
|
+
res[block_name] = block_content
|
|
193
|
+
|
|
194
|
+
return res
|
|
195
|
+
|
|
196
|
+
# Legacy format fallback
|
|
197
|
+
txts = txt.split("""yes '' | sed 3q; echo "----------------------------- installing """)
|
|
198
|
+
res = {}
|
|
199
|
+
for chunk in txts[1:]:
|
|
200
|
+
try:
|
|
201
|
+
k = chunk.split("----")[0].rstrip().lstrip()
|
|
202
|
+
v = "\n".join(chunk.split("\n")[1:])
|
|
203
|
+
res[k] = v
|
|
204
|
+
except IndexError as e:
|
|
205
|
+
print(f"""
|
|
206
|
+
❌ Error parsing chunk:
|
|
207
|
+
{"-" * 50}
|
|
208
|
+
{chunk}
|
|
209
|
+
{"-" * 50}""")
|
|
210
|
+
raise e
|
|
211
|
+
return res
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def parse_apps_installer_windows(txt: str) -> dict[str, Any]:
|
|
215
|
+
chunks: list[str] = []
|
|
216
|
+
for idx, item in enumerate(txt.split(sep="winget install")):
|
|
217
|
+
if idx == 0:
|
|
218
|
+
continue
|
|
219
|
+
if idx == 1:
|
|
220
|
+
chunks.append(item)
|
|
221
|
+
else:
|
|
222
|
+
chunks.append("winget install" + item)
|
|
223
|
+
# progs = L(txt.splitlines()).filter(lambda x: x.startswith("winget ") or x.startswith("#winget"))
|
|
224
|
+
res: dict[str, str] = {}
|
|
225
|
+
for a_chunk in chunks:
|
|
226
|
+
try:
|
|
227
|
+
name = a_chunk.split("--name ")[1]
|
|
228
|
+
if "--Id" not in name:
|
|
229
|
+
print(f"⚠️ Warning: {name} does not have an Id, skipping")
|
|
230
|
+
continue
|
|
231
|
+
name = name.split(" --Id ", maxsplit=1)[0].strip('"').strip('"')
|
|
232
|
+
res[name] = a_chunk
|
|
233
|
+
except IndexError as e:
|
|
234
|
+
print(f"""
|
|
235
|
+
❌ Error parsing chunk:
|
|
236
|
+
{"-" * 50}
|
|
237
|
+
{a_chunk}
|
|
238
|
+
{"-" * 50}""")
|
|
239
|
+
raise e
|
|
240
|
+
return res
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from machineconfig.utils.path_extended import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.installer_utils.installer_abc import find_move_delete_linux, find_move_delete_windows
|
|
3
3
|
from machineconfig.utils.source_of_truth import INSTALL_TMP_DIR, INSTALL_VERSION_ROOT, LIBRARY_ROOT
|
|
4
|
-
from machineconfig.utils.
|
|
4
|
+
from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
|
|
5
5
|
from machineconfig.utils.io import read_json
|
|
6
6
|
from machineconfig.utils.schemas.installer.installer_types import InstallerData, InstallerDataFiles, get_os_name, get_normalized_arch
|
|
7
7
|
|
|
@@ -356,43 +356,3 @@ class Installer:
|
|
|
356
356
|
return None, None
|
|
357
357
|
browser_download_url = f"{repo_url}/releases/download/{actual_version}/{filename}"
|
|
358
358
|
return browser_download_url, actual_version
|
|
359
|
-
|
|
360
|
-
@staticmethod
|
|
361
|
-
def check_if_installed_already(exe_name: str, version: Optional[str], use_cache: bool) -> tuple[str, str, str]:
|
|
362
|
-
print(f"\n{'=' * 80}\n🔍 CHECKING INSTALLATION STATUS: {exe_name} 🔍\n{'=' * 80}")
|
|
363
|
-
INSTALL_VERSION_ROOT.joinpath(exe_name).parent.mkdir(parents=True, exist_ok=True)
|
|
364
|
-
tmp_path = INSTALL_VERSION_ROOT.joinpath(exe_name)
|
|
365
|
-
|
|
366
|
-
if use_cache:
|
|
367
|
-
print("🗂️ Using cached version information...")
|
|
368
|
-
if tmp_path.exists():
|
|
369
|
-
existing_version = tmp_path.read_text(encoding="utf-8").rstrip()
|
|
370
|
-
print(f"📄 Found cached version: {existing_version}")
|
|
371
|
-
else:
|
|
372
|
-
existing_version = None
|
|
373
|
-
print("ℹ️ No cached version information found")
|
|
374
|
-
else:
|
|
375
|
-
print("🔍 Checking installed version directly...")
|
|
376
|
-
result = subprocess.run([exe_name, "--version"], check=False, capture_output=True, text=True)
|
|
377
|
-
if result.stdout.strip() == "":
|
|
378
|
-
existing_version = None
|
|
379
|
-
print("ℹ️ Could not detect installed version")
|
|
380
|
-
else:
|
|
381
|
-
existing_version = result.stdout.strip()
|
|
382
|
-
print(f"📄 Detected installed version: {existing_version}")
|
|
383
|
-
|
|
384
|
-
if existing_version is not None and version is not None:
|
|
385
|
-
if existing_version == version:
|
|
386
|
-
print(f"✅ {exe_name} is up to date (version {version})")
|
|
387
|
-
print(f"📂 Version information stored at: {INSTALL_VERSION_ROOT}")
|
|
388
|
-
return ("✅ Up to date", version.strip(), version.strip())
|
|
389
|
-
else:
|
|
390
|
-
print(f"🔄 {exe_name} needs update: {existing_version.rstrip()} → {version}")
|
|
391
|
-
tmp_path.write_text(version, encoding="utf-8")
|
|
392
|
-
return ("❌ Outdated", existing_version.strip(), version.strip())
|
|
393
|
-
else:
|
|
394
|
-
print(f"📦 {exe_name} is not installed. Will install version: {version}")
|
|
395
|
-
# tmp_path.write_text(version, encoding="utf-8")
|
|
396
|
-
|
|
397
|
-
print(f"{'=' * 80}")
|
|
398
|
-
return ("⚠️ NotInstalled", "None", version or "unknown")
|
machineconfig/utils/options.py
CHANGED
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
+
from machineconfig.utils.installer_utils.installer_abc import check_tool_exists
|
|
2
3
|
from rich.text import Text
|
|
3
4
|
from rich.panel import Panel
|
|
4
5
|
from rich.console import Console
|
|
5
|
-
import platform
|
|
6
6
|
import subprocess
|
|
7
7
|
from typing import Optional, Union, Iterable, overload, Literal
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def check_tool_exists(tool_name: str) -> bool:
|
|
12
|
-
if platform.system() == "Windows":
|
|
13
|
-
tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
14
|
-
res1 = any([Path(WINDOWS_INSTALL_PATH).joinpath(tool_name).is_file(), Path.home().joinpath("AppData/Roaming/npm").joinpath(tool_name).is_file()])
|
|
15
|
-
tool_name = tool_name.replace(".exe", "") + ".exe"
|
|
16
|
-
res2 = any([Path(WINDOWS_INSTALL_PATH).joinpath(tool_name).is_file(), Path.home().joinpath("AppData/Roaming/npm").joinpath(tool_name).is_file()])
|
|
17
|
-
return res1 or res2
|
|
18
|
-
elif platform.system() in ["Linux", "Darwin"]:
|
|
19
|
-
root_path = Path(LINUX_INSTALL_PATH)
|
|
20
|
-
return any([Path("/usr/local/bin").joinpath(tool_name).is_file(), Path("/usr/bin").joinpath(tool_name).is_file(), root_path.joinpath(tool_name).is_file()])
|
|
21
|
-
else:
|
|
22
|
-
raise NotImplementedError(f"platform {platform.system()} not implemented")
|
|
8
|
+
|
|
9
|
+
|
|
23
10
|
# _ = cmd
|
|
24
11
|
# cmd = "where.exe"
|
|
25
12
|
# cmd = "which"
|