machineconfig 2.8__py3-none-any.whl → 2.9__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/python/python_ve_symlink.py +1 -1
- machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc +0 -0
- machineconfig/scripts/python/devops.py +18 -64
- machineconfig/scripts/python/devops_add_identity.py +6 -2
- machineconfig/scripts/python/devops_add_ssh_key.py +5 -2
- machineconfig/scripts/python/devops_backup_retrieve.py +3 -15
- machineconfig/scripts/python/devops_devapps_install.py +8 -6
- machineconfig/scripts/python/devops_update_repos.py +125 -226
- machineconfig/scripts/python/fire_agents.py +108 -151
- machineconfig/scripts/python/fire_agents_help_launch.py +97 -0
- machineconfig/scripts/python/fire_agents_help_search.py +83 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +2 -5
- machineconfig/scripts/python/repos.py +1 -1
- machineconfig/scripts/python/repos_helper_update.py +288 -0
- machineconfig/utils/installer_utils/installer_class.py +3 -3
- machineconfig/utils/notifications.py +24 -4
- machineconfig/utils/path.py +2 -1
- machineconfig/utils/procs.py +7 -7
- machineconfig/utils/source_of_truth.py +2 -0
- {machineconfig-2.8.dist-info → machineconfig-2.9.dist-info}/METADATA +7 -10
- {machineconfig-2.8.dist-info → machineconfig-2.9.dist-info}/RECORD +26 -22
- {machineconfig-2.8.dist-info → machineconfig-2.9.dist-info}/WHEEL +0 -0
- {machineconfig-2.8.dist-info → machineconfig-2.9.dist-info}/entry_points.txt +0 -0
- {machineconfig-2.8.dist-info → machineconfig-2.9.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import TypedDict
|
|
3
|
+
import subprocess
|
|
4
|
+
import git
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RepositoryUpdateResult(TypedDict):
|
|
9
|
+
"""Result of updating a single repository."""
|
|
10
|
+
repo_path: str
|
|
11
|
+
status: str # "success", "error", "skipped", "auth_failed"
|
|
12
|
+
had_uncommitted_changes: bool
|
|
13
|
+
uncommitted_files: list[str]
|
|
14
|
+
commit_before: str
|
|
15
|
+
commit_after: str
|
|
16
|
+
commits_changed: bool
|
|
17
|
+
pyproject_changed: bool
|
|
18
|
+
uv_lock_changed: bool
|
|
19
|
+
dependencies_changed: bool
|
|
20
|
+
uv_sync_ran: bool
|
|
21
|
+
uv_sync_success: bool
|
|
22
|
+
remotes_processed: list[str]
|
|
23
|
+
remotes_skipped: list[str]
|
|
24
|
+
error_message: str | None
|
|
25
|
+
is_machineconfig_repo: bool
|
|
26
|
+
permissions_updated: bool
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def set_permissions_recursive(path: Path, executable: bool = True) -> None:
|
|
30
|
+
"""Set permissions recursively for a directory."""
|
|
31
|
+
if not path.exists():
|
|
32
|
+
return
|
|
33
|
+
if path.is_file():
|
|
34
|
+
if executable:
|
|
35
|
+
path.chmod(0o755)
|
|
36
|
+
else:
|
|
37
|
+
path.chmod(0o644)
|
|
38
|
+
elif path.is_dir():
|
|
39
|
+
path.chmod(0o755)
|
|
40
|
+
for item in path.rglob("*"):
|
|
41
|
+
set_permissions_recursive(item, executable)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run_uv_sync(repo_path: Path) -> bool:
|
|
45
|
+
"""Run uv sync in the given repository path. Returns True if successful."""
|
|
46
|
+
try:
|
|
47
|
+
print(f"🔄 Running uv sync in {repo_path}")
|
|
48
|
+
# Run uv sync with output directly to terminal (no capture)
|
|
49
|
+
subprocess.run(["uv", "sync"], cwd=repo_path, check=True)
|
|
50
|
+
print("✅ uv sync completed successfully")
|
|
51
|
+
return True
|
|
52
|
+
except subprocess.CalledProcessError as e:
|
|
53
|
+
print(f"❌ uv sync failed with return code {e.returncode}")
|
|
54
|
+
return False
|
|
55
|
+
except FileNotFoundError:
|
|
56
|
+
print("⚠️ uv command not found. Please install uv first.")
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_file_hash(file_path: Path) -> str | None:
|
|
61
|
+
"""Get SHA256 hash of a file, return None if file doesn't exist."""
|
|
62
|
+
if not file_path.exists():
|
|
63
|
+
return None
|
|
64
|
+
import hashlib
|
|
65
|
+
return hashlib.sha256(file_path.read_bytes()).hexdigest()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def update_repository(repo: git.Repo, auto_sync: bool, allow_password_prompt: bool) -> RepositoryUpdateResult:
|
|
69
|
+
"""Update a single repository and return detailed information about what happened."""
|
|
70
|
+
repo_path = Path(repo.working_dir)
|
|
71
|
+
print(f"🔄 {'Updating ' + str(repo_path):.^80}")
|
|
72
|
+
|
|
73
|
+
# Initialize result dict
|
|
74
|
+
result: RepositoryUpdateResult = {
|
|
75
|
+
"repo_path": str(repo_path),
|
|
76
|
+
"status": "success",
|
|
77
|
+
"had_uncommitted_changes": False,
|
|
78
|
+
"uncommitted_files": [],
|
|
79
|
+
"commit_before": "",
|
|
80
|
+
"commit_after": "",
|
|
81
|
+
"commits_changed": False,
|
|
82
|
+
"pyproject_changed": False,
|
|
83
|
+
"uv_lock_changed": False,
|
|
84
|
+
"dependencies_changed": False,
|
|
85
|
+
"uv_sync_ran": False,
|
|
86
|
+
"uv_sync_success": False,
|
|
87
|
+
"remotes_processed": [],
|
|
88
|
+
"remotes_skipped": [],
|
|
89
|
+
"error_message": None,
|
|
90
|
+
"is_machineconfig_repo": "machineconfig" in str(repo_path),
|
|
91
|
+
"permissions_updated": False,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Check git status first
|
|
95
|
+
print("📊 Checking git status...")
|
|
96
|
+
if repo.is_dirty():
|
|
97
|
+
# Get the list of modified files
|
|
98
|
+
changed_files_raw = [item.a_path for item in repo.index.diff(None)]
|
|
99
|
+
changed_files_raw.extend([item.a_path for item in repo.index.diff("HEAD")])
|
|
100
|
+
# Filter out None values and remove duplicates
|
|
101
|
+
changed_files = list(set(file for file in changed_files_raw if file is not None))
|
|
102
|
+
|
|
103
|
+
result["had_uncommitted_changes"] = True
|
|
104
|
+
result["uncommitted_files"] = changed_files
|
|
105
|
+
print(f"⚠️ Repository has uncommitted changes: {', '.join(changed_files)}")
|
|
106
|
+
|
|
107
|
+
# Check if the only change is uv.lock
|
|
108
|
+
if len(changed_files) == 1 and changed_files[0] == "uv.lock":
|
|
109
|
+
print("🔒 Only uv.lock has changes, resetting it...")
|
|
110
|
+
try:
|
|
111
|
+
# Reset uv.lock file
|
|
112
|
+
subprocess.run(["git", "checkout", "HEAD", "--", "uv.lock"], cwd=repo_path, check=True)
|
|
113
|
+
print("✅ uv.lock has been reset")
|
|
114
|
+
except subprocess.CalledProcessError as e:
|
|
115
|
+
result["status"] = "error"
|
|
116
|
+
result["error_message"] = f"Failed to reset uv.lock: {e}"
|
|
117
|
+
print(f"❌ Failed to reset uv.lock: {e}")
|
|
118
|
+
return result
|
|
119
|
+
else:
|
|
120
|
+
# Multiple files or files other than uv.lock have changes
|
|
121
|
+
result["status"] = "error"
|
|
122
|
+
result["error_message"] = f"Cannot update repository - there are pending changes in: {', '.join(changed_files)}. Please commit or stash your changes first."
|
|
123
|
+
raise RuntimeError(result["error_message"])
|
|
124
|
+
else:
|
|
125
|
+
print("✅ Repository is clean")
|
|
126
|
+
|
|
127
|
+
# Check if this repo has pyproject.toml or uv.lock
|
|
128
|
+
pyproject_path = repo_path / "pyproject.toml"
|
|
129
|
+
uv_lock_path = repo_path / "uv.lock"
|
|
130
|
+
|
|
131
|
+
# Get hashes before pull
|
|
132
|
+
pyproject_hash_before = get_file_hash(pyproject_path)
|
|
133
|
+
uv_lock_hash_before = get_file_hash(uv_lock_path)
|
|
134
|
+
|
|
135
|
+
# Get current commit hash before pull
|
|
136
|
+
result["commit_before"] = repo.head.commit.hexsha
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
# Use subprocess for git pull to get better output control
|
|
140
|
+
|
|
141
|
+
# Get list of remotes
|
|
142
|
+
remotes = list(repo.remotes)
|
|
143
|
+
if not remotes:
|
|
144
|
+
print("⚠️ No remotes configured for this repository")
|
|
145
|
+
result["status"] = "skipped"
|
|
146
|
+
result["error_message"] = "No remotes configured for this repository"
|
|
147
|
+
return result
|
|
148
|
+
|
|
149
|
+
for remote in remotes:
|
|
150
|
+
try:
|
|
151
|
+
print(f"📥 Fetching from {remote.name}...")
|
|
152
|
+
|
|
153
|
+
# Set up environment for git commands
|
|
154
|
+
env = None
|
|
155
|
+
if not allow_password_prompt:
|
|
156
|
+
# Disable interactive prompts
|
|
157
|
+
import os
|
|
158
|
+
|
|
159
|
+
env = os.environ.copy()
|
|
160
|
+
env["GIT_TERMINAL_PROMPT"] = "0"
|
|
161
|
+
env["GIT_ASKPASS"] = "echo" # Returns empty string for any credential request
|
|
162
|
+
|
|
163
|
+
# First fetch to see what's available
|
|
164
|
+
fetch_result = subprocess.run(
|
|
165
|
+
["git", "fetch", remote.name, "--verbose"],
|
|
166
|
+
cwd=repo_path,
|
|
167
|
+
capture_output=True,
|
|
168
|
+
text=True,
|
|
169
|
+
env=env,
|
|
170
|
+
timeout=30, # Add timeout to prevent hanging
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Check if fetch failed due to authentication
|
|
174
|
+
if fetch_result.returncode != 0 and not allow_password_prompt:
|
|
175
|
+
auth_error_indicators = [
|
|
176
|
+
"Authentication failed",
|
|
177
|
+
"Password for",
|
|
178
|
+
"Username for",
|
|
179
|
+
"could not read Username",
|
|
180
|
+
"could not read Password",
|
|
181
|
+
"fatal: Authentication failed",
|
|
182
|
+
"fatal: could not read Username",
|
|
183
|
+
"fatal: could not read Password",
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
error_output = (fetch_result.stderr or "") + (fetch_result.stdout or "")
|
|
187
|
+
if any(indicator in error_output for indicator in auth_error_indicators):
|
|
188
|
+
print(f"⚠️ Skipping {remote.name} - authentication required but password prompts are disabled")
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
if fetch_result.stdout:
|
|
192
|
+
print(f"📡 Fetch output: {fetch_result.stdout.strip()}")
|
|
193
|
+
if fetch_result.stderr:
|
|
194
|
+
print(f"📡 Fetch info: {fetch_result.stderr.strip()}")
|
|
195
|
+
|
|
196
|
+
# Now pull with verbose output
|
|
197
|
+
print(f"📥 Pulling from {remote.name}/{repo.active_branch.name}...")
|
|
198
|
+
pull_result = subprocess.run(["git", "pull", remote.name, repo.active_branch.name, "--verbose"], cwd=repo_path, capture_output=True, text=True, env=env, timeout=30)
|
|
199
|
+
|
|
200
|
+
# Check if pull failed due to authentication
|
|
201
|
+
if pull_result.returncode != 0 and not allow_password_prompt:
|
|
202
|
+
auth_error_indicators = [
|
|
203
|
+
"Authentication failed",
|
|
204
|
+
"Password for",
|
|
205
|
+
"Username for",
|
|
206
|
+
"could not read Username",
|
|
207
|
+
"could not read Password",
|
|
208
|
+
"fatal: Authentication failed",
|
|
209
|
+
"fatal: could not read Username",
|
|
210
|
+
"fatal: could not read Password",
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
error_output = (pull_result.stderr or "") + (pull_result.stdout or "")
|
|
214
|
+
if any(indicator in error_output for indicator in auth_error_indicators):
|
|
215
|
+
print(f"⚠️ Skipping pull from {remote.name} - authentication required but password prompts are disabled")
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
if pull_result.stdout:
|
|
219
|
+
print(f"📦 Pull output: {pull_result.stdout.strip()}")
|
|
220
|
+
if pull_result.stderr:
|
|
221
|
+
print(f"📦 Pull info: {pull_result.stderr.strip()}")
|
|
222
|
+
|
|
223
|
+
# Check if pull was successful
|
|
224
|
+
if pull_result.returncode == 0:
|
|
225
|
+
result["remotes_processed"].append(remote.name)
|
|
226
|
+
# Check if commits changed
|
|
227
|
+
result["commit_after"] = repo.head.commit.hexsha
|
|
228
|
+
if result["commit_before"] != result["commit_after"]:
|
|
229
|
+
result["commits_changed"] = True
|
|
230
|
+
print(f"✅ Repository updated: {result['commit_before'][:8]} → {result['commit_after'][:8]}")
|
|
231
|
+
else:
|
|
232
|
+
print("✅ Already up to date")
|
|
233
|
+
else:
|
|
234
|
+
result["remotes_skipped"].append(remote.name)
|
|
235
|
+
print(f"❌ Pull failed with return code {pull_result.returncode}")
|
|
236
|
+
|
|
237
|
+
except Exception as e:
|
|
238
|
+
result["remotes_skipped"].append(remote.name)
|
|
239
|
+
print(f"⚠️ Failed to pull from {remote.name}: {e}")
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
# Check if pyproject.toml or uv.lock changed after pull
|
|
243
|
+
pyproject_hash_after = get_file_hash(pyproject_path)
|
|
244
|
+
uv_lock_hash_after = get_file_hash(uv_lock_path)
|
|
245
|
+
|
|
246
|
+
if pyproject_hash_before != pyproject_hash_after:
|
|
247
|
+
print("📋 pyproject.toml has changed")
|
|
248
|
+
result["pyproject_changed"] = True
|
|
249
|
+
result["dependencies_changed"] = True
|
|
250
|
+
|
|
251
|
+
if uv_lock_hash_before != uv_lock_hash_after:
|
|
252
|
+
print("🔒 uv.lock has changed")
|
|
253
|
+
result["uv_lock_changed"] = True
|
|
254
|
+
result["dependencies_changed"] = True
|
|
255
|
+
|
|
256
|
+
# Special handling for machineconfig repository
|
|
257
|
+
if result["is_machineconfig_repo"]:
|
|
258
|
+
print("🛠 Special handling for machineconfig repository...")
|
|
259
|
+
scripts_path = Path.home() / "scripts"
|
|
260
|
+
if scripts_path.exists():
|
|
261
|
+
set_permissions_recursive(scripts_path)
|
|
262
|
+
result["permissions_updated"] = True
|
|
263
|
+
print(f"✅ Set permissions for {scripts_path}")
|
|
264
|
+
|
|
265
|
+
linux_jobs_path = repo_path / "src" / "machineconfig" / "jobs" / "linux"
|
|
266
|
+
if linux_jobs_path.exists():
|
|
267
|
+
set_permissions_recursive(linux_jobs_path)
|
|
268
|
+
result["permissions_updated"] = True
|
|
269
|
+
print(f"✅ Set permissions for {linux_jobs_path}")
|
|
270
|
+
|
|
271
|
+
lf_exe_path = repo_path / "src" / "machineconfig" / "settings" / "lf" / "linux" / "exe"
|
|
272
|
+
if lf_exe_path.exists():
|
|
273
|
+
set_permissions_recursive(lf_exe_path)
|
|
274
|
+
result["permissions_updated"] = True
|
|
275
|
+
print(f"✅ Set permissions for {lf_exe_path}")
|
|
276
|
+
|
|
277
|
+
# Run uv sync if dependencies changed and auto_sync is enabled
|
|
278
|
+
if result["dependencies_changed"] and auto_sync:
|
|
279
|
+
result["uv_sync_ran"] = True
|
|
280
|
+
result["uv_sync_success"] = run_uv_sync(repo_path)
|
|
281
|
+
|
|
282
|
+
return result
|
|
283
|
+
|
|
284
|
+
except Exception as e:
|
|
285
|
+
result["status"] = "error"
|
|
286
|
+
result["error_message"] = str(e)
|
|
287
|
+
print(f"❌ Error updating repository {repo_path}: {e}")
|
|
288
|
+
return result
|
|
@@ -94,14 +94,14 @@ class Installer:
|
|
|
94
94
|
|
|
95
95
|
if old_version_cli == new_version_cli:
|
|
96
96
|
print(f"ℹ️ Same version detected: {old_version_cli}")
|
|
97
|
-
return f"""
|
|
97
|
+
return f"""📦️ 😑 {self.exe_name}, same version: {old_version_cli}"""
|
|
98
98
|
else:
|
|
99
99
|
print(f"🚀 Update successful: {old_version_cli} ➡️ {new_version_cli}")
|
|
100
|
-
return f"""
|
|
100
|
+
return f"""📦️ 🤩 {self.exe_name} updated from {old_version_cli} ➡️ TO ➡️ {new_version_cli}"""
|
|
101
101
|
|
|
102
102
|
except Exception as ex:
|
|
103
103
|
print(f"❌ ERROR: Installation failed for {self.exe_name}: {ex}")
|
|
104
|
-
return f"""
|
|
104
|
+
return f"""📦️ ❌ Failed to install `{self.name}` with error: {ex}"""
|
|
105
105
|
|
|
106
106
|
def install(self, version: Optional[str]):
|
|
107
107
|
print(f"\n{'=' * 80}\n🔧 INSTALLATION PROCESS: {self.exe_name} 🔧\n{'=' * 80}")
|
|
@@ -14,7 +14,8 @@ import imaplib
|
|
|
14
14
|
from email.mime.text import MIMEText
|
|
15
15
|
from email.mime.multipart import MIMEMultipart
|
|
16
16
|
from typing import Optional, Any, Union
|
|
17
|
-
from
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.markdown import Markdown
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def download_to_memory(path: Path, allow_redirects: bool = True, timeout: Optional[float] = None, params: Any = None) -> "Any":
|
|
@@ -30,8 +31,27 @@ def get_github_markdown_css() -> str:
|
|
|
30
31
|
return download_to_memory(Path(pp)).text
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
def md2html(body: str):
|
|
34
|
-
|
|
34
|
+
def md2html(body: str) -> str:
|
|
35
|
+
"""Convert markdown to HTML using Rich library."""
|
|
36
|
+
# Use Rich's HTML export functionality to convert markdown to HTML
|
|
37
|
+
console = Console(record=True, width=120)
|
|
38
|
+
markdown_obj = Markdown(body)
|
|
39
|
+
console.print(markdown_obj)
|
|
40
|
+
html_output = console.export_html(inline_styles=True)
|
|
41
|
+
|
|
42
|
+
# Try to load GitHub CSS style, fallback to basic style if not found
|
|
43
|
+
gh_style_path = Path(__file__).parent.joinpath("gh_style.css")
|
|
44
|
+
if gh_style_path.exists():
|
|
45
|
+
gh_style = gh_style_path.read_text()
|
|
46
|
+
else:
|
|
47
|
+
# Fallback basic styling
|
|
48
|
+
gh_style = """
|
|
49
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
|
|
50
|
+
h1, h2, h3, h4, h5, h6 { color: #0366d6; }
|
|
51
|
+
code { background-color: #f6f8fa; padding: 2px 4px; border-radius: 3px; }
|
|
52
|
+
pre { background-color: #f6f8fa; padding: 16px; border-radius: 6px; overflow: auto; }
|
|
53
|
+
"""
|
|
54
|
+
|
|
35
55
|
return f"""
|
|
36
56
|
<!DOCTYPE html>
|
|
37
57
|
<html>
|
|
@@ -51,7 +71,7 @@ def md2html(body: str):
|
|
|
51
71
|
</style>
|
|
52
72
|
<body>
|
|
53
73
|
<div class="markdown-body">
|
|
54
|
-
{
|
|
74
|
+
{html_output}
|
|
55
75
|
</div>
|
|
56
76
|
</body>
|
|
57
77
|
</html>"""
|
machineconfig/utils/path.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from machineconfig.utils.path_reduced import PathExtended as PathExtended
|
|
2
2
|
from machineconfig.utils.options import choose_one_option
|
|
3
|
+
from machineconfig.utils.source_of_truth import EXCLUDE_DIRS
|
|
3
4
|
from rich.console import Console
|
|
4
5
|
from rich.panel import Panel
|
|
5
6
|
import platform
|
|
@@ -54,7 +55,7 @@ def find_scripts(root: Path, name_substring: str, suffixes: set[str]) -> tuple[l
|
|
|
54
55
|
partial_path_matches = []
|
|
55
56
|
for entry in root.iterdir():
|
|
56
57
|
if entry.is_dir():
|
|
57
|
-
if entry.name in
|
|
58
|
+
if entry.name in set(EXCLUDE_DIRS):
|
|
58
59
|
# prune this entire subtree
|
|
59
60
|
continue
|
|
60
61
|
tmp1, tmp2 = find_scripts(entry, name_substring, suffixes)
|
machineconfig/utils/procs.py
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import psutil
|
|
4
4
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
5
|
-
from
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
6
6
|
from machineconfig.utils.options import display_options
|
|
7
7
|
from typing import Optional, Any
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.panel import Panel
|
|
10
|
-
from datetime import datetime
|
|
10
|
+
from datetime import datetime, timezone
|
|
11
11
|
from machineconfig.utils.utils2 import pprint
|
|
12
12
|
|
|
13
13
|
console = Console()
|
|
@@ -72,8 +72,8 @@ class ProcessManager:
|
|
|
72
72
|
try:
|
|
73
73
|
mem_usage_mb = proc.memory_info().rss / (1024 * 1024)
|
|
74
74
|
# Convert create_time to local timezone
|
|
75
|
-
create_time_utc = datetime.fromtimestamp(proc.create_time(), tz=timezone
|
|
76
|
-
create_time_local = create_time_utc.astimezone(
|
|
75
|
+
create_time_utc = datetime.fromtimestamp(proc.create_time(), tz=timezone.utc)
|
|
76
|
+
create_time_local = create_time_utc.astimezone(ZoneInfo("Australia/Adelaide"))
|
|
77
77
|
|
|
78
78
|
process_info.append(
|
|
79
79
|
{
|
|
@@ -222,13 +222,13 @@ def get_age(create_time: Any) -> str:
|
|
|
222
222
|
try:
|
|
223
223
|
if isinstance(create_time, (int, float)):
|
|
224
224
|
# Handle timestampz
|
|
225
|
-
create_time_utc = datetime.fromtimestamp(create_time, tz=timezone
|
|
226
|
-
create_time_local = create_time_utc.astimezone(
|
|
225
|
+
create_time_utc = datetime.fromtimestamp(create_time, tz=timezone.utc)
|
|
226
|
+
create_time_local = create_time_utc.astimezone(ZoneInfo("Australia/Adelaide"))
|
|
227
227
|
else:
|
|
228
228
|
# Already a datetime object
|
|
229
229
|
create_time_local = create_time
|
|
230
230
|
|
|
231
|
-
now_local = datetime.now(tz=
|
|
231
|
+
now_local = datetime.now(tz=ZoneInfo("Australia/Adelaide"))
|
|
232
232
|
age = now_local - create_time_local
|
|
233
233
|
return str(age)
|
|
234
234
|
except Exception as e:
|
|
@@ -5,6 +5,8 @@ Utils
|
|
|
5
5
|
import machineconfig
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
|
+
EXCLUDE_DIRS = [".links", ".ai", ".scripts", ".venv", ".git", ".idea", ".vscode", "node_modules", "__pycache__", ".mypy_cache"]
|
|
9
|
+
|
|
8
10
|
LIBRARY_ROOT = Path(machineconfig.__file__).resolve().parent
|
|
9
11
|
REPO_ROOT = LIBRARY_ROOT.parent.parent
|
|
10
12
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: machineconfig
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9
|
|
4
4
|
Summary: Dotfiles management package
|
|
5
5
|
Author-email: Alex Al-Saffar <programmer@usa.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -13,21 +13,18 @@ Requires-Python: >=3.13
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
Requires-Dist: cryptography>=44.0.2
|
|
15
15
|
Requires-Dist: fire>=0.7.0
|
|
16
|
-
Requires-Dist: gitpython>=3.1.44
|
|
17
16
|
Requires-Dist: joblib>=1.5.2
|
|
18
|
-
Requires-Dist: markdown>=3.9
|
|
19
17
|
Requires-Dist: paramiko>=3.5.1
|
|
20
|
-
Requires-Dist: psutil>=7.0.0
|
|
21
|
-
Requires-Dist: pydantic>=2.11.3
|
|
22
|
-
Requires-Dist: pyfzf>=0.3.1
|
|
23
|
-
Requires-Dist: pyjson5>=1.6.9
|
|
24
|
-
Requires-Dist: pytz>=2025.2
|
|
25
|
-
Requires-Dist: pyyaml>=6.0.2
|
|
26
18
|
Requires-Dist: randomname>=0.2.1
|
|
27
|
-
Requires-Dist: rclone-python>=0.1.23
|
|
28
19
|
Requires-Dist: requests>=2.32.5
|
|
29
20
|
Requires-Dist: rich>=14.0.0
|
|
30
21
|
Requires-Dist: tenacity>=9.1.2
|
|
22
|
+
Requires-Dist: psutil>=7.0.0
|
|
23
|
+
Requires-Dist: gitpython>=3.1.44
|
|
24
|
+
Requires-Dist: pyfzf>=0.3.1
|
|
25
|
+
Requires-Dist: rclone-python>=0.1.23
|
|
26
|
+
Requires-Dist: pyjson5>=1.6.9
|
|
27
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
31
28
|
Requires-Dist: toml>=0.10.2
|
|
32
29
|
Requires-Dist: tomli>=2.2.1
|
|
33
30
|
Provides-Extra: windows
|
|
@@ -48,7 +48,7 @@ machineconfig/jobs/python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
48
48
|
machineconfig/jobs/python/check_installations.py,sha256=o0mrZ-ivdED_7Od1tdtwJ4XSQRfiY6d_HciXLh1PXtY,11126
|
|
49
49
|
machineconfig/jobs/python/create_bootable_media.py,sha256=KKtcPk0rFLQc4eNVP6nbeYX-P7Gpqi0HvfIcUM6rVVs,827
|
|
50
50
|
machineconfig/jobs/python/python_cargo_build_share.py,sha256=RDe1QiTH3vLQ1wjN0kE5NxMIqwB-_WHz6O3svyuH_VE,2133
|
|
51
|
-
machineconfig/jobs/python/python_ve_symlink.py,sha256=
|
|
51
|
+
machineconfig/jobs/python/python_ve_symlink.py,sha256=ico4Xq_v7eKB2jGtL0puJI-KgAt3_NVIF4UCU3Z_7RM,845
|
|
52
52
|
machineconfig/jobs/python/tasks.py,sha256=hrBDQOnBmcXtauTkicVgC8J2AOGcfdFfyx0K8eI6Coc,150
|
|
53
53
|
machineconfig/jobs/python/vscode/api.py,sha256=BYF3XeNXYCpM4ApDCz2ZM1AInY4WgBqrpX9l_MWYqfM,1670
|
|
54
54
|
machineconfig/jobs/python/vscode/link_ve.py,sha256=1tXXgfWcl18K6avdhm2V7zng18opR9pfI0PSLxMFbiQ,1439
|
|
@@ -165,14 +165,16 @@ machineconfig/scripts/python/cloud_mount.py,sha256=QdokpaQ31S5oVBsZTApVNXbquWfJL
|
|
|
165
165
|
machineconfig/scripts/python/cloud_repo_sync.py,sha256=-aGiNw-K706vcuVwsuBXUL2FMHpa6OnHiKIIaydAt38,9940
|
|
166
166
|
machineconfig/scripts/python/cloud_sync.py,sha256=UVxFG2mRARU0CEU51RvdjyR4vxxF8bemzmDgWvZ9SRs,3858
|
|
167
167
|
machineconfig/scripts/python/croshell.py,sha256=7b7I5NSXQvQoiyWH7zKNIi2mO1tZ8yi8XkWn38XkreE,9509
|
|
168
|
-
machineconfig/scripts/python/devops.py,sha256=
|
|
169
|
-
machineconfig/scripts/python/devops_add_identity.py,sha256=
|
|
170
|
-
machineconfig/scripts/python/devops_add_ssh_key.py,sha256=
|
|
171
|
-
machineconfig/scripts/python/devops_backup_retrieve.py,sha256=
|
|
172
|
-
machineconfig/scripts/python/devops_devapps_install.py,sha256=
|
|
173
|
-
machineconfig/scripts/python/devops_update_repos.py,sha256=
|
|
168
|
+
machineconfig/scripts/python/devops.py,sha256=X_Q7-EqIoBEVieITICyZHVYHPE9Q8X84-HOxl2dU4pk,6181
|
|
169
|
+
machineconfig/scripts/python/devops_add_identity.py,sha256=mRoH2_SD_i4xzhBSuXk97W3fv7R9emZFDUX2lunxXrI,4066
|
|
170
|
+
machineconfig/scripts/python/devops_add_ssh_key.py,sha256=zdVlHtD6vlpXMLuwIqN5nHcm023ty2qVArWe7zPxgCo,6949
|
|
171
|
+
machineconfig/scripts/python/devops_backup_retrieve.py,sha256=I7lghN_HJT1mPaZ7zcD1jIJnTjNOvUC7nQZAoySPp7o,5563
|
|
172
|
+
machineconfig/scripts/python/devops_devapps_install.py,sha256=xQaOHlr6ziHxrx7e5Ri7WQ-FjIW0hjzDhlPJC-3qwks,8596
|
|
173
|
+
machineconfig/scripts/python/devops_update_repos.py,sha256=dIvHfq9zRkZhvFjYXOamJEAuF-AWXwtSnwGLOf2SE7E,8350
|
|
174
174
|
machineconfig/scripts/python/dotfile.py,sha256=miL8mQH2AqPdnHSz0Cxa7qQavaOmzTD9DAF66H2PRzA,2259
|
|
175
|
-
machineconfig/scripts/python/fire_agents.py,sha256=
|
|
175
|
+
machineconfig/scripts/python/fire_agents.py,sha256=RFUZ-bULx93a3MkjSl-2rGlx_bguQUcGqoTkefuUN_I,8739
|
|
176
|
+
machineconfig/scripts/python/fire_agents_help_launch.py,sha256=41GagLs0QJCdoS-5aVcm4EpApRKzAXdc_SFFUTdSbIQ,3746
|
|
177
|
+
machineconfig/scripts/python/fire_agents_help_search.py,sha256=EmvCh9HD45sePgrE1skvd3CZsOVh13AC7S0PNeMP__0,2992
|
|
176
178
|
machineconfig/scripts/python/fire_jobs.py,sha256=FvfJ-LrTUTVJRVT7i1LeP2YEUYH21RkYwKlVoEJxZNs,16616
|
|
177
179
|
machineconfig/scripts/python/fire_jobs_args_helper.py,sha256=TfCKSExGZhYrZ6JmXIHsd0wpNSWcKeLeRh9gFR3FG-M,4330
|
|
178
180
|
machineconfig/scripts/python/fire_jobs_layout_helper.py,sha256=_cx6ZnWezes-s13HXyKiI16vj9gF-Mpo_OF4StDQfFs,3222
|
|
@@ -185,8 +187,9 @@ machineconfig/scripts/python/mount_nw_drive.py,sha256=_7Rq-OGdJ8eYivESqGqdtWfSER
|
|
|
185
187
|
machineconfig/scripts/python/mount_ssh.py,sha256=uALax_ZVKihspQF6TuUtvtoAo-VRHCkdKQ7N9GRcry8,2234
|
|
186
188
|
machineconfig/scripts/python/onetimeshare.py,sha256=bmGsNnskym5OWfIhpOfZG5jq3m89FS0a6dF5Sb8LaZM,2539
|
|
187
189
|
machineconfig/scripts/python/pomodoro.py,sha256=SPkfeoZGv8rylGiOyzQ7UK3aXZ3G2FIOuGkSuBUggOI,2019
|
|
188
|
-
machineconfig/scripts/python/repos.py,sha256=
|
|
190
|
+
machineconfig/scripts/python/repos.py,sha256=nV5iJV7BLwBKiS_aqnrh63cfp-3k7nZyfUGtspwjKsM,7229
|
|
189
191
|
machineconfig/scripts/python/repos_helper_record.py,sha256=rtnjcr2ZCzt4j7oSPws25fPiCzshOEU_jH1ORW4QWx0,11372
|
|
192
|
+
machineconfig/scripts/python/repos_helper_update.py,sha256=yEXG1eFbeZPCDOTKA3_X9TpHa-WzbZcUFTlXAlT-itQ,12160
|
|
190
193
|
machineconfig/scripts/python/scheduler.py,sha256=7IBjMMOHMkklcWzYwz93EH9XzbJ5uPqU03bJ_lYbRNo,3083
|
|
191
194
|
machineconfig/scripts/python/snapshot.py,sha256=aDvKeoniZaeTSNv9zWBUajaj2yagAxVdfuvO1_tgq5Y,1026
|
|
192
195
|
machineconfig/scripts/python/start_slidev.py,sha256=MPCN0JgRzOAXywj6n9s0iZYcLFAscP-eOPQvkUyElRA,4525
|
|
@@ -197,9 +200,10 @@ machineconfig/scripts/python/wifi_conn.py,sha256=2FJ4srVthGHsy3KSXpvndAyVkNO8n_X
|
|
|
197
200
|
machineconfig/scripts/python/wsl_windows_transfer.py,sha256=RfSf5SJejnqrg36YfXSwu52ceEW77uNP4WC6QQUyRTA,3650
|
|
198
201
|
machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc,sha256=IppPGJ6Wk5Ma5oPWfJpN5QDW6-NvCk29la5JGbgAJ6E,171
|
|
199
202
|
machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-313.pyc,sha256=sWCevZgeQxIHoQ5Ea7a0OpjgJpkcKMoZylwCyizXYqA,10555
|
|
200
|
-
machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc,sha256=
|
|
203
|
+
machineconfig/scripts/python/__pycache__/devops.cpython-313.pyc,sha256=Rs6A-GpFrTXuXAn0tO3cGvilciJMHk0ujt_JPlsxaJ4,8216
|
|
201
204
|
machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-313.pyc,sha256=BrMh1Rzca_iY0EvlPsSXBqpwOZw-De6I3y8blXJZCuc,8616
|
|
202
|
-
machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc,sha256=
|
|
205
|
+
machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-313.pyc,sha256=6gTDTRboQbrSfF-8gR7SSJZSqygd6yNf_08xnxZYLno,23284
|
|
206
|
+
machineconfig/scripts/python/__pycache__/fire_agents.cpython-313.pyc,sha256=61OZTwltoquxWzQPwHue4SehHuiaDExcl57aN07Cpww,15829
|
|
203
207
|
machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-313.pyc,sha256=jx8_Gyx1kkqCwIe8W51h9hO89yuzTdd4Vh5oPRBOLio,826
|
|
204
208
|
machineconfig/scripts/python/__pycache__/repos.cpython-313.pyc,sha256=CoqJX-Oc-7suZ03dIkNqWscKXsuiswHHoGPM_VaZzS8,9642
|
|
205
209
|
machineconfig/scripts/python/__pycache__/repos_helper_record.cpython-313.pyc,sha256=VWsAX_sFJxVD0MUiZ1d9Bj9p4kw5gquukGLoJlID-lQ,13573
|
|
@@ -218,7 +222,7 @@ machineconfig/scripts/python/archive/im2text.py,sha256=WLyic89vxi_pqQMzo-MOrGf39
|
|
|
218
222
|
machineconfig/scripts/python/archive/tmate_conn.py,sha256=BiZQmYabl4K4-mbOpcb_R1JzYfEwxuAnk5FOciqGHFo,1231
|
|
219
223
|
machineconfig/scripts/python/archive/tmate_start.py,sha256=Hp7xv32u-fRuCG_f-Cy6qg0VemoaPOP-Pxs_gsFYcyg,1518
|
|
220
224
|
machineconfig/scripts/python/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
221
|
-
machineconfig/scripts/python/helpers/cloud_helpers.py,sha256=
|
|
225
|
+
machineconfig/scripts/python/helpers/cloud_helpers.py,sha256=ogjMFla4IAM50xkRCJU79sqylY02x31e9ulqYoRF8BY,4874
|
|
222
226
|
machineconfig/scripts/python/helpers/helpers2.py,sha256=TU1sA0GKNXHXT1_0y8piBLT_BeXfk0uBmkmUeyVYj58,7295
|
|
223
227
|
machineconfig/scripts/python/helpers/helpers4.py,sha256=SFYfxRleEq3gIbffIxFM8d1Ra5zNheUuTIOi2iBsqlE,7708
|
|
224
228
|
machineconfig/scripts/python/helpers/helpers5.py,sha256=dPBvA9Tcyx9TMgM6On49A1CueGMhBdRzikDnlJGf3J0,1123
|
|
@@ -390,13 +394,13 @@ machineconfig/utils/code.py,sha256=y2CTzD9elNOqMfiAJgxyTNhYG28IaYtKOv5zjTiRchE,4
|
|
|
390
394
|
machineconfig/utils/installer.py,sha256=725N-0gmMbrWWh6_9sn-2w9aXrfjIPrnuu9o13T4N4o,10941
|
|
391
395
|
machineconfig/utils/io_save.py,sha256=iC1YTH0MOlBS8bWUB8Xhdl4CG_bEKQ3OVwMohdCOazI,3145
|
|
392
396
|
machineconfig/utils/links.py,sha256=5rDQ6Id-vTtJRFwOWX8xZDXIOR5lY0DgLy0HpxIKLhk,10247
|
|
393
|
-
machineconfig/utils/notifications.py,sha256=
|
|
397
|
+
machineconfig/utils/notifications.py,sha256=q1kZM1y4ZmNhAHlEWZlzLE2HS3nnevrSUpunxd8rAT4,9307
|
|
394
398
|
machineconfig/utils/options.py,sha256=F8w5AEvm8yxgipYpLgeHlXJtvoXigZvDJjSqA5TU9J0,8554
|
|
395
|
-
machineconfig/utils/path.py,sha256=
|
|
399
|
+
machineconfig/utils/path.py,sha256=k0iyDaYsO-4yWCzjZ88-SxXDZLZPTGYX2WeSU8Pn-6w,8056
|
|
396
400
|
machineconfig/utils/path_reduced.py,sha256=hccGUTSMtggU-LpLNS7W_c1s-CqhjHMbTQ59-1EqgmY,52473
|
|
397
|
-
machineconfig/utils/procs.py,sha256=
|
|
401
|
+
machineconfig/utils/procs.py,sha256=aw2UyFvoJw69zDzfcxG46z-4O8MA5WsXvRpvFTqht-4,11484
|
|
398
402
|
machineconfig/utils/scheduling.py,sha256=8xjeoR_D5QHT0d7299Mgsj6JUbvkE_PX_pWq3myi658,11184
|
|
399
|
-
machineconfig/utils/source_of_truth.py,sha256=
|
|
403
|
+
machineconfig/utils/source_of_truth.py,sha256=GnjcVkKm11RyZFHGnPbne5YDEBYoZ5yryBNkpfGC7O4,854
|
|
400
404
|
machineconfig/utils/ssh.py,sha256=YBL2avhPUC43L1wxi2Doh2-cK-I7j81eUWEmfzAj9Jc,21196
|
|
401
405
|
machineconfig/utils/terminal.py,sha256=k3xoMwJPGvmaeL9CxEIwrcQjRNN6EnJItoIIxXrUY8c,12258
|
|
402
406
|
machineconfig/utils/upgrade_packages.py,sha256=H96zVJEWXJW07nh5vhjuSCrPtXGqoUb7xeJsFYYdmCI,3330
|
|
@@ -409,11 +413,11 @@ machineconfig/utils/cloud/onedrive/setup_oauth.py,sha256=ZTVkqgrwbV_EoPvyT8dyOTU
|
|
|
409
413
|
machineconfig/utils/cloud/onedrive/transaction.py,sha256=m-aNcnWj_gfZVvJOSpkdIqjZxU_3nXx2CA-qKbQgP3I,26232
|
|
410
414
|
machineconfig/utils/installer_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
411
415
|
machineconfig/utils/installer_utils/installer_abc.py,sha256=jR5XvrCjisIzoxk2VL0VhoVv-PkKdstDHGLcYky5Mqs,5319
|
|
412
|
-
machineconfig/utils/installer_utils/installer_class.py,sha256=
|
|
416
|
+
machineconfig/utils/installer_utils/installer_class.py,sha256=3jknokPmaN4LuBR5n6LrewIYo4-PrTuhYnFJIuhjjuY,20342
|
|
413
417
|
machineconfig/utils/schemas/layouts/layout_types.py,sha256=SPgoc0ncazKlXtQUhi7u1mMP2JvDt5jSG9YQmBr7-zc,616
|
|
414
418
|
machineconfig/utils/schemas/repos/repos_types.py,sha256=beWlwPRNDBABmlzxHBYkAsMXS1MgFFdDZf4G2ge8J-I,408
|
|
415
|
-
machineconfig-2.
|
|
416
|
-
machineconfig-2.
|
|
417
|
-
machineconfig-2.
|
|
418
|
-
machineconfig-2.
|
|
419
|
-
machineconfig-2.
|
|
419
|
+
machineconfig-2.9.dist-info/METADATA,sha256=7SpBqWO0al_Ce8zqCT2AyKdpFzT5bJ_7QvD_nMoK0qQ,7048
|
|
420
|
+
machineconfig-2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
421
|
+
machineconfig-2.9.dist-info/entry_points.txt,sha256=71EzS7_2LTIigVxC1YXNxHXhC9mu5Me2Feyq2KocXBI,977
|
|
422
|
+
machineconfig-2.9.dist-info/top_level.txt,sha256=porRtB8qms8fOIUJgK-tO83_FeH6Bpe12oUVC670teA,14
|
|
423
|
+
machineconfig-2.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|