Created
January 8, 2026 14:47
-
-
Save venetanji/1eb549705d2a8077b8a706b0025858c8 to your computer and use it in GitHub Desktop.
Powershell script to install comfyui with cuda
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| $ErrorActionPreference = 'Stop' | |
| function Write-Section([string]$Message) { | |
| Write-Host "\n== $Message ==" -ForegroundColor Cyan | |
| } | |
| function Refresh-Path { | |
| $machinePath = [System.Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| $userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User') | |
| $env:Path = "$machinePath;$userPath" | |
| } | |
| function Ensure-Command([string]$Name, [string]$InstallHint) { | |
| if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) { | |
| throw "Required command '$Name' not found. $InstallHint" | |
| } | |
| } | |
| function Ensure-Uv { | |
| if (Get-Command uv -ErrorAction SilentlyContinue) { | |
| return | |
| } | |
| Write-Section "Installing uv (Astral) via winget" | |
| Ensure-Command winget "Install App Installer / winget, then re-run this script." | |
| winget install -e --id Astral-Sh.uv | |
| Refresh-Path | |
| Ensure-Command uv "uv install appears to have failed; open a new terminal and re-run." | |
| } | |
| function Ensure-Repo([string]$RepoUrl, [string]$TargetDir) { | |
| Ensure-Command git "Install Git (e.g. winget install -e --id Git.Git), then re-run." | |
| if (Test-Path -LiteralPath $TargetDir) { | |
| $doPull = Read-Host "Repo folder exists at '$TargetDir'. Run 'git pull' there? (y/N)" | |
| if ($doPull -match '^(y|yes)$') { | |
| Push-Location $TargetDir | |
| try { | |
| git pull | |
| } finally { | |
| Pop-Location | |
| } | |
| } | |
| return | |
| } | |
| Write-Section "Cloning ComfyUI" | |
| git clone $RepoUrl $TargetDir | |
| } | |
| function Ensure-UvVenv([string]$RepoDir, [string]$VenvDirName = '.venv') { | |
| $venvDir = Join-Path $RepoDir $VenvDirName | |
| $pythonExe = Join-Path $venvDir 'Scripts\\python.exe' | |
| function Get-PreferredPythonSpec { | |
| # Prefer versions that typically have PyTorch wheels first. | |
| $candidates = @('3.12', '3.11', '3.10') | |
| $pyLauncher = Get-Command py -ErrorAction SilentlyContinue | |
| if (-not $pyLauncher) { | |
| return $null | |
| } | |
| foreach ($spec in $candidates) { | |
| try { | |
| & $pyLauncher.Source "-$spec" -c "import sys; print(sys.version)" 1>$null 2>$null | |
| if ($LASTEXITCODE -eq 0) { | |
| return $spec | |
| } | |
| } catch { | |
| # ignore | |
| } | |
| } | |
| return $null | |
| } | |
| function New-Venv([string]$PythonSpec) { | |
| Write-Section "Creating uv venv ($VenvDirName)" | |
| Push-Location $RepoDir | |
| try { | |
| if ($PythonSpec) { | |
| Write-Host "Using Python: $PythonSpec" -ForegroundColor Gray | |
| uv venv $VenvDirName --python $PythonSpec | |
| } else { | |
| uv venv $VenvDirName | |
| } | |
| } finally { | |
| Pop-Location | |
| } | |
| } | |
| if (-not (Test-Path -LiteralPath $venvDir)) { | |
| $preferred = Get-PreferredPythonSpec | |
| New-Venv -PythonSpec $preferred | |
| } | |
| if (-not (Test-Path -LiteralPath $pythonExe)) { | |
| throw "Expected venv python not found at '$pythonExe'." | |
| } | |
| # If the existing venv uses a Python version that likely lacks PyTorch wheels (e.g. 3.13), offer to recreate. | |
| try { | |
| $verText = & $pythonExe --version 2>$null | |
| if ($verText -match 'Python\s+([0-9]+)\.([0-9]+)') { | |
| $maj = [int]$Matches[1] | |
| $min = [int]$Matches[2] | |
| if ($maj -ge 3 -and $min -ge 13) { | |
| $preferred = Get-PreferredPythonSpec | |
| if ($preferred) { | |
| $recreate = Read-Host "Venv is using Python $maj.$min. PyTorch may not be available for this version. Recreate venv with Python $preferred? (y/N)" | |
| if ($recreate -match '^(y|yes)$') { | |
| Remove-Item -LiteralPath $venvDir -Recurse -Force | |
| New-Venv -PythonSpec $preferred | |
| } | |
| } | |
| } | |
| } | |
| } catch { | |
| # ignore | |
| } | |
| return $pythonExe | |
| } | |
| function Get-CudaInfo { | |
| $toolkitRoot = 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA' | |
| $toolkitVersions = @() | |
| if (Test-Path -LiteralPath $toolkitRoot) { | |
| $toolkitVersions = Get-ChildItem -LiteralPath $toolkitRoot -Directory -ErrorAction SilentlyContinue | | |
| Where-Object { $_.Name -match '^v\d+(\.\d+)?$' } | | |
| Select-Object -ExpandProperty Name | |
| } | |
| $nvidiaSmi = Get-Command nvidia-smi -ErrorAction SilentlyContinue | |
| $nvcc = Get-Command nvcc -ErrorAction SilentlyContinue | |
| $nvidiaSmiCuda = $null | |
| $nvidiaSmiDriver = $null | |
| if ($nvidiaSmi) { | |
| try { | |
| $out = & $nvidiaSmi.Source 2>$null | Out-String | |
| if ($out -match 'CUDA Version:\s*([0-9]+\.[0-9]+)') { | |
| $nvidiaSmiCuda = $Matches[1] | |
| } | |
| if ($out -match 'Driver Version:\s*([0-9]+\.[0-9]+)') { | |
| $nvidiaSmiDriver = $Matches[1] | |
| } | |
| } catch { | |
| # ignore | |
| } | |
| } | |
| $nvccCuda = $null | |
| if ($nvcc) { | |
| try { | |
| $out = & $nvcc.Source --version 2>$null | Out-String | |
| if ($out -match 'release\s+([0-9]+\.[0-9]+)') { | |
| $nvccCuda = $Matches[1] | |
| } | |
| } catch { | |
| # ignore | |
| } | |
| } | |
| [pscustomobject]@{ | |
| HasNvidiaSmi = [bool]$nvidiaSmi | |
| HasNvcc = [bool]$nvcc | |
| NvidiaSmiCudaVersion = $nvidiaSmiCuda | |
| NvidiaSmiDriverVersion = $nvidiaSmiDriver | |
| NvccCudaVersion = $nvccCuda | |
| InstalledToolkitVersions = $toolkitVersions | |
| CudaPath = $env:CUDA_PATH | |
| } | |
| } | |
| function Get-LatestCudaVersionMajorMinor([string]$Fallback = '13.1') { | |
| # Best-effort: NVIDIA hosts a directory listing with redistrib_<version>.json files. | |
| # If this fetch fails, fall back to the provided version. | |
| try { | |
| $ProgressPreference = 'SilentlyContinue' | |
| $html = (Invoke-WebRequest -UseBasicParsing -Uri 'https://developer.download.nvidia.com/compute/cuda/redist/' -TimeoutSec 15).Content | |
| $versions = [regex]::Matches($html, 'redistrib_([0-9]+\.[0-9]+\.[0-9]+)\.json') | | |
| ForEach-Object { $_.Groups[1].Value } | | |
| Sort-Object -Unique | |
| if (-not $versions -or $versions.Count -eq 0) { | |
| return $Fallback | |
| } | |
| $latest = $versions | Sort-Object {[version]$_} -Descending | Select-Object -First 1 | |
| $parts = $latest.Split('.') | |
| if ($parts.Length -ge 2) { | |
| return "$($parts[0]).$($parts[1])" | |
| } | |
| return $Fallback | |
| } catch { | |
| return $Fallback | |
| } | |
| } | |
| function Install-Torch([string]$PythonExe, [string]$CudaMajorMinor, [bool]$HasNvidiaSmi) { | |
| Write-Section "Installing PyTorch" | |
| $defaultChoice = if ($HasNvidiaSmi -or $CudaMajorMinor) { 'cuda' } else { 'cpu' } | |
| $choice = Read-Host "Install PyTorch for 'cuda' or 'cpu'? (default: $defaultChoice)" | |
| if ([string]::IsNullOrWhiteSpace($choice)) { | |
| $choice = $defaultChoice | |
| } | |
| $choice = $choice.Trim().ToLowerInvariant() | |
| if ($choice -eq 'cpu') { | |
| uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --python $PythonExe | |
| return | |
| } | |
| function Test-HttpOk([string]$Url) { | |
| try { | |
| $ProgressPreference = 'SilentlyContinue' | |
| Invoke-WebRequest -UseBasicParsing -Uri $Url -Method Head -TimeoutSec 15 | Out-Null | |
| return $true | |
| } catch { | |
| return $false | |
| } | |
| } | |
| # IMPORTANT: PyTorch CUDA wheel indexes (cuXXX) do NOT necessarily exist for the newest CUDA Toolkit | |
| # version (e.g., CUDA 13.1 -> there is no cu131 index at the time of writing). | |
| # The safest approach is to pick the newest published cuXXX index. | |
| $candidates = @('cu130', 'cu128', 'cu126', 'cu124', 'cu121', 'cu118') | |
| $available = @() | |
| foreach ($c in $candidates) { | |
| $u = "https://download.pytorch.org/whl/$c" | |
| if (Test-HttpOk $u) { | |
| $available += $c | |
| } | |
| } | |
| $auto = if ($available.Count -gt 0) { $available[0] } else { $null } | |
| if ($CudaMajorMinor) { | |
| Write-Host "Detected CUDA (driver/toolkit): $CudaMajorMinor" -ForegroundColor Gray | |
| } | |
| if ($auto) { | |
| Write-Host "Auto-selected PyTorch CUDA wheels: $auto" -ForegroundColor Yellow | |
| } else { | |
| Write-Host "Could not probe a PyTorch cuXXX index-url (network issue?)" -ForegroundColor Yellow | |
| } | |
| $hint = if ($auto) { "(default: $auto)" } else { "(examples: cu128, cu126, cpu)" } | |
| $build = Read-Host "Torch build to install: type 'cpu' or a cuXXX (e.g. cu128) $hint" | |
| if ([string]::IsNullOrWhiteSpace($build)) { | |
| $build = $auto | |
| } | |
| $build = $build.Trim().ToLowerInvariant() | |
| if ($build -eq 'cpu') { | |
| uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu --python $PythonExe | |
| return | |
| } | |
| if (-not $build -or $build -notmatch '^cu\d{3}$') { | |
| throw "Invalid torch build '$build'. Use 'cpu' or something like 'cu128'." | |
| } | |
| $indexUrl = "https://download.pytorch.org/whl/$build" | |
| Write-Host "Using PyTorch index-url: $indexUrl" -ForegroundColor Yellow | |
| uv pip install torch torchvision torchaudio --index-url $indexUrl --python $PythonExe | |
| } | |
| function Install-ComfyDeps([string]$RepoDir, [string]$PythonExe) { | |
| Write-Section "Installing ComfyUI requirements" | |
| Push-Location $RepoDir | |
| try { | |
| uv pip install -r requirements.txt --python $PythonExe | |
| } finally { | |
| Pop-Location | |
| } | |
| Write-Section "Installing comfyui_manager (pre-release)" | |
| uv pip install --pre comfyui_manager --python $PythonExe | |
| } | |
| Write-Section "Prereqs" | |
| Ensure-Uv | |
| Refresh-Path | |
| $repoUrl = 'https://github.com/Comfy-Org/ComfyUI' | |
| $repoDir = Join-Path $PSScriptRoot 'ComfyUI' | |
| Ensure-Repo -RepoUrl $repoUrl -TargetDir $repoDir | |
| $pythonExe = Ensure-UvVenv -RepoDir $repoDir | |
| Write-Section "CUDA detection" | |
| $cuda = Get-CudaInfo | |
| Write-Host "nvidia-smi present: $($cuda.HasNvidiaSmi)" -ForegroundColor Gray | |
| Write-Host "nvcc present: $($cuda.HasNvcc)" -ForegroundColor Gray | |
| Write-Host "CUDA via nvidia-smi: $($cuda.NvidiaSmiCudaVersion)" -ForegroundColor Gray | |
| Write-Host "Driver version: $($cuda.NvidiaSmiDriverVersion)" -ForegroundColor Gray | |
| Write-Host "CUDA via nvcc: $($cuda.NvccCudaVersion)" -ForegroundColor Gray | |
| Write-Host "CUDA_PATH: $($cuda.CudaPath)" -ForegroundColor Gray | |
| Write-Host "Toolkit folders: $([string]::Join(', ', $cuda.InstalledToolkitVersions))" -ForegroundColor Gray | |
| $latestCuda = Get-LatestCudaVersionMajorMinor -Fallback '13.1' | |
| Write-Host "Latest CUDA (best-effort): $latestCuda" -ForegroundColor Yellow | |
| $openCuda = Read-Host "Open CUDA Toolkit download page in browser? (y/N)" | |
| if ($openCuda -match '^(y|yes)$') { | |
| Start-Process 'https://developer.nvidia.com/cuda-downloads' | |
| } | |
| # Prefer an installed toolkit version (highest) or fall back to detected runtime / latest. | |
| $installedToolkitMajorMinor = $null | |
| if ($cuda.InstalledToolkitVersions -and $cuda.InstalledToolkitVersions.Count -gt 0) { | |
| $installedToolkitMajorMinor = $cuda.InstalledToolkitVersions | | |
| ForEach-Object { $_ -replace '^v', '' } | | |
| Sort-Object {[version]$_} -Descending | | |
| Select-Object -First 1 | |
| } | |
| $cudaForTorch = $installedToolkitMajorMinor | |
| if (-not $cudaForTorch) { $cudaForTorch = $cuda.NvccCudaVersion } | |
| if (-not $cudaForTorch) { $cudaForTorch = $cuda.NvidiaSmiCudaVersion } | |
| if (-not $cudaForTorch) { $cudaForTorch = $latestCuda } | |
| Install-Torch -PythonExe $pythonExe -CudaMajorMinor $cudaForTorch -HasNvidiaSmi $cuda.HasNvidiaSmi | |
| Install-ComfyDeps -RepoDir $repoDir -PythonExe $pythonExe | |
| Write-Host "\nDone." -ForegroundColor Green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment