owpengram-server/scripts/install-prereqs.ps1
2026-09-11 16:19:13 +03:00

185 lines
7.8 KiB
PowerShell

#Requires -Version 5.1
<#
.SYNOPSIS
Installs everything owpengram-server.bat checks for, via winget: Go, Python 3
(+ the panel's packages in a venv) and OpenSSL.
.DESCRIPTION
Docker is the deliberate exception. It is only reported, with a link: on Windows
the containers this server needs (PostgreSQL, Redis, MinIO) are Linux images, so
the daemon has to sit on a Linux kernel -- which means Docker Desktop with WSL2,
an install that wants a reboot and carries its own licence terms. That is not a
thing to start behind someone's back.
.PARAMETER DryRun
Only report what would be installed.
.PARAMETER Yes
Skip the confirmation prompt.
#>
param(
[switch]$DryRun,
[switch]$Yes
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$DockerDocs = 'https://docs.docker.com/desktop/setup/install/windows-install/'
$GoMinMinor = 25
function Write-Ok { param([string]$Text) Write-Host "[ok] $Text" -ForegroundColor Green }
function Write-Info { param([string]$Text) Write-Host "[..] $Text" -ForegroundColor Cyan }
function Write-Warn { param([string]$Text) Write-Host "[WARN] $Text" -ForegroundColor Yellow }
function Write-Err { param([string]$Text) Write-Host "[ERROR] $Text" -ForegroundColor Red }
function Test-Command { param([string]$Name) [bool](Get-Command $Name -ErrorAction SilentlyContinue) }
# winget puts new tools on the machine/user PATH, but this process was started
# with the old one -- without this the venv step cannot find the Python it just
# installed, and the closing "go version" would report the absence of Go.
function Update-PathFromRegistry {
$machine = [Environment]::GetEnvironmentVariable('Path', 'Machine')
$user = [Environment]::GetEnvironmentVariable('Path', 'User')
$env:Path = (@($machine, $user) | Where-Object { $_ }) -join ';'
}
# python.exe on a stock Windows is often the Microsoft Store's app-execution
# alias: present on PATH, and it opens the Store instead of running anything.
# Only a real version string counts, which is the same trap owpengram-server.bat
# documents.
function Get-WorkingPython {
foreach ($candidate in @('py -3', 'python3', 'python')) {
$parts = $candidate.Split(' ')
if (-not (Test-Command $parts[0])) { continue }
try {
$out = & $parts[0] @($parts[1..($parts.Length - 1)] + '--version') 2>&1 | Out-String
} catch { continue }
if ($out -match 'Python 3\.') { return $candidate }
}
return $null
}
function Test-GoRecentEnough {
if (-not (Test-Command 'go')) { return $false }
try { $version = (& go env GOVERSION 2>$null | Out-String).Trim() } catch { return $false }
if ($version -match '^go1\.(\d+)') { return [int]$Matches[1] -ge $GoMinMinor }
return $false
}
# Same precedence owpengram-server.bat uses when it picks an interpreter: the
# venv when one exists, otherwise whatever Python is on PATH. Checking only the
# venv would report the packages missing on a machine that already has them
# installed system-wide, and build a venv nobody asked for.
function Test-PanelDepsInstalled {
$checker = Join-Path $RepoRoot 'tui-panel\check_deps.py'
$venvPython = Join-Path $RepoRoot '.venv\Scripts\python.exe'
if (Test-Path $venvPython) {
$missing = & $venvPython $checker 2>$null | Out-String
return [string]::IsNullOrWhiteSpace($missing)
}
$fallback = Get-WorkingPython
if (-not $fallback) { return $false }
$parts = $fallback.Split(' ')
$missing = & $parts[0] @($parts[1..($parts.Length - 1)] + $checker) 2>$null | Out-String
return [string]::IsNullOrWhiteSpace($missing)
}
function Install-WingetPackage {
param([string]$Id, [string]$Label)
Write-Info "Installing $Label ($Id)"
& winget install --exact --id $Id --source winget `
--accept-package-agreements --accept-source-agreements --disable-interactivity
# 0 is installed; -1978335189 is "no applicable upgrade", i.e. already current.
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne -1978335189) {
throw "winget failed to install $Id (exit $LASTEXITCODE)"
}
Update-PathFromRegistry
}
if (-not (Test-Command 'winget')) {
Write-Err 'winget is not available. Install "App Installer" from the Microsoft Store, then run this again.'
exit 1
}
# --- what is missing ---------------------------------------------------------
$python = Get-WorkingPython
$needed = [System.Collections.Generic.List[string]]::new()
if (-not (Test-GoRecentEnough)) { $needed.Add('go') }
if (-not $python) { $needed.Add('python') }
if (-not (Test-Command 'openssl')) { $needed.Add('openssl') }
if (-not (Test-PanelDepsInstalled)) { $needed.Add('pydeps') }
$dockerMissing = -not (Test-Command 'docker')
if ($needed.Count -eq 0 -and -not $dockerMissing) {
Write-Ok 'All prerequisites are already installed.'
exit 0
}
Write-Host ''
Write-Host '== Missing prerequisites ==' -ForegroundColor Yellow
foreach ($item in $needed) {
switch ($item) {
'go' { Write-Host " - Go 1.$GoMinMinor+ (builds owpengram-server and the admin panel)" }
'python' { Write-Host ' - Python 3 (runs the server-panel TUI)' }
'pydeps' { Write-Host ' - Python packages: textual, psutil, cryptography (into .\.venv)' }
'openssl' { Write-Host " - OpenSSL (exports the server's RSA public key for clients)" }
}
}
if ($dockerMissing) { Write-Host ' - Docker (runs PostgreSQL, Redis and MinIO) -- install this one yourself, see below' }
Write-Host ''
if ($DryRun) {
Write-Ok 'Dry run -- nothing was installed.'
exit 0
}
if ($needed.Count -gt 0 -and -not $Yes) {
$answer = Read-Host 'Install these now? [Y/n]'
if ($answer -and $answer -notmatch '^(y|yes)$') {
Write-Err 'cancelled'
exit 1
}
}
if ($needed -contains 'go') { Install-WingetPackage -Id 'GoLang.Go' -Label 'Go' }
if ($needed -contains 'python') { Install-WingetPackage -Id 'Python.Python.3.13' -Label 'Python 3.13' }
if ($needed -contains 'openssl') { Install-WingetPackage -Id 'ShiningLight.OpenSSL.Light' -Label 'OpenSSL' }
# --- the panel's Python packages ---------------------------------------------
# A venv rather than the machine-wide interpreter, to match what the Linux side
# does and what owpengram-server.bat already prefers once one exists.
if ($needed -contains 'pydeps' -or $needed -contains 'python') {
$python = Get-WorkingPython
if (-not $python) {
Write-Err 'Python still is not callable. Open a new terminal and run this script again.'
exit 1
}
Write-Info 'Installing the panel''s Python packages into .\.venv'
$venvPython = Join-Path $RepoRoot '.venv\Scripts\python.exe'
if (-not (Test-Path $venvPython)) {
$parts = $python.Split(' ')
& $parts[0] @($parts[1..($parts.Length - 1)] + @('-m', 'venv', (Join-Path $RepoRoot '.venv')))
if ($LASTEXITCODE -ne 0) { throw 'could not create .venv' }
}
& $venvPython -m pip install --quiet --upgrade pip
& $venvPython -m pip install --quiet -r (Join-Path $RepoRoot 'tui-panel\requirements-panel.txt')
if ($LASTEXITCODE -ne 0) { throw 'could not install the panel''s Python packages' }
Write-Ok 'Python packages installed'
}
Write-Host ''
if ($dockerMissing) {
Write-Warn 'Docker still has to be installed by hand.'
Write-Host " The server's PostgreSQL, Redis and MinIO are Linux containers, so Windows"
Write-Host ' needs Docker Desktop (it brings the WSL2 backend that runs them).'
Write-Host " $DockerDocs"
Write-Host ' Install it, reboot if it asks, then run this script again.'
Write-Host ''
}
if ($needed.Count -gt 0) {
Write-Ok 'Prerequisites are ready.'
Write-Host ' Open a new terminal so the updated PATH is picked up, then start the server with:'
Write-Host ' owpengram-server.bat'
}