From 548d0c753ab566134264dfd718243e743e088b0e Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Thu, 21 May 2026 09:51:39 +0530 Subject: [PATCH] feat: one-click setup scripts for macOS, Linux, and Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- scripts/setup.ps1 | 89 +++++++++++++++++++++++++++++++++++++++++++ scripts/setup.sh | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 scripts/setup.ps1 create mode 100644 scripts/setup.sh diff --git a/scripts/setup.ps1 b/scripts/setup.ps1 new file mode 100644 index 0000000..b86e128 --- /dev/null +++ b/scripts/setup.ps1 @@ -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 diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100644 index 0000000..74ff563 --- /dev/null +++ b/scripts/setup.sh @@ -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 </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