Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -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
115 changes: 61 additions & 54 deletions setup.ps1
Original file line number Diff line number Diff line change
@@ -1,83 +1,90 @@
# 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/"
}

# 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"
}

# 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/"
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 ""
Loading