feat: one-click setup scripts for macOS, Linux, and Windows
scripts/setup.sh (bash) and scripts/setup.ps1 (PowerShell) wrap the canonical Docker Compose flow: detect Docker + Compose v2 (with legacy docker-compose fallback), copy .env.example to .env if missing, pull the GHCR image, bring the stack up, poll /health until it's ready (90s cap), print a summary, and open the URL in the default browser. Idempotent — safe to re-run. README quick-start now points designers at the one-liner as the primary path, with the raw docker compose commands kept underneath for reference. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
76a6e462ee
commit
548d0c753a
@@ -0,0 +1,89 @@
|
|||||||
|
# RefBoard one-click installer (Windows PowerShell).
|
||||||
|
# Spins up the Docker Compose stack, waits for the backend to come up, and
|
||||||
|
# opens the browser. Idempotent.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# pwsh -File scripts/setup.ps1
|
||||||
|
# # or in a PowerShell session at the repo root:
|
||||||
|
# .\scripts\setup.ps1
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
param()
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Write-Info { param([string]$Msg) Write-Host "[setup] $Msg" -ForegroundColor Cyan }
|
||||||
|
function Write-Ok { param([string]$Msg) Write-Host "[setup] $Msg" -ForegroundColor Green }
|
||||||
|
function Write-Fail { param([string]$Msg) Write-Host "[setup] $Msg" -ForegroundColor Red; exit 1 }
|
||||||
|
|
||||||
|
# 1. Docker check
|
||||||
|
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Fail "Docker is not installed. Install Docker Desktop from https://www.docker.com/products/docker-desktop/"
|
||||||
|
}
|
||||||
|
|
||||||
|
$null = docker info 2>&1
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Fail "Docker daemon is not running. Start Docker Desktop, then re-run this script."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Compose (v2 plugin preferred, legacy docker-compose fallback)
|
||||||
|
$composeCmd = $null
|
||||||
|
$null = docker compose version 2>&1
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
$composeCmd = @('docker', 'compose')
|
||||||
|
} elseif (Get-Command docker-compose -ErrorAction SilentlyContinue) {
|
||||||
|
$composeCmd = @('docker-compose')
|
||||||
|
} else {
|
||||||
|
Write-Fail "Docker Compose is not installed. Re-install Docker Desktop (it bundles Compose v2)."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. Working dir = repo root
|
||||||
|
Set-Location (Split-Path -Parent $PSScriptRoot)
|
||||||
|
|
||||||
|
# 4. .env
|
||||||
|
if (-not (Test-Path .env)) {
|
||||||
|
if (-not (Test-Path .env.example)) {
|
||||||
|
Write-Fail "Neither .env nor .env.example found in $(Get-Location). Are you running this from the repo?"
|
||||||
|
}
|
||||||
|
Copy-Item .env.example .env
|
||||||
|
Write-Ok "Copied .env.example -> .env (defaults are fine for a local install)"
|
||||||
|
} else {
|
||||||
|
Write-Info ".env already exists; leaving it untouched."
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Pull + up
|
||||||
|
$composeStr = ($composeCmd -join ' ')
|
||||||
|
Write-Info "Pulling image (this may take a moment on first run)..."
|
||||||
|
& $composeCmd[0] $composeCmd[1..($composeCmd.Length - 1)] pull
|
||||||
|
|
||||||
|
Write-Info "Starting RefBoard..."
|
||||||
|
& $composeCmd[0] $composeCmd[1..($composeCmd.Length - 1)] up -d --build
|
||||||
|
|
||||||
|
# 6. Wait for /health
|
||||||
|
$Url = "http://localhost:8000"
|
||||||
|
Write-Info "Waiting for backend at $Url/health (max 90s)..."
|
||||||
|
$up = $false
|
||||||
|
for ($i = 1; $i -le 90; $i++) {
|
||||||
|
try {
|
||||||
|
$resp = Invoke-WebRequest -UseBasicParsing -Uri "$Url/health" -TimeoutSec 2
|
||||||
|
if ($resp.StatusCode -eq 200) { $up = $true; break }
|
||||||
|
} catch {}
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
}
|
||||||
|
if (-not $up) {
|
||||||
|
Write-Fail "Backend didn't respond within 90s. Check logs with: $composeStr logs -f refboard"
|
||||||
|
}
|
||||||
|
Write-Ok "Backend is up."
|
||||||
|
|
||||||
|
# 7. Summary + browser
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " RefBoard is running." -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " URL: $Url"
|
||||||
|
Write-Host " Admin setup: open the URL. The first account you create becomes admin."
|
||||||
|
Write-Host " Data dir: .\.docker-data\ (SQLite + MinIO objects; back this up)"
|
||||||
|
Write-Host " Stop: $composeStr down"
|
||||||
|
Write-Host " Logs: $composeStr logs -f refboard"
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Start-Process $Url
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# RefBoard one-click installer (macOS / Linux).
|
||||||
|
# Spins up the Docker Compose stack, waits for the backend to come up, and
|
||||||
|
# opens the browser. Idempotent — safe to re-run.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/setup.sh
|
||||||
|
# # or pipe-to-bash from the repo:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/metalfinger/refboard/main/scripts/setup.sh | bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
BLUE=$'\033[34m'; GREEN=$'\033[32m'; RED=$'\033[31m'; DIM=$'\033[2m'; RESET=$'\033[0m'
|
||||||
|
info() { printf "%s[setup]%s %s\n" "$BLUE" "$RESET" "$*"; }
|
||||||
|
ok() { printf "%s[setup]%s %s\n" "$GREEN" "$RESET" "$*"; }
|
||||||
|
fail() { printf "%s[setup]%s %s\n" "$RED" "$RESET" "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 1. Docker check
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Darwin) fail "Docker is not installed. Install Docker Desktop: https://www.docker.com/products/docker-desktop/" ;;
|
||||||
|
Linux) fail "Docker is not installed. On Debian/Ubuntu: sudo apt install docker.io docker-compose-plugin" ;;
|
||||||
|
*) fail "Docker is not installed." ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
fail "Docker daemon is not running. Start Docker Desktop (macOS) or 'sudo systemctl start docker' (Linux), then re-run."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compose v2 plugin vs. legacy
|
||||||
|
if docker compose version >/dev/null 2>&1; then
|
||||||
|
COMPOSE=(docker compose)
|
||||||
|
elif command -v docker-compose >/dev/null 2>&1; then
|
||||||
|
COMPOSE=(docker-compose)
|
||||||
|
else
|
||||||
|
fail "Docker Compose is not installed. On Linux: sudo apt install docker-compose-plugin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Working dir = repo root (parent of this script)
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]:-$0}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR/.."
|
||||||
|
|
||||||
|
# 3. .env
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
if [ ! -f .env.example ]; then
|
||||||
|
fail "Neither .env nor .env.example found in $(pwd). Are you running this from the repo?"
|
||||||
|
fi
|
||||||
|
cp .env.example .env
|
||||||
|
ok "Copied .env.example -> .env (defaults are fine for a local install)"
|
||||||
|
else
|
||||||
|
info ".env already exists; leaving it untouched."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Pull + up
|
||||||
|
info "Pulling image (this may take a moment on first run)..."
|
||||||
|
"${COMPOSE[@]}" pull --ignore-pull-failures 2>/dev/null || "${COMPOSE[@]}" pull || true
|
||||||
|
|
||||||
|
info "Starting RefBoard..."
|
||||||
|
"${COMPOSE[@]}" up -d --build
|
||||||
|
|
||||||
|
# 5. Wait for /health
|
||||||
|
URL="http://localhost:8000"
|
||||||
|
info "Waiting for backend to come up at ${URL}/health (max 90s)..."
|
||||||
|
for i in $(seq 1 90); do
|
||||||
|
if curl -fsS "${URL}/health" >/dev/null 2>&1; then
|
||||||
|
ok "Backend is up."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ "$i" = "90" ]; then
|
||||||
|
fail "Backend didn't respond within 90s. Check logs: ${COMPOSE[*]} logs -f refboard"
|
||||||
|
fi
|
||||||
|
printf "${DIM}.${RESET}"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
printf "\n"
|
||||||
|
|
||||||
|
# 6. Print summary + open browser
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
${GREEN}RefBoard is running.${RESET}
|
||||||
|
|
||||||
|
URL: ${URL}
|
||||||
|
Admin setup: open the URL — the first account you create becomes admin.
|
||||||
|
Data dir: ./.docker-data/ (SQLite + MinIO objects; back this up)
|
||||||
|
Stop: ${COMPOSE[*]} down
|
||||||
|
Logs: ${COMPOSE[*]} logs -f refboard
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Open browser (best-effort)
|
||||||
|
if command -v open >/dev/null 2>&1; then
|
||||||
|
open "$URL" >/dev/null 2>&1 || true
|
||||||
|
elif command -v xdg-open >/dev/null 2>&1; then
|
||||||
|
xdg-open "$URL" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user