fix installer
This commit is contained in:
@@ -1619,14 +1619,14 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|||||||
break
|
break
|
||||||
|
|
||||||
script_dir = Path(__file__).resolve().parent
|
script_dir = Path(__file__).resolve().parent
|
||||||
ensure_run_client_helper(dest, script_dir)
|
installed_helper = ensure_run_client_helper(dest, script_dir)
|
||||||
|
|
||||||
run_client_script = None
|
run_client_script = None
|
||||||
if client_found:
|
if client_found:
|
||||||
# Prefer run_client helper located in the cloned repo; if missing, fall back to top-level scripts folder helper.
|
# Prefer the helper installed directly into the Hydrus repository.
|
||||||
helper_candidates = [dest / "run_client.py", script_dir / "run_client.py"]
|
helper_candidates = [installed_helper, dest / "run_client.py", script_dir / "run_client.py"]
|
||||||
for cand in helper_candidates:
|
for cand in helper_candidates:
|
||||||
if cand.exists():
|
if cand and cand.exists():
|
||||||
run_client_script = cand
|
run_client_script = cand
|
||||||
break
|
break
|
||||||
if getattr(args,
|
if getattr(args,
|
||||||
@@ -1830,20 +1830,22 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|||||||
|
|
||||||
# Helpful hint: show the new run_client helper and direct run example
|
# Helpful hint: show the new run_client helper and direct run example
|
||||||
try:
|
try:
|
||||||
helper_to_show = (
|
|
||||||
run_client_script if
|
|
||||||
(run_client_script and run_client_script.exists()) else
|
|
||||||
(script_dir / "run_client.py")
|
|
||||||
)
|
|
||||||
if venv_py:
|
if venv_py:
|
||||||
logging.info(
|
if run_client_script and run_client_script.exists() and run_client_script.resolve().parent == dest.resolve():
|
||||||
"To run the Hydrus client using the repo venv (no activation needed):\n %s %s [args]\nOr use the helper: %s --help\nHelper examples:\n %s --install-deps --verify\n %s --headless --detached",
|
logging.info(
|
||||||
venv_py,
|
"To run the Hydrus client using the repo venv (no activation needed):\n %s %s [args]\nOr use the helper: %s --help\nHelper examples:\n %s --install-deps --verify\n %s --no-update --headless --detached",
|
||||||
dest / "hydrus_client.py",
|
venv_py,
|
||||||
helper_to_show,
|
dest / "hydrus_client.py",
|
||||||
helper_to_show,
|
run_client_script,
|
||||||
helper_to_show,
|
run_client_script,
|
||||||
)
|
run_client_script,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logging.info(
|
||||||
|
"To run the Hydrus client using the repo venv (no activation needed):\n %s %s [args]",
|
||||||
|
venv_py,
|
||||||
|
dest / "hydrus_client.py",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logging.info(
|
logging.info(
|
||||||
"To run the Hydrus client: python %s [args]",
|
"To run the Hydrus client: python %s [args]",
|
||||||
@@ -1875,14 +1877,11 @@ def ensure_run_client_helper(dest: Path, script_dir: Path) -> Optional[Path]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
helper_dest = dest / "run_client.py"
|
helper_dest = dest / "run_client.py"
|
||||||
if helper_dest.exists():
|
|
||||||
return helper_dest
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
shutil.copy2(helper_src, helper_dest)
|
shutil.copy2(helper_src, helper_dest)
|
||||||
if os.name != "nt":
|
if os.name != "nt":
|
||||||
helper_dest.chmod(helper_dest.stat().st_mode | 0o111)
|
helper_dest.chmod(helper_dest.stat().st_mode | 0o111)
|
||||||
logging.debug("Copied run_client helper to %s", helper_dest)
|
logging.debug("Installed run_client helper to %s", helper_dest)
|
||||||
return helper_dest
|
return helper_dest
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logging.debug("Failed to copy run_client helper: %s", exc)
|
logging.debug("Failed to copy run_client helper: %s", exc)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import pwd
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -30,6 +30,11 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pwd
|
||||||
|
except ImportError:
|
||||||
|
pwd = None
|
||||||
|
|
||||||
|
|
||||||
def get_python_in_venv(venv_dir: Path) -> Optional[Path]:
|
def get_python_in_venv(venv_dir: Path) -> Optional[Path]:
|
||||||
try:
|
try:
|
||||||
@@ -72,6 +77,7 @@ def find_requirements(root: Path) -> Optional[Path]:
|
|||||||
|
|
||||||
def install_requirements(
|
def install_requirements(
|
||||||
venv_py: Path,
|
venv_py: Path,
|
||||||
|
repo_root: Path,
|
||||||
req_path: Path,
|
req_path: Path,
|
||||||
reinstall: bool = False,
|
reinstall: bool = False,
|
||||||
upgrade: bool = False
|
upgrade: bool = False
|
||||||
@@ -93,20 +99,13 @@ def install_requirements(
|
|||||||
check=True,
|
check=True,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
install_cmd = [str(venv_py), "-m", "pip", "install", "-r", str(req_path)]
|
python_version = get_python_version_info(venv_py)
|
||||||
|
install_cmd = [str(venv_py), "-m", "pip", "install", "--prefer-binary"]
|
||||||
if upgrade:
|
if upgrade:
|
||||||
install_cmd = [str(venv_py), "-m", "pip", "install", "--upgrade", "-r", str(req_path)]
|
install_cmd.append("--upgrade")
|
||||||
if reinstall:
|
if reinstall:
|
||||||
install_cmd = [
|
install_cmd.extend(["--upgrade", "--force-reinstall"])
|
||||||
str(venv_py),
|
install_cmd.extend(build_hydrus_install_targets(req_path, repo_root, python_version))
|
||||||
"-m",
|
|
||||||
"pip",
|
|
||||||
"install",
|
|
||||||
"--upgrade",
|
|
||||||
"--force-reinstall",
|
|
||||||
"-r",
|
|
||||||
str(req_path),
|
|
||||||
]
|
|
||||||
subprocess.run(install_cmd, check=True, **kwargs)
|
subprocess.run(install_cmd, check=True, **kwargs)
|
||||||
return True
|
return True
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
@@ -140,6 +139,69 @@ def parse_requirements_file(req_path: Path) -> List[str]:
|
|||||||
return names
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def get_python_version_info(python_exe: Path) -> Optional[tuple[int, int, int]]:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
str(python_exe),
|
||||||
|
"-c",
|
||||||
|
"import sys; print('.'.join(str(part) for part in sys.version_info[:3]))",
|
||||||
|
],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
version_text = (result.stdout or "").strip()
|
||||||
|
major, minor, micro = version_text.split(".", 2)
|
||||||
|
return int(major), int(minor), int(micro)
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_hydrus_repo(repo_root: Path) -> bool:
|
||||||
|
return (repo_root / "setup_venv.py").exists() and (repo_root / "hydrus_client.py").exists()
|
||||||
|
|
||||||
|
|
||||||
|
def build_hydrus_install_targets(
|
||||||
|
req_path: Path,
|
||||||
|
repo_root: Path,
|
||||||
|
python_version: Optional[tuple[int, int, int]],
|
||||||
|
) -> List[str]:
|
||||||
|
if python_version is None or python_version < (3, 13) or not is_hydrus_repo(repo_root):
|
||||||
|
return ["-r", str(req_path)]
|
||||||
|
|
||||||
|
overrides = {
|
||||||
|
"pyside6": "PySide6==6.10.1",
|
||||||
|
"qtpy": "QtPy==2.4.3",
|
||||||
|
"opencv-python-headless": "opencv-python-headless==4.13.0.90",
|
||||||
|
"numpy": "numpy==2.4.1",
|
||||||
|
}
|
||||||
|
targets: List[str] = []
|
||||||
|
seen_packages = set()
|
||||||
|
|
||||||
|
for raw_line in req_path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
package_name = re.split(r"[<>=!~\[]", line, maxsplit=1)[0].strip().lower()
|
||||||
|
if package_name in overrides:
|
||||||
|
line = overrides[package_name]
|
||||||
|
|
||||||
|
targets.append(line)
|
||||||
|
seen_packages.add(package_name)
|
||||||
|
|
||||||
|
if os.name == "nt" and "pywin32" not in seen_packages:
|
||||||
|
targets.append("pywin32")
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"Using Hydrus compatibility dependency set for Python {python_version[0]}.{python_version[1]}.{python_version[2]}"
|
||||||
|
)
|
||||||
|
return targets
|
||||||
|
|
||||||
|
|
||||||
def verify_imports(venv_py: Path, packages: List[str]) -> bool:
|
def verify_imports(venv_py: Path, packages: List[str]) -> bool:
|
||||||
# Skip mpv check as it is problematic to install and causes slow startups
|
# Skip mpv check as it is problematic to install and causes slow startups
|
||||||
packages = [p for p in packages if p.lower() != "mpv"]
|
packages = [p for p in packages if p.lower() != "mpv"]
|
||||||
@@ -228,6 +290,8 @@ def is_first_run(repo_root: Path) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _user_exists(username: str) -> bool:
|
def _user_exists(username: str) -> bool:
|
||||||
|
if pwd is None:
|
||||||
|
return False
|
||||||
try:
|
try:
|
||||||
pwd.getpwnam(username)
|
pwd.getpwnam(username)
|
||||||
return True
|
return True
|
||||||
@@ -914,7 +978,12 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|||||||
p.add_argument(
|
p.add_argument(
|
||||||
"--pull",
|
"--pull",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Run 'git pull' before starting the client",
|
help="Force a repository update before starting the client (legacy alias; startup update is enabled by default)",
|
||||||
|
)
|
||||||
|
p.add_argument(
|
||||||
|
"--no-update",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip the default repository git pull before startup",
|
||||||
)
|
)
|
||||||
p.add_argument(
|
p.add_argument(
|
||||||
"--update-deps",
|
"--update-deps",
|
||||||
@@ -984,19 +1053,36 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|||||||
else:
|
else:
|
||||||
repo_root = workspace_root
|
repo_root = workspace_root
|
||||||
|
|
||||||
# Handle git pull update if requested
|
repo_updated = False
|
||||||
# Skip execution during service install/uninstall; it will run when the service starts
|
should_pull_repo = not args.no_update and not (args.install_service or args.uninstall_service)
|
||||||
if args.pull and not (args.install_service or args.uninstall_service):
|
if should_pull_repo:
|
||||||
if shutil.which("git"):
|
if shutil.which("git"):
|
||||||
if (repo_root / ".git").exists():
|
if (repo_root / ".git").exists():
|
||||||
if not args.quiet:
|
if not args.quiet:
|
||||||
print(f"Updating repository via 'git pull' in {repo_root}...")
|
print(f"Updating repository via 'git pull --ff-only' in {repo_root}...")
|
||||||
try:
|
try:
|
||||||
# Use creationflags to hide the window on Windows
|
|
||||||
k = {}
|
k = {}
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
k["creationflags"] = 0x08000000
|
k["creationflags"] = 0x08000000
|
||||||
subprocess.run(["git", "pull"], cwd=str(repo_root), check=False, **k)
|
result = subprocess.run(
|
||||||
|
["git", "pull", "--ff-only"],
|
||||||
|
cwd=str(repo_root),
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
**k,
|
||||||
|
)
|
||||||
|
pull_output = "\n".join(
|
||||||
|
part.strip()
|
||||||
|
for part in [result.stdout or "", result.stderr or ""]
|
||||||
|
if part.strip()
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Warning: git pull failed: {pull_output or result.returncode}")
|
||||||
|
else:
|
||||||
|
repo_updated = "already up to date" not in pull_output.lower()
|
||||||
|
if pull_output and not args.quiet:
|
||||||
|
print(pull_output)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: git pull failed: {e}")
|
print(f"Warning: git pull failed: {e}")
|
||||||
else:
|
else:
|
||||||
@@ -1127,7 +1213,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not should_update and args.pull and not (args.install_service or args.uninstall_service):
|
if not should_update and (args.pull or repo_updated) and not (args.install_service or args.uninstall_service):
|
||||||
should_update = True
|
should_update = True
|
||||||
|
|
||||||
if args.install_deps or args.reinstall or should_update:
|
if args.install_deps or args.reinstall or should_update:
|
||||||
@@ -1135,7 +1221,7 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|||||||
if not req:
|
if not req:
|
||||||
print("No requirements.txt found; skipping install")
|
print("No requirements.txt found; skipping install")
|
||||||
else:
|
else:
|
||||||
ok = install_requirements(venv_py, req, reinstall=args.reinstall, upgrade=should_update)
|
ok = install_requirements(venv_py, repo_root, req, reinstall=args.reinstall, upgrade=should_update)
|
||||||
if not ok:
|
if not ok:
|
||||||
print("Dependency installation failed; aborting")
|
print("Dependency installation failed; aborting")
|
||||||
return 4
|
return 4
|
||||||
|
|||||||
Reference in New Issue
Block a user