309 lines
10 KiB
Python
309 lines
10 KiB
Python
#!/usr/bin/env python3
|
||||
|
|
"""
|
|||
|
|
install_dependencies.py
|
|||
|
|
Installs all requirements for compress_folder.py:
|
|||
|
|
- Checks the Python version (3.10+)
|
|||
|
|
- Detects the OS and installs 7-Zip via the appropriate method
|
|||
|
|
- Verifies the installation when done
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import shutil
|
|||
|
|
import subprocess
|
|||
|
|
import platform
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Terminal colors (no third-party deps needed here either) ─────────────────
|
|||
|
|
|
|||
|
|
IS_TTY = sys.stdout.isatty()
|
|||
|
|
|
|||
|
|
def _c(code, text):
|
|||
|
|
return f"\033[{code}m{text}\033[0m" if IS_TTY else text
|
|||
|
|
|
|||
|
|
def green(t): return _c("32", t)
|
|||
|
|
def yellow(t): return _c("33", t)
|
|||
|
|
def cyan(t): return _c("36", t)
|
|||
|
|
def bold(t): return _c("1", t)
|
|||
|
|
def dim(t): return _c("2", t)
|
|||
|
|
def red(t): return _c("31", t)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def header(text):
|
|||
|
|
print(f"\n{bold('── ' + text + ' ' + '─' * max(0, 54 - len(text)))}")
|
|||
|
|
|
|||
|
|
def ok(text): print(f" {green('✔')} {text}")
|
|||
|
|
def info(text): print(f" {cyan('ℹ')} {text}")
|
|||
|
|
def warn(text): print(f" {yellow('⚠')} {text}")
|
|||
|
|
def fail(text): print(f" {red('✘')} {text}")
|
|||
|
|
|
|||
|
|
def run(cmd, check=True, capture=False):
|
|||
|
|
"""Run a shell command, print it first, and return the CompletedProcess."""
|
|||
|
|
print(f"\n {dim('$')} {dim(' '.join(cmd))}")
|
|||
|
|
return subprocess.run(
|
|||
|
|
cmd,
|
|||
|
|
check=check,
|
|||
|
|
capture_output=capture,
|
|||
|
|
text=True,
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Step 1: Python version ────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def check_python():
|
|||
|
|
header("Checking Python version")
|
|||
|
|
major, minor = sys.version_info[:2]
|
|||
|
|
ver = f"{major}.{minor}.{sys.version_info.micro}"
|
|||
|
|
if (major, minor) >= (3, 10):
|
|||
|
|
ok(f"Python {ver} — requirement satisfied (3.10+)")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
fail(f"Python {ver} is too old. compress_folder.py requires Python 3.10 or higher.")
|
|||
|
|
info("Download the latest Python from https://www.python.org/downloads/")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Step 2: Detect OS and pick install strategy ───────────────────────────────
|
|||
|
|
|
|||
|
|
def detect_os() -> dict:
|
|||
|
|
"""Return a dict describing the OS and the best install method."""
|
|||
|
|
system = platform.system()
|
|||
|
|
|
|||
|
|
if system == "Windows":
|
|||
|
|
return {"os": "windows"}
|
|||
|
|
|
|||
|
|
if system == "Darwin":
|
|||
|
|
brew = shutil.which("brew")
|
|||
|
|
port = shutil.which("port")
|
|||
|
|
method = "homebrew" if brew else ("macports" if port else "manual")
|
|||
|
|
return {"os": "macos", "method": method, "brew": brew, "port": port}
|
|||
|
|
|
|||
|
|
if system == "Linux":
|
|||
|
|
# Identify distro family
|
|||
|
|
for mgr, cmd in (
|
|||
|
|
("apt", ["apt", "--version"]),
|
|||
|
|
("dnf", ["dnf", "--version"]),
|
|||
|
|
("yum", ["yum", "--version"]),
|
|||
|
|
("pacman", ["pacman", "--version"]),
|
|||
|
|
("zypper", ["zypper", "--version"]),
|
|||
|
|
):
|
|||
|
|
if shutil.which(mgr):
|
|||
|
|
return {"os": "linux", "manager": mgr}
|
|||
|
|
return {"os": "linux", "manager": None}
|
|||
|
|
|
|||
|
|
return {"os": "unknown"}
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Step 3: Install 7-Zip ─────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def already_installed() -> str | None:
|
|||
|
|
"""Return the path to 7-Zip if it is already on PATH."""
|
|||
|
|
for name in ("7z", "7za", "7zz"):
|
|||
|
|
path = shutil.which(name)
|
|||
|
|
if path:
|
|||
|
|
return path
|
|||
|
|
if sys.platform == "win32":
|
|||
|
|
for p in (
|
|||
|
|
r"C:\Program Files\7-Zip\7z.exe",
|
|||
|
|
r"C:\Program Files (x86)\7-Zip\7z.exe",
|
|||
|
|
):
|
|||
|
|
if os.path.isfile(p):
|
|||
|
|
return p
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_7zip_version(path: str) -> str:
|
|||
|
|
"""Return the version string reported by 7-Zip."""
|
|||
|
|
try:
|
|||
|
|
result = subprocess.run(
|
|||
|
|
[path, "i"], capture_output=True, text=True, check=False
|
|||
|
|
)
|
|||
|
|
for line in (result.stdout + result.stderr).splitlines():
|
|||
|
|
if "7-Zip" in line:
|
|||
|
|
return line.strip()
|
|||
|
|
except Exception:
|
|||
|
|
pass
|
|||
|
|
return "(version unknown)"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_linux(manager: str) -> bool:
|
|||
|
|
install_cmds = {
|
|||
|
|
"apt": ["sudo", "apt", "install", "-y", "p7zip-full"],
|
|||
|
|
"dnf": ["sudo", "dnf", "install", "-y", "p7zip", "p7zip-plugins"],
|
|||
|
|
"yum": ["sudo", "yum", "install", "-y", "p7zip", "p7zip-plugins"],
|
|||
|
|
"pacman": ["sudo", "pacman", "-S", "--noconfirm", "p7zip"],
|
|||
|
|
"zypper": ["sudo", "zypper", "install", "-y", "p7zip"],
|
|||
|
|
}
|
|||
|
|
cmd = install_cmds.get(manager)
|
|||
|
|
if not cmd:
|
|||
|
|
return False
|
|||
|
|
try:
|
|||
|
|
run(cmd)
|
|||
|
|
return True
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_macos(method: str, brew: str | None, port: str | None) -> bool:
|
|||
|
|
if method == "homebrew":
|
|||
|
|
try:
|
|||
|
|
run([brew, "install", "p7zip"])
|
|||
|
|
return True
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return False
|
|||
|
|
if method == "macports":
|
|||
|
|
try:
|
|||
|
|
run(["sudo", port, "install", "p7zip"])
|
|||
|
|
return True
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
return False
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_windows() -> bool:
|
|||
|
|
"""Try winget first, then chocolatey, then fall back to manual."""
|
|||
|
|
if shutil.which("winget"):
|
|||
|
|
try:
|
|||
|
|
run(["winget", "install", "--id", "7zip.7zip", "-e", "--silent"])
|
|||
|
|
return True
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
if shutil.which("choco"):
|
|||
|
|
try:
|
|||
|
|
run(["choco", "install", "7zip", "-y"])
|
|||
|
|
return True
|
|||
|
|
except subprocess.CalledProcessError:
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
def install_7zip(os_info: dict) -> bool:
|
|||
|
|
header("Installing 7-Zip")
|
|||
|
|
|
|||
|
|
# Already present?
|
|||
|
|
path = already_installed()
|
|||
|
|
if path:
|
|||
|
|
ver = get_7zip_version(path)
|
|||
|
|
ok(f"7-Zip is already installed: {dim(path)}")
|
|||
|
|
ok(f"{dim(ver)}")
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
system = os_info["os"]
|
|||
|
|
|
|||
|
|
if system == "linux":
|
|||
|
|
manager = os_info.get("manager")
|
|||
|
|
if not manager:
|
|||
|
|
fail("No supported package manager found (apt / dnf / yum / pacman / zypper).")
|
|||
|
|
info("Install 7-Zip manually: https://www.7-zip.org/download.html")
|
|||
|
|
return False
|
|||
|
|
info(f"Using package manager: {bold(manager)}")
|
|||
|
|
success = install_linux(manager)
|
|||
|
|
|
|||
|
|
elif system == "macos":
|
|||
|
|
method = os_info.get("method", "manual")
|
|||
|
|
if method == "manual":
|
|||
|
|
fail("Neither Homebrew nor MacPorts was found.")
|
|||
|
|
info("Install Homebrew first: https://brew.sh")
|
|||
|
|
info("Then re-run this script, or install p7zip manually.")
|
|||
|
|
return False
|
|||
|
|
info(f"Using: {bold(method)}")
|
|||
|
|
success = install_macos(method, os_info.get("brew"), os_info.get("port"))
|
|||
|
|
|
|||
|
|
elif system == "windows":
|
|||
|
|
info("Trying winget → chocolatey …")
|
|||
|
|
success = install_windows()
|
|||
|
|
if not success:
|
|||
|
|
fail("Automatic install failed.")
|
|||
|
|
info("Download 7-Zip manually from: https://www.7-zip.org/download.html")
|
|||
|
|
info("After installing, re-run this script to verify.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
fail(f"Unsupported OS: {platform.system()}")
|
|||
|
|
info("Install 7-Zip manually: https://www.7-zip.org/download.html")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
if not success:
|
|||
|
|
fail("Installation command failed. Check the output above for details.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Step 4: Verify everything ─────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def verify():
|
|||
|
|
header("Verifying installation")
|
|||
|
|
|
|||
|
|
path = already_installed()
|
|||
|
|
if path:
|
|||
|
|
ver = get_7zip_version(path)
|
|||
|
|
ok(f"7-Zip found: {dim(path)}")
|
|||
|
|
ok(f"{dim(ver)}")
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
fail("7-Zip still not found on PATH after installation.")
|
|||
|
|
warn("You may need to restart your terminal or add 7-Zip to your PATH manually.")
|
|||
|
|
if sys.platform == "win32":
|
|||
|
|
info("Typical Windows location: C:\\Program Files\\7-Zip\\")
|
|||
|
|
info("Add that directory to your PATH environment variable.")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── Main ──────────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
print(f"\n{bold('🔧 compress_folder.py — Dependency Installer')}")
|
|||
|
|
print(f" Platform: {cyan(platform.system())} {platform.release()} | "
|
|||
|
|
f"Python {sys.version.split()[0]}")
|
|||
|
|
|
|||
|
|
all_ok = True
|
|||
|
|
|
|||
|
|
# 1. Python check
|
|||
|
|
if not check_python():
|
|||
|
|
all_ok = False
|
|||
|
|
|
|||
|
|
# 2. Detect OS
|
|||
|
|
header("Detecting operating system")
|
|||
|
|
os_info = detect_os()
|
|||
|
|
system = os_info["os"]
|
|||
|
|
|
|||
|
|
if system == "linux":
|
|||
|
|
mgr = os_info.get("manager", "unknown")
|
|||
|
|
ok(f"Linux detected — package manager: {bold(mgr)}")
|
|||
|
|
elif system == "macos":
|
|||
|
|
method = os_info.get("method", "manual")
|
|||
|
|
ok(f"macOS detected — install method: {bold(method)}")
|
|||
|
|
elif system == "windows":
|
|||
|
|
ok("Windows detected")
|
|||
|
|
else:
|
|||
|
|
warn(f"Unrecognised OS: {platform.system()}")
|
|||
|
|
|
|||
|
|
# 3. Install 7-Zip
|
|||
|
|
if not install_7zip(os_info):
|
|||
|
|
all_ok = False
|
|||
|
|
|
|||
|
|
# 4. Verify
|
|||
|
|
verified = verify()
|
|||
|
|
if not verified:
|
|||
|
|
all_ok = False
|
|||
|
|
|
|||
|
|
# 5. Final report
|
|||
|
|
header("Summary")
|
|||
|
|
if all_ok and verified:
|
|||
|
|
print(f"\n {green('✅ All dependencies satisfied.')}")
|
|||
|
|
print(f" You are ready to run: {bold('python compress_folder.py')}\n")
|
|||
|
|
else:
|
|||
|
|
print(f"\n {yellow('⚠ One or more steps did not complete successfully.')}")
|
|||
|
|
print( " Review the output above and resolve any issues before running")
|
|||
|
|
print(f" {bold('compress_folder.py')}.\n")
|
|||
|
|
sys.exit(1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|