diff --git a/scripts/bootstrap.ps1 b/scripts/bootstrap.ps1 index b1f945a..e5ff436 100644 --- a/scripts/bootstrap.ps1 +++ b/scripts/bootstrap.ps1 @@ -1,6 +1,47 @@ <# .SYNOPSIS Bootstrap a Python virtualenv and install the project on Windows (PowerShell). + +.DESCRIPTION +Creates a Python virtual environment (default: .venv), upgrades pip, installs the project +(either editable or normal), and optionally creates Desktop and Start Menu shortcuts. + +.EXAMPLE +# Create .venv and install in editable mode, create Desktop shortcut +.\scripts\bootstrap.ps1 -Editable -CreateDesktopShortcut + +.EXAMPLE +# Use a specific python executable and force overwrite existing venv +.\scripts\bootstrap.ps1 -Python "C:\\Python39\\python.exe" -Force +# Note: you may need to run PowerShell with ExecutionPolicy Bypass: +# powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap.ps1 -Editable +#> + +param( + [switch]$Editable, + [switch]$CreateDesktopShortcut, + [switch]$CreateStartMenuShortcut, + [string]$VenvPath = ".venv", + [string]$Python = "", + [switch]$Force, + [switch]$NoInstall, + [switch]$NoPlaywright, + [string]$PlaywrightBrowsers = "chromium", + [switch]$FixUrllib3, + [switch]$RemovePth, + [switch]$Quiet +) + +# Resolve OS detection in a broad-compatible way +try { $IsWindowsPlatform = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows) } catch { $IsWindowsPlatform = $env:OS -match 'Windows' } + +function Write-Log { + param([string]$msg,[string]$lvl="INFO") + if (-not $Quiet) { + if ($lvl -eq "ERROR") { Write-Host "[$lvl] $msg" -ForegroundColor Red } else { Write-Host "[$lvl] $msg" } + } +} + function Ensure-Mpv { # mpv is used by some pipelines; try to ensure it's available on PATH. try { @@ -39,7 +80,7 @@ function Ensure-Mpv { $mpvCmd2 = Get-Command mpv -ErrorAction SilentlyContinue if ($mpvCmd2) { try { - $v2 = & mpv --version 2>$null | Select-Object -First 1 + $v2 = & mpv --version 2> $null | Select-Object -First 1 if ($v2) { Write-Log "mpv installed: $v2" "INFO" } else { Write-Log "mpv installed: $($mpvCmd2.Path)" "INFO" } } catch { Write-Log "mpv installed: $($mpvCmd2.Path)" "INFO" @@ -54,46 +95,8 @@ if ($IsWindowsPlatform) { Ensure-Mpv } -.DESCRIPTION -Creates a Python virtual environment (default: .venv), upgrades pip, installs the project -(either editable or normal), and optionally creates Desktop and Start Menu shortcuts. - -.EXAMPLE -# Create .venv and install in editable mode, create Desktop shortcut -.\scripts\bootstrap.ps1 -Editable -CreateDesktopShortcut - -.EXAMPLE -# Use a specific python executable and force overwrite existing venv -.\scripts\bootstrap.ps1 -Python "C:\\Python39\\python.exe" -Force -# Note: you may need to run PowerShell with ExecutionPolicy Bypass: -# powershell -ExecutionPolicy Bypass -File .\scripts\bootstrap.ps1 -Editable -#> - -param( - [switch]$Editable, - [switch]$CreateDesktopShortcut, - [switch]$CreateStartMenuShortcut, - [string]$VenvPath = ".venv", - [string]$Python = "", - [switch]$Force, - [switch]$NoInstall, - [switch]$NoPlaywright, - [string]$PlaywrightBrowsers = "chromium", - [switch]$FixUrllib3, - [switch]$RemovePth, - [switch]$Quiet -) - # Track whether the user chose an interactive auto-fix (so we can auto-remove .pth files) $AutoFixInteractive = $false -) - -function Write-Log { - param([string]$msg,[string]$lvl="INFO") - if (-not $Quiet) { - if ($lvl -eq "ERROR") { Write-Host "[$lvl] $msg" -ForegroundColor Red } else { Write-Host "[$lvl] $msg" } - } -} function Find-Python { param([string]$preferred) @@ -114,9 +117,6 @@ function Find-Python { return $null } -# Resolve OS detection in a broad-compatible way -try { $IsWindowsPlatform = [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows) } catch { $IsWindowsPlatform = $env:OS -match 'Windows' } - # operate from repo root (parent of scripts dir) $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $repoRoot = (Resolve-Path (Join-Path $scriptDir "..")).Path @@ -628,5 +628,5 @@ Write-Host " $ .\$VenvPath\Scripts\mm.exe (Windows) or" Write-Host " $ ./$VenvPath/bin/mm (Linux) or" Write-Host " $ $venvPython -m scripts.cli_entry" Write-Host "" -Write-Host "If the global 'mm' launcher fails, collect runtime diagnostics by setting MM_DEBUG and re-running the command:"" +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.py b/scripts/bootstrap.py index 9757c4c..64f5832 100644 --- a/scripts/bootstrap.py +++ b/scripts/bootstrap.py @@ -64,6 +64,18 @@ import time from typing import Optional +def _ensure_interactive_stdin() -> None: + """If stdin is piped (e.g. via curl | python), re-open it to the terminal.""" + if not sys.stdin.isatty(): + try: + if platform.system().lower() == "windows": + sys.stdin = open("CONIN$", "r") + else: + sys.stdin = open("/dev/tty", "r") + except Exception: + pass + + def run(cmd: list[str], quiet: bool = False, debug: bool = False, cwd: Optional[Path] = None) -> None: if debug: print(f"\n> {' '.join(cmd)}") @@ -476,6 +488,11 @@ def main() -> int: ) args = parser.parse_args() + # If stdout is a TTY but stdin is a pipe (e.g. curl | python), + # re-open stdin to the console so interactive prompts work. + if sys.stdout.isatty() and not args.quiet: + _ensure_interactive_stdin() + # Ensure repo_root is always the project root, not the current working directory # This prevents issues when bootstrap.py is run from different directories try: @@ -540,12 +557,14 @@ def main() -> int: if _is_valid_mm_repo(install_path): print(f"Found existing repository in {install_path}.") repo_root = install_path + is_in_repo = True else: print(f"Cloning Medeos-Macina into {install_path}...") print(f"Source: {REPO_URL}") try: subprocess.check_call(["git", "clone", REPO_URL, str(install_path)]) repo_root = install_path + is_in_repo = True except Exception as e: print(f"Error: Failed to clone repository: {e}", file=sys.stderr) return 1 @@ -966,7 +985,7 @@ def main() -> int: return 0 # If no specific action flag is passed and we're in a terminal, show the menu - if sys.stdin.isatty() and not args.quiet: + if (sys.stdin.isatty() or sys.stdout.isatty()) and not args.quiet: sel = _interactive_menu() if sel == "install": # user chose to install/reinstall; set defaults and continue