diff --git a/docs/BOOTSTRAP.md b/docs/BOOTSTRAP.md index 1640e2b..abececd 100644 --- a/docs/BOOTSTRAP.md +++ b/docs/BOOTSTRAP.md @@ -56,6 +56,25 @@ Additionally, the setup helpers install a global `mm` launcher into your user bi The scripts back up any existing `mm` shims before replacing them and will print actionable messages when a shell restart is required. +Debugging the global `mm` launcher + +- POSIX: set MM_DEBUG=1 and run `mm` to print runtime diagnostics (resolved REPO, VENV, and Python import checks): + + ```bash + MM_DEBUG=1 mm + ``` + +- PowerShell: set and export `$env:MM_DEBUG='1'` then run `mm.ps1` or the installed `mm` shim: + + ```powershell + $env:MM_DEBUG = '1' + mm + ``` + +- CMD: `set MM_DEBUG=1` then run `mm`. + +These diagnostics help identify whether the global launcher is selecting the correct repository and virtual environment; please include the output when reporting launcher failures. + PowerShell (Windows): ```powershell irm https://deno.land/install.ps1 | iex diff --git a/scripts/bootstrap.ps1 b/scripts/bootstrap.ps1 index 2505755..617f09b 100644 --- a/scripts/bootstrap.ps1 +++ b/scripts/bootstrap.ps1 @@ -446,8 +446,17 @@ try { $cmdText = @" @echo off set "REPO=__REPO__" +if exist "%REPO%\.venv\Scripts\mm.exe" "%REPO%\.venv\Scripts\mm.exe" %* +if defined MM_DEBUG ( + echo MM_DEBUG: REPO=%REPO% + 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]);" + ) else ( + python -c "import sys,importlib,importlib.util; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);" + ) +) if exist "%REPO%\.venv\Scripts\python.exe" ( - "%REPO%\.venv\Scripts\python.exe" "%REPO%\CLI.py" %* + "%REPO%\.venv\Scripts\python.exe" -m medeia_macina.cli_entry %* exit /b %ERRORLEVEL% ) if exist "%REPO%\CLI.py" ( @@ -470,12 +479,18 @@ python -m medeia_macina.cli_entry %* Param([Parameter(ValueFromRemainingArguments=$true)] $args) $repo = "__REPO__" $venv = Join-Path $repo '.venv' +$exe = Join-Path $venv 'Scripts\mm.exe' +if (Test-Path $exe) { & $exe @args; exit $LASTEXITCODE } $py = Join-Path $venv 'Scripts\python.exe' -$cli = Join-Path $repo 'CLI.py' -if (Test-Path $py) { & $py $cli @args; exit $LASTEXITCODE } -if (Test-Path $cli) { & $py $cli @args; exit $LASTEXITCODE } +if ($env:MM_DEBUG) { + Write-Host "MM_DEBUG: diagnostics" -ForegroundColor Yellow + if (Test-Path $py) { & $py -c "import sys,importlib,importlib.util,traceback; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);" } + else { python -c "import sys,importlib,importlib.util,traceback; print('sys.executable:', sys.executable); print('sys.path (first 8):', sys.path[:8]);" } +} +if (Test-Path $py) { & $py -m medeia_macina.cli_entry @args; exit $LASTEXITCODE } +if (Test-Path (Join-Path $repo 'CLI.py')) { & python (Join-Path $repo 'CLI.py') @args; exit $LASTEXITCODE } # fallback -python $cli @args +python -m medeia_macina.cli_entry @args '@ # Inject the actual repo path safely (escape embedded double-quotes if any) $ps1Text = $ps1Text.Replace('__REPO__', $repo.Replace('"', '""')) @@ -521,3 +536,6 @@ Write-Host "To run the app:" Write-Host " $ .\$VenvPath\Scripts\mm.exe (Windows) or" Write-Host " $ ./$VenvPath/bin/mm (Linux) or" Write-Host " $ $venvPython -m medeia_macina.cli_entry" +Write-Host "" +Write-Host "If the global 'mm' launcher fails, collect runtime diagnostics by setting MM_DEBUG and re-running the command:"" +if ($IsWindowsPlatform) { Write-Host " PowerShell: $env:MM_DEBUG = '1'; mm" } else { Write-Host " POSIX: MM_DEBUG=1 mm" } diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh index 1f7ee44..f6578eb 100644 --- a/scripts/bootstrap.sh +++ b/scripts/bootstrap.sh @@ -459,26 +459,71 @@ cat > "$USER_BIN/mm" <<'MM' #!/usr/bin/env bash set -e -# REPO is injected at install time; if it doesn't look like a project, try to -# find the repo by walking up from the current working directory. +# REPO is injected at install time; try to resolve canonical project root using +# git when available to avoid mistakenly selecting parent directories. REPO="__REPO__" -# If the embedded REPO does not contain a canonical project marker, search -# upward from the current working directory for a project root. Use only -# explicit project markers (CLI.py or pyproject.toml) to avoid false positives -# from subdirectories like 'scripts' which may contain their own setup.py. -if [ ! -f "$REPO/CLI.py" ] && [ ! -f "$REPO/pyproject.toml" ]; then - CUR="$(pwd -P)" - while [ "$CUR" != "/" ] && [ "$CUR" != "" ]; do - if [ -f "$CUR/CLI.py" ] || [ -f "$CUR/pyproject.toml" ]; then - REPO="$CUR" - break +if command -v git >/dev/null 2>&1; then + gitroot=$(git -C "$REPO" rev-parse --show-toplevel 2>/dev/null || true) + if [ -n "$gitroot" ]; then + REPO="$gitroot" + else + # Try resolving from the current working directory + gitroot=$(git -C "$(pwd -P)" rev-parse --show-toplevel 2>/dev/null || true) + if [ -n "$gitroot" ]; then + REPO="$gitroot" fi - CUR="$(dirname "$CUR")" - done + fi +else + # Fallback: walk up from CWD to find CLI.py or pyproject.toml + if [ ! -f "$REPO/CLI.py" ] && [ ! -f "$REPO/pyproject.toml" ]; then + CUR="$(pwd -P)" + while [ "$CUR" != "/" ] && [ "$CUR" != "" ]; do + if [ -f "$CUR/CLI.py" ] || [ -f "$CUR/pyproject.toml" ]; then + REPO="$CUR" + break + fi + CUR="$(dirname "$CUR")" + done + fi fi VENV="$REPO/.venv" +# Debug mode: set MM_DEBUG=1 to print repository, venv, and import diagnostics +if [ -n "${MM_DEBUG:-}" ]; then + echo "MM_DEBUG: diagnostics" >&2 + echo "Resolved REPO: $REPO" >&2 + echo "Resolved VENV: $VENV" >&2 + echo "VENV exists: $( [ -d "$VENV" ] && echo yes || echo no )" >&2 + echo "Candidates:" >&2 + echo " VENV/bin/mm: $( [ -x "$VENV/bin/mm" ] && echo yes || echo no )" >&2 + echo " VENV/bin/python3: $( [ -x "$VENV/bin/python3" ] && echo yes || echo no )" >&2 + echo " VENV/bin/python: $( [ -x "$VENV/bin/python" ] && echo yes || echo no )" >&2 + echo " system python3: $(command -v python3 || echo none)" >&2 + echo " system python: $(command -v python || echo none)" >&2 + for pycmd in "$VENV/bin/python3" "$VENV/bin/python" "$(command -v python3 2>/dev/null)" "$(command -v python 2>/dev/null)"; do + if [ -n "$pycmd" ] && [ -x "$pycmd" ]; then + echo "---- Testing with: $pycmd ----" >&2 + "$pycmd" - <<'PY' +import sys, importlib, traceback, importlib.util +print('sys.executable:', sys.executable) +print('sys.path (first 8):', sys.path[:8]) +for mod in ('CLI','medeia_macina','medeia_macina.cli_entry'): + try: + spec = importlib.util.find_spec(mod) + print(mod, 'spec:', spec) + if spec: + m = importlib.import_module(mod) + print(mod, 'loaded at', getattr(m, '__file__', None)) + except Exception: + print(mod, 'import failed') + traceback.print_exc() +PY + fi + done + echo "MM_DEBUG: end diagnostics" >&2 +fi + # Packaged console script in the venv if available if [ -x "$VENV/bin/mm" ]; then exec "$VENV/bin/mm" "$@" @@ -574,4 +619,7 @@ To run the app: $VENV_PY -m medeia_macina.cli_entry # alternative Global launcher installed: $USER_BIN/mm + +If the global 'mm' launcher fails to run, collect diagnostics with MM_DEBUG=1: + MM_DEBUG=1 mm EOF diff --git a/scripts/setup.py b/scripts/setup.py index 4b74d04..1d9437a 100644 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -313,8 +313,12 @@ python $cli @args 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 exist \"%REPO%\\.venv\\Scripts\\python.exe\" \"%REPO%\\.venv\\Scripts\\python.exe\" -m medeia_entry %*\r\n" - f"python -m medeia_entry %*\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())}") @@ -330,8 +334,14 @@ python $cli @args "$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) { & $py -m medeia_entry @args; exit $LASTEXITCODE }\n" - "python -m medeia_entry @args\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())}") @@ -364,9 +374,14 @@ python $cli @args "#!/usr/bin/env bash\n" "set -e\n" f"REPO=\"{repo}\"\n" - "# If the packaged REPO does not look valid at runtime, try to locate the repo by walking\n" - "# up from the current working directory so the launcher works when executed inside the\n" - "# project tree or when the global bin is symlinked into the 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" @@ -378,15 +393,38 @@ python $cli @args " 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"