mirror of
https://github.com/daimyomizukagebay61/SteamPanel.git
synced 2026-07-25 03:34:30 +00:00
291 lines
9.6 KiB
Python
291 lines
9.6 KiB
Python
import sys
|
|
import os
|
|
import subprocess
|
|
import urllib.request
|
|
import urllib.error
|
|
import zipfile
|
|
import shutil
|
|
import hashlib
|
|
import json
|
|
import tempfile
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
GITHUB_REPO = "daimyomizukagebay61/SteamPanel"
|
|
BRANCH = "main"
|
|
ROOT = Path(__file__).parent
|
|
|
|
# dirs to never touch when copying from ZIP
|
|
PROTECTED = {"data", "data_backup", "venv", ".git", "__pycache__"}
|
|
|
|
|
|
# ── helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
def get_version():
|
|
ver_file = ROOT / "version.py"
|
|
if not ver_file.exists():
|
|
return "unknown"
|
|
try:
|
|
ns = {}
|
|
exec(ver_file.read_text(encoding="utf-8"), ns)
|
|
return ns.get("VERSION", "unknown")
|
|
except Exception:
|
|
return "unknown"
|
|
|
|
|
|
def file_hash(path):
|
|
try:
|
|
return hashlib.md5(Path(path).read_bytes()).hexdigest()
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
def git(*args, capture=False):
|
|
kwargs = {"cwd": str(ROOT)}
|
|
if capture:
|
|
kwargs["capture_output"] = True
|
|
kwargs["text"] = True
|
|
return subprocess.run(["git", "--no-pager"] + list(args), **kwargs)
|
|
|
|
|
|
def reinstall_requirements():
|
|
print("Installing dependencies...")
|
|
venv_pip = ROOT / "venv" / "Scripts" / "pip.exe"
|
|
if venv_pip.exists():
|
|
cmd = [str(venv_pip), "install", "-r", str(ROOT / "requirements.txt"), "-q"]
|
|
else:
|
|
cmd = [sys.executable, "-m", "pip", "install", "-r", str(ROOT / "requirements.txt"), "-q"]
|
|
subprocess.run(cmd)
|
|
hash_file = ROOT / "data" / ".req_hash"
|
|
hash_file.parent.mkdir(exist_ok=True)
|
|
hash_file.write_text(file_hash(ROOT / "requirements.txt") or "")
|
|
|
|
|
|
def rebuild_frontend():
|
|
frontend = ROOT / "frontend"
|
|
if not frontend.exists():
|
|
return
|
|
try:
|
|
node_ok = subprocess.run(["npm", "--version"], capture_output=True).returncode == 0
|
|
except FileNotFoundError:
|
|
node_ok = False
|
|
if not node_ok:
|
|
print("[!] Node.js not found — skipping frontend rebuild.")
|
|
print(" If UI looks broken: cd frontend && npm install && npm run build")
|
|
return
|
|
print("Rebuilding frontend...")
|
|
subprocess.run(["npm", "install", "--silent"], cwd=str(frontend))
|
|
subprocess.run(["npm", "run", "build"], cwd=str(frontend))
|
|
print("Frontend rebuilt.")
|
|
|
|
|
|
def download(url, dest):
|
|
req = urllib.request.Request(url, headers={"User-Agent": "SteamPanel-Updater"})
|
|
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
total = int(resp.headers.get("Content-Length", 0))
|
|
done = 0
|
|
with open(dest, "wb") as f:
|
|
while True:
|
|
chunk = resp.read(65536)
|
|
if not chunk:
|
|
break
|
|
f.write(chunk)
|
|
done += len(chunk)
|
|
if total:
|
|
print(f"\r {done * 100 // total:3d}% {done / 1048576:.1f} MB", end="", flush=True)
|
|
else:
|
|
print(f"\r {done / 1048576:.1f} MB", end="", flush=True)
|
|
print()
|
|
|
|
|
|
# ── git update path ───────────────────────────────────────────────────────────
|
|
|
|
def update_via_git():
|
|
try:
|
|
git_ok = subprocess.run(["git", "--version"], capture_output=True).returncode == 0
|
|
except FileNotFoundError:
|
|
git_ok = False
|
|
if not git_ok:
|
|
print("[!] git not found. Install Git from https://git-scm.com")
|
|
return
|
|
|
|
print("Checking for updates...")
|
|
if git("fetch", "origin", BRANCH).returncode != 0:
|
|
print("[!] Failed to reach GitHub. Check your internet connection.")
|
|
return
|
|
|
|
local = git("rev-parse", "HEAD", capture=True).stdout.strip()
|
|
remote = git("rev-parse", f"origin/{BRANCH}", capture=True).stdout.strip()
|
|
|
|
if local == remote:
|
|
print("\n[+] Already up to date.")
|
|
return
|
|
|
|
print("\nNew commits:")
|
|
print("-" * 40)
|
|
git("log", f"HEAD..origin/{BRANCH}", "--oneline")
|
|
print("-" * 40)
|
|
|
|
if input("\nUpdate now? [Y/N]: ").strip().upper() != "Y":
|
|
print("Cancelled.")
|
|
return
|
|
|
|
# stash local changes so pull is clean
|
|
stash = git("stash", capture=True)
|
|
had_stash = "No local changes" not in (stash.stdout or "")
|
|
if had_stash:
|
|
print("Local changes stashed.")
|
|
|
|
# backup branch as a safety net
|
|
backup = f"backup_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}"
|
|
git("branch", backup)
|
|
print(f"Backup branch: {backup}")
|
|
|
|
req_hash_before = file_hash(ROOT / "requirements.txt")
|
|
frontend_hash_before = file_hash(ROOT / "frontend" / "package.json")
|
|
|
|
print("Pulling updates...")
|
|
if git("pull", "origin", BRANCH).returncode != 0:
|
|
print("\n[!] Pull failed. Restoring backup branch...")
|
|
git("reset", "--hard", backup)
|
|
if had_stash:
|
|
git("stash", "pop")
|
|
return
|
|
|
|
if had_stash:
|
|
result = git("stash", "pop")
|
|
if result.returncode != 0:
|
|
print("[!] Could not restore stashed changes automatically.")
|
|
print(f" Run: git stash pop (or check: git stash list)")
|
|
|
|
new_ver = get_version()
|
|
print(f"\nUpdated: {local[:7]} -> {new_ver}")
|
|
|
|
if file_hash(ROOT / "requirements.txt") != req_hash_before:
|
|
reinstall_requirements()
|
|
|
|
if file_hash(ROOT / "frontend" / "package.json") != frontend_hash_before:
|
|
rebuild_frontend()
|
|
|
|
print(f"\n[+] Update complete! Run run.bat to start.")
|
|
|
|
|
|
# ── ZIP download path ─────────────────────────────────────────────────────────
|
|
|
|
def update_via_zip():
|
|
print("No .git folder — using GitHub release download.\n")
|
|
|
|
# fetch latest release info
|
|
api = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
|
|
zip_url = tag_name = None
|
|
try:
|
|
req = urllib.request.Request(api, headers={"User-Agent": "SteamPanel-Updater"})
|
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
|
data = json.loads(resp.read())
|
|
zip_url = data.get("zipball_url")
|
|
tag_name = data.get("tag_name", "latest")
|
|
except urllib.error.HTTPError as e:
|
|
if e.code != 404:
|
|
print(f"[!] GitHub API error: {e}")
|
|
return
|
|
except Exception as e:
|
|
print(f"[!] Failed to reach GitHub: {e}")
|
|
return
|
|
|
|
if not zip_url:
|
|
# no releases yet — grab main branch zipball
|
|
zip_url = f"https://api.github.com/repos/{GITHUB_REPO}/zipball/{BRANCH}"
|
|
tag_name = BRANCH
|
|
|
|
current_ver = get_version()
|
|
print(f"Installed : {current_ver}")
|
|
print(f"Available : {tag_name}")
|
|
|
|
if input("\nDownload and install? [Y/N]: ").strip().upper() != "Y":
|
|
print("Cancelled.")
|
|
return
|
|
|
|
# backup data/ before touching anything
|
|
data_dir = ROOT / "data"
|
|
data_backup = ROOT / "data_backup"
|
|
if data_dir.exists():
|
|
print("Backing up data/...")
|
|
if data_backup.exists():
|
|
shutil.rmtree(data_backup)
|
|
shutil.copytree(data_dir, data_backup)
|
|
print("Backed up to data_backup/")
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
zip_path = os.path.join(tmp, "update.zip")
|
|
|
|
print("Downloading...")
|
|
try:
|
|
download(zip_url, zip_path)
|
|
except Exception as e:
|
|
print(f"[!] Download failed: {e}")
|
|
return
|
|
|
|
req_hash_before = file_hash(ROOT / "requirements.txt")
|
|
frontend_hash_before = file_hash(ROOT / "frontend" / "package.json")
|
|
|
|
print("Extracting...")
|
|
extract_dir = os.path.join(tmp, "src")
|
|
with zipfile.ZipFile(zip_path) as z:
|
|
z.extractall(extract_dir)
|
|
|
|
# GitHub always wraps in one top-level folder (reponame-sha/)
|
|
entries = os.listdir(extract_dir)
|
|
source = Path(extract_dir) / entries[0] if len(entries) == 1 else Path(extract_dir)
|
|
|
|
print("Installing...")
|
|
for item in source.iterdir():
|
|
if item.name in PROTECTED:
|
|
continue
|
|
dest = ROOT / item.name
|
|
if item.is_dir():
|
|
if dest.exists():
|
|
shutil.rmtree(dest)
|
|
shutil.copytree(item, dest)
|
|
else:
|
|
shutil.copy2(item, dest)
|
|
|
|
# safety net: restore data if something went wrong
|
|
if not data_dir.exists() and data_backup.exists():
|
|
print("Restoring data/ from backup...")
|
|
shutil.copytree(data_backup, data_dir)
|
|
|
|
if file_hash(ROOT / "requirements.txt") != req_hash_before:
|
|
reinstall_requirements()
|
|
elif not (ROOT / "data" / ".req_hash").exists():
|
|
reinstall_requirements()
|
|
|
|
if file_hash(ROOT / "frontend" / "package.json") != frontend_hash_before:
|
|
rebuild_frontend()
|
|
|
|
new_ver = get_version()
|
|
if new_ver != current_ver:
|
|
print(f"\nUpdated: {current_ver} -> {new_ver}")
|
|
|
|
print("\n[+] Update complete! Run run.bat to start.")
|
|
|
|
|
|
# ── entry point ───────────────────────────────────────────────────────────────
|
|
|
|
def main():
|
|
print()
|
|
print(" SteamPanel Updater")
|
|
print(" ==================")
|
|
print(f"\n Current version: {get_version()}\n")
|
|
|
|
if (ROOT / ".git").exists():
|
|
update_via_git()
|
|
else:
|
|
update_via_zip()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print("\nCancelled.")
|