diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af8f204 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +.PHONY: setup dev \ + dev-backend dev-web \ + test test-backend test-web test-e2e \ + build build-backend build-web \ + lint lint-backend lint-web lint-mobile \ + tidy itest-backend + +# ── Setup ────────────────────────────────────────────────────────────────────── + +setup: +ifeq ($(OS),Windows_NT) + powershell -ExecutionPolicy Bypass -File setup.ps1 +else + bash setup.sh +endif + +# ── Development ──────────────────────────────────────────────────────────────── + +dev: +ifeq ($(OS),Windows_NT) + powershell -ExecutionPolicy Bypass -File dev.ps1 +else + bash dev.sh +endif + +dev-backend: + cd backend && make watch + +dev-web: + cd web && pnpm dev + +# ── Testing ──────────────────────────────────────────────────────────────────── + +test-backend: + cd backend && go test ./... + +test-web: + cd web && pnpm test:run + +test-e2e: + cd web && pnpm test:e2e + +test: test-backend test-web test-e2e + +# ── Build ────────────────────────────────────────────────────────────────────── + +build-backend: + cd backend && make build + +build-web: + cd web && pnpm build + +build: build-backend build-web + +# ── Utilities ────────────────────────────────────────────────────────────────── + +itest-backend: + cd backend && make itest + +lint-backend: + cd backend && go vet ./... + +lint-web: + cd web && pnpm lint + +GRADLEW := $(if $(filter Windows_NT,$(OS)),gradlew.bat,./gradlew) + +lint-mobile: + cd mobile && $(GRADLEW) lint + +lint: lint-backend lint-web lint-mobile + +tidy: + cd backend && go mod tidy + cd web && pnpm install diff --git a/setup.ps1 b/setup.ps1 index 2abac18..ccef726 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -1,49 +1,56 @@ -# setup.ps1 — first-run setup for new contributors (Windows / PowerShell) +# setup.ps1 -- first-run setup for new contributors (Windows / PowerShell) # Usage: .\setup.ps1 # Run once after cloning the repo. -$ErrorActionPreference = "Stop" +$ErrorActionPreference = "Continue" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition $Errors = 0 -# ── Banner ───────────────────────────────────────────────────────────────────── +# Banner Write-Host "" -Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Blue -Write-Host "║ Fullstack Template — First-Run Setup ║" -ForegroundColor Blue -Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Blue +Write-Host "============================================================" -ForegroundColor Blue +Write-Host " Fullstack Template -- First-Run Setup " -ForegroundColor Blue +Write-Host "============================================================" -ForegroundColor Blue Write-Host "" -# ── Helpers ──────────────────────────────────────────────────────────────────── -function Pass { param($msg) Write-Host " ✓ $msg" -ForegroundColor Green } -function Fail { param($msg) Write-Host " ✗ $msg" -ForegroundColor Red; $script:Errors++ } -function Info { param($msg) Write-Host " → $msg" -ForegroundColor Yellow } -function Header { param($msg) Write-Host "`n$msg" -ForegroundColor White } +# Helpers +function Pass { param($msg) Write-Host " [OK] $msg" -ForegroundColor Green } +function Fail { param($msg) Write-Host " [!!] $msg" -ForegroundColor Red; $script:Errors++ } +function Info { param($msg) Write-Host " --> $msg" -ForegroundColor Yellow } +function Header { param($msg) Write-Host ""; Write-Host "$msg" -ForegroundColor White } -# ── 1. Prerequisite checks ───────────────────────────────────────────────────── +# 1. Prerequisite checks Header "Checking prerequisites..." -# go ≥ 1.25 +# go >= 1.25 try { - $goVersion = (go version 2>&1) -replace '.*go(\d+\.\d+).*','$1' - $parts = $goVersion -split '\.' - $major = [int]$parts[0]; $minor = [int]$parts[1] - if ($major -gt 1 -or ($major -eq 1 -and $minor -ge 25)) { - Pass "go $goVersion (≥ 1.25)" + $goRaw = & go version 2>&1 + if ($goRaw -match 'go(\d+)\.(\d+)') { + $major = [int]$Matches[1]; $minor = [int]$Matches[2] + if ($major -gt 1 -or ($major -eq 1 -and $minor -ge 25)) { + Pass "go $major.$minor (>= 1.25)" + } else { + Fail "go $major.$minor found -- need >= 1.25. Install from https://go.dev/dl/" + } } else { - Fail "go $goVersion found — need ≥ 1.25. Install from https://go.dev/dl/" + Fail "Could not parse go version output. Install from https://go.dev/dl/" } } catch { Fail "go not found. Install from https://go.dev/dl/" } -# node ≥ 22 +# node >= 22 try { - $nodeVersion = (node --version 2>&1).TrimStart('v') - $nodeMajor = [int]($nodeVersion -split '\.')[0] - if ($nodeMajor -ge 22) { - Pass "node v$nodeVersion (≥ 22)" + $nodeRaw = & node --version 2>&1 + if ($nodeRaw -match 'v(\d+)') { + $nodeMajor = [int]$Matches[1] + if ($nodeMajor -ge 22) { + Pass "node v$nodeMajor (>= 22)" + } else { + Fail "node v$nodeMajor found -- need >= 22. Install from https://nodejs.org/" + } } else { - Fail "node v$nodeVersion found — need ≥ 22. Install from https://nodejs.org/" + Fail "Could not parse node version. Install from https://nodejs.org/" } } catch { Fail "node not found. Install from https://nodejs.org/" @@ -51,7 +58,7 @@ try { # pnpm (any version) try { - $pnpmVersion = (pnpm --version 2>&1) + $pnpmVersion = & pnpm --version 2>&1 Pass "pnpm $pnpmVersion" } catch { Fail "pnpm not found. Install with: npm install -g pnpm" @@ -59,25 +66,25 @@ try { # docker (running) try { - $null = docker info 2>&1 + $null = & docker info 2>$null if ($LASTEXITCODE -eq 0) { Pass "docker (running)" } else { - Fail "docker is installed but not running. Start Docker Desktop and retry." + Fail "Could not connect to Docker. Start Docker Desktop if you have it, or install from https://www.docker.com/products/docker-desktop/ then re-run." } } catch { - Fail "docker not found. Install Docker Desktop from https://www.docker.com/products/docker-desktop/" + Fail "Could not connect to Docker. Start Docker Desktop if you have it, or install from https://www.docker.com/products/docker-desktop/ then re-run." } -# java ≥ 17 +# java >= 17 try { - $javaOut = (java -version 2>&1) | Select-Object -First 1 + $javaOut = & java -version 2>&1 | Select-Object -First 1 if ($javaOut -match '"(\d+)') { $javaMajor = [int]$Matches[1] if ($javaMajor -ge 17) { - Pass "java $javaMajor (≥ 17)" + Pass "java $javaMajor (>= 17)" } else { - Fail "java $javaMajor found — need ≥ 17. Install from https://adoptium.net/" + Fail "java $javaMajor found -- need >= 17. Install from https://adoptium.net/" } } else { Fail "Could not parse java version. Install from https://adoptium.net/" @@ -94,13 +101,13 @@ if (-not $androidHome -or -not (Test-Path $androidHome)) { if (Test-Path $androidHome) { Pass "Android SDK at $androidHome" if (-not $env:ANDROID_HOME) { - Info "ANDROID_HOME is not set as an environment variable — tools like the Gradle wrapper will still work, but you may want to add it:" + Info "ANDROID_HOME is not set -- tools like the Gradle wrapper will still work, but you may want to add it:" Info " [System.Environment]::SetEnvironmentVariable('ANDROID_HOME', '$androidHome', 'User')" } } else { Fail "Android SDK not found at ANDROID_HOME or $env:LOCALAPPDATA\Android\Sdk" - Info "Install Android Studio — it sets up the SDK automatically." - Info "Then optionally set ANDROID_HOME to: $env:LOCALAPPDATA\Android\Sdk" + Info "Install Android Studio -- it sets up the SDK automatically." + Info "Then optionally set ANDROID_HOME to: $env:LOCALAPPDATA\Android\Sdk" } # Abort on failures @@ -111,35 +118,35 @@ if ($Errors -gt 0) { exit 1 } -# ── 2. Install web dependencies ──────────────────────────────────────────────── +# 2. Install web dependencies Header "Installing web dependencies (pnpm install)..." Set-Location "$ScriptDir\web" -pnpm install +& pnpm install Set-Location $ScriptDir Pass "web dependencies installed" -# ── 3. Copy env files (if not already present) ──────────────────────────────── +# 3. Copy env files (if not already present) Header "Copying environment files..." -$backendEnv = "$ScriptDir\backend\.env" +$backendEnv = "$ScriptDir\backend\.env" $backendExample = "$ScriptDir\backend\.env.example" if (Test-Path $backendEnv) { - Info "backend\.env already exists — skipping" + Info "backend\.env already exists -- skipping" } else { Copy-Item $backendExample $backendEnv - Pass "backend\.env.example → backend\.env" + Pass "backend\.env.example -> backend\.env" } $webEnvLocal = "$ScriptDir\web\.env.local" $webExample = "$ScriptDir\web\.env.example" if (Test-Path $webEnvLocal) { - Info "web\.env.local already exists — skipping" + Info "web\.env.local already exists -- skipping" } else { Copy-Item $webExample $webEnvLocal - Pass "web\.env.example → web\.env.local" + Pass "web\.env.example -> web\.env.local" } -# ── 4. Start Postgres and run migrations ────────────────────────────────────── +# 4. Start Postgres and run migrations Header "Starting Postgres (Docker Compose) in a background window..." Start-Process powershell -ArgumentList "-NoExit", "-ExecutionPolicy", "Bypass", "-Command", "Set-Location '$ScriptDir\backend'; make docker-run" ` -WindowStyle Minimized @@ -149,25 +156,25 @@ Start-Sleep 5 Header "Running database migrations..." Set-Location "$ScriptDir\backend" -make migrate-up +& make migrate-up Set-Location $ScriptDir Pass "Migrations applied" -# ── 5. Ready summary ────────────────────────────────────────────────────────── +# 5. Ready summary Write-Host "" -Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Green -Write-Host "║ You're all set! Run these commands to start developing: ║" -ForegroundColor Green -Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Green +Write-Host "============================================================" -ForegroundColor Green +Write-Host " You're all set! Run these commands to start developing: " -ForegroundColor Green +Write-Host "============================================================" -ForegroundColor Green Write-Host "" Write-Host " Quickstart (all services in separate windows):" -ForegroundColor White Write-Host " .\dev.ps1" -ForegroundColor Yellow Write-Host "" Write-Host " Or start each service individually:" -ForegroundColor White Write-Host " cd backend; make docker-run # Postgres" -ForegroundColor Blue -Write-Host " cd backend; make watch # Go backend → http://localhost:8080" -ForegroundColor Green -Write-Host " cd web; pnpm dev # Next.js web → http://localhost:3000" -ForegroundColor Yellow +Write-Host " cd backend; make watch # Go backend -> http://localhost:8080" -ForegroundColor Green +Write-Host " cd web; pnpm dev # Next.js web -> http://localhost:3000" -ForegroundColor Yellow Write-Host "" Write-Host " Edit your env files before starting:" -ForegroundColor White -Write-Host " backend\.env — add DB credentials, Firebase config, etc." -Write-Host " web\.env.local — add Firebase client config, AUTH_SECRET, etc." +Write-Host " backend\.env -- add DB credentials, Firebase config, etc." +Write-Host " web\.env.local -- add Firebase client config, AUTH_SECRET, etc." Write-Host ""