mirror of
https://github.com/ollama/ollama.git
synced 2026-03-27 02:58:43 +07:00
win: add curl-style install script (#14178)
This adds a new powershell install script suitable for running via irm https://ollama.com/install.ps1 | iex If you download the script and run '-?' it reports basic usage information, as well as usage examples for common customization options. The script is signed as part of the release process to ensure it can run on a typically configured Windows system. This does not include doc updates - we can merge those after a release ships to avoid user confusion.
This commit is contained in:
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -337,6 +337,7 @@ jobs:
|
|||||||
name: bundles-windows
|
name: bundles-windows
|
||||||
path: |
|
path: |
|
||||||
dist/*.zip
|
dist/*.zip
|
||||||
|
dist/*.ps1
|
||||||
dist/OllamaSetup.exe
|
dist/OllamaSetup.exe
|
||||||
|
|
||||||
linux-build:
|
linux-build:
|
||||||
@@ -514,6 +515,9 @@ jobs:
|
|||||||
- name: Log dist contents
|
- name: Log dist contents
|
||||||
run: |
|
run: |
|
||||||
ls -l dist/
|
ls -l dist/
|
||||||
|
- name: Copy install scripts to dist
|
||||||
|
run: |
|
||||||
|
cp scripts/install.sh dist/install.sh
|
||||||
- name: Generate checksum file
|
- name: Generate checksum file
|
||||||
run: find . -type f -not -name 'sha256sum.txt' | xargs sha256sum | tee sha256sum.txt
|
run: find . -type f -not -name 'sha256sum.txt' | xargs sha256sum | tee sha256sum.txt
|
||||||
working-directory: dist
|
working-directory: dist
|
||||||
@@ -536,7 +540,7 @@ jobs:
|
|||||||
- name: Upload release artifacts
|
- name: Upload release artifacts
|
||||||
run: |
|
run: |
|
||||||
pids=()
|
pids=()
|
||||||
for payload in dist/*.txt dist/*.zip dist/*.tgz dist/*.tar.zst dist/*.exe dist/*.dmg ; do
|
for payload in dist/*.txt dist/*.zip dist/*.tgz dist/*.tar.zst dist/*.exe dist/*.dmg dist/*.ps1 dist/*.sh ; do
|
||||||
echo "Uploading $payload"
|
echo "Uploading $payload"
|
||||||
gh release upload ${GITHUB_REF_NAME} $payload --clobber &
|
gh release upload ${GITHUB_REF_NAME} $payload --clobber &
|
||||||
pids[$!]=$!
|
pids[$!]=$!
|
||||||
|
|||||||
@@ -302,12 +302,22 @@ function deps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sign {
|
function sign {
|
||||||
|
# Copy install.ps1 to dist for release packaging
|
||||||
|
write-host "Copying install.ps1 to dist"
|
||||||
|
Copy-Item -Path "${script:SRC_DIR}\scripts\install.ps1" -Destination "${script:SRC_DIR}\dist\install.ps1"
|
||||||
|
|
||||||
if ("${env:KEY_CONTAINER}") {
|
if ("${env:KEY_CONTAINER}") {
|
||||||
write-host "Signing Ollama executables, scripts and libraries"
|
write-host "Signing Ollama executables, scripts and libraries"
|
||||||
& "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
|
& "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
|
||||||
/csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} `
|
/csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} `
|
||||||
$(get-childitem -path "${script:SRC_DIR}\dist\windows-*" -r -include @('*.exe', '*.dll'))
|
$(get-childitem -path "${script:SRC_DIR}\dist\windows-*" -r -include @('*.exe', '*.dll'))
|
||||||
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
||||||
|
|
||||||
|
write-host "Signing install.ps1"
|
||||||
|
& "${script:SignTool}" sign /v /fd sha256 /t http://timestamp.digicert.com /f "${script:OLLAMA_CERT}" `
|
||||||
|
/csp "Google Cloud KMS Provider" /kc ${env:KEY_CONTAINER} `
|
||||||
|
"${script:SRC_DIR}\dist\install.ps1"
|
||||||
|
if ($LASTEXITCODE -ne 0) { exit($LASTEXITCODE)}
|
||||||
} else {
|
} else {
|
||||||
write-host "Signing not enabled"
|
write-host "Signing not enabled"
|
||||||
}
|
}
|
||||||
|
|||||||
271
scripts/install.ps1
Normal file
271
scripts/install.ps1
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
<#
|
||||||
|
.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 {
|
||||||
|
Invoke-WebRequest -Uri $Url -OutFile $OutFile -UseBasicParsing
|
||||||
|
$size = (Get-Item $OutFile).Length
|
||||||
|
Write-Status " Downloaded: $([math]::Round($size / 1MB, 1)) MB"
|
||||||
|
} catch {
|
||||||
|
if ($_.Exception.Response.StatusCode -eq 404) {
|
||||||
|
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..."
|
||||||
|
}
|
||||||
|
|
||||||
|
$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..."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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. You can now run 'ollama'."
|
||||||
|
}
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if ($Uninstall) {
|
||||||
|
Invoke-Uninstall
|
||||||
|
} else {
|
||||||
|
Invoke-Install
|
||||||
|
}
|
||||||
@@ -35,6 +35,8 @@ case "$ARCH" in
|
|||||||
*) error "Unsupported architecture: $ARCH" ;;
|
*) error "Unsupported architecture: $ARCH" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
VER_PARAM="${OLLAMA_VERSION:+?version=$OLLAMA_VERSION}"
|
||||||
|
|
||||||
###########################################
|
###########################################
|
||||||
# macOS
|
# macOS
|
||||||
###########################################
|
###########################################
|
||||||
@@ -49,11 +51,7 @@ if [ "$OS" = "Darwin" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${OLLAMA_VERSION:-}" ]; then
|
DOWNLOAD_URL="https://ollama.com/download/Ollama-darwin.zip${VER_PARAM}"
|
||||||
DOWNLOAD_URL="https://github.com/ollama/ollama/releases/download/${OLLAMA_VERSION}/Ollama-darwin.zip"
|
|
||||||
else
|
|
||||||
DOWNLOAD_URL="https://github.com/ollama/ollama/releases/latest/download/Ollama-darwin.zip"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if pgrep -x Ollama >/dev/null 2>&1; then
|
if pgrep -x Ollama >/dev/null 2>&1; then
|
||||||
status "Stopping running Ollama instance..."
|
status "Stopping running Ollama instance..."
|
||||||
@@ -103,8 +101,6 @@ case "$KERN" in
|
|||||||
*) ;;
|
*) ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
VER_PARAM="${OLLAMA_VERSION:+?version=$OLLAMA_VERSION}"
|
|
||||||
|
|
||||||
SUDO=
|
SUDO=
|
||||||
if [ "$(id -u)" -ne 0 ]; then
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
# Running as root, no need for sudo
|
# Running as root, no need for sudo
|
||||||
|
|||||||
Reference in New Issue
Block a user