From 4224b9d15c08fde1b3f4c1396f0cf1a2ab3eef5f Mon Sep 17 00:00:00 2001 From: onysd Date: Fri, 11 Sep 2026 16:19:13 +0300 Subject: [PATCH] updated server install scripts --- README.md | 10 ++ owpengram-server.bat | 34 ++++- owpengram-server.sh | 23 ++- scripts/install-prereqs.ps1 | 185 ++++++++++++++++++++++ scripts/install-prereqs.sh | 296 ++++++++++++++++++++++++++++++++++++ 5 files changed, 544 insertions(+), 4 deletions(-) create mode 100644 scripts/install-prereqs.ps1 create mode 100755 scripts/install-prereqs.sh diff --git a/README.md b/README.md index 8c1a60a9..3b3c88bd 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,16 @@ re-run commands from scratch every time. Both launchers check prerequisites first (Go, Python 3, and the panel's own dependencies via `tui-panel/requirements-panel.txt`), then start the panel. +Anything missing is installed for you rather than listed: the launcher hands off +to `scripts/install-prereqs.sh` (Arch and Ubuntu/Debian — asks for root once, +then installs Go, Python, Docker and OpenSSL) or `scripts/install-prereqs.ps1` +(Windows, via winget). Run either directly with `--dry-run` to see what it would +install without touching anything. + +Docker on Windows is the one exception: its containers are Linux images, so the +daemon needs Docker Desktop's WSL2 backend — an install with a reboot and its own +licence terms. The script reports it with a link instead of starting it. + What it does: - 🧙 **First-run setup wizard** — walks through the required `.env` values diff --git a/owpengram-server.bat b/owpengram-server.bat index daa995af..7e7268e9 100644 --- a/owpengram-server.bat +++ b/owpengram-server.bat @@ -71,10 +71,38 @@ if defined PYTHON ( ) if "%PROBLEMS%"=="1" ( + rem Hand off to the winget installer rather than stopping at a shopping list. + rem OWPENGRAM_PREREQS_TRIED bounds this to a single retry, so something that + rem will not install ends in a message instead of a loop. + if defined OWPENGRAM_PREREQS_TRIED ( + echo. + echo [ERROR] prerequisites are still missing after the install attempt -- see the messages above + pause + exit /b 1 + ) + if not exist "scripts\install-prereqs.ps1" ( + echo. + echo [ERROR] missing prerequisites above -- install them and re-run this script + pause + exit /b 1 + ) echo. - echo [ERROR] missing prerequisites above -- install them and re-run this script - pause - exit /b 1 + echo == Installing the missing prerequisites == + powershell -NoProfile -ExecutionPolicy Bypass -File "scripts\install-prereqs.ps1" + if errorlevel 1 ( + echo. + echo [ERROR] could not install the prerequisites -- see the messages above + pause + exit /b 1 + ) + rem winget writes the new PATH to the registry, but this console still holds + rem the one it started with -- reload it so the re-check below can see what + rem was just installed instead of asking for a fresh terminal. + for /f "usebackq delims=" %%p in (`powershell -NoProfile -Command "[Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User')"`) do set "PATH=%%p" + set "OWPENGRAM_PREREQS_TRIED=1" + echo. + call "%~f0" %* + exit /b !errorlevel! ) echo. diff --git a/owpengram-server.sh b/owpengram-server.sh index b17ace28..5baa3e6c 100755 --- a/owpengram-server.sh +++ b/owpengram-server.sh @@ -86,7 +86,28 @@ fi if [[ "$PROBLEMS" -ne 0 ]]; then echo - die "missing prerequisites above -- install them and re-run this script" + # Hand off to the installer rather than stopping at a shopping list. It asks + # for confirmation once, takes root once, and installs only what is missing. + # The re-exec is what makes the freshly installed tools count: this shell + # resolved `go`/`python3` before any of them existed. OWPENGRAM_PREREQS_TRIED + # bounds it to a single retry, so a package that still will not install ends + # in a message instead of a loop. + if [[ -n "${OWPENGRAM_PREREQS_TRIED:-}" ]]; then + die "prerequisites are still missing after the install attempt -- see the messages above" + fi + if [[ ! -x scripts/install-prereqs.sh ]]; then + die "missing prerequisites above -- install them and re-run this script" + fi + echo "== Installing the missing prerequisites ==" + if ! scripts/install-prereqs.sh; then + die "could not install the prerequisites -- see the messages above" + fi + # /etc/profile.d/go.sh only applies to shells started later, and the venv is + # not on PATH at all; both are picked up by the re-exec below because the + # re-check looks for .venv/bin/python first. + [[ -d /usr/local/go/bin ]] && export PATH="$PATH:/usr/local/go/bin" + echo + OWPENGRAM_PREREQS_TRIED=1 exec "$0" "$@" fi echo diff --git a/scripts/install-prereqs.ps1 b/scripts/install-prereqs.ps1 new file mode 100644 index 00000000..2c19c826 --- /dev/null +++ b/scripts/install-prereqs.ps1 @@ -0,0 +1,185 @@ +#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' +} diff --git a/scripts/install-prereqs.sh b/scripts/install-prereqs.sh new file mode 100755 index 00000000..101e4204 --- /dev/null +++ b/scripts/install-prereqs.sh @@ -0,0 +1,296 @@ +#!/usr/bin/env bash +# Installs everything owpengram-server.sh checks for, so a fresh machine needs +# one command instead of a shopping list: Go, Python 3 (+ the panel's packages +# in a venv), Docker and OpenSSL. +# +# ./scripts/install-prereqs.sh install whatever is missing +# ./scripts/install-prereqs.sh --dry-run only report what it would install +# ./scripts/install-prereqs.sh --yes no confirmation prompt +# +# Supported: Arch (pacman) and Ubuntu/Debian (apt). Anything else is reported +# rather than guessed at -- a wrong package manager on a production box is a +# worse outcome than a clear "install these yourself". +# +# Root is taken once, up front, and held for the whole run: a sudo prompt +# appearing ten minutes in, after the user walked away, is how half-installed +# machines happen. +set -euo pipefail +cd "$(dirname "$0")/.." +REPO_ROOT="$PWD" + +ok() { printf '[ok] %s\n' "$*"; } +info() { printf '[..] %s\n' "$*"; } +warn() { printf '[WARN] %s\n' "$*"; } +die() { printf '[ERROR] %s\n' "$*" >&2; exit 1; } + +DRY_RUN=0 +ASSUME_YES=0 +for arg in "$@"; do + case "$arg" in + --dry-run) DRY_RUN=1 ;; + --yes|-y) ASSUME_YES=1 ;; + -h|--help) sed -n '2,14p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) die "unknown argument: $arg" ;; + esac +done + +# --- distro ------------------------------------------------------------------ +# ID_LIKE is what makes derivatives work without listing every one of them: +# EndeavourOS says ID_LIKE=arch, Pop!_OS and Mint say ID_LIKE=ubuntu/debian. +FAMILY="" +DISTRO_NAME="unknown" +if [[ -r /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + DISTRO_NAME="${PRETTY_NAME:-$ID}" + case " ${ID:-} ${ID_LIKE:-} " in + *" arch "*) FAMILY="arch" ;; + *" ubuntu "*|*" debian "*) FAMILY="debian" ;; + esac +fi +[[ -n "$FAMILY" ]] || die "unsupported distribution: ${DISTRO_NAME}. Supported: Arch and Ubuntu/Debian. Install Go 1.25+, Python 3, Docker and OpenSSL by hand, then run ./owpengram-server.sh" + +# --- what is missing --------------------------------------------------------- +have() { command -v "$1" >/dev/null 2>&1; } + +# Go's own version gate: go.mod asks for 1.25, and a too-old toolchain fails at +# build time with a message that does not obviously point back here. +GO_MIN_MINOR=25 +go_is_recent_enough() { + have go || return 1 + local v minor + v="$(go env GOVERSION 2>/dev/null || true)" # e.g. go1.25.7 + minor="${v#go1.}" + minor="${minor%%.*}" + [[ "$minor" =~ ^[0-9]+$ ]] && (( minor >= GO_MIN_MINOR )) +} + +venv_python() { printf '%s/.venv/bin/python' "$REPO_ROOT"; } + +# Same precedence owpengram-server.sh uses when it picks an interpreter: the +# venv when one exists, otherwise whatever python3 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. +pydeps_satisfied() { + local py + if [[ -x "$(venv_python)" ]]; then + py="$(venv_python)" + elif have python3; then + py="python3" + else + return 1 + fi + [[ -z "$("$py" tui-panel/check_deps.py 2>/dev/null)" ]] +} + +NEEDED=() +go_is_recent_enough || NEEDED+=("go") +have python3 || NEEDED+=("python") +have docker || NEEDED+=("docker") +have openssl || NEEDED+=("openssl") +# The venv is built with python3, so it can only be settled after Python is. +pydeps_satisfied || NEEDED+=("pydeps") + +if [[ ${#NEEDED[@]} -eq 0 ]]; then + ok "All prerequisites are already installed." + exit 0 +fi + +echo "== Missing prerequisites on ${DISTRO_NAME} ==" +for item in "${NEEDED[@]}"; do + case "$item" in + go) echo " - Go 1.${GO_MIN_MINOR}+ (builds owpengram-server and the admin panel)" ;; + python) echo " - Python 3 (runs the server-panel TUI)" ;; + pydeps) echo " - Python packages: textual, psutil, cryptography (into ./.venv)" ;; + docker) echo " - Docker (runs PostgreSQL, Redis and MinIO)" ;; + openssl) echo " - OpenSSL (exports the server's RSA public key for clients)" ;; + esac +done +echo + +if [[ "$DRY_RUN" -eq 1 ]]; then + ok "Dry run -- nothing was installed." + exit 0 +fi + +if [[ "$ASSUME_YES" -ne 1 ]]; then + read -r -p "Install these now? This needs root. [Y/n] " answer + case "${answer:-y}" in + [Yy]|[Yy][Ee][Ss]|"") ;; + *) die "cancelled" ;; + esac +fi + +# Ask for the password once, then refresh the timestamp in the background so a +# long Go download or apt run does not hit a second prompt mid-way. +if [[ $EUID -ne 0 ]]; then + have sudo || die "sudo is not installed and this is not running as root" + sudo -v || die "could not acquire root" + while true; do sudo -n true 2>/dev/null; sleep 50; done & + SUDO_KEEPALIVE=$! + trap 'kill "$SUDO_KEEPALIVE" 2>/dev/null || true' EXIT + SUDO="sudo" +else + SUDO="" +fi + +needs() { local x; for x in "${NEEDED[@]}"; do [[ "$x" == "$1" ]] && return 0; done; return 1; } + +# --- package manager --------------------------------------------------------- +APT_UPDATED=0 +apt_update_once() { + if [[ "$APT_UPDATED" -eq 0 ]]; then + $SUDO apt-get update -qq + APT_UPDATED=1 + fi +} +pkg_install() { + case "$FAMILY" in + arch) $SUDO pacman -S --needed --noconfirm "$@" ;; + debian) apt_update_once; $SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "$@" ;; + esac +} + +# --- Python + OpenSSL -------------------------------------------------------- +if needs python; then + info "Installing Python 3" + case "$FAMILY" in + arch) pkg_install python ;; + # python3-venv is separate on Debian/Ubuntu and is what the panel's + # dependencies go into: both distros mark the system Python + # externally-managed, so pip into it is refused by design. + debian) pkg_install python3 python3-venv python3-pip ;; + esac + ok "Python installed: $(python3 --version)" +fi + +if needs openssl; then + info "Installing OpenSSL" + pkg_install openssl + ok "OpenSSL installed: $(openssl version)" +fi + +# --- Go ---------------------------------------------------------------------- +# Arch ships a current Go; Debian/Ubuntu do not (24.04 is still on 1.22, below +# what go.mod needs), so there the official tarball is the only sane source. +install_go_tarball() { + local version arch tarball url tmp expected actual + version="$(curl -fsSL 'https://go.dev/VERSION?m=text' | head -n1)" + [[ "$version" == go* ]] || die "could not determine the latest Go version" + case "$(uname -m)" in + x86_64) arch="amd64" ;; + aarch64) arch="arm64" ;; + armv7l) arch="armv6l" ;; + *) die "unsupported CPU architecture for the Go tarball: $(uname -m)" ;; + esac + tarball="${version}.linux-${arch}.tar.gz" + url="https://go.dev/dl/${tarball}" + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' RETURN + + info "Downloading ${tarball}" + curl -fsSL "$url" -o "$tmp/$tarball" + + # The published checksum lives in go.dev's release index, so the download is + # verified against the site's metadata rather than trusted on arrival. + expected="$(curl -fsSL 'https://go.dev/dl/?mode=json&include=all' \ + | python3 -c 'import json,sys +want = sys.argv[1] +for release in json.load(sys.stdin): + for f in release.get("files", []): + if f.get("filename") == want: + print(f.get("sha256", "")) + raise SystemExit +' "$tarball")" + [[ -n "$expected" ]] || die "no published checksum for ${tarball}" + actual="$(sha256sum "$tmp/$tarball" | cut -d' ' -f1)" + [[ "$actual" == "$expected" ]] || die "checksum mismatch for ${tarball}: expected ${expected}, got ${actual}" + ok "Checksum verified" + + $SUDO rm -rf /usr/local/go + $SUDO tar -C /usr/local -xzf "$tmp/$tarball" + # /usr/local/go/bin is not on a default PATH; drop a profile snippet so new + # shells find it. The current shell still will not, hence the note at the end. + printf 'export PATH=$PATH:/usr/local/go/bin\n' | $SUDO tee /etc/profile.d/go.sh >/dev/null + $SUDO chmod 0644 /etc/profile.d/go.sh + export PATH="$PATH:/usr/local/go/bin" +} + +GO_NEEDS_NEW_SHELL=0 +if needs go; then + info "Installing Go" + case "$FAMILY" in + arch) pkg_install go ;; + debian) install_go_tarball; GO_NEEDS_NEW_SHELL=1 ;; + esac + ok "Go installed: $(go version)" +fi + +# --- Docker ------------------------------------------------------------------ +DOCKER_NEEDS_RELOGIN=0 +install_docker_debian() { + local codename repo_distro + # Derivatives (Mint, Pop!_OS) carry their own codename that Docker's repo + # does not publish; UBUNTU_CODENAME is the upstream one it does. + # shellcheck disable=SC1091 + . /etc/os-release + codename="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}" + [[ -n "$codename" ]] || die "could not determine the distribution codename for Docker's repository" + case " ${ID:-} ${ID_LIKE:-} " in + *" ubuntu "*) repo_distro="ubuntu" ;; + *) repo_distro="debian" ;; + esac + + pkg_install ca-certificates curl gnupg + $SUDO install -m 0755 -d /etc/apt/keyrings + $SUDO curl -fsSL "https://download.docker.com/linux/${repo_distro}/gpg" -o /etc/apt/keyrings/docker.asc + $SUDO chmod a+r /etc/apt/keyrings/docker.asc + printf 'deb [arch=%s signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/%s %s stable\n' \ + "$(dpkg --print-architecture)" "$repo_distro" "$codename" \ + | $SUDO tee /etc/apt/sources.list.d/docker.list >/dev/null + APT_UPDATED=0 # the new repository has to be picked up + pkg_install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin +} + +if needs docker; then + info "Installing Docker" + case "$FAMILY" in + arch) pkg_install docker docker-compose ;; + debian) install_docker_debian ;; + esac + if have systemctl; then + $SUDO systemctl enable --now docker + ok "Docker service started" + else + warn "systemd not found -- start the Docker daemon yourself before running ./owpengram-server.sh" + fi + # Without this every docker call needs sudo, which the panel does not use. + if [[ $EUID -ne 0 ]] && ! id -nG "$USER" | tr ' ' '\n' | grep -qx docker; then + $SUDO usermod -aG docker "$USER" + DOCKER_NEEDS_RELOGIN=1 + fi + ok "Docker installed: $(docker --version)" +fi + +# --- the panel's Python packages --------------------------------------------- +# Always a venv, never the system interpreter: Arch and Debian both refuse pip +# into it (PEP 668), and owpengram-server.sh already prefers ./.venv when present. +if needs pydeps; then + info "Installing the panel's Python packages into ./.venv" + [[ -x "$(venv_python)" ]] || python3 -m venv "$REPO_ROOT/.venv" + "$(venv_python)" -m pip install --quiet --upgrade pip + "$(venv_python)" -m pip install --quiet -r tui-panel/requirements-panel.txt + ok "Python packages installed" +fi + +echo +ok "Prerequisites are ready." +if [[ "$GO_NEEDS_NEW_SHELL" -eq 1 ]]; then + echo " Go was installed to /usr/local/go. Open a new shell, or run:" + echo " export PATH=\$PATH:/usr/local/go/bin" +fi +if [[ "$DOCKER_NEEDS_RELOGIN" -eq 1 ]]; then + echo " You were added to the 'docker' group. Log out and back in (or run" + echo " 'newgrp docker') before docker works without sudo." +fi +echo " Then start the server with: ./owpengram-server.sh"