f
This commit is contained in:
@@ -43,6 +43,9 @@ Optional flags:
|
||||
--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
|
||||
--check-install Verify that the 'mm' command was installed correctly
|
||||
--debug Show detailed diagnostic information during installation
|
||||
--quiet Suppress output (used internally by platform scripts)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -289,6 +292,16 @@ def main() -> int:
|
||||
action="store_true",
|
||||
help="Upgrade pip/setuptools/wheel before installing requirements",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
action="store_true",
|
||||
help="Show detailed diagnostic information during installation",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--check-install",
|
||||
action="store_true",
|
||||
help="Verify that the 'mm' command was installed correctly",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--uninstall",
|
||||
action="store_true",
|
||||
@@ -528,10 +541,140 @@ def main() -> int:
|
||||
if args.uninstall:
|
||||
return _do_uninstall()
|
||||
|
||||
# If invoked without any arguments and not asked to skip delegation, prefer
|
||||
# the interactive menu when running in a TTY; otherwise delegate to the
|
||||
# platform-specific bootstrap helper (non-interactive).
|
||||
if len(sys.argv) == 1 and not args.no_delegate:
|
||||
if args.check_install:
|
||||
# Verify mm command is properly installed
|
||||
home = Path.home()
|
||||
system = platform.system().lower()
|
||||
|
||||
print("Checking 'mm' command installation...")
|
||||
print()
|
||||
|
||||
if system == "windows":
|
||||
user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin"
|
||||
mm_ps1 = user_bin / "mm.ps1"
|
||||
mm_bat = user_bin / "mm.bat"
|
||||
|
||||
print(f"Checking for shim files:")
|
||||
print(f" mm.ps1: {'✓' if mm_ps1.exists() else '✗'} ({mm_ps1})")
|
||||
print(f" mm.bat: {'✓' if mm_bat.exists() else '✗'} ({mm_bat})")
|
||||
print()
|
||||
|
||||
if mm_ps1.exists():
|
||||
ps1_content = mm_ps1.read_text(encoding="utf-8")
|
||||
if "$repo" in ps1_content or "$REPO" in ps1_content:
|
||||
print(f" mm.ps1 content looks valid ({len(ps1_content)} bytes)")
|
||||
else:
|
||||
print(f" ⚠️ mm.ps1 content may be corrupted")
|
||||
|
||||
if mm_bat.exists():
|
||||
bat_content = mm_bat.read_text(encoding="utf-8")
|
||||
if "REPO=" in bat_content or "PY=" in bat_content:
|
||||
print(f" mm.bat content looks valid ({len(bat_content)} bytes)")
|
||||
else:
|
||||
print(f" ⚠️ mm.bat content may be corrupted")
|
||||
print()
|
||||
|
||||
# Check PATH
|
||||
path = os.environ.get("PATH", "")
|
||||
user_bin_str = str(user_bin)
|
||||
in_path = user_bin_str in path
|
||||
print(f"Checking PATH environment variable:")
|
||||
print(f" {user_bin_str} in current session PATH: {'✓' if in_path else '✗'}")
|
||||
|
||||
# Check registry
|
||||
try:
|
||||
import winreg
|
||||
reg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
|
||||
key = winreg.OpenKey(reg, "Environment", 0, winreg.KEY_READ)
|
||||
current_path = winreg.QueryValueEx(key, "Path")[0]
|
||||
winreg.CloseKey(key)
|
||||
in_reg = user_bin_str in current_path
|
||||
print(f" {user_bin_str} in registry PATH: {'✓' if in_reg else '✗'}")
|
||||
if not in_reg:
|
||||
print()
|
||||
print("📝 Note: Path is not in registry. It may work in this session but won't persist.")
|
||||
print(f" To fix, run: [Environment]::SetEnvironmentVariable('PATH', '{user_bin_str};' + [Environment]::GetEnvironmentVariable('PATH','User'), 'User')")
|
||||
except Exception as e:
|
||||
print(f" Could not check registry: {e}")
|
||||
print()
|
||||
|
||||
# Test if mm command works
|
||||
print("Testing 'mm' command...")
|
||||
try:
|
||||
result = subprocess.run(["mm", "--help"], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ 'mm --help' works!")
|
||||
print(f" Output (first line): {result.stdout.split(chr(10))[0]}")
|
||||
else:
|
||||
print(f" ✗ 'mm --help' failed with exit code {result.returncode}")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr.strip()}")
|
||||
except FileNotFoundError:
|
||||
# mm not found via PATH, try calling the .ps1 directly
|
||||
print(f" ✗ 'mm' command not found in PATH")
|
||||
print(f" Shims exist but command is not accessible via PATH")
|
||||
print()
|
||||
print("Attempting to call shim directly...")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["powershell", "-NoProfile", "-Command", f"& '{mm_ps1}' --help"],
|
||||
capture_output=True, text=True, timeout=5
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ Direct shim call works!")
|
||||
print(f" The shim files are valid and functional.")
|
||||
print()
|
||||
print("⚠️ 'mm' is not in PATH, but the shims are working correctly.")
|
||||
print()
|
||||
print("Possible causes and fixes:")
|
||||
print(f" 1. Terminal needs restart: Close and reopen your terminal/PowerShell")
|
||||
print(f" 2. PATH reload: Run: $env:Path = [Environment]::GetEnvironmentVariable('PATH', 'User') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'Machine')")
|
||||
print(f" 3. Manual PATH: Add C:\\Users\\Admin\\bin to your system PATH manually")
|
||||
else:
|
||||
print(f" ✗ Direct shim call failed")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr.strip()}")
|
||||
except Exception as e:
|
||||
print(f" ✗ Could not test direct shim: {e}")
|
||||
except subprocess.TimeoutExpired:
|
||||
print(f" ✗ 'mm' command timed out")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error testing 'mm': {e}")
|
||||
else:
|
||||
# POSIX (Linux/macOS)
|
||||
user_bin = home / ".local" / "bin"
|
||||
mm_sh = user_bin / "mm"
|
||||
|
||||
print(f"Checking for shim file:")
|
||||
print(f" mm: {'✓' if mm_sh.exists() else '✗'} ({mm_sh})")
|
||||
print()
|
||||
|
||||
path = os.environ.get("PATH", "")
|
||||
user_bin_str = str(user_bin)
|
||||
in_path = user_bin_str in path
|
||||
print(f"Checking PATH environment variable:")
|
||||
print(f" {user_bin_str} in current session PATH: {'✓' if in_path else '✗'}")
|
||||
print()
|
||||
|
||||
# Test if mm command works
|
||||
print("Testing 'mm' command...")
|
||||
try:
|
||||
result = subprocess.run(["mm", "--help"], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ 'mm --help' works!")
|
||||
print(f" Output (first line): {result.stdout.split(chr(10))[0]}")
|
||||
else:
|
||||
print(f" ✗ 'mm --help' failed with exit code {result.returncode}")
|
||||
if result.stderr:
|
||||
print(f" Error: {result.stderr.strip()}")
|
||||
except FileNotFoundError:
|
||||
print(f" ✗ 'mm' command not found in PATH")
|
||||
except Exception as e:
|
||||
print(f" ✗ Error testing 'mm': {e}")
|
||||
|
||||
print()
|
||||
print("✅ Installation check complete!")
|
||||
return 0
|
||||
if sys.stdin.isatty() and not args.quiet:
|
||||
sel = _interactive_menu()
|
||||
if sel == "install":
|
||||
@@ -852,6 +995,12 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
user_bin = Path(os.environ.get("USERPROFILE", str(home))) / "bin"
|
||||
user_bin.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Validate repo path
|
||||
if not (repo / ".venv").exists():
|
||||
print(f"WARNING: venv not found at {repo}/.venv - mm command may not work", file=sys.stderr)
|
||||
if not (repo / "scripts").exists():
|
||||
print(f"WARNING: scripts folder not found at {repo}/scripts - mm command may not work", file=sys.stderr)
|
||||
|
||||
# Convert repo path to string with proper escaping
|
||||
repo_str = str(repo).replace("\\", "\\\\")
|
||||
|
||||
@@ -877,6 +1026,8 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
bak = mm_ps1.with_suffix(f".bak{int(time.time())}")
|
||||
mm_ps1.replace(bak)
|
||||
mm_ps1.write_text(ps1_text, encoding="utf-8")
|
||||
if not args.quiet:
|
||||
print(f"Created {mm_ps1}")
|
||||
|
||||
# Write mm.bat (CMD shim for better compatibility)
|
||||
mm_bat = user_bin / "mm.bat"
|
||||
@@ -905,6 +1056,20 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
mm_bat.replace(bak)
|
||||
mm_bat.write_text(bat_text, encoding="utf-8")
|
||||
|
||||
# Validate that shim files were created correctly
|
||||
ps1_ok = mm_ps1.exists() and len(mm_ps1.read_text(encoding="utf-8")) > 0
|
||||
bat_ok = mm_bat.exists() and len(mm_bat.read_text(encoding="utf-8")) > 0
|
||||
|
||||
if not ps1_ok or not bat_ok:
|
||||
raise RuntimeError(f"Failed to create shim files: mm.ps1={ps1_ok}, mm.bat={bat_ok}")
|
||||
|
||||
if args.debug:
|
||||
print(f"DEBUG: Created mm.ps1 ({len(ps1_text)} bytes)")
|
||||
print(f"DEBUG: Created mm.bat ({len(bat_text)} bytes)")
|
||||
print(f"DEBUG: Repo path embedded in shims: {repo}")
|
||||
print(f"DEBUG: Venv location: {repo}/.venv")
|
||||
print(f"DEBUG: Shim directory: {user_bin}")
|
||||
|
||||
# Add user_bin to PATH for current and future sessions
|
||||
str_bin = str(user_bin)
|
||||
cur_path = os.environ.get("PATH", "")
|
||||
@@ -912,8 +1077,6 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
# Update current session PATH if not already present
|
||||
if str_bin not in cur_path:
|
||||
os.environ["PATH"] = str_bin + ";" + cur_path
|
||||
if not args.quiet:
|
||||
print(f"Added {user_bin} to current session PATH")
|
||||
|
||||
# Persist to user's Windows registry PATH for future sessions
|
||||
try:
|
||||
@@ -922,21 +1085,51 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
"$cur = [Environment]::GetEnvironmentVariable('PATH','User');"
|
||||
"if ($cur -notlike \"*$bin*\") {{"
|
||||
"[Environment]::SetEnvironmentVariable('PATH', ($bin + ';' + ($cur -ne $null ? $cur : '')), 'User');"
|
||||
"Write-Host 'Added {bin} to User PATH in registry' -ForegroundColor Green"
|
||||
"}}"
|
||||
).format(bin=str_bin.replace("\\", "\\\\"))
|
||||
subprocess.run(
|
||||
result = subprocess.run(
|
||||
["powershell",
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
ps_cmd],
|
||||
check=False,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if args.debug and result.stderr:
|
||||
print(f"DEBUG: PowerShell output: {result.stderr}")
|
||||
|
||||
# Also reload PATH in current session for immediate availability
|
||||
reload_cmd = (
|
||||
"$env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'User') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'Machine')"
|
||||
)
|
||||
subprocess.run(
|
||||
["powershell",
|
||||
"-NoProfile",
|
||||
"-Command",
|
||||
reload_cmd],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
except Exception as e:
|
||||
if not args.quiet:
|
||||
print(f"Note: Could not persist PATH to registry (this is non-critical): {e}", file=sys.stderr)
|
||||
if args.debug:
|
||||
print(f"DEBUG: Could not persist PATH to registry: {e}", file=sys.stderr)
|
||||
|
||||
if not args.quiet:
|
||||
print(f"Installed global launchers to: {user_bin}")
|
||||
print(f"✓ mm.ps1 (PowerShell)")
|
||||
print(f"✓ mm.bat (Command Prompt)")
|
||||
print()
|
||||
print("You can now run 'mm' from any shell to start the application.")
|
||||
print()
|
||||
print("📝 If 'mm' is not recognized, you may need to:")
|
||||
print(f" 1. Restart your terminal")
|
||||
print(f" 2. Or add '{user_bin}' manually to your PATH")
|
||||
print()
|
||||
print("🐛 Debug mode: Set MM_DEBUG=1 environment variable for detailed info:")
|
||||
print(" PowerShell: $env:MM_DEBUG=1; mm --help")
|
||||
print(" CMD: set MM_DEBUG=1 && mm --help")
|
||||
|
||||
if not args.quiet:
|
||||
print(f"\nInstalled global launchers to: {user_bin}")
|
||||
@@ -1052,7 +1245,18 @@ if (Test-Path (Join-Path $repo 'CLI.py')) {
|
||||
|
||||
_install_user_shims(repo_root)
|
||||
|
||||
print("Setup complete.")
|
||||
if not args.quiet:
|
||||
print("\n✅ Setup complete!")
|
||||
print()
|
||||
print("The 'mm' command should now work from any terminal.")
|
||||
print()
|
||||
print("Verify the installation:")
|
||||
print(" python scripts/bootstrap.py --check-install")
|
||||
print()
|
||||
print("Then run the app:")
|
||||
print(" mm --help")
|
||||
print()
|
||||
print("💡 If 'mm' is not recognized, close and reopen your terminal.")
|
||||
return 0
|
||||
|
||||
except subprocess.CalledProcessError as exc:
|
||||
|
||||
Reference in New Issue
Block a user