dfdf
Some checks failed
smoke-mm / Install & smoke test mm --help (push) Has been cancelled

This commit is contained in:
nose
2025-12-25 05:10:39 -08:00
parent 4bf9d1b93a
commit cd823a5a2e
5 changed files with 673 additions and 6 deletions

View File

@@ -646,7 +646,7 @@ def _capture(options: ScreenshotOptions, destination: Path, warnings: List[str],
debug(f"[_capture] Exception launching browser/page: {exc}") debug(f"[_capture] Exception launching browser/page: {exc}")
msg = str(exc).lower() msg = str(exc).lower()
if any(k in msg for k in ["executable", "not found", "no such file", "cannot find", "install"]): if any(k in msg for k in ["executable", "not found", "no such file", "cannot find", "install"]):
raise ScreenshotError("Chromium Playwright browser binaries not found. Install them: python ./scripts/setup.py --playwright-only --browsers chromium") from exc raise ScreenshotError("Chromium Playwright browser binaries not found. Install them: python ./scripts/bootstrap.py --playwright-only --browsers chromium") from exc
raise raise
except ScreenshotError: except ScreenshotError:
# Re-raise ScreenshotError raised intentionally (do not wrap) # Re-raise ScreenshotError raised intentionally (do not wrap)
@@ -1064,7 +1064,7 @@ CMDLET = Cmdlet(
], ],
detail=[ detail=[
"Uses Playwright Chromium engine only. Install Chromium with: python ./scripts/setup.py --playwright-only --browsers chromium", "Uses Playwright Chromium engine only. Install Chromium with: python ./scripts/bootstrap.py --playwright-only --browsers chromium",
"PDF output requires headless Chromium (the cmdlet will enforce headless mode for PDF).", "PDF output requires headless Chromium (the cmdlet will enforce headless mode for PDF).",
"Screenshots are temporary artifacts stored in the configured `temp` directory.", "Screenshots are temporary artifacts stored in the configured `temp` directory.",
] ]

View File

@@ -41,7 +41,7 @@ The bootstrap scripts will automatically install Deno if it is not already prese
Opinionated behavior Opinionated behavior
Running `python ./scripts/setup.py` is intentionally opinionated: it will create a local virtual environment at `./.venv` (repo root), install Python dependencies and the project into that venv, install Playwright browsers, install Deno, and write small launcher scripts in the project root: Running `python ./scripts/bootstrap.py` is intentionally opinionated: it will create a local virtual environment at `./.venv` (repo root), install Python dependencies and the project into that venv, install Playwright browsers, install Deno, and write small launcher scripts in the project root:
- `mm` (POSIX shell) - `mm` (POSIX shell)
- `mm.ps1` (PowerShell) - `mm.ps1` (PowerShell)

View File

@@ -13,7 +13,7 @@ Medios-Macina is a CLI media manager and toolkit focused on downloading, tagging
GIT CLONE https://code.glowers.club/goyimnose/Medios-Macina GIT CLONE https://code.glowers.club/goyimnose/Medios-Macina
1. run python setup.py 1. run python scripts\bootstrap.py.py
2. rename config.conf.remove to config.conf the store=folder path should be empty folder with no other files in it. 2. rename config.conf.remove to config.conf the store=folder path should be empty folder with no other files in it.

648
scripts/bootstrap.py Normal file
View File

@@ -0,0 +1,648 @@
#!/usr/bin/env python3
"""scripts/bootstrap.py
Unified project bootstrap helper (Python-only).
This script installs Python dependencies from `requirements.txt` and then
downloads Playwright browser binaries by running `python -m playwright install`.
By default this script installs **Chromium** only to conserve space; pass
`--browsers all` to install all supported engines (chromium, firefox, webkit).
When invoked without any arguments, `bootstrap.py` will automatically select and
run the platform-specific bootstrap helper (`scripts/bootstrap.ps1` on Windows
or `scripts/bootstrap.sh` on POSIX) in **non-interactive (quiet)** mode so a
single `python ./scripts/bootstrap.py` call does the usual bootstrap on your OS.
The platform bootstrap scripts also attempt (best-effort) to install `mpv` if
it is not found on your PATH, since some workflows use it.
This file replaces the old `scripts/setup.py` to ensure the repository only has
one `setup.py` (at the repository root) for packaging.
Usage:
python ./scripts/bootstrap.py # install deps and playwright browsers (or run platform bootstrap if no args)
python ./scripts/bootstrap.py --skip-deps
python ./scripts/bootstrap.py --playwright-only
Optional flags:
--skip-deps Skip `pip install -r requirements.txt` step
--no-playwright Skip running `python -m playwright install` (still installs deps)
--playwright-only Install only Playwright browsers (installs playwright package if missing)
--browsers Comma-separated list of Playwright browsers to install (default: chromium)
--install-editable Install the project in editable mode (pip install -e .) for running tests
--install-deno Install the Deno runtime using the official installer
--no-deno Skip installing the Deno runtime
--deno-version Pin a specific Deno version to install (e.g., v1.34.3)
--upgrade-pip Upgrade pip, setuptools, and wheel before installing deps
"""
from __future__ import annotations
import argparse
import os
import platform
from pathlib import Path
import shutil
import subprocess
import sys
import time
def run(cmd: list[str]) -> None:
print(f"> {' '.join(cmd)}")
subprocess.check_call(cmd)
# Helpers to find shell executables and to run the platform-specific
# bootstrap script (scripts/bootstrap.sh or scripts/bootstrap.ps1).
def _find_powershell() -> str | None:
for name in ("pwsh", "powershell"):
p = shutil.which(name)
if p:
return p
return None
def _find_shell() -> str | None:
for name in ("bash", "sh"):
p = shutil.which(name)
if p:
return p
return None
def run_platform_bootstrap(repo_root: Path) -> int:
"""Run the platform bootstrap script in quiet/non-interactive mode if present.
Returns the script exit code (0 on success). If no script is present this is a
no-op and returns 0.
"""
ps1 = repo_root / "scripts" / "bootstrap.ps1"
sh_script = repo_root / "scripts" / "bootstrap.sh"
system = platform.system().lower()
if system == "windows" and ps1.exists():
exe = _find_powershell()
if not exe:
print("PowerShell not found; cannot run bootstrap.ps1", file=sys.stderr)
return 1
cmd = [exe, "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", str(ps1), "-Quiet"]
elif sh_script.exists():
shell = _find_shell()
if not shell:
print("Shell not found; cannot run bootstrap.sh", file=sys.stderr)
return 1
# Use -q (quiet) to skip interactive prompts when supported.
cmd = [shell, str(sh_script), "-q"]
else:
# Nothing to run
return 0
print("Running platform bootstrap script:", " ".join(cmd))
rc = subprocess.run(cmd, cwd=str(repo_root))
if rc.returncode != 0:
print(f"Bootstrap script failed with exit code {rc.returncode}", file=sys.stderr)
return int(rc.returncode or 0)
def playwright_package_installed() -> bool:
try:
import playwright # type: ignore
return True
except Exception:
return False
def _build_playwright_install_cmd(browsers: str | None) -> list[str]:
"""Return the command to install Playwright browsers.
- If browsers is None or empty: default to install Chromium only.
- If browsers contains 'all': install all engines by running 'playwright install' with no extra args.
- Otherwise, validate entries and return a command that installs the named engines.
"""
base = [sys.executable, "-m", "playwright", "install"]
if not browsers:
return base + ["chromium"]
items = [b.strip().lower() for b in browsers.split(",") if b.strip()]
if not items:
return base + ["chromium"]
if "all" in items:
return base
allowed = {"chromium", "firefox", "webkit"}
invalid = [b for b in items if b not in allowed]
if invalid:
raise ValueError(
f"invalid browsers specified: {invalid}. Valid choices: chromium, firefox, webkit, or 'all'"
)
return base + items
def _install_deno(version: str | None = None) -> int:
"""Install Deno runtime for the current platform.
Uses the official Deno install scripts:
- Unix/macOS: curl -fsSL https://deno.land/x/install/install.sh | sh [-s <version>]
- Windows: powershell iwr https://deno.land/x/install/install.ps1 -useb | iex; Install-Deno [-Version <version>]
Returns exit code 0 on success, non-zero otherwise.
"""
system = platform.system().lower()
try:
if system == "windows":
# Use official PowerShell installer
if version:
ver = version if version.startswith("v") else f"v{version}"
ps_cmd = f"iwr https://deno.land/x/install/install.ps1 -useb | iex; Install-Deno -Version {ver}"
else:
ps_cmd = "iwr https://deno.land/x/install/install.ps1 -useb | iex"
run(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", ps_cmd])
else:
# POSIX: use curl + sh installer
if version:
ver = version if version.startswith("v") else f"v{version}"
cmd = f"curl -fsSL https://deno.land/x/install/install.sh | sh -s {ver}"
else:
cmd = "curl -fsSL https://deno.land/x/install/install.sh | sh"
run(["sh", "-c", cmd])
# Check that 'deno' is now available in PATH
if shutil.which("deno"):
print(f"Deno installed at: {shutil.which('deno')}")
return 0
print(
"Deno installation completed but 'deno' not found in PATH. You may need to add Deno's bin directory to your PATH manually.",
file=sys.stderr,
)
return 1
except subprocess.CalledProcessError as exc:
print(f"Deno install failed: {exc}", file=sys.stderr)
return int(exc.returncode or 1)
def main() -> int:
parser = argparse.ArgumentParser(description="Bootstrap Medios-Macina: install deps and Playwright browsers")
parser.add_argument(
"--skip-deps", action="store_true", help="Skip installing Python dependencies from requirements.txt"
)
parser.add_argument(
"--no-playwright", action="store_true", help="Skip running 'playwright install' (only install packages)"
)
parser.add_argument(
"--playwright-only", action="store_true", help="Only run 'playwright install' (skips dependency installation)"
)
parser.add_argument(
"--browsers",
type=str,
default="chromium",
help="Comma-separated list of browsers to install: chromium,firefox,webkit or 'all' (default: chromium)",
)
parser.add_argument(
"--install-editable",
action="store_true",
help="Install the project in editable mode (pip install -e .) for running tests",
)
deno_group = parser.add_mutually_exclusive_group()
deno_group.add_argument(
"--install-deno", action="store_true", help="Install the Deno runtime (default behavior; kept for explicitness)"
)
deno_group.add_argument("--no-deno", action="store_true", help="Skip installing Deno runtime (opt out)")
parser.add_argument("--deno-version", type=str, default=None, help="Specific Deno version to install (e.g., v1.34.3)")
parser.add_argument(
"--upgrade-pip", action="store_true", help="Upgrade pip/setuptools/wheel before installing requirements"
)
args = parser.parse_args()
repo_root = Path(__file__).resolve().parent.parent
# If invoked without any arguments, prefer to delegate to the platform
# bootstrap script (if present). The bootstrap scripts support a quiet/
# non-interactive mode, which we use so "python ./scripts/bootstrap.py" just
# does the right thing on Windows and *nix without extra flags.
if len(sys.argv) == 1:
rc = run_platform_bootstrap(repo_root)
if rc != 0:
return rc
print("Platform bootstrap completed successfully.")
return 0
if sys.version_info < (3, 8):
print("Warning: Python 3.8+ is recommended.", file=sys.stderr)
# Opinionated: always create or use a local venv at the project root (.venv)
venv_dir = repo_root / ".venv"
def _venv_python(p: Path) -> Path:
if platform.system().lower() == "windows":
return p / "Scripts" / "python.exe"
return p / "bin" / "python"
def _ensure_local_venv() -> Path:
"""Create (if missing) and return the path to the venv's python executable."""
try:
if not venv_dir.exists():
print(f"Creating local virtualenv at: {venv_dir}")
run([sys.executable, "-m", "venv", str(venv_dir)])
else:
print(f"Using existing virtualenv at: {venv_dir}")
py = _venv_python(venv_dir)
if not py.exists():
# Try recreating venv if python is missing
print(f"Local venv python not found at {py}; recreating venv")
run([sys.executable, "-m", "venv", str(venv_dir)])
py = _venv_python(venv_dir)
if not py.exists():
raise RuntimeError(f"Unable to locate venv python at {py}")
return py
except subprocess.CalledProcessError as exc:
print(f"Failed to create or prepare local venv: {exc}", file=sys.stderr)
raise
# Ensure a local venv is present and use it for subsequent installs.
venv_python = _ensure_local_venv()
print(f"Using venv python: {venv_python}")
# Enforce opinionated behavior: install deps, playwright, deno, and install project in editable mode.
# Ignore `--skip-deps` and `--install-editable` flags to keep the setup deterministic.
args.skip_deps = False
args.install_editable = True
args.no_playwright = False
try:
if args.playwright_only:
if not playwright_package_installed():
print("'playwright' package not found; installing it via pip...")
run([sys.executable, "-m", "pip", "install", "playwright"])
print("Installing Playwright browsers (this may download several hundred MB)...")
try:
cmd = _build_playwright_install_cmd(args.browsers)
except ValueError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 2
run(cmd)
print("Playwright browsers installed successfully.")
return 0
if args.upgrade_pip:
print("Upgrading pip, setuptools, and wheel in local venv...")
run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"])
if not args.skip_deps:
req_file = repo_root / "requirements.txt"
if not req_file.exists():
print(f"requirements.txt not found at {req_file}; skipping dependency installation.", file=sys.stderr)
else:
print(f"Installing Python dependencies into local venv from {req_file}...")
run([str(venv_python), "-m", "pip", "install", "-r", str(req_file)])
if not args.no_playwright:
if not playwright_package_installed():
print("'playwright' package not installed in venv; installing it...")
run([str(venv_python), "-m", "pip", "install", "playwright"])
print("Installing Playwright browsers (this may download several hundred MB)...")
try:
cmd = _build_playwright_install_cmd(args.browsers)
except ValueError as exc:
print(f"Error: {exc}", file=sys.stderr)
return 2
# Run Playwright install using the venv's python so binaries are available in venv
cmd[0] = str(venv_python)
run(cmd)
# Install the project into the local venv (editable mode is the default, opinionated)
print("Installing project into local venv (editable mode)")
run([str(venv_python), "-m", "pip", "install", "-e", "."])
# Verify top-level 'CLI' import and, if missing, attempt to make it available
print("Verifying top-level 'CLI' import in venv...")
try:
rc = subprocess.run(
[str(venv_python), "-c", "import importlib; importlib.import_module('CLI')"],
check=False,
)
if rc.returncode == 0:
print("OK: top-level 'CLI' is importable in the venv.")
else:
print(
"Top-level 'CLI' not importable; attempting to add repo path to venv site-packages via a .pth file..."
)
cmd = [
str(venv_python),
"-c",
(
"import site, sysconfig\n"
"out=[]\n"
"try:\n out.extend(site.getsitepackages())\nexcept Exception:\n pass\n"
"try:\n p = sysconfig.get_paths().get('purelib')\n if p:\n out.append(p)\nexcept Exception:\n pass\n"
"seen=[]; res=[]\n"
"for x in out:\n if x and x not in seen:\n seen.append(x); res.append(x)\n"
"for s in res:\n print(s)\n"
),
]
out = subprocess.check_output(cmd, text=True).strip().splitlines()
site_dir: Path | None = None
for sp in out:
if sp and Path(sp).exists():
site_dir = Path(sp)
break
if site_dir is None:
print("Could not determine venv site-packages directory; skipping .pth fallback")
else:
pth_file = site_dir / "medeia_repo.pth"
if pth_file.exists():
txt = pth_file.read_text(encoding="utf-8")
if str(repo_root) in txt:
print(f".pth already contains repo root: {pth_file}")
else:
with pth_file.open("a", encoding="utf-8") as fh:
fh.write(str(repo_root) + "\n")
print(f"Appended repo root to existing .pth: {pth_file}")
else:
with pth_file.open("w", encoding="utf-8") as fh:
fh.write(str(repo_root) + "\n")
print(f"Wrote .pth adding repo root to venv site-packages: {pth_file}")
# Re-check whether CLI can be imported now
rc2 = subprocess.run(
[str(venv_python), "-c", "import importlib; importlib.import_module('CLI')"], check=False
)
if rc2.returncode == 0:
print("Top-level 'CLI' import works after adding .pth")
else:
print(
"Adding .pth did not make top-level 'CLI' importable; consider creating an egg-link or checking the venv."
)
except Exception as exc:
print(f"Warning: failed to verify or modify site-packages for top-level CLI: {exc}")
# Optional: install Deno runtime (default: install unless --no-deno is passed)
install_deno_requested = True
if getattr(args, "no_deno", False):
install_deno_requested = False
elif getattr(args, "install_deno", False):
install_deno_requested = True
if install_deno_requested:
print("Installing Deno runtime (local/system)...")
rc = _install_deno(args.deno_version)
if rc != 0:
print("Deno installation failed.", file=sys.stderr)
return rc
# Write project-local launcher scripts (project root) that prefer the local .venv
def _write_launchers() -> None:
sh = repo_root / "mm"
ps1 = repo_root / "mm.ps1"
bat = repo_root / "mm.bat"
sh_text = """#!/usr/bin/env bash
set -e
SCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"
REPO=\"$SCRIPT_DIR\"
VENV=\"$REPO/.venv\"
# Make tools installed into the local venv available in PATH for provider discovery
export PATH=\"$VENV/bin:$PATH\"
PY=\"$VENV/bin/python\"
if [ -x \"$PY\" ]; then
exec \"$PY\" -m medeia_macina.cli_entry \"$@\"
else
exec python -m medeia_macina.cli_entry \"$@\"
fi
"""
try:
sh.write_text(sh_text, encoding="utf-8")
sh.chmod(sh.stat().st_mode | 0o111)
except Exception:
pass
ps1_text = r"""Param([Parameter(ValueFromRemainingArguments=$true)] $args)
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repo = $scriptDir
$venv = Join-Path $repo '.venv'
# Ensure venv Scripts dir is on PATH for provider discovery
$venvScripts = Join-Path $venv 'Scripts'
if (Test-Path $venvScripts) { $env:PATH = $venvScripts + ';' + $env:PATH }
$py = Join-Path $venv 'Scripts\python.exe'
$cli = Join-Path $repo 'CLI.py'
if (Test-Path $py) { & $py -m medeia_macina.cli_entry @args; exit $LASTEXITCODE }
if (Test-Path $cli) { & python $cli @args; exit $LASTEXITCODE }
# fallback
python -m medeia_macina.cli_entry @args
"""
try:
ps1.write_text(ps1_text, encoding="utf-8")
except Exception:
pass
bat_text = (
"@echo off\r\n"
"set SCRIPT_DIR=%~dp0\r\n"
"set PATH=%SCRIPT_DIR%\\.venv\\Scripts;%PATH%\r\n"
"if exist \"%SCRIPT_DIR%\\.venv\\Scripts\\python.exe\" \"%SCRIPT_DIR%\\.venv\\Scripts\\python.exe\" -m medeia_macina.cli_entry %*\r\n"
"if exist \"%SCRIPT_DIR%\\CLI.py\" python \"%SCRIPT_DIR%\\CLI.py\" %*\r\n"
"python -m medeia_macina.cli_entry %*\r\n"
)
try:
bat.write_text(bat_text, encoding="utf-8")
except Exception:
pass
_write_launchers()
# Install user-global shims so `mm` can be executed from any shell session.
def _install_user_shims(repo: Path) -> None:
try:
home = Path.home()
system = platform.system().lower()
if system == "windows":
user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin"
user_bin.mkdir(parents=True, exist_ok=True)
# Write mm.cmd (CMD shim)
mm_cmd = user_bin / "mm.cmd"
cmd_text = (
f"@echo off\r\n"
f"set REPO={repo}\r\n"
f"if exist \"%REPO%\\.venv\\Scripts\\mm.exe\" \"%REPO%\\.venv\\Scripts\\mm.exe\" %*\r\n"
f"if defined MM_DEBUG (\r\n"
f" echo MM_DEBUG: REPO=%REPO%\r\n"
f" if exist \"%REPO%\\.venv\\Scripts\\python.exe\" \"%REPO%\\.venv\\Scripts\\python.exe\" -c \"import sys,importlib,importlib.util; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);\" \r\n"
f")\r\n"
f"if exist \"%REPO%\\.venv\\Scripts\\python.exe\" \"%REPO%\\.venv\\Scripts\\python.exe\" -m medeia_macina.cli_entry %*\r\n"
f"python -m medeia_macina.cli_entry %*\r\n"
)
if mm_cmd.exists():
bak = mm_cmd.with_suffix(f".bak{int(time.time())}")
mm_cmd.replace(bak)
mm_cmd.write_text(cmd_text, encoding="utf-8")
# Write mm.ps1 (PowerShell shim)
mm_ps1 = user_bin / "mm.ps1"
ps1_text = (
"Param([Parameter(ValueFromRemainingArguments=$true)] $args)\n"
f"$repo = \"{repo}\"\n"
"$venv = Join-Path $repo '.venv'\n"
"$exe = Join-Path $venv 'Scripts\\mm.exe'\n"
"if (Test-Path $exe) { & $exe @args; exit $LASTEXITCODE }\n"
"$py = Join-Path $venv 'Scripts\\python.exe'\n"
"if (Test-Path $py) {\n"
" if ($env:MM_DEBUG) {\n"
" Write-Host \"MM_DEBUG: diagnostics\" -ForegroundColor Yellow\n"
" & $py -c \"import sys,importlib,importlib.util,traceback; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);\"\n"
" }\n"
" & $py -m medeia_macina.cli_entry @args; exit $LASTEXITCODE\n"
"}\n"
"python -m medeia_macina.cli_entry @args\n"
)
if mm_ps1.exists():
bak = mm_ps1.with_suffix(f".bak{int(time.time())}")
mm_ps1.replace(bak)
mm_ps1.write_text(ps1_text, encoding="utf-8")
# Attempt to add user_bin to the user's PATH if it's not present.
try:
cur = os.environ.get("PATH", "")
str_bin = str(user_bin)
if str_bin not in cur:
ps_cmd = (
"$bin = '{bin}';"
"$cur = [Environment]::GetEnvironmentVariable('PATH','User');"
"if ($cur -notlike \"*$bin*\") {[Environment]::SetEnvironmentVariable('PATH', ($bin + ';' + ($cur -ne $null ? $cur : '')), 'User')}"
).format(bin=str_bin.replace("\\", "\\\\"))
subprocess.run(["powershell", "-NoProfile", "-Command", ps_cmd], check=False)
except Exception:
pass
print(f"Installed global launchers to: {user_bin}")
else:
# POSIX
user_bin = Path(os.environ.get("XDG_BIN_HOME", str(home / ".local/bin")))
user_bin.mkdir(parents=True, exist_ok=True)
mm_sh = user_bin / "mm"
sh_text = (
"#!/usr/bin/env bash\n"
"set -e\n"
f"REPO=\"{repo}\"\n"
"# Prefer git top-level when available to avoid embedding a parent path.\n"
"if command -v git >/dev/null 2>&1; then\n"
" gitroot=$(git -C \"$REPO\" rev-parse --show-toplevel 2>/dev/null || true)\n"
" if [ -n \"$gitroot\" ]; then\n"
" REPO=\"$gitroot\"\n"
" fi\n"
"fi\n"
"# If git not available or didn't resolve, walk up from CWD to find a project root.\n"
"if [ ! -f \"$REPO/CLI.py\" ] && [ ! -f \"$REPO/pyproject.toml\" ]; then\n"
" CUR=\"$(pwd -P)\"\n"
" while [ \"$CUR\" != \"/\" ] && [ \"$CUR\" != \"\" ]; do\n"
" if [ -f \"$CUR/CLI.py\" ] || [ -f \"$CUR/pyproject.toml\" ]; then\n"
" REPO=\"$CUR\"\n"
" break\n"
" fi\n"
" CUR=\"$(dirname \"$CUR\")\"\n"
" done\n"
"fi\n"
"VENV=\"$REPO/.venv\"\n"
"# Debug mode: set MM_DEBUG=1 to print repository, venv, and import diagnostics\n"
"if [ -n \"${MM_DEBUG:-}\" ]; then\n"
" echo \"MM_DEBUG: diagnostics\" >&2\n"
" echo \"Resolved REPO: $REPO\" >&2\n"
" echo \"Resolved VENV: $VENV\" >&2\n"
" echo \"VENV exists: $( [ -d \"$VENV\" ] && echo yes || echo no )\" >&2\n"
" echo \"Candidates:\" >&2\n"
" echo \" VENV/bin/mm: $( [ -x \"$VENV/bin/mm\" ] && echo yes || echo no )\" >&2\n"
" echo \" VENV/bin/python3: $( [ -x \"$VENV/bin/python3\" ] && echo yes || echo no )\" >&2\n"
" echo \" VENV/bin/python: $( [ -x \"$VENV/bin/python\" ] && echo yes || echo no )\" >&2\n"
" echo \" system python3: $(command -v python3 || echo none)\" >&2\n"
" echo \" system python: $(command -v python || echo none)\" >&2\n"
" for pycmd in \"$VENV/bin/python3\" \"$VENV/bin/python\" \"$(command -v python3 2>/dev/null)\" \"$(command -v python 2>/dev/null)\"; do\n"
" if [ -n \"$pycmd\" ] && [ -x \"$pycmd\" ]; then\n"
" echo \"---- Testing with: $pycmd ----\" >&2\n"
" $pycmd - <<'PY'\nimport sys, importlib, traceback, importlib.util\nprint('sys.executable:', sys.executable)\nprint('sys.path (first 8):', sys.path[:8])\nfor mod in ('CLI','medeia_macina','medeia_macina.cli_entry'):\n try:\n spec = importlib.util.find_spec(mod)\n print(mod, 'spec:', spec)\n if spec:\n m = importlib.import_module(mod)\n print(mod, 'loaded at', getattr(m, '__file__', None))\n except Exception:\n print(mod, 'import failed')\n traceback.print_exc()\nPY\n"
" fi\n"
" done\n"
" echo \"MM_DEBUG: end diagnostics\" >&2\n"
"fi\n"
"# Packaged console script in the venv if available\n"
"if [ -x \"$VENV/bin/mm\" ]; then\n"
" exec \"$VENV/bin/mm\" \"$@\"\n"
"fi\n"
"# Prefer venv's python3, then venv's python\n"
"if [ -x \"$VENV/bin/python3\" ]; then\n"
" exec \"$VENV/bin/python3\" -m medeia_macina.cli_entry \"$@\"\n"
"fi\n"
"if [ -x \"$VENV/bin/python\" ]; then\n"
" exec \"$VENV/bin/python\" -m medeia_macina.cli_entry \"$@\"\n"
"fi\n"
"# Fallback to system python3, then system python (only if it's Python 3)\n"
"if command -v python3 >/dev/null 2>&1; then\n"
" exec python3 -m medeia_macina.cli_entry \"$@\"\n"
"fi\n"
"if command -v python >/dev/null 2>&1; then\n"
" if python -c 'import sys; sys.exit(0 if sys.version_info[0] >= 3 else 1)'; then\n"
" exec python -m medeia_macina.cli_entry \"$@\"\n"
" fi\n"
"fi\n"
"echo 'Error: no suitable Python 3 interpreter found. Please install Python 3 or use the venv.' >&2\n"
"exit 127\n"
)
if mm_sh.exists():
bak = mm_sh.with_suffix(f".bak{int(time.time())}")
mm_sh.replace(bak)
mm_sh.write_text(sh_text, encoding="utf-8")
mm_sh.chmod(mm_sh.stat().st_mode | 0o111)
# Ensure the user's bin is on PATH for future sessions by adding to ~/.profile
cur_path = os.environ.get("PATH", "")
if str(user_bin) not in cur_path:
profile = home / ".profile"
snippet = (
"# Added by Medeia-Macina setup: ensure user local bin is on PATH\n"
"if [ -d \"$HOME/.local/bin\" ] && [[ \":$PATH:\" != *\":$HOME/.local/bin:\"* ]]; then\n"
" PATH=\"$HOME/.local/bin:$PATH\"\n"
"fi\n"
)
try:
txt = profile.read_text() if profile.exists() else ""
if snippet.strip() not in txt:
with profile.open("a", encoding="utf-8") as fh:
fh.write("\n" + snippet)
except Exception:
pass
print(f"Installed global launcher to: {mm_sh}")
except Exception as exc: # pragma: no cover - best effort
print(f"Failed to install global shims: {exc}", file=sys.stderr)
_install_user_shims(repo_root)
print("Setup complete.")
return 0
except subprocess.CalledProcessError as exc:
print(f"Error: command failed with exit {exc.returncode}: {exc}", file=sys.stderr)
return int(exc.returncode or 1)
except Exception as exc: # pragma: no cover - defensive
print(f"Unexpected error: {exc}", file=sys.stderr)
return 2
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -1,4 +1,23 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""DEPRECATED: scripts/setup.py
This file has been renamed to `scripts/bootstrap.py` to avoid having multiple
`setup.py` files in the repository. Please use:
python ./scripts/bootstrap.py
This shim remains temporarily for backwards compatibility.
---
Original docstring:
scripts/setup.py
"""
Unified project setup helper (Python-only).
#!/usr/bin/env python3
"""scripts/setup.py """scripts/setup.py
Unified project setup helper (Python-only). Unified project setup helper (Python-only).
@@ -17,7 +36,7 @@ The platform bootstrap scripts also attempt (best-effort) to install `mpv` if
it is not found on your PATH, since some workflows use it. it is not found on your PATH, since some workflows use it.
Usage: Usage:
python ./scripts/setup.py # install deps and playwright browsers (or run platform bootstrap if no args) python ./scripts/bootstrap.py # install deps and playwright browsers (or run platform bootstrap if no args)
python ./scripts/setup.py --skip-deps python ./scripts/setup.py --skip-deps
python ./scripts/setup.py --playwright-only python ./scripts/setup.py --playwright-only
@@ -418,7 +437,7 @@ python -m medeia_macina.cli_entry @args
"python -m medeia_macina.cli_entry %*\r\n" "python -m medeia_macina.cli_entry %*\r\n"
) )
try: try:
bat.write_text(bat_text, encoding="utf-8") # non-interactive mode, which we use so "python ./scripts/bootstrap.py" just
except Exception: except Exception:
pass pass