Files
ollama/scripts/install.ps1
Daniel Hiltgen de39e24bf7 win: progress reporting on install download (#14219)
* win: progress reporting on install download

Downloading Ollama...
  [####################################    ] 91%  1106.6 / 1204.2 MB

* review comments
2026-02-12 12:06:56 -08:00

324 lines
10 KiB
PowerShell

<#
.SYNOPSIS
Install, upgrade, or uninstall Ollama on Windows.
.DESCRIPTION
Downloads and installs Ollama.
Quick install:
irm https://ollama.com/install.ps1 | iex
Specific version:
$env:OLLAMA_VERSION="0.5.7"; irm https://ollama.com/install.ps1 | iex
Custom install directory:
$env:OLLAMA_INSTALL_DIR="D:\Ollama"; irm https://ollama.com/install.ps1 | iex
Uninstall:
$env:OLLAMA_UNINSTALL=1; irm https://ollama.com/install.ps1 | iex
Environment variables:
OLLAMA_VERSION Target version (default: latest stable)
OLLAMA_INSTALL_DIR Custom install directory
OLLAMA_UNINSTALL Set to 1 to uninstall Ollama
OLLAMA_DEBUG Enable verbose output
.EXAMPLE
irm https://ollama.com/install.ps1 | iex
.EXAMPLE
$env:OLLAMA_VERSION = "0.5.7"; irm https://ollama.com/install.ps1 | iex
.LINK
https://ollama.com
#>
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
# --------------------------------------------------------------------------
# Configuration from environment variables
# --------------------------------------------------------------------------
$Version = if ($env:OLLAMA_VERSION) { $env:OLLAMA_VERSION } else { "" }
$InstallDir = if ($env:OLLAMA_INSTALL_DIR) { $env:OLLAMA_INSTALL_DIR } else { "" }
$Uninstall = $env:OLLAMA_UNINSTALL -eq "1"
$DebugInstall = [bool]$env:OLLAMA_DEBUG
# --------------------------------------------------------------------------
# Constants
# --------------------------------------------------------------------------
# OLLAMA_DOWNLOAD_URL for developer testing only
$DownloadBaseURL = if ($env:OLLAMA_DOWNLOAD_URL) { $env:OLLAMA_DOWNLOAD_URL.TrimEnd('/') } else { "https://ollama.com/download" }
$InnoSetupUninstallGuid = "{44E83376-CE68-45EB-8FC1-393500EB558C}_is1"
# --------------------------------------------------------------------------
# Helpers
# --------------------------------------------------------------------------
function Write-Status {
param([string]$Message)
if ($DebugInstall) { Write-Host $Message }
}
function Write-Step {
param([string]$Message)
if ($DebugInstall) { Write-Host ">>> $Message" -ForegroundColor Cyan }
}
function Test-Signature {
param([string]$FilePath)
$sig = Get-AuthenticodeSignature -FilePath $FilePath
if ($sig.Status -ne "Valid") {
Write-Status " Signature status: $($sig.Status)"
return $false
}
# Verify it's signed by Ollama Inc. (check exact organization name)
# Anchor with comma/boundary to prevent "O=Not Ollama Inc." from matching
$subject = $sig.SignerCertificate.Subject
if ($subject -notmatch "(^|, )O=Ollama Inc\.(,|$)") {
Write-Status " Unexpected signer: $subject"
return $false
}
Write-Status " Signature valid: $subject"
return $true
}
function Find-InnoSetupInstall {
# Check both HKCU (per-user) and HKLM (per-machine) locations
$possibleKeys = @(
"HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid",
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid",
"HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$InnoSetupUninstallGuid"
)
foreach ($key in $possibleKeys) {
if (Test-Path $key) {
Write-Status " Found install at: $key"
return $key
}
}
return $null
}
function Update-SessionPath {
# Update PATH in current session so 'ollama' works immediately
if ($InstallDir) {
$ollamaDir = $InstallDir
} else {
$ollamaDir = Join-Path $env:LOCALAPPDATA "Programs\Ollama"
}
# Add to PATH if not already present
if (Test-Path $ollamaDir) {
$currentPath = $env:PATH -split ';'
if ($ollamaDir -notin $currentPath) {
$env:PATH = "$ollamaDir;$env:PATH"
Write-Status " Added $ollamaDir to session PATH"
}
}
}
function Invoke-Download {
param(
[string]$Url,
[string]$OutFile
)
Write-Status " Downloading: $Url"
try {
$request = [System.Net.HttpWebRequest]::Create($Url)
$request.AllowAutoRedirect = $true
$response = $request.GetResponse()
$totalBytes = $response.ContentLength
$stream = $response.GetResponseStream()
$fileStream = [System.IO.FileStream]::new($OutFile, [System.IO.FileMode]::Create)
$buffer = [byte[]]::new(65536)
$totalRead = 0
$lastUpdate = [DateTime]::MinValue
$barWidth = 40
try {
while (($read = $stream.Read($buffer, 0, $buffer.Length)) -gt 0) {
$fileStream.Write($buffer, 0, $read)
$totalRead += $read
$now = [DateTime]::UtcNow
if (($now - $lastUpdate).TotalMilliseconds -ge 250) {
if ($totalBytes -gt 0) {
$pct = [math]::Min(100.0, ($totalRead / $totalBytes) * 100)
$filled = [math]::Floor($barWidth * $pct / 100)
$empty = $barWidth - $filled
$bar = ('#' * $filled) + (' ' * $empty)
$pctFmt = $pct.ToString("0.0")
Write-Host -NoNewline "`r$bar ${pctFmt}%"
} else {
$sizeMB = [math]::Round($totalRead / 1MB, 1)
Write-Host -NoNewline "`r${sizeMB} MB downloaded..."
}
$lastUpdate = $now
}
}
# Final progress update
if ($totalBytes -gt 0) {
$bar = '#' * $barWidth
Write-Host "`r$bar 100.0%"
} else {
$sizeMB = [math]::Round($totalRead / 1MB, 1)
Write-Host "`r${sizeMB} MB downloaded. "
}
} finally {
$fileStream.Close()
$stream.Close()
$response.Close()
}
} catch {
if ($_.Exception -is [System.Net.WebException]) {
$webEx = [System.Net.WebException]$_.Exception
if ($webEx.Response -and ([System.Net.HttpWebResponse]$webEx.Response).StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
throw "Download failed: not found at $Url"
}
}
if ($_.Exception.InnerException -is [System.Net.WebException]) {
$webEx = [System.Net.WebException]$_.Exception.InnerException
if ($webEx.Response -and ([System.Net.HttpWebResponse]$webEx.Response).StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
throw "Download failed: not found at $Url"
}
}
throw "Download failed for ${Url}: $($_.Exception.Message)"
}
}
# --------------------------------------------------------------------------
# Uninstall
# --------------------------------------------------------------------------
function Invoke-Uninstall {
Write-Step "Uninstalling Ollama"
$regKey = Find-InnoSetupInstall
if (-not $regKey) {
Write-Host ">>> Ollama is not installed."
return
}
$uninstallString = (Get-ItemProperty -Path $regKey).UninstallString
if (-not $uninstallString) {
Write-Warning "No uninstall string found in registry"
return
}
# Strip quotes if present
$uninstallExe = $uninstallString -replace '"', ''
Write-Status " Uninstaller: $uninstallExe"
if (-not (Test-Path $uninstallExe)) {
Write-Warning "Uninstaller not found at: $uninstallExe"
return
}
Write-Host ">>> Launching uninstaller..."
# Run with GUI so user can choose whether to keep models
Start-Process -FilePath $uninstallExe -Wait
# Verify removal
if (Find-InnoSetupInstall) {
Write-Warning "Uninstall may not have completed"
} else {
Write-Host ">>> Ollama has been uninstalled."
}
}
# --------------------------------------------------------------------------
# Install
# --------------------------------------------------------------------------
function Invoke-Install {
# Determine installer URL
if ($Version) {
$installerUrl = "$DownloadBaseURL/OllamaSetup.exe?version=$Version"
} else {
$installerUrl = "$DownloadBaseURL/OllamaSetup.exe"
}
# Download installer
Write-Step "Downloading Ollama"
if (-not $DebugInstall) {
Write-Host ">>> Downloading Ollama for Windows..."
}
$tempInstaller = Join-Path $env:TEMP "OllamaSetup.exe"
Invoke-Download -Url $installerUrl -OutFile $tempInstaller
# Verify signature
Write-Step "Verifying signature"
if (-not (Test-Signature -FilePath $tempInstaller)) {
Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue
throw "Installer signature verification failed"
}
# Build installer arguments
$installerArgs = "/VERYSILENT /NORESTART /SUPPRESSMSGBOXES"
if ($InstallDir) {
$installerArgs += " /DIR=`"$InstallDir`""
}
Write-Status " Installer args: $installerArgs"
# Run installer
Write-Step "Installing Ollama"
if (-not $DebugInstall) {
Write-Host ">>> Installing Ollama..."
}
# Create upgrade marker so the app starts hidden
# The app checks for this file on startup and removes it after
$markerDir = Join-Path $env:LOCALAPPDATA "Ollama"
$markerFile = Join-Path $markerDir "upgraded"
if (-not (Test-Path $markerDir)) {
New-Item -ItemType Directory -Path $markerDir -Force | Out-Null
}
New-Item -ItemType File -Path $markerFile -Force | Out-Null
Write-Status " Created upgrade marker: $markerFile"
# Start installer and wait for just the installer process (not children)
# Using -Wait would wait for Ollama to exit too, which we don't want
$proc = Start-Process -FilePath $tempInstaller `
-ArgumentList $installerArgs `
-PassThru
$proc.WaitForExit()
if ($proc.ExitCode -ne 0) {
Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue
throw "Installation failed with exit code $($proc.ExitCode)"
}
# Cleanup
Remove-Item $tempInstaller -Force -ErrorAction SilentlyContinue
# Update PATH in current session so 'ollama' works immediately
Write-Step "Updating session PATH"
Update-SessionPath
Write-Host ">>> Install complete. Run 'ollama' from the command line."
}
# --------------------------------------------------------------------------
# Main
# --------------------------------------------------------------------------
if ($Uninstall) {
Invoke-Uninstall
} else {
Invoke-Install
}