From 050247442814436b2827e9cc517e73eb173bdb98 Mon Sep 17 00:00:00 2001 From: aliviahossain Date: Mon, 29 Jun 2026 11:01:33 +0530 Subject: [PATCH 1/3] added dev-setup and launch script for windows, linux and MacOS --- SETUP.md | 162 ++++++++++++++++++++++++++++ dev-setup.ps1 | 275 +++++++++++++++++++++++++++++++++++++++++++++++ dev-setup.sh | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 727 insertions(+) create mode 100644 SETUP.md create mode 100644 dev-setup.ps1 create mode 100644 dev-setup.sh diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 000000000..6be9a5003 --- /dev/null +++ b/SETUP.md @@ -0,0 +1,162 @@ +# PictoPy Dev Setup Guide + +This guide covers how to set up and run PictoPy locally for development on **Windows** and **Linux/macOS**. + +--- + +## Prerequisites + +| Tool | Version | Purpose | +|------|---------|---------| +| Node.js | 18+ | Frontend build | +| Rust + Cargo | latest stable | Tauri desktop framework | +| Python | 3.12 | Backend + sync microservice | +| Conda (Miniconda) | any | Python environment management | + +--- + +## Quick Start (Recommended) + +Both scripts handle all prerequisite installation, environment setup, and launch all three services automatically. + +### Windows + +Open **PowerShell** in the PictoPy root directory and run: + +```powershell +Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +.\dev-setup.ps1 +``` + +> If prompted by Windows Defender, choose "Run anyway". + +### Linux / macOS + +Open a terminal in the PictoPy root directory and run: + +```bash +bash dev-setup.sh +``` + +> On macOS, Homebrew must be installed first: https://brew.sh + +--- + +## What the Scripts Do + +Both scripts run the same four phases: + +**Phase 1 — Prerequisites** +- Installs Node.js if missing +- Installs Rust + Cargo via rustup if missing +- Installs Visual Studio C++ Build Tools (Windows) or system libs (Linux) required by Tauri +- Installs Miniconda if missing + +**Phase 2 — Service Setup** +- Installs frontend npm dependencies (`frontend/node_modules`) +- Creates conda environment for the backend (`backend/.env`) and installs Python packages +- Creates conda environment for the sync microservice (`sync-microservice/.sync-env`) and installs Python packages + +**Phase 3 — Launch** +- Starts the Python backend on port `52123` +- Starts the sync microservice on port `52124` +- Starts the Tauri frontend (React + Rust) + +**Phase 4 — Health Check** +- Verifies backend and sync microservice are reachable +- Streams logs from all three services with colour-coded prefixes (`[BACKEND]`, `[SYNC]`, `[FRONTEND]`) + +Press **Ctrl+C** to stop all services. + +--- + +## Manual Setup (Alternative) + +If you prefer to set up each service individually: + +### 1. Frontend + +```bash +cd frontend +npm install +npm run tauri dev +``` + +> **Windows only:** Rust requires Visual Studio C++ Build Tools (`link.exe`). +> Install via: `winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"` + +### 2. Backend + +```bash +cd backend +conda create -p .env python=3.12 -y +conda run -p .env pip install -r requirements.txt +conda run -p .env python main.py +``` + +Backend runs at: `http://localhost:52123` + +### 3. Sync Microservice + +```bash +cd sync-microservice +conda create -p .sync-env python=3.12 -y +conda run -p .sync-env pip install -r requirements.txt +conda run -p .sync-env fastapi dev --port 52124 +``` + +Sync microservice runs at: `http://localhost:52124` + +--- + +## Services Overview + +| Service | Port | Description | +|---------|------|-------------| +| Backend (Python/FastAPI) | 52123 | Image processing, face recognition, albums | +| Sync Microservice (Python/FastAPI) | 52124 | File sync and watching | +| Frontend (Tauri/React) | — | Desktop app window (not a browser port) | + +All three must be running for the app to work correctly. + +--- + +## Troubleshooting + +### `cargo` not found after installing Rust +Close and reopen your terminal — Rust adds itself to PATH only for new sessions. + +### `link.exe` not found (Windows) +Visual Studio C++ Build Tools are missing. Run: +```powershell +winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended" +``` +Then reopen your terminal and retry. + +### `chmod` not recognized (Windows) +`chmod` is a Linux command. On Windows, skip it — just run `.\dev-setup.ps1` directly. + +### App window opens but shows a blank page +The Python backend is not running. Start it manually: +```bash +cd backend +conda run -p .env python main.py +``` + +### Script fails on emoji / encoding error (Windows PowerShell) +Your PowerShell version may not support Unicode. Upgrade to PowerShell 7+: +```powershell +winget install Microsoft.PowerShell +``` + +### Port already in use +Kill the process occupying the port: +```powershell +# Windows +netstat -ano | findstr :52123 +taskkill /PID /F +``` +```bash +# Linux/macOS +lsof -ti:52123 | xargs kill +``` diff --git a/dev-setup.ps1 b/dev-setup.ps1 new file mode 100644 index 000000000..b2f9f7060 --- /dev/null +++ b/dev-setup.ps1 @@ -0,0 +1,275 @@ +# ============================================================================= +# PictoPy Dev Setup & Launch Script - Windows PowerShell +# Usage: Right-click -> "Run with PowerShell" OR +# Open PowerShell in PictoPy root and run: .\dev-setup.ps1 +# ============================================================================= + +# Allow script execution if blocked: +# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned + +$ErrorActionPreference = "Stop" + +# ── Colors / Helpers ────────────────────────────────────────────────────────── +function Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } +function Skip($msg) { Write-Host "[SKIP] $msg (already installed, skipping)" -ForegroundColor Cyan } +function Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Blue } +function Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } +function Fail($msg) { Write-Host "[FAIL] $msg" -ForegroundColor Red; exit 1 } +function Header($msg) { + Write-Host "" + Write-Host "------------------------------------------------" -ForegroundColor Blue + Write-Host " $msg" -ForegroundColor White + Write-Host "------------------------------------------------" -ForegroundColor Blue +} + +# ── Validate we are in the PictoPy root ─────────────────────────────────────── +$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path +$FRONTEND_DIR = Join-Path $SCRIPT_DIR "frontend" +$BACKEND_DIR = Join-Path $SCRIPT_DIR "backend" +$SYNC_DIR = Join-Path $SCRIPT_DIR "sync-microservice" + +if (-not (Test-Path $FRONTEND_DIR) -or -not (Test-Path $BACKEND_DIR) -or -not (Test-Path $SYNC_DIR)) { + Fail "Please run this script from the root of the PictoPy repository." +} + +# ============================================================================= +# PHASE 1 — PREREQUISITES +# ============================================================================= +Header "Phase 1: Checking Prerequisites" + +# ── Winget (built into Windows 11, available on Win10 too) ─────────────────── +if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { + Warn "winget not found. Install 'App Installer' from the Microsoft Store, then re-run." + Fail "winget is required for auto-installation." +} + +# ── Node.js ─────────────────────────────────────────────────────────────────── +if (Get-Command node -ErrorAction SilentlyContinue) { + Skip "Node.js ($(node --version))" +} else { + Info "Installing Node.js via winget..." + winget install -e --id OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements + # Refresh PATH + $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") + Ok "Node.js installed ($(node --version))" +} + +# ── Rust / Cargo ────────────────────────────────────────────────────────────── +if (Get-Command cargo -ErrorAction SilentlyContinue) { + Skip "Rust/Cargo ($(cargo --version))" +} else { + Info "Installing Rust via rustup..." + winget install -e --id Rustlang.Rustup --accept-source-agreements --accept-package-agreements + # Refresh PATH + $env:PATH = "$env:USERPROFILE\.cargo\bin;" + $env:PATH + if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { + Warn "Rust installed but cargo not in PATH yet." + Warn "Please restart PowerShell after this script finishes, then re-run." + } else { + Ok "Rust installed ($(cargo --version))" + } +} + +# ── Visual Studio C++ Build Tools (required by Tauri on Windows) ────────────── +Info "Checking Visual Studio C++ Build Tools..." +$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" +$hasBuildTools = $false +if (Test-Path $vswhere) { + $vsInstalls = & $vswhere -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -format json 2>$null | ConvertFrom-Json + if ($vsInstalls.Count -gt 0) { $hasBuildTools = $true } +} +if ($hasBuildTools) { + Skip "Visual Studio C++ Build Tools" +} else { + Info "Installing Visual Studio Build Tools (this may take a while)..." + winget install -e --id Microsoft.VisualStudio.2022.BuildTools --accept-source-agreements --accept-package-agreements ` + --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended" + Ok "Visual Studio C++ Build Tools installed" +} + +# ── WebView2 (required by Tauri on Windows) ─────────────────────────────────── +Info "Checking WebView2 Runtime..." +$webview2Key = "HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" +if (Test-Path $webview2Key) { + Skip "WebView2 Runtime" +} else { + Info "Installing WebView2 Runtime..." + winget install -e --id Microsoft.EdgeWebView2Runtime --accept-source-agreements --accept-package-agreements + Ok "WebView2 Runtime installed" +} + +# ── Miniconda ───────────────────────────────────────────────────────────────── +if (Get-Command conda -ErrorAction SilentlyContinue) { + Skip "Miniconda/Conda ($(conda --version))" +} else { + Info "Installing Miniconda..." + $minicondaUrl = "https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe" + $minicondaInstaller = "$env:TEMP\miniconda.exe" + Invoke-WebRequest -Uri $minicondaUrl -OutFile $minicondaInstaller + Start-Process -FilePath $minicondaInstaller -ArgumentList "/S /D=$env:USERPROFILE\miniconda3" -Wait + Remove-Item $minicondaInstaller + + # Add conda to PATH for this session + $env:PATH = "$env:USERPROFILE\miniconda3\Scripts;" + "$env:USERPROFILE\miniconda3;" + $env:PATH + + if (-not (Get-Command conda -ErrorAction SilentlyContinue)) { + Warn "Miniconda installed but conda not in PATH yet." + Warn "Please restart PowerShell after this script finishes, then re-run." + Fail "conda not available in current session." + } + Ok "Miniconda installed ($(conda --version))" +} + +# Initialize conda for PowerShell if not already done +$condaHook = "$env:USERPROFILE\miniconda3\shell\condabin\conda-hook.ps1" +if (Test-Path $condaHook) { + & $condaHook +} + +# ============================================================================= +# PHASE 2 — SERVICE SETUP +# ============================================================================= +Header "Phase 2: Setting Up Services" + +# ── Frontend ────────────────────────────────────────────────────────────────── +Info "Frontend: checking node_modules..." +if (Test-Path (Join-Path $FRONTEND_DIR "node_modules")) { + Skip "Frontend npm dependencies" +} else { + Info "Frontend: running npm install..." + Push-Location $FRONTEND_DIR + npm install + Pop-Location + Ok "Frontend dependencies installed" +} + +# ── Backend ─────────────────────────────────────────────────────────────────── +Info "Backend: checking conda environment..." +$BACKEND_ENV = Join-Path $BACKEND_DIR ".env" +if (Test-Path $BACKEND_ENV) { + Skip "Backend conda environment (.env)" +} else { + Info "Backend: creating conda environment with Python 3.12..." + conda create -p $BACKEND_ENV python=3.12 -y + Ok "Backend conda environment created" +} + +Info "Backend: checking Python packages..." +$backendHasFastapi = conda run -p $BACKEND_ENV python -c "import fastapi" 2>$null +if ($LASTEXITCODE -eq 0) { + Skip "Backend Python packages" +} else { + Info "Backend: installing Python packages..." + conda run -p $BACKEND_ENV pip install -r (Join-Path $BACKEND_DIR "requirements.txt") + Ok "Backend Python packages installed" +} + +# ── Sync Microservice ───────────────────────────────────────────────────────── +Info "Sync microservice: checking conda environment..." +$SYNC_ENV = Join-Path $SYNC_DIR ".sync-env" +if (Test-Path $SYNC_ENV) { + Skip "Sync microservice conda environment (.sync-env)" +} else { + Info "Sync microservice: creating conda environment with Python 3.12..." + conda create -p $SYNC_ENV python=3.12 -y + Ok "Sync microservice conda environment created" +} + +Info "Sync microservice: checking Python packages..." +$syncHasFastapi = conda run -p $SYNC_ENV python -c "import fastapi" 2>$null +if ($LASTEXITCODE -eq 0) { + Skip "Sync microservice Python packages" +} else { + Info "Sync microservice: installing Python packages..." + conda run -p $SYNC_ENV pip install -r (Join-Path $SYNC_DIR "requirements.txt") + Ok "Sync microservice Python packages installed" +} + +# ============================================================================= +# PHASE 3 — LAUNCH ALL SERVICES +# ============================================================================= +Header "Phase 3: Launching All Services" + +# Track jobs for cleanup +$jobs = @() + +# ── Launch Backend ──────────────────────────────────────────────────────────── +Info "Starting backend on port 52123..." +$backendJob = Start-Job -ScriptBlock { + param($dir, $env) + Set-Location $dir + conda run -p $env fastapi dev --port 52123 2>&1 | ForEach-Object { "[BACKEND] $_" } +} -ArgumentList $BACKEND_DIR, $BACKEND_ENV +$jobs += $backendJob + +# ── Launch Sync Microservice ────────────────────────────────────────────────── +Info "Starting sync microservice on port 52124..." +$syncJob = Start-Job -ScriptBlock { + param($dir, $env) + Set-Location $dir + conda run -p $env fastapi dev --port 52124 2>&1 | ForEach-Object { "[SYNC] $_" } +} -ArgumentList $SYNC_DIR, $SYNC_ENV +$jobs += $syncJob + +# ── Launch Frontend ─────────────────────────────────────────────────────────── +Info "Starting Tauri frontend..." +$frontendJob = Start-Job -ScriptBlock { + param($dir) + Set-Location $dir + npm run tauri dev 2>&1 | ForEach-Object { "[FRONTEND] $_" } +} -ArgumentList $FRONTEND_DIR +$jobs += $frontendJob + +# ============================================================================= +# PHASE 4 — HEALTH CHECK + STREAM LOGS +# ============================================================================= +Header "Phase 4: Health Check" + +Info "Waiting 10 seconds for services to start..." +Start-Sleep -Seconds 10 + +# Check backend +try { + Invoke-WebRequest -Uri "http://localhost:52123/docs" -UseBasicParsing -TimeoutSec 3 | Out-Null + Ok "Backend is running at http://localhost:52123" +} catch { + Warn "Backend may still be starting up — check [BACKEND] logs below" +} + +# Check sync microservice +try { + Invoke-WebRequest -Uri "http://localhost:52124/docs" -UseBasicParsing -TimeoutSec 3 | Out-Null + Ok "Sync microservice is running at http://localhost:52124" +} catch { + Warn "Sync microservice may still be starting — check [SYNC] logs below" +} + +Write-Host "" +Write-Host "[OK] PictoPy dev environment is up!" -ForegroundColor Green +Write-Host " Press Ctrl+C to stop all services." +Write-Host "" + +# ── Stream logs from all jobs + handle Ctrl+C ──────────────────────────────── +try { + while ($true) { + foreach ($job in $jobs) { + $output = Receive-Job -Job $job -ErrorAction SilentlyContinue + if ($output) { + foreach ($line in $output) { + if ($line -like "\[BACKEND\]*") { Write-Host $line -ForegroundColor Green } + elseif ($line -like "\[SYNC\]*") { Write-Host $line -ForegroundColor Yellow } + else { Write-Host $line -ForegroundColor Blue } + } + } + } + Start-Sleep -Milliseconds 500 + } +} finally { + Write-Host "" + Warn "Shutting down all services..." + foreach ($job in $jobs) { + Stop-Job -Job $job -ErrorAction SilentlyContinue + Remove-Job -Job $job -ErrorAction SilentlyContinue + } + Ok "All services stopped. Goodbye!" +} \ No newline at end of file diff --git a/dev-setup.sh b/dev-setup.sh new file mode 100644 index 000000000..ad5c00640 --- /dev/null +++ b/dev-setup.sh @@ -0,0 +1,290 @@ +#!/bin/bash + +# ============================================================================= +# PictoPy Dev Setup & Launch Script +# Supports: Linux (Debian/Ubuntu/Arch) and macOS +# Usage: bash dev-setup.sh +# ============================================================================= + +set -e # exit on error + +# ── Colors ─────────────────────────────────────────────────────────────────── +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# ── Helpers ─────────────────────────────────────────────────────────────────── +ok() { echo -e "${GREEN}✓${NC} $1"; } +skip() { echo -e "${CYAN}→${NC} $1 (already installed, skipping)"; } +info() { echo -e "${BLUE}▸${NC} $1"; } +warn() { echo -e "${YELLOW}⚠${NC} $1"; } +fail() { echo -e "${RED}✗${NC} $1"; exit 1; } +header() { + echo "" + echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${BOLD} $1${NC}" + echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" +} + +# ── Detect OS ───────────────────────────────────────────────────────────────── +detect_os() { + if [[ "$OSTYPE" == "darwin"* ]]; then + OS="mac" + elif [[ -f /etc/debian_version ]]; then + OS="debian" + elif [[ -f /etc/arch-release ]]; then + OS="arch" + elif [[ -f /etc/fedora-release ]]; then + OS="fedora" + else + OS="linux-other" + fi + info "Detected OS: $OS" +} + +# ── Get script directory (root of PictoPy) ──────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FRONTEND_DIR="$SCRIPT_DIR/frontend" +BACKEND_DIR="$SCRIPT_DIR/backend" +SYNC_DIR="$SCRIPT_DIR/sync-microservice" + +# ── Validate we are in the PictoPy root ─────────────────────────────────────── +if [[ ! -d "$FRONTEND_DIR" || ! -d "$BACKEND_DIR" || ! -d "$SYNC_DIR" ]]; then + fail "Please run this script from the root of the PictoPy repository." +fi + +# ============================================================================= +# PHASE 1 — PREREQUISITES +# ============================================================================= +header "Phase 1: Checking Prerequisites" + +detect_os + +# ── Node.js ─────────────────────────────────────────────────────────────────── +if command -v node &>/dev/null; then + skip "Node.js ($(node --version))" +else + info "Installing Node.js..." + if [[ "$OS" == "mac" ]]; then + brew install node || fail "Homebrew not found. Install it from https://brew.sh first." + elif [[ "$OS" == "debian" ]]; then + curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - + sudo apt-get install -y nodejs + elif [[ "$OS" == "arch" ]]; then + sudo pacman -S --noconfirm nodejs npm + else + warn "Cannot auto-install Node.js on this OS. Please install it manually from https://nodejs.org" + fail "Node.js is required." + fi + ok "Node.js installed ($(node --version))" +fi + +# ── Rust / Cargo ────────────────────────────────────────────────────────────── +if command -v cargo &>/dev/null; then + skip "Rust/Cargo ($(cargo --version))" +else + info "Installing Rust via rustup..." + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + source "$HOME/.cargo/env" + ok "Rust installed ($(cargo --version))" +fi + +# ── Tauri system dependencies (Linux only) ──────────────────────────────────── +if [[ "$OS" == "debian" ]]; then + info "Checking Tauri system dependencies..." + TAURI_DEPS=( + libwebkit2gtk-4.1-dev + libappindicator3-dev + librsvg2-dev + patchelf + libglib2.0-dev + libgl1-mesa-glx + pkg-config + build-essential + curl + wget + file + libxdo-dev + libssl-dev + libayatana-appindicator3-dev + ) + MISSING_DEPS=() + for dep in "${TAURI_DEPS[@]}"; do + dpkg -s "$dep" &>/dev/null || MISSING_DEPS+=("$dep") + done + + if [[ ${#MISSING_DEPS[@]} -eq 0 ]]; then + skip "All Tauri system dependencies" + else + info "Installing missing system deps: ${MISSING_DEPS[*]}" + sudo apt-get update -qq + sudo apt-get install -y "${MISSING_DEPS[@]}" + ok "Tauri system dependencies installed" + fi +elif [[ "$OS" == "arch" ]]; then + info "Checking Tauri system dependencies (Arch)..." + sudo pacman -S --needed --noconfirm webkit2gtk base-devel curl wget file openssl appmenu-gtk-module gtk3 libappindicator-gtk3 librsvg libvips + ok "Tauri system dependencies installed" +elif [[ "$OS" == "mac" ]]; then + skip "Tauri system dependencies (not needed on macOS)" +fi + +# ── Miniconda ───────────────────────────────────────────────────────────────── +if command -v conda &>/dev/null; then + skip "Miniconda/Conda ($(conda --version))" +else + info "Installing Miniconda..." + if [[ "$OS" == "mac" ]]; then + MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh" + # Use x86_64 if not Apple Silicon + [[ "$(uname -m)" != "arm64" ]] && MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh" + else + MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh" + fi + curl -fsSL "$MINICONDA_URL" -o /tmp/miniconda.sh + bash /tmp/miniconda.sh -b -p "$HOME/miniconda3" + rm /tmp/miniconda.sh + export PATH="$HOME/miniconda3/bin:$PATH" + conda init bash 2>/dev/null || true + ok "Miniconda installed" + warn "You may need to restart your terminal after setup for conda to work globally." +fi + +# Make sure conda is available in this shell session +CONDA_BIN=$(conda info --base 2>/dev/null)/bin/conda +export PATH="$(conda info --base 2>/dev/null)/bin:$PATH" + +# ============================================================================= +# PHASE 2 — SERVICE SETUP +# ============================================================================= +header "Phase 2: Setting Up Services" + +# ── Frontend ────────────────────────────────────────────────────────────────── +info "Frontend: checking node_modules..." +if [[ -d "$FRONTEND_DIR/node_modules" ]]; then + skip "Frontend npm dependencies" +else + info "Frontend: running npm install..." + (cd "$FRONTEND_DIR" && npm install) + ok "Frontend dependencies installed" +fi + +# ── Backend ─────────────────────────────────────────────────────────────────── +info "Backend: checking conda environment..." +BACKEND_ENV="$BACKEND_DIR/.env" +if [[ -d "$BACKEND_ENV" ]]; then + skip "Backend conda environment (.env)" +else + info "Backend: creating conda environment with Python 3.12..." + conda create -p "$BACKEND_ENV" python=3.12 -y + ok "Backend conda environment created" +fi + +info "Backend: installing Python dependencies..." +# Check if deps are already installed by testing a key package +if conda run -p "$BACKEND_ENV" python -c "import fastapi" &>/dev/null; then + skip "Backend Python packages" +else + conda run -p "$BACKEND_ENV" pip install -r "$BACKEND_DIR/requirements.txt" + ok "Backend Python packages installed" +fi + +# ── Sync Microservice ───────────────────────────────────────────────────────── +info "Sync microservice: checking conda environment..." +SYNC_ENV="$SYNC_DIR/.sync-env" +if [[ -d "$SYNC_ENV" ]]; then + skip "Sync microservice conda environment (.sync-env)" +else + info "Sync microservice: creating conda environment with Python 3.12..." + conda create -p "$SYNC_ENV" python=3.12 -y + ok "Sync microservice conda environment created" +fi + +info "Sync microservice: installing Python dependencies..." +if conda run -p "$SYNC_ENV" python -c "import fastapi" &>/dev/null; then + skip "Sync microservice Python packages" +else + conda run -p "$SYNC_ENV" pip install -r "$SYNC_DIR/requirements.txt" + ok "Sync microservice Python packages installed" +fi + +# ============================================================================= +# PHASE 3 — LAUNCH ALL SERVICES +# ============================================================================= +header "Phase 3: Launching All Services" + +# Store background process PIDs for cleanup +PIDS=() + +# Cleanup on Ctrl+C or exit +cleanup() { + echo "" + warn "Shutting down all services..." + for pid in "${PIDS[@]}"; do + kill "$pid" 2>/dev/null || true + done + ok "All services stopped. Goodbye!" + exit 0 +} +trap cleanup SIGINT SIGTERM + +# ── Launch Backend ──────────────────────────────────────────────────────────── +info "Starting backend on port 52123..." +( + cd "$BACKEND_DIR" + conda run -p "$BACKEND_ENV" fastapi dev --port 52123 2>&1 | \ + while IFS= read -r line; do echo -e "${GREEN}[BACKEND]${NC} $line"; done +) & +PIDS+=($!) + +# ── Launch Sync Microservice ────────────────────────────────────────────────── +info "Starting sync microservice on port 52124..." +( + cd "$SYNC_DIR" + conda run -p "$SYNC_ENV" fastapi dev --port 52124 2>&1 | \ + while IFS= read -r line; do echo -e "${YELLOW}[SYNC]${NC} $line"; done +) & +PIDS+=($!) + +# ── Launch Frontend ─────────────────────────────────────────────────────────── +info "Starting Tauri frontend..." +( + cd "$FRONTEND_DIR" + npm run tauri dev 2>&1 | \ + while IFS= read -r line; do echo -e "${BLUE}[FRONTEND]${NC} $line"; done +) & +PIDS+=($!) + +# ============================================================================= +# PHASE 4 — HEALTH CHECK +# ============================================================================= +header "Phase 4: Health Check" + +info "Waiting 10 seconds for services to start..." +sleep 10 + +# Check backend +if curl -s "http://localhost:52123" &>/dev/null || curl -s "http://localhost:52123/docs" &>/dev/null; then + ok "Backend is running at http://localhost:52123" +else + warn "Backend may still be starting up — check [BACKEND] logs above" +fi + +# Check sync microservice +if curl -s "http://localhost:52124" &>/dev/null || curl -s "http://localhost:52124/docs" &>/dev/null; then + ok "Sync microservice is running at http://localhost:52124" +else + warn "Sync microservice may still be starting — check [SYNC] logs above" +fi + +echo "" +echo -e "${BOLD}${GREEN}✓ PictoPy dev environment is up!${NC}" +echo -e " Press ${BOLD}Ctrl+C${NC} to stop all services." +echo "" + +# Keep script alive so logs keep streaming and Ctrl+C works +wait \ No newline at end of file From 5fcffab9ba82f5137f9abc8ed998a36dda727ead Mon Sep 17 00:00:00 2001 From: aliviahossain Date: Mon, 29 Jun 2026 11:30:38 +0530 Subject: [PATCH 2/3] fix: resolve CodeRabbit review comments on ps1 script --- dev-setup.ps1 | 96 ++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/dev-setup.ps1 b/dev-setup.ps1 index b2f9f7060..370b3dc7b 100644 --- a/dev-setup.ps1 +++ b/dev-setup.ps1 @@ -5,21 +5,21 @@ # ============================================================================= # Allow script execution if blocked: -# Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +# Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned $ErrorActionPreference = "Stop" # ── Colors / Helpers ────────────────────────────────────────────────────────── -function Ok($msg) { Write-Host "[OK] $msg" -ForegroundColor Green } -function Skip($msg) { Write-Host "[SKIP] $msg (already installed, skipping)" -ForegroundColor Cyan } -function Info($msg) { Write-Host "[INFO] $msg" -ForegroundColor Blue } -function Warn($msg) { Write-Host "[WARN] $msg" -ForegroundColor Yellow } -function Fail($msg) { Write-Host "[FAIL] $msg" -ForegroundColor Red; exit 1 } +function Ok($msg) { Write-Host "✓ $msg" -ForegroundColor Green } +function Skip($msg) { Write-Host "→ $msg (already installed, skipping)" -ForegroundColor Cyan } +function Info($msg) { Write-Host "▸ $msg" -ForegroundColor Blue } +function Warn($msg) { Write-Host "⚠ $msg" -ForegroundColor Yellow } +function Fail($msg) { Write-Host "✗ $msg" -ForegroundColor Red; exit 1 } function Header($msg) { Write-Host "" - Write-Host "------------------------------------------------" -ForegroundColor Blue + Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Blue Write-Host " $msg" -ForegroundColor White - Write-Host "------------------------------------------------" -ForegroundColor Blue + Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Blue } # ── Validate we are in the PictoPy root ─────────────────────────────────────── @@ -37,7 +37,7 @@ if (-not (Test-Path $FRONTEND_DIR) -or -not (Test-Path $BACKEND_DIR) -or -not (T # ============================================================================= Header "Phase 1: Checking Prerequisites" -# ── Winget (built into Windows 11, available on Win10 too) ─────────────────── +# ── Winget ──────────────────────────────────────────────────────────────────── if (-not (Get-Command winget -ErrorAction SilentlyContinue)) { Warn "winget not found. Install 'App Installer' from the Microsoft Store, then re-run." Fail "winget is required for auto-installation." @@ -49,8 +49,9 @@ if (Get-Command node -ErrorAction SilentlyContinue) { } else { Info "Installing Node.js via winget..." winget install -e --id OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements - # Refresh PATH + if ($LASTEXITCODE -ne 0) { Fail "Node.js installation failed." } $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User") + if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Fail "Node.js installed but not found in PATH. Please restart PowerShell and re-run." } Ok "Node.js installed ($(node --version))" } @@ -60,14 +61,12 @@ if (Get-Command cargo -ErrorAction SilentlyContinue) { } else { Info "Installing Rust via rustup..." winget install -e --id Rustlang.Rustup --accept-source-agreements --accept-package-agreements - # Refresh PATH + if ($LASTEXITCODE -ne 0) { Fail "Rust installation failed." } $env:PATH = "$env:USERPROFILE\.cargo\bin;" + $env:PATH if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) { - Warn "Rust installed but cargo not in PATH yet." - Warn "Please restart PowerShell after this script finishes, then re-run." - } else { - Ok "Rust installed ($(cargo --version))" + Fail "Rust installed but cargo not in PATH. Please restart PowerShell and re-run." } + Ok "Rust installed ($(cargo --version))" } # ── Visual Studio C++ Build Tools (required by Tauri on Windows) ────────────── @@ -84,6 +83,7 @@ if ($hasBuildTools) { Info "Installing Visual Studio Build Tools (this may take a while)..." winget install -e --id Microsoft.VisualStudio.2022.BuildTools --accept-source-agreements --accept-package-agreements ` --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended" + if ($LASTEXITCODE -ne 0) { Fail "Visual Studio Build Tools installation failed." } Ok "Visual Studio C++ Build Tools installed" } @@ -95,6 +95,7 @@ if (Test-Path $webview2Key) { } else { Info "Installing WebView2 Runtime..." winget install -e --id Microsoft.EdgeWebView2Runtime --accept-source-agreements --accept-package-agreements + if ($LASTEXITCODE -ne 0) { Fail "WebView2 Runtime installation failed." } Ok "WebView2 Runtime installed" } @@ -108,23 +109,16 @@ if (Get-Command conda -ErrorAction SilentlyContinue) { Invoke-WebRequest -Uri $minicondaUrl -OutFile $minicondaInstaller Start-Process -FilePath $minicondaInstaller -ArgumentList "/S /D=$env:USERPROFILE\miniconda3" -Wait Remove-Item $minicondaInstaller - - # Add conda to PATH for this session $env:PATH = "$env:USERPROFILE\miniconda3\Scripts;" + "$env:USERPROFILE\miniconda3;" + $env:PATH - if (-not (Get-Command conda -ErrorAction SilentlyContinue)) { - Warn "Miniconda installed but conda not in PATH yet." - Warn "Please restart PowerShell after this script finishes, then re-run." - Fail "conda not available in current session." + Fail "Miniconda installed but conda not in PATH. Please restart PowerShell and re-run." } Ok "Miniconda installed ($(conda --version))" } # Initialize conda for PowerShell if not already done $condaHook = "$env:USERPROFILE\miniconda3\shell\condabin\conda-hook.ps1" -if (Test-Path $condaHook) { - & $condaHook -} +if (Test-Path $condaHook) { & $condaHook } # ============================================================================= # PHASE 2 — SERVICE SETUP @@ -139,6 +133,7 @@ if (Test-Path (Join-Path $FRONTEND_DIR "node_modules")) { Info "Frontend: running npm install..." Push-Location $FRONTEND_DIR npm install + if ($LASTEXITCODE -ne 0) { Pop-Location; Fail "npm install failed." } Pop-Location Ok "Frontend dependencies installed" } @@ -151,16 +146,18 @@ if (Test-Path $BACKEND_ENV) { } else { Info "Backend: creating conda environment with Python 3.12..." conda create -p $BACKEND_ENV python=3.12 -y + if ($LASTEXITCODE -ne 0) { Fail "Failed to create backend conda environment." } Ok "Backend conda environment created" } Info "Backend: checking Python packages..." -$backendHasFastapi = conda run -p $BACKEND_ENV python -c "import fastapi" 2>$null +conda run -p $BACKEND_ENV python -c "import fastapi" 2>$null if ($LASTEXITCODE -eq 0) { Skip "Backend Python packages" } else { Info "Backend: installing Python packages..." conda run -p $BACKEND_ENV pip install -r (Join-Path $BACKEND_DIR "requirements.txt") + if ($LASTEXITCODE -ne 0) { Fail "Backend pip install failed." } Ok "Backend Python packages installed" } @@ -172,16 +169,18 @@ if (Test-Path $SYNC_ENV) { } else { Info "Sync microservice: creating conda environment with Python 3.12..." conda create -p $SYNC_ENV python=3.12 -y + if ($LASTEXITCODE -ne 0) { Fail "Failed to create sync microservice conda environment." } Ok "Sync microservice conda environment created" } Info "Sync microservice: checking Python packages..." -$syncHasFastapi = conda run -p $SYNC_ENV python -c "import fastapi" 2>$null +conda run -p $SYNC_ENV python -c "import fastapi" 2>$null if ($LASTEXITCODE -eq 0) { Skip "Sync microservice Python packages" } else { Info "Sync microservice: installing Python packages..." conda run -p $SYNC_ENV pip install -r (Join-Path $SYNC_DIR "requirements.txt") + if ($LASTEXITCODE -ne 0) { Fail "Sync microservice pip install failed." } Ok "Sync microservice Python packages installed" } @@ -190,34 +189,30 @@ if ($LASTEXITCODE -eq 0) { # ============================================================================= Header "Phase 3: Launching All Services" -# Track jobs for cleanup $jobs = @() # ── Launch Backend ──────────────────────────────────────────────────────────── Info "Starting backend on port 52123..." $backendJob = Start-Job -ScriptBlock { - param($dir, $env) - Set-Location $dir - conda run -p $env fastapi dev --port 52123 2>&1 | ForEach-Object { "[BACKEND] $_" } -} -ArgumentList $BACKEND_DIR, $BACKEND_ENV + Set-Location $using:BACKEND_DIR + conda run -p $using:BACKEND_ENV fastapi dev --port 52123 2>&1 | ForEach-Object { "[BACKEND] $_" } +} $jobs += $backendJob # ── Launch Sync Microservice ────────────────────────────────────────────────── Info "Starting sync microservice on port 52124..." $syncJob = Start-Job -ScriptBlock { - param($dir, $env) - Set-Location $dir - conda run -p $env fastapi dev --port 52124 2>&1 | ForEach-Object { "[SYNC] $_" } -} -ArgumentList $SYNC_DIR, $SYNC_ENV + Set-Location $using:SYNC_DIR + conda run -p $using:SYNC_ENV fastapi dev --port 52124 2>&1 | ForEach-Object { "[SYNC] $_" } +} $jobs += $syncJob # ── Launch Frontend ─────────────────────────────────────────────────────────── Info "Starting Tauri frontend..." $frontendJob = Start-Job -ScriptBlock { - param($dir) - Set-Location $dir + Set-Location $using:FRONTEND_DIR npm run tauri dev 2>&1 | ForEach-Object { "[FRONTEND] $_" } -} -ArgumentList $FRONTEND_DIR +} $jobs += $frontendJob # ============================================================================= @@ -229,11 +224,13 @@ Info "Waiting 10 seconds for services to start..." Start-Sleep -Seconds 10 # Check backend +$allHealthy = $true try { Invoke-WebRequest -Uri "http://localhost:52123/docs" -UseBasicParsing -TimeoutSec 3 | Out-Null Ok "Backend is running at http://localhost:52123" } catch { - Warn "Backend may still be starting up — check [BACKEND] logs below" + Warn "Backend did not respond — check [BACKEND] logs below" + $allHealthy = $false } # Check sync microservice @@ -241,13 +238,18 @@ try { Invoke-WebRequest -Uri "http://localhost:52124/docs" -UseBasicParsing -TimeoutSec 3 | Out-Null Ok "Sync microservice is running at http://localhost:52124" } catch { - Warn "Sync microservice may still be starting — check [SYNC] logs below" + Warn "Sync microservice did not respond — check [SYNC] logs below" + $allHealthy = $false } -Write-Host "" -Write-Host "[OK] PictoPy dev environment is up!" -ForegroundColor Green -Write-Host " Press Ctrl+C to stop all services." -Write-Host "" +if (-not $allHealthy) { + Warn "One or more services failed to start. Check logs below then press Ctrl+C to exit." +} else { + Write-Host "" + Write-Host "✓ PictoPy dev environment is up!" -ForegroundColor Green + Write-Host " Press Ctrl+C to stop all services." + Write-Host "" +} # ── Stream logs from all jobs + handle Ctrl+C ──────────────────────────────── try { @@ -256,9 +258,9 @@ try { $output = Receive-Job -Job $job -ErrorAction SilentlyContinue if ($output) { foreach ($line in $output) { - if ($line -like "\[BACKEND\]*") { Write-Host $line -ForegroundColor Green } - elseif ($line -like "\[SYNC\]*") { Write-Host $line -ForegroundColor Yellow } - else { Write-Host $line -ForegroundColor Blue } + if ($line.StartsWith('[BACKEND]')) { Write-Host $line -ForegroundColor Green } + elseif ($line.StartsWith('[SYNC]')) { Write-Host $line -ForegroundColor Yellow } + else { Write-Host $line -ForegroundColor Blue } } } } From 861660daa7d88d6787d02263266e999a1dbe13a3 Mon Sep 17 00:00:00 2001 From: aliviahossain Date: Mon, 29 Jun 2026 11:35:07 +0530 Subject: [PATCH 3/3] fix: address CodeRabbit review comments on sh script and SETUP.md --- SETUP.md | 156 +++++++++++++++++---------------------------- dev-setup.sh | 176 +++++++++++++++++++++++---------------------------- 2 files changed, 138 insertions(+), 194 deletions(-) diff --git a/SETUP.md b/SETUP.md index 6be9a5003..d21a59e5c 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,80 +1,66 @@ # PictoPy Dev Setup Guide -This guide covers how to set up and run PictoPy locally for development on **Windows** and **Linux/macOS**. - ---- - -## Prerequisites - -| Tool | Version | Purpose | -|------|---------|---------| -| Node.js | 18+ | Frontend build | -| Rust + Cargo | latest stable | Tauri desktop framework | -| Python | 3.12 | Backend + sync microservice | -| Conda (Miniconda) | any | Python environment management | +This guide covers how to get the PictoPy development environment running on your machine — either automatically with a single script, or manually step by step. --- ## Quick Start (Recommended) -Both scripts handle all prerequisite installation, environment setup, and launch all three services automatically. - ### Windows -Open **PowerShell** in the PictoPy root directory and run: - ```powershell -Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned +# One-time: allow script execution for this session only +Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned + +# Run the setup script .\dev-setup.ps1 ``` -> If prompted by Windows Defender, choose "Run anyway". - ### Linux / macOS -Open a terminal in the PictoPy root directory and run: - ```bash -bash dev-setup.sh +chmod +x dev-setup.sh +./dev-setup.sh ``` -> On macOS, Homebrew must be installed first: https://brew.sh +The script handles everything: installs prerequisites, creates conda environments, installs dependencies, launches all 3 services, and runs a health check. On re-runs it skips anything already installed. --- -## What the Scripts Do +## What the Script Does -Both scripts run the same four phases: +**Phase 1 — Prerequisites:** Detects your OS and installs Node.js, Rust/Cargo, Miniconda, and platform-specific Tauri dependencies (webkit2gtk on Linux, VS C++ Build Tools + WebView2 on Windows) if missing. -**Phase 1 — Prerequisites** -- Installs Node.js if missing -- Installs Rust + Cargo via rustup if missing -- Installs Visual Studio C++ Build Tools (Windows) or system libs (Linux) required by Tauri -- Installs Miniconda if missing +**Phase 2 — Service Setup:** Creates conda environments for the backend (`.env`) and sync microservice (`.sync-env`), installs pip dependencies, and runs `npm install` for the frontend. -**Phase 2 — Service Setup** -- Installs frontend npm dependencies (`frontend/node_modules`) -- Creates conda environment for the backend (`backend/.env`) and installs Python packages -- Creates conda environment for the sync microservice (`sync-microservice/.sync-env`) and installs Python packages +**Phase 3 — Launch:** Starts all 3 services in a single terminal with color-coded log prefixes `[BACKEND]`, `[SYNC]`, `[FRONTEND]`. Press `Ctrl+C` to stop all services cleanly. -**Phase 3 — Launch** -- Starts the Python backend on port `52123` -- Starts the sync microservice on port `52124` -- Starts the Tauri frontend (React + Rust) +**Phase 4 — Health Check:** Pings both Python servers after 10 seconds to confirm they started successfully. -**Phase 4 — Health Check** -- Verifies backend and sync microservice are reachable -- Streams logs from all three services with colour-coded prefixes (`[BACKEND]`, `[SYNC]`, `[FRONTEND]`) +--- + +## Services & Ports -Press **Ctrl+C** to stop all services. +| Service | Port | Start Command | +|-------------------|-------|----------------------------------------| +| Python Backend | 52123 | `fastapi dev --port 52123` | +| Sync Microservice | 52124 | `fastapi dev --port 52124` | +| Tauri Frontend | — | `npm run tauri dev` | --- -## Manual Setup (Alternative) +## Manual Setup + +If you prefer to set up manually or the script doesn't work on your OS: + +### Prerequisites -If you prefer to set up each service individually: +- [Miniconda](https://www.anaconda.com/docs/getting-started/miniconda/install) +- [Node.js](https://nodejs.org) (v20+) +- [Rust](https://rustup.rs) +- Tauri system dependencies — see [tauri.app/start/prerequisites](https://tauri.app/start/prerequisites/) -### 1. Frontend +### Step 1 — Frontend ```bash cd frontend @@ -82,81 +68,55 @@ npm install npm run tauri dev ``` -> **Windows only:** Rust requires Visual Studio C++ Build Tools (`link.exe`). -> Install via: `winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"` - -### 2. Backend +### Step 2 — Backend ```bash cd backend conda create -p .env python=3.12 -y -conda run -p .env pip install -r requirements.txt -conda run -p .env python main.py +conda activate ./.env +pip install -r requirements.txt +fastapi dev --port 52123 ``` -Backend runs at: `http://localhost:52123` - -### 3. Sync Microservice +### Step 3 — Sync Microservice ```bash cd sync-microservice conda create -p .sync-env python=3.12 -y -conda run -p .sync-env pip install -r requirements.txt -conda run -p .sync-env fastapi dev --port 52124 +conda activate ./.sync-env +pip install -r requirements.txt +fastapi dev --port 52124 ``` -Sync microservice runs at: `http://localhost:52124` - ---- - -## Services Overview - -| Service | Port | Description | -|---------|------|-------------| -| Backend (Python/FastAPI) | 52123 | Image processing, face recognition, albums | -| Sync Microservice (Python/FastAPI) | 52124 | File sync and watching | -| Frontend (Tauri/React) | — | Desktop app window (not a browser port) | - -All three must be running for the app to work correctly. +Run each in a separate terminal. --- ## Troubleshooting -### `cargo` not found after installing Rust -Close and reopen your terminal — Rust adds itself to PATH only for new sessions. - -### `link.exe` not found (Windows) -Visual Studio C++ Build Tools are missing. Run: -```powershell -winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended" +**Missing `libGL.so.1` (OpenCV error on Linux)** +```bash +sudo apt install -y libglib2.0-dev libgl1-mesa-glx ``` -Then reopen your terminal and retry. -### `chmod` not recognized (Windows) -`chmod` is a Linux command. On Windows, skip it — just run `.\dev-setup.ps1` directly. - -### App window opens but shows a blank page -The Python backend is not running. Start it manually: +**`gobject-2.0` not found** ```bash -cd backend -conda run -p .env python main.py +sudo apt install -y libglib2.0-dev pkg-config ``` -### Script fails on emoji / encoding error (Windows PowerShell) -Your PowerShell version may not support Unicode. Upgrade to PowerShell 7+: -```powershell -winget install Microsoft.PowerShell -``` +**Port already in use** -### Port already in use -Kill the process occupying the port: -```powershell -# Windows -netstat -ano | findstr :52123 -taskkill /PID /F -``` +Find and kill the process holding the port: ```bash -# Linux/macOS -lsof -ti:52123 | xargs kill +# Safe form — only kills if a process is found +lsof -ti :52123 | xargs -r kill -9 +lsof -ti :52124 | xargs -r kill -9 ``` + +**conda not found after fresh install** + +Restart your terminal. Conda adds itself to PATH in `.bashrc`/`.zshrc` but the change only takes effect in a new session. + +**Rust/cargo not found after fresh install (Windows)** + +Restart PowerShell after rustup installs, then re-run `dev-setup.ps1`. \ No newline at end of file diff --git a/dev-setup.sh b/dev-setup.sh index ad5c00640..f56c3f41f 100644 --- a/dev-setup.sh +++ b/dev-setup.sh @@ -6,7 +6,7 @@ # Usage: bash dev-setup.sh # ============================================================================= -set -e # exit on error +set -euo pipefail # ── Colors ─────────────────────────────────────────────────────────────────── RED='\033[0;31m' @@ -15,14 +15,14 @@ YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' -NC='\033[0m' # No Color +NC='\033[0m' # ── Helpers ─────────────────────────────────────────────────────────────────── -ok() { echo -e "${GREEN}✓${NC} $1"; } -skip() { echo -e "${CYAN}→${NC} $1 (already installed, skipping)"; } -info() { echo -e "${BLUE}▸${NC} $1"; } -warn() { echo -e "${YELLOW}⚠${NC} $1"; } -fail() { echo -e "${RED}✗${NC} $1"; exit 1; } +ok() { echo -e "${GREEN}✓${NC} $1"; } +skip() { echo -e "${CYAN}→${NC} $1 (already installed, skipping)"; } +info() { echo -e "${BLUE}▸${NC} $1"; } +warn() { echo -e "${YELLOW}⚠${NC} $1"; } +fail() { echo -e "${RED}✗${NC} $1"; exit 1; } header() { echo "" echo -e "${BOLD}${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" @@ -46,17 +46,32 @@ detect_os() { info "Detected OS: $OS" } -# ── Get script directory (root of PictoPy) ──────────────────────────────────── +# ── Script directory ────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" FRONTEND_DIR="$SCRIPT_DIR/frontend" BACKEND_DIR="$SCRIPT_DIR/backend" SYNC_DIR="$SCRIPT_DIR/sync-microservice" -# ── Validate we are in the PictoPy root ─────────────────────────────────────── +# ── Validate PictoPy root ───────────────────────────────────────────────────── if [[ ! -d "$FRONTEND_DIR" || ! -d "$BACKEND_DIR" || ! -d "$SYNC_DIR" ]]; then fail "Please run this script from the root of the PictoPy repository." fi +# ── Process group cleanup on Ctrl+C ────────────────────────────────────────── +# Each service is started in its own process group (setsid) so kill -TERM -- -PID +# kills the entire group including fastapi/tauri child processes, not just the wrapper. +PGRPS=() +cleanup() { + echo "" + warn "Shutting down all services..." + for pgrp in "${PGRPS[@]}"; do + kill -TERM -- "-$pgrp" 2>/dev/null || true + done + ok "All services stopped. Goodbye!" + exit 0 +} +trap cleanup SIGINT SIGTERM + # ============================================================================= # PHASE 1 — PREREQUISITES # ============================================================================= @@ -72,12 +87,16 @@ else if [[ "$OS" == "mac" ]]; then brew install node || fail "Homebrew not found. Install it from https://brew.sh first." elif [[ "$OS" == "debian" ]]; then - curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - + # Download to temp file and verify before executing + NODESOURCE_SCRIPT="$(mktemp)" + curl -fsSL https://deb.nodesource.com/setup_20.x -o "$NODESOURCE_SCRIPT" + sudo -E bash "$NODESOURCE_SCRIPT" + rm -f "$NODESOURCE_SCRIPT" sudo apt-get install -y nodejs elif [[ "$OS" == "arch" ]]; then sudo pacman -S --noconfirm nodejs npm else - warn "Cannot auto-install Node.js on this OS. Please install it manually from https://nodejs.org" + warn "Cannot auto-install Node.js on this OS. Install manually from https://nodejs.org" fail "Node.js is required." fi ok "Node.js installed ($(node --version))" @@ -97,26 +116,14 @@ fi if [[ "$OS" == "debian" ]]; then info "Checking Tauri system dependencies..." TAURI_DEPS=( - libwebkit2gtk-4.1-dev - libappindicator3-dev - librsvg2-dev - patchelf - libglib2.0-dev - libgl1-mesa-glx - pkg-config - build-essential - curl - wget - file - libxdo-dev - libssl-dev - libayatana-appindicator3-dev + libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + libglib2.0-dev libgl1-mesa-glx pkg-config build-essential + curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev ) MISSING_DEPS=() for dep in "${TAURI_DEPS[@]}"; do dpkg -s "$dep" &>/dev/null || MISSING_DEPS+=("$dep") done - if [[ ${#MISSING_DEPS[@]} -eq 0 ]]; then skip "All Tauri system dependencies" else @@ -127,7 +134,8 @@ if [[ "$OS" == "debian" ]]; then fi elif [[ "$OS" == "arch" ]]; then info "Checking Tauri system dependencies (Arch)..." - sudo pacman -S --needed --noconfirm webkit2gtk base-devel curl wget file openssl appmenu-gtk-module gtk3 libappindicator-gtk3 librsvg libvips + sudo pacman -S --needed --noconfirm webkit2gtk base-devel curl wget file openssl \ + appmenu-gtk-module gtk3 libappindicator-gtk3 librsvg libvips ok "Tauri system dependencies installed" elif [[ "$OS" == "mac" ]]; then skip "Tauri system dependencies (not needed on macOS)" @@ -140,22 +148,22 @@ else info "Installing Miniconda..." if [[ "$OS" == "mac" ]]; then MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-arm64.sh" - # Use x86_64 if not Apple Silicon [[ "$(uname -m)" != "arm64" ]] && MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh" else MINICONDA_URL="https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh" fi - curl -fsSL "$MINICONDA_URL" -o /tmp/miniconda.sh - bash /tmp/miniconda.sh -b -p "$HOME/miniconda3" - rm /tmp/miniconda.sh + # Download to temp file + MINICONDA_SCRIPT="$(mktemp)" + curl -fsSL "$MINICONDA_URL" -o "$MINICONDA_SCRIPT" + bash "$MINICONDA_SCRIPT" -b -p "$HOME/miniconda3" + rm -f "$MINICONDA_SCRIPT" export PATH="$HOME/miniconda3/bin:$PATH" conda init bash 2>/dev/null || true ok "Miniconda installed" warn "You may need to restart your terminal after setup for conda to work globally." fi -# Make sure conda is available in this shell session -CONDA_BIN=$(conda info --base 2>/dev/null)/bin/conda +# Ensure conda is available in this session export PATH="$(conda info --base 2>/dev/null)/bin:$PATH" # ============================================================================= @@ -184,14 +192,10 @@ else ok "Backend conda environment created" fi -info "Backend: installing Python dependencies..." -# Check if deps are already installed by testing a key package -if conda run -p "$BACKEND_ENV" python -c "import fastapi" &>/dev/null; then - skip "Backend Python packages" -else - conda run -p "$BACKEND_ENV" pip install -r "$BACKEND_DIR/requirements.txt" - ok "Backend Python packages installed" -fi +# Always run pip install — idempotent and catches requirements.txt changes +info "Backend: installing/syncing Python dependencies..." +conda run -p "$BACKEND_ENV" pip install -r "$BACKEND_DIR/requirements.txt" --quiet +ok "Backend Python packages ready" # ── Sync Microservice ───────────────────────────────────────────────────────── info "Sync microservice: checking conda environment..." @@ -204,60 +208,33 @@ else ok "Sync microservice conda environment created" fi -info "Sync microservice: installing Python dependencies..." -if conda run -p "$SYNC_ENV" python -c "import fastapi" &>/dev/null; then - skip "Sync microservice Python packages" -else - conda run -p "$SYNC_ENV" pip install -r "$SYNC_DIR/requirements.txt" - ok "Sync microservice Python packages installed" -fi +# Always run pip install — idempotent and catches requirements.txt changes +info "Sync microservice: installing/syncing Python dependencies..." +conda run -p "$SYNC_ENV" pip install -r "$SYNC_DIR/requirements.txt" --quiet +ok "Sync microservice Python packages ready" # ============================================================================= # PHASE 3 — LAUNCH ALL SERVICES # ============================================================================= header "Phase 3: Launching All Services" -# Store background process PIDs for cleanup -PIDS=() - -# Cleanup on Ctrl+C or exit -cleanup() { - echo "" - warn "Shutting down all services..." - for pid in "${PIDS[@]}"; do - kill "$pid" 2>/dev/null || true - done - ok "All services stopped. Goodbye!" - exit 0 -} -trap cleanup SIGINT SIGTERM +# Launch each service in its own process group via setsid +# This ensures kill -TERM -- -PGRP kills fastapi/tauri children too -# ── Launch Backend ──────────────────────────────────────────────────────────── info "Starting backend on port 52123..." -( - cd "$BACKEND_DIR" - conda run -p "$BACKEND_ENV" fastapi dev --port 52123 2>&1 | \ - while IFS= read -r line; do echo -e "${GREEN}[BACKEND]${NC} $line"; done -) & -PIDS+=($!) - -# ── Launch Sync Microservice ────────────────────────────────────────────────── +setsid bash -c "cd '$BACKEND_DIR' && conda run -p '$BACKEND_ENV' fastapi dev --port 52123 2>&1 | \ + while IFS= read -r line; do echo -e '${GREEN}[BACKEND]${NC} '"'"'$line'"'"'; done" & +PGRPS+=("$!") + info "Starting sync microservice on port 52124..." -( - cd "$SYNC_DIR" - conda run -p "$SYNC_ENV" fastapi dev --port 52124 2>&1 | \ - while IFS= read -r line; do echo -e "${YELLOW}[SYNC]${NC} $line"; done -) & -PIDS+=($!) - -# ── Launch Frontend ─────────────────────────────────────────────────────────── +setsid bash -c "cd '$SYNC_DIR' && conda run -p '$SYNC_ENV' fastapi dev --port 52124 2>&1 | \ + while IFS= read -r line; do echo -e '${YELLOW}[SYNC]${NC} '"'"'$line'"'"'; done" & +PGRPS+=("$!") + info "Starting Tauri frontend..." -( - cd "$FRONTEND_DIR" - npm run tauri dev 2>&1 | \ - while IFS= read -r line; do echo -e "${BLUE}[FRONTEND]${NC} $line"; done -) & -PIDS+=($!) +setsid bash -c "cd '$FRONTEND_DIR' && npm run tauri dev 2>&1 | \ + while IFS= read -r line; do echo -e '${BLUE}[FRONTEND]${NC} '"'"'$line'"'"'; done" & +PGRPS+=("$!") # ============================================================================= # PHASE 4 — HEALTH CHECK @@ -267,24 +244,31 @@ header "Phase 4: Health Check" info "Waiting 10 seconds for services to start..." sleep 10 -# Check backend -if curl -s "http://localhost:52123" &>/dev/null || curl -s "http://localhost:52123/docs" &>/dev/null; then +HEALTHY=true + +if curl -s --max-time 3 "http://localhost:52123/docs" &>/dev/null; then ok "Backend is running at http://localhost:52123" else - warn "Backend may still be starting up — check [BACKEND] logs above" + warn "Backend did not respond on port 52123" + HEALTHY=false fi -# Check sync microservice -if curl -s "http://localhost:52124" &>/dev/null || curl -s "http://localhost:52124/docs" &>/dev/null; then +if curl -s --max-time 3 "http://localhost:52124/docs" &>/dev/null; then ok "Sync microservice is running at http://localhost:52124" else - warn "Sync microservice may still be starting — check [SYNC] logs above" + warn "Sync microservice did not respond on port 52124" + HEALTHY=false fi -echo "" -echo -e "${BOLD}${GREEN}✓ PictoPy dev environment is up!${NC}" -echo -e " Press ${BOLD}Ctrl+C${NC} to stop all services." -echo "" +if [[ "$HEALTHY" == false ]]; then + warn "One or more services failed to start. Check logs above." + warn "Press Ctrl+C to stop all services." +else + echo "" + echo -e "${BOLD}${GREEN}✓ PictoPy dev environment is up!${NC}" + echo -e " Press ${BOLD}Ctrl+C${NC} to stop all services." + echo "" +fi -# Keep script alive so logs keep streaming and Ctrl+C works +# Keep alive so logs stream and Ctrl+C triggers cleanup wait \ No newline at end of file