Skip to content

Commit 7514df8

Browse files
authored
Merge pull request #12 from DataBoySu/cross-platform
Cross platform
2 parents 6f1c818 + cd9b917 commit 7514df8

11 files changed

Lines changed: 471 additions & 201 deletions

File tree

.github/workflows/translate.yml

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ on:
1414
permissions:
1515
contents: write
1616

17+
concurrency:
18+
group: translate-${{ github.ref }}
19+
cancel-in-progress: true
20+
1721
jobs:
1822
prepare-matrix:
1923
name: Prepare Translation Matrix
@@ -86,6 +90,8 @@ jobs:
8690
steps:
8791
- name: Checkout Code
8892
uses: actions/checkout@v4
93+
with:
94+
fetch-depth: 0
8995

9096
- name: Download All Translations
9197
uses: actions/download-artifact@v4
@@ -95,17 +101,28 @@ jobs:
95101
merge-multiple: true
96102

97103
- name: Commit and Push
104+
env:
105+
BRANCH: ${{ github.ref_name }}
98106
run: |
99107
git config --global user.name "DataBoySu's Readme Translator"
100108
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
109+
110+
# Ensure we're on the correct branch and have full history
111+
git fetch origin "$BRANCH"
112+
git checkout "$BRANCH"
113+
101114
git add locales/*.md
102115
git commit -m "docs: update translations" || echo "No changes to commit"
103-
104-
# Retry logic for concurrent pushes
105-
for i in {1..5}; do
106-
git pull --rebase
107-
if git push; then exit 0; fi
108-
echo "Push failed, retrying in 5s..."
116+
117+
# Retry logic for pushes that fail due to remote updates
118+
for i in 1 2 3 4 5; do
119+
git pull --rebase origin "$BRANCH" || true
120+
if git push origin "$BRANCH"; then
121+
echo "Push succeeded"
122+
exit 0
123+
fi
124+
echo "Push failed (attempt $i), retrying in 5s..."
109125
sleep 5
110126
done
127+
echo "Push failed after retries"
111128
exit 1

README.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
<!-- HTML_BLOCK: no change to url; output entire as it is... -->
2323
![License](https://img.shields.io/badge/license-MIT-orange.svg)
2424
![Python](https://img.shields.io/badge/python-3.10%2B-pink)
25-
![Version](https://img.shields.io/badge/version-1.2.3-green)
26-
![Platform](https://img.shields.io/badge/platform-Windows10/11-blue)
25+
![Version](https://img.shields.io/badge/version-1.3.0-green)
26+
![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-blue)
2727
![cuda 12.x](https://img.shields.io/badge/CUDA-12.x-0f9d58?logo=nvidia)
2828

2929
## Gallery
@@ -109,8 +109,8 @@ Contributions are welcome! Main future points to cover would be:
109109
- **Containerization**: Official Docker support for easy deployment in containerized environments.
110110
- **Remote Access**: SSH tunneling integration and secure remote management.
111111
- **Cross-Platform**:
112-
- [ ] Linux Support (Ubuntu/Debian focus).
113-
- [ ] macOS Support (Apple Silicon monitoring).
112+
- [x] Linux Support (Ubuntu/Debian focus).
113+
- [x] macOS Support (Apple Silicon monitoring).
114114
- **Hardware Agnostic**:
115115
- [ ] AMD ROCm support.
116116
- [ ] Intel Arc support.
@@ -122,11 +122,11 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for how to get involved.
122122

123123
## Requirements
124124

125-
- **OS**: Windows 10/11
125+
- **OS**: Windows 10/11, Linux, macOS
126126
- **Python**: 3.10+
127-
- **Hardware**: NVIDIA GPU with installed drivers.
128-
- **CUDA**: Toolkit 12.x (Strictly required for Benchmarking/Simulation features).
129-
- *Note: If CUDA 12.x is not detected, GPU-specific benchmarking features will be disabled.*
127+
- **Hardware**: NVIDIA GPU (all platforms), Apple Silicon (macOS), or CPU-only.
128+
- **CUDA**: Toolkit 12.x (Recommended for Benchmarking/Simulation on NVIDIA).
129+
- *Note: If CUDA/MPS is not detected, some benchmarking features may be disabled.*
130130

131131
---
132132

@@ -159,16 +159,23 @@ Best for development and stress testing.
159159

160160
### Quick Start
161161

162-
1. **Download** the latest release or clone the repo.
162+
1. **Download** or clone the repository.
163163
2. **Run Setup**:
164164

165-
```powershell
166-
.\setup.ps1
167-
```
165+
**Windows**:
166+
```powershell
167+
.\setup.ps1
168+
```
169+
170+
**Linux/macOS**:
171+
```bash
172+
chmod +x setup.sh
173+
./setup.sh
174+
```
168175

169176
3. **Launch**:
170177

171-
```powershell
178+
```bash
172179
# Start the web dashboard (Standard/Full)
173180
python health_monitor.py web
174181

health_monitor.py

Lines changed: 78 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -388,56 +388,60 @@ def _format_gpu_grid(gpus, total_width: int = None):
388388
def _run_app(config_path, port, nodes, once, web_mode=False, cli_mode=False):
389389
"""Helper to run main application logic."""
390390
# If the user requested admin mode via --admin in argv, and the process is not elevated,
391-
# attempt to relaunch elevated (Windows UAC). This project targets Windows only,
391+
# attempt to relaunch elevated.
392392
try:
393393
import sys
394394
import platform
395395
import os
396+
import subprocess
397+
396398
def _is_elevated():
397-
try:
398-
import ctypes
399-
return bool(ctypes.windll.shell32.IsUserAnAdmin())
400-
except Exception:
401-
return False
399+
if platform.system() == 'Windows':
400+
try:
401+
import ctypes
402+
return bool(ctypes.windll.shell32.IsUserAnAdmin())
403+
except Exception:
404+
return False
405+
else:
406+
return os.getuid() == 0
402407

403408
if '--admin' in (sys.argv[1:] if len(sys.argv) > 1 else []) and not _is_elevated():
404-
# Attempt relaunch elevated on Windows using ShellExecuteW or PowerShell Start-Process
405-
try:
406-
import ctypes
407-
import subprocess
408-
params = '"' + os.path.abspath(sys.argv[0]) + '"'
409-
other_args = [a for a in sys.argv[1:]]
410-
if other_args:
411-
params += ' ' + ' '.join(str(a) for a in other_args)
412-
409+
if platform.system() == 'Windows':
410+
# Attempt relaunch elevated on Windows
413411
try:
412+
import ctypes
413+
params = '"' + os.path.abspath(sys.argv[0]) + '"'
414+
other_args = [a for a in sys.argv[1:]]
415+
if other_args:
416+
params += ' ' + ' '.join(str(a) for a in other_args)
417+
414418
ret = ctypes.windll.shell32.ShellExecuteW(None, 'runas', sys.executable, params, None, 1)
415-
try:
416-
ok = int(ret) > 32
417-
except Exception:
418-
ok = False
419-
if ok:
420-
print('Relaunching elevated, exiting original process')
421-
try: os._exit(0)
422-
except Exception: pass
419+
if int(ret) > 32:
420+
os._exit(0)
423421
except Exception:
424-
def _ps_quote(s):
425-
return "'" + str(s).replace("'", "''") + "'"
426-
ps_args = [os.path.abspath(sys.argv[0])] + list(sys.argv[1:])
427-
arglist_literal = ','.join(_ps_quote(a) for a in ps_args)
428-
ps_cmd = [
429-
'powershell', '-NoProfile', '-NonInteractive', '-Command',
430-
f"Start-Process -FilePath '{sys.executable}' -ArgumentList {arglist_literal} -Verb RunAs"
431-
]
422+
# Fallback to powershell if ShellExecuteW fails
432423
try:
433-
proc = subprocess.run(ps_cmd, capture_output=True, text=True, timeout=15)
434-
if proc.returncode == 0:
435-
try: os._exit(0)
436-
except Exception: pass
424+
def _ps_quote(s):
425+
return "'" + str(s).replace("'", "''") + "'"
426+
ps_args = [os.path.abspath(sys.argv[0])] + list(sys.argv[1:])
427+
arglist_literal = ','.join(_ps_quote(a) for a in ps_args)
428+
ps_cmd = [
429+
'powershell', '-NoProfile', '-NonInteractive', '-Command',
430+
f"Start-Process -FilePath '{sys.executable}' -ArgumentList {arglist_literal} -Verb RunAs"
431+
]
432+
subprocess.run(ps_cmd, capture_output=True, text=True, timeout=15)
433+
os._exit(0)
437434
except Exception:
438435
pass
439-
except Exception:
440-
pass
436+
else:
437+
# POSIX relaunch with sudo
438+
try:
439+
args = ['sudo', sys.executable, os.path.abspath(sys.argv[0])] + sys.argv[1:]
440+
# Ensure we don't end up in an infinite loop if sudo fails or doesn't grant root
441+
if 'SUDO_COMMAND' not in os.environ:
442+
os.execvp('sudo', args)
443+
except Exception:
444+
pass
441445
except Exception:
442446
pass
443447

@@ -484,42 +488,43 @@ async def main():
484488
@click.pass_context
485489
def cli(ctx, config, port, update, admin):
486490
"""MyGPU: Real-time GPU and system health monitoring."""
487-
# If the user requested admin mode, attempt to relaunch this process elevated
488-
# on platforms that support elevation (Windows -> UAC, POSIX -> sudo).
491+
# If admin requested and not already elevated, attempt to relaunch elevated
489492
def _is_elevated():
490-
try:
491-
import ctypes
492-
return bool(ctypes.windll.shell32.IsUserAnAdmin())
493-
except Exception:
494-
return False
493+
import platform
494+
import os
495+
if platform.system() == 'Windows':
496+
try:
497+
import ctypes
498+
return bool(ctypes.windll.shell32.IsUserAnAdmin())
499+
except Exception:
500+
return False
501+
else:
502+
try:
503+
return os.getuid() == 0
504+
except Exception:
505+
return False
495506

496507
def _relaunch_elevated():
497-
try:
498-
import sys
499-
import os
500-
import subprocess
501-
script = os.path.abspath(sys.argv[0])
502-
args = sys.argv[1:]
503-
if '--admin' not in args:
504-
args = args + ['--admin']
508+
import sys
509+
import os
510+
import platform
511+
import subprocess
512+
script = os.path.abspath(sys.argv[0])
513+
args = sys.argv[1:]
514+
if '--admin' not in args:
515+
args = args + ['--admin']
505516

517+
if platform.system() == 'Windows':
506518
try:
507519
import ctypes
508520
params = '"' + script + '"'
509521
if args:
510522
params += ' ' + ' '.join(str(a) for a in args)
511523
ret = ctypes.windll.shell32.ShellExecuteW(None, 'runas', sys.executable, params, None, 1)
512-
try:
513-
ok = int(ret) > 32
514-
except Exception:
515-
ok = False
516-
if ok:
517-
print('Relaunching elevated, exiting original process')
518-
try: os._exit(0)
519-
except SystemExit: raise
520-
except Exception: pass
521-
except Exception as e:
522-
# PowerShell fallback if ShellExecuteW is not possible
524+
if int(ret) > 32:
525+
os._exit(0)
526+
except Exception:
527+
# PowerShell fallback
523528
try:
524529
def _ps_quote(s):
525530
return "'" + str(s).replace("'", "''") + "'"
@@ -529,17 +534,19 @@ def _ps_quote(s):
529534
'powershell', '-NoProfile', '-NonInteractive', '-Command',
530535
f"Start-Process -FilePath '{sys.executable}' -ArgumentList {arglist_literal} -Verb RunAs"
531536
]
532-
proc = subprocess.run(ps_cmd, capture_output=True, text=True, timeout=15)
533-
if proc.returncode == 0:
534-
try: os._exit(0)
535-
except Exception: pass
537+
subprocess.run(ps_cmd, capture_output=True, text=True, timeout=15)
538+
os._exit(0)
536539
except Exception:
537540
pass
541+
else:
542+
# POSIX relaunch with sudo
543+
try:
544+
sudo_args = ['sudo', sys.executable, script] + args
545+
if 'SUDO_COMMAND' not in os.environ:
546+
os.execvp('sudo', sudo_args)
547+
except Exception as e:
548+
print(f'Relaunch elevation error: {e}')
538549

539-
except Exception as e:
540-
print('Relaunch elevation error:', e)
541-
542-
# If admin requested and not already elevated, attempt to relaunch elevated
543550
try:
544551
if admin and not _is_elevated():
545552
_relaunch_elevated()

monitor/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
__version__ = "1.3.0"
1+
__version__ = "1.4.0"
22
__author__ = "DataBoySu"
33
__license__ = "MIT"

0 commit comments

Comments
 (0)