|
@@ -0,0 +1,345 @@
|
|
|
|
|
+<#
|
|
|
|
|
+.SYNOPSIS
|
|
|
|
|
+ BamBuddy Docker installation script for Windows (Docker Desktop).
|
|
|
|
|
+
|
|
|
|
|
+.DESCRIPTION
|
|
|
|
|
+ Mirrors install/docker-install.sh for Windows. Verifies Docker Desktop is
|
|
|
|
|
+ installed and running, downloads docker-compose.yml, rewrites it for
|
|
|
|
|
+ Docker Desktop (no host networking), writes a .env, and starts the
|
|
|
|
|
+ container.
|
|
|
|
|
+
|
|
|
|
|
+.PARAMETER InstallPath
|
|
|
|
|
+ Installation directory. Default: $env:USERPROFILE\bambuddy
|
|
|
|
|
+
|
|
|
|
|
+.PARAMETER Port
|
|
|
|
|
+ Port to expose. Default: 8000
|
|
|
|
|
+
|
|
|
|
|
+.PARAMETER TimeZone
|
|
|
|
|
+ IANA timezone string (e.g. Europe/Berlin). Default: derived from
|
|
|
|
|
+ Get-TimeZone or UTC.
|
|
|
|
|
+
|
|
|
|
|
+.PARAMETER Build
|
|
|
|
|
+ Build the image from source instead of pulling the pre-built image.
|
|
|
|
|
+
|
|
|
|
|
+.PARAMETER Yes
|
|
|
|
|
+ Non-interactive mode; accept defaults.
|
|
|
|
|
+
|
|
|
|
|
+.EXAMPLE
|
|
|
|
|
+ Interactive install (cmd or PowerShell):
|
|
|
|
|
+ powershell -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/maziggy/bambuddy/main/install/docker-install.ps1 -OutFile docker-install.ps1; .\docker-install.ps1"
|
|
|
|
|
+
|
|
|
|
|
+.EXAMPLE
|
|
|
|
|
+ Unattended install:
|
|
|
|
|
+ .\docker-install.ps1 -InstallPath C:\bambuddy -Port 8080 -TimeZone Europe/Berlin -Yes
|
|
|
|
|
+#>
|
|
|
|
|
+
|
|
|
|
|
+[CmdletBinding()]
|
|
|
|
|
+param(
|
|
|
|
|
+ [string]$InstallPath,
|
|
|
|
|
+ [int]$Port,
|
|
|
|
|
+ [string]$TimeZone,
|
|
|
|
|
+ [switch]$Build,
|
|
|
|
|
+ [switch]$Yes,
|
|
|
|
|
+ [switch]$Help
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+$ErrorActionPreference = 'Stop'
|
|
|
|
|
+[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
|
|
|
+
|
|
|
|
|
+# --- Helpers --------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+function Write-Banner {
|
|
|
|
|
+ Write-Host ''
|
|
|
|
|
+ Write-Host '==========================================================' -ForegroundColor Cyan
|
|
|
|
|
+ Write-Host ' Bambuddy - Docker Installation (Windows)' -ForegroundColor Cyan
|
|
|
|
|
+ Write-Host '==========================================================' -ForegroundColor Cyan
|
|
|
|
|
+ Write-Host ''
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Info { param($m) Write-Host "[INFO] $m" -ForegroundColor Blue }
|
|
|
|
|
+function Ok { param($m) Write-Host "[OK] $m" -ForegroundColor Green }
|
|
|
|
|
+function Warn { param($m) Write-Host "[WARN] $m" -ForegroundColor Yellow }
|
|
|
|
|
+function Fail { param($m) Write-Host "[ERR] $m" -ForegroundColor Red }
|
|
|
|
|
+
|
|
|
|
|
+function Read-Default {
|
|
|
|
|
+ param([string]$Prompt, [string]$Default)
|
|
|
|
|
+ if ($Yes) { return $Default }
|
|
|
|
|
+ $val = Read-Host "$Prompt [$Default]"
|
|
|
|
|
+ if ([string]::IsNullOrWhiteSpace($val)) { return $Default }
|
|
|
|
|
+ return $val
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Read-YesNo {
|
|
|
|
|
+ param([string]$Prompt, [string]$Default = 'y')
|
|
|
|
|
+ if ($Yes) { return ($Default -eq 'y') }
|
|
|
|
|
+ $hint = if ($Default -eq 'y') { '[Y/n]' } else { '[y/N]' }
|
|
|
|
|
+ while ($true) {
|
|
|
|
|
+ $val = Read-Host "$Prompt $hint"
|
|
|
|
|
+ if ([string]::IsNullOrWhiteSpace($val)) { $val = $Default }
|
|
|
|
|
+ switch -Regex ($val.Trim().ToLower()) {
|
|
|
|
|
+ '^(y|yes)$' { return $true }
|
|
|
|
|
+ '^(n|no)$' { return $false }
|
|
|
|
|
+ default { Write-Host 'Please answer yes or no.' }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Show-Help {
|
|
|
|
|
+ Get-Help $PSCommandPath -Full
|
|
|
|
|
+ exit 0
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# --- Detection ------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+function Test-Docker {
|
|
|
|
|
+ if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
|
|
|
+ Fail 'Docker is not installed.'
|
|
|
|
|
+ Write-Host ''
|
|
|
|
|
+ Write-Host ' Install Docker Desktop for Windows:' -ForegroundColor Yellow
|
|
|
|
|
+ Write-Host ' https://www.docker.com/products/docker-desktop' -ForegroundColor Cyan
|
|
|
|
|
+ Write-Host ''
|
|
|
|
|
+ Write-Host ' After install, launch Docker Desktop and re-run this script.' -ForegroundColor Yellow
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Info 'Docker found, checking daemon...'
|
|
|
|
|
+ try {
|
|
|
|
|
+ docker info --format '{{.ServerVersion}}' *> $null
|
|
|
|
|
+ if ($LASTEXITCODE -ne 0) { throw 'docker info failed' }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ Fail 'Docker daemon is not reachable. Is Docker Desktop running?'
|
|
|
|
|
+ Write-Host ''
|
|
|
|
|
+ Write-Host ' Open Docker Desktop, wait for the whale icon to settle,' -ForegroundColor Yellow
|
|
|
|
|
+ Write-Host ' then re-run this script.' -ForegroundColor Yellow
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+ Ok 'Docker daemon is running'
|
|
|
|
|
+
|
|
|
|
|
+ docker compose version *> $null
|
|
|
|
|
+ if ($LASTEXITCODE -eq 0) {
|
|
|
|
|
+ $script:DockerCompose = 'docker compose'
|
|
|
|
|
+ Ok 'Found Docker Compose v2'
|
|
|
|
|
+ } elseif (Get-Command docker-compose -ErrorAction SilentlyContinue) {
|
|
|
|
|
+ $script:DockerCompose = 'docker-compose'
|
|
|
|
|
+ Ok 'Found Docker Compose v1'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Fail 'Docker Compose not found. Install Docker Desktop 4.x+ (ships compose v2).'
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Get-DefaultTimeZone {
|
|
|
|
|
+ if ($TimeZone) { return $TimeZone }
|
|
|
|
|
+ try {
|
|
|
|
|
+ $tz = (Get-TimeZone).Id
|
|
|
|
|
+ # Get-TimeZone returns Windows IDs ("W. Europe Standard Time"). Docker
|
|
|
|
|
+ # expects IANA. Fall back to UTC if we can't translate.
|
|
|
|
|
+ $iana = ConvertTo-IanaTimeZone $tz
|
|
|
|
|
+ if ($iana) { return $iana }
|
|
|
|
|
+ } catch {}
|
|
|
|
|
+ return 'UTC'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function ConvertTo-IanaTimeZone {
|
|
|
|
|
+ param([string]$WindowsId)
|
|
|
|
|
+ # Minimal mapping of common Windows IDs to IANA. Users can override via -TimeZone.
|
|
|
|
|
+ $map = @{
|
|
|
|
|
+ 'UTC' = 'UTC'
|
|
|
|
|
+ 'GMT Standard Time' = 'Europe/London'
|
|
|
|
|
+ 'W. Europe Standard Time' = 'Europe/Berlin'
|
|
|
|
|
+ 'Central European Standard Time' = 'Europe/Warsaw'
|
|
|
|
|
+ 'Romance Standard Time' = 'Europe/Paris'
|
|
|
|
|
+ 'Russian Standard Time' = 'Europe/Moscow'
|
|
|
|
|
+ 'Eastern Standard Time' = 'America/New_York'
|
|
|
|
|
+ 'Central Standard Time' = 'America/Chicago'
|
|
|
|
|
+ 'Mountain Standard Time' = 'America/Denver'
|
|
|
|
|
+ 'Pacific Standard Time' = 'America/Los_Angeles'
|
|
|
|
|
+ 'AUS Eastern Standard Time' = 'Australia/Sydney'
|
|
|
|
|
+ 'Tokyo Standard Time' = 'Asia/Tokyo'
|
|
|
|
|
+ 'China Standard Time' = 'Asia/Shanghai'
|
|
|
|
|
+ 'India Standard Time' = 'Asia/Kolkata'
|
|
|
|
|
+ }
|
|
|
|
|
+ return $map[$WindowsId]
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Get-LanIp {
|
|
|
|
|
+ try {
|
|
|
|
|
+ $ip = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction Stop |
|
|
|
|
|
+ Where-Object {
|
|
|
|
|
+ $_.IPAddress -notmatch '^(127\.|169\.254\.)' -and
|
|
|
|
|
+ $_.PrefixOrigin -ne 'WellKnown' -and
|
|
|
|
|
+ $_.InterfaceAlias -notmatch '^(vEthernet|Loopback)'
|
|
|
|
|
+ } | Select-Object -First 1
|
|
|
|
|
+ if ($ip) { return $ip.IPAddress }
|
|
|
|
|
+ } catch {}
|
|
|
|
|
+ return '<your-ip>'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# --- Steps ----------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+function Initialize-InstallDir {
|
|
|
|
|
+ Info "Creating installation directory: $InstallPath"
|
|
|
|
|
+ if (-not (Test-Path $InstallPath)) {
|
|
|
|
|
+ New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
|
|
|
|
|
+ }
|
|
|
|
|
+ Set-Location $InstallPath
|
|
|
|
|
+ Ok "Directory ready: $InstallPath"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Get-ComposeFile {
|
|
|
|
|
+ Info 'Downloading docker-compose.yml...'
|
|
|
|
|
+
|
|
|
|
|
+ if ($Build) {
|
|
|
|
|
+ if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
|
|
|
+ Fail 'git is required for --Build but was not found. Install Git for Windows: https://git-scm.com/download/win'
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Test-Path '.git') {
|
|
|
|
|
+ Info 'Existing repository found, updating...'
|
|
|
|
|
+ git fetch origin
|
|
|
|
|
+ git reset --hard origin/main
|
|
|
|
|
+ } else {
|
|
|
|
|
+ git clone https://github.com/maziggy/bambuddy.git .
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Invoke-WebRequest `
|
|
|
|
|
+ -Uri 'https://raw.githubusercontent.com/maziggy/bambuddy/main/docker-compose.yml' `
|
|
|
|
|
+ -OutFile 'docker-compose.yml' `
|
|
|
|
|
+ -UseBasicParsing
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Ok 'docker-compose.yml ready'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Write-EnvFile {
|
|
|
|
|
+ Info 'Writing .env...'
|
|
|
|
|
+ $envBody = @"
|
|
|
|
|
+# BamBuddy Docker Configuration
|
|
|
|
|
+# Generated by docker-install.ps1 on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
|
|
|
|
|
+
|
|
|
|
|
+PORT=$Port
|
|
|
|
|
+TZ=$TimeZone
|
|
|
|
|
+"@
|
|
|
|
|
+ [System.IO.File]::WriteAllText((Join-Path $InstallPath '.env'), $envBody)
|
|
|
|
|
+ Ok '.env written'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Update-ComposeForDockerDesktop {
|
|
|
|
|
+ # Docker Desktop on Windows does not support network_mode: host. Comment
|
|
|
|
|
+ # it out and uncomment the port mappings. Mirrors what the bash script
|
|
|
|
|
+ # does on macOS via sed.
|
|
|
|
|
+ $path = Join-Path $InstallPath 'docker-compose.yml'
|
|
|
|
|
+ Info 'Rewriting compose for Docker Desktop (no host networking)...'
|
|
|
|
|
+
|
|
|
|
|
+ $content = [System.IO.File]::ReadAllText($path)
|
|
|
|
|
+ Copy-Item $path "$path.bak" -Force
|
|
|
|
|
+
|
|
|
|
|
+ $content = $content -replace '(?m)^(\s*)network_mode: host', '#$1network_mode: host'
|
|
|
|
|
+ $content = $content -replace '(?m)^(\s*)#ports:', '$1ports:'
|
|
|
|
|
+ $content = $content -replace '(?m)^(\s*)#(\s*)- "\$\{PORT:-8000\}:8000"', '$1$2- "$${PORT:-8000}:8000"'
|
|
|
|
|
+
|
|
|
|
|
+ [System.IO.File]::WriteAllText($path, $content)
|
|
|
|
|
+ Warn 'Printer auto-discovery (SSDP) does NOT work on Docker Desktop. Add printers manually by IP.'
|
|
|
|
|
+ Warn 'Virtual Printer ports (322, 990, 2024-2026, 3000/3002, 6000, 8883, 50000-50100) stay commented out.'
|
|
|
|
|
+ Warn 'If you plan to use a Virtual Printer, edit docker-compose.yml and uncomment the relevant `- "PORT:PORT"` lines under `ports:`.'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function Start-Bambuddy {
|
|
|
|
|
+ Info 'Starting Bambuddy container...'
|
|
|
|
|
+ if ($Build) {
|
|
|
|
|
+ & cmd /c "$DockerCompose up -d --build"
|
|
|
|
|
+ } else {
|
|
|
|
|
+ & cmd /c "$DockerCompose up -d"
|
|
|
|
|
+ }
|
|
|
|
|
+ if ($LASTEXITCODE -ne 0) {
|
|
|
|
|
+ Fail 'Container start failed. Inspect logs with:'
|
|
|
|
|
+ Write-Host " cd $InstallPath" -ForegroundColor Yellow
|
|
|
|
|
+ Write-Host " $DockerCompose logs bambuddy" -ForegroundColor Yellow
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Info 'Waiting for container to be Up...'
|
|
|
|
|
+ $attempts = 0
|
|
|
|
|
+ while ($attempts -lt 15) {
|
|
|
|
|
+ Start-Sleep -Seconds 2
|
|
|
|
|
+ $ps = & cmd /c "$DockerCompose ps" 2>&1
|
|
|
|
|
+ if ($ps -match 'Up') { Ok 'Bambuddy container is running'; return }
|
|
|
|
|
+ if ($ps -match 'Exited') {
|
|
|
|
|
+ Fail 'Container exited unexpectedly.'
|
|
|
|
|
+ Write-Host " $DockerCompose logs bambuddy" -ForegroundColor Yellow
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ }
|
|
|
|
|
+ $attempts++
|
|
|
|
|
+ }
|
|
|
|
|
+ Warn "Container may still be starting. Check with: $DockerCompose ps"
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+# --- Main -----------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+if ($Help) { Show-Help }
|
|
|
|
|
+
|
|
|
|
|
+Write-Banner
|
|
|
|
|
+
|
|
|
|
|
+Info 'Detecting environment...'
|
|
|
|
|
+Test-Docker
|
|
|
|
|
+
|
|
|
|
|
+# Defaults
|
|
|
|
|
+if (-not $InstallPath) { $InstallPath = Read-Default 'Installation directory' (Join-Path $env:USERPROFILE 'bambuddy') }
|
|
|
|
|
+if (-not $Port) { $Port = [int](Read-Default 'Port to expose' '8000') }
|
|
|
|
|
+if (-not $TimeZone) {
|
|
|
|
|
+ $detected = Get-DefaultTimeZone
|
|
|
|
|
+ $TimeZone = Read-Default 'Timezone (IANA)' $detected
|
|
|
|
|
+}
|
|
|
|
|
+if (-not $Build -and -not $Yes) {
|
|
|
|
|
+ if (Read-YesNo 'Build from source? (No = use pre-built image)' 'n') { $Build = $true }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host 'Installation Summary' -ForegroundColor Cyan
|
|
|
|
|
+Write-Host '-----------------------------------------'
|
|
|
|
|
+Write-Host " Install path: $InstallPath"
|
|
|
|
|
+Write-Host " Port: $Port"
|
|
|
|
|
+Write-Host " Timezone: $TimeZone"
|
|
|
|
|
+Write-Host " Build source: $Build"
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+
|
|
|
|
|
+if (-not (Read-YesNo 'Proceed with installation?' 'y')) {
|
|
|
|
|
+ Write-Host 'Cancelled.' -ForegroundColor Yellow
|
|
|
|
|
+ exit 0
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Initialize-InstallDir
|
|
|
|
|
+Get-ComposeFile
|
|
|
|
|
+Write-EnvFile
|
|
|
|
|
+Update-ComposeForDockerDesktop
|
|
|
|
|
+Start-Bambuddy
|
|
|
|
|
+
|
|
|
|
|
+$lanIp = Get-LanIp
|
|
|
|
|
+
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host '==========================================================' -ForegroundColor Green
|
|
|
|
|
+Write-Host ' Installation Complete!' -ForegroundColor Green
|
|
|
|
|
+Write-Host '==========================================================' -ForegroundColor Green
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host " Access Bambuddy: http://localhost:$Port" -ForegroundColor Cyan
|
|
|
|
|
+Write-Host " http://${lanIp}:$Port (from other devices)" -ForegroundColor Cyan
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host ' Manage container:'
|
|
|
|
|
+Write-Host " Status: cd `"$InstallPath`"; $DockerCompose ps"
|
|
|
|
|
+Write-Host " Logs: cd `"$InstallPath`"; $DockerCompose logs -f bambuddy"
|
|
|
|
|
+Write-Host " Stop: cd `"$InstallPath`"; $DockerCompose down"
|
|
|
|
|
+Write-Host " Start: cd `"$InstallPath`"; $DockerCompose up -d"
|
|
|
|
|
+Write-Host " Restart: cd `"$InstallPath`"; $DockerCompose restart"
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host ' Update Bambuddy:'
|
|
|
|
|
+if ($Build) {
|
|
|
|
|
+ Write-Host " cd `"$InstallPath`"; git pull; $DockerCompose up -d --build"
|
|
|
|
|
+} else {
|
|
|
|
|
+ Write-Host " cd `"$InstallPath`"; $DockerCompose pull; $DockerCompose up -d"
|
|
|
|
|
+}
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host ' Documentation: https://wiki.bambuddy.cool' -ForegroundColor Cyan
|
|
|
|
|
+Write-Host ''
|
|
|
|
|
+Write-Host ' Note: Printer discovery is unavailable on Docker Desktop.' -ForegroundColor Yellow
|
|
|
|
|
+Write-Host ' Add your printers manually by IP address in the UI.' -ForegroundColor Yellow
|
|
|
|
|
+Write-Host ''
|