This should have been committed 100 years ago

This commit is contained in:
Evan Carroll 2026-01-16 04:19:40 -06:00
parent e57323ff3f
commit a102c96bb4

365
run-dev.sh Executable file
View file

@ -0,0 +1,365 @@
#!/bin/bash
# Run Chattyness servers in development mode
set -eu
cd "$(dirname "$0")"
# =============================================================================
# Constants
# =============================================================================
readonly APP_PORT=3000
readonly OWNER_PORT=3001
readonly LOCKFILE=".run-dev.lock"
# Process IDs (set during runtime)
OWNER_PID=""
APP_PID=""
# =============================================================================
# Helper Functions
# =============================================================================
usage() {
cat <<EOF
Usage: $0 [build|watch] [--user-app|--owner-app|--both] [-f|-k|-s|-r]
Commands:
build Build and run servers (default)
watch Watch mode with hot reload
Options:
--user-app Run only the user app (port $APP_PORT)
--owner-app Run only the owner admin (port $OWNER_PORT)
--both Run both servers (default)
-f, --force Kill existing instance and start new one
-k, --kill Kill existing instance and exit
-s, --status Check if an instance is running
-r, --release Build and run in release mode
Examples:
$0 # Build and run both
$0 watch # Watch mode for both
$0 --owner-app # Build and run owner only
$0 watch --user-app # Watch mode for user app only
$0 -f watch # Kill existing, then watch mode
$0 -k # Kill existing instance
$0 -s # Check status
$0 -r # Build and run in release mode
EOF
exit 1
}
# Target helper functions
run_owner() { [ "$TARGET" = "both" ] || [ "$TARGET" = "owner" ]; }
run_app() { [ "$TARGET" = "both" ] || [ "$TARGET" = "app" ]; }
# Cleanup function - kills child processes and removes lock file
cleanup() {
echo ""
echo "Shutting down..."
[ -n "$OWNER_PID" ] && kill "$OWNER_PID" 2>/dev/null || true
[ -n "$APP_PID" ] && kill "$APP_PID" 2>/dev/null || true
[ -n "$OWNER_PID" ] && wait "$OWNER_PID" 2>/dev/null || true
[ -n "$APP_PID" ] && wait "$APP_PID" 2>/dev/null || true
rm -f "$LOCKFILE"
echo "Done."
}
# Print server URLs based on what's running
print_server_info() {
local mode="$1"
local build_type="Debug"
[ "$RELEASE" = "true" ] && build_type="Release"
echo ""
echo "========================================"
echo " Chattyness Development ($mode - $build_type)"
echo "========================================"
echo ""
if run_owner; then
echo " Owner Admin: http://127.0.0.1:$OWNER_PORT"
fi
if run_app; then
echo " User App: http://127.0.0.1:$APP_PORT"
echo " Realm Admin: http://127.0.0.1:$APP_PORT/admin"
fi
echo ""
if [ "$TARGET" = "both" ]; then
echo "Admin CSS lazy-loaded via symlink from owner build."
fi
echo "Press Ctrl+C to stop"
echo ""
}
# Wait for running child processes
wait_for_processes() {
if [ "$TARGET" = "both" ]; then
wait "$OWNER_PID" "$APP_PID"
elif [ "$TARGET" = "owner" ]; then
wait "$OWNER_PID"
else
wait "$APP_PID"
fi
}
# Create symlink for admin CSS (user app loads admin CSS from owner build)
setup_admin_css_symlink() {
mkdir -p apps/chattyness-app/public
local target="../../../target/site-owner/static/chattyness-owner.css"
local link="apps/chattyness-app/public/admin.css"
if [ ! -L "$link" ]; then
ln -sf "$target" "$link"
echo "Created symlink: $link -> $target"
fi
}
# Load environment variables from .env file
load_env() {
if [ -f .env ]; then
set -a
# shellcheck source=/dev/null
source .env
set +a
fi
}
# Validate required environment variables
check_env() {
if [ -z "${DB_CHATTYNESS_OWNER:-}" ]; then
echo "Error: DB_CHATTYNESS_OWNER not set"
echo "Create a .env file with DB_CHATTYNESS_OWNER=your_password"
exit 1
fi
if [ -z "${DB_CHATTYNESS_APP:-}" ]; then
echo "Error: DB_CHATTYNESS_APP not set"
echo "Create a .env file with DB_CHATTYNESS_APP=your_password"
exit 1
fi
}
# =============================================================================
# Lock File Management
# =============================================================================
# Check if another instance is running (returns PID or empty)
get_running_pid() {
if [ -f "$LOCKFILE" ]; then
local pid
pid=$(cat "$LOCKFILE" 2>/dev/null)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
echo "$pid"
fi
fi
}
# Handle --status flag
handle_status() {
local pid
pid=$(get_running_pid)
if [ -n "$pid" ]; then
echo "Running (PID: $pid)"
exit 0
else
echo "Not running"
[ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock
exit 1
fi
}
# Kill any running instance (shared by -k and -f)
kill_running_instance() {
local pid
pid=$(get_running_pid)
if [ -n "$pid" ]; then
echo "Killing existing run-dev.sh (PID: $pid)..."
kill "$pid"
else
echo "No running instance found."
[ -f "$LOCKFILE" ] && rm -f "$LOCKFILE" # Clean stale lock
fi
echo "Killing chattyness processes..."
killall -9 chattyness-app 2>/dev/null || true
killall -9 chattyness-owner 2>/dev/null || true
echo "Done."
}
# Handle --kill flag
handle_kill() {
kill_running_instance
exit 0
}
# Handle --force flag (kill existing, then continue)
handle_force() {
kill_running_instance
sleep 1 # Allow lock release
}
# Acquire exclusive lock
acquire_lock() {
exec 200>"$LOCKFILE"
if ! flock -n 200; then
local pid
pid=$(cat "$LOCKFILE" 2>/dev/null)
echo ""
echo "ERROR: Another run-dev.sh instance is already running (PID: $pid)"
echo ""
echo "Options:"
echo " $0 -k Kill the existing instance"
echo " $0 -f [args...] Kill existing and start new instance"
echo " $0 -s Check status"
echo ""
exit 1
fi
echo $$ >"$LOCKFILE"
}
# =============================================================================
# Main Commands
# =============================================================================
do_watch() {
echo "Starting watch mode..."
echo ""
local release_flag=""
[ "$RELEASE" = "true" ] && release_flag="--release"
# Build owner first to create CSS (needed if user app needs admin CSS)
if run_owner; then
echo "Building owner app first (for admin CSS)..."
cargo leptos build -p chattyness-owner $release_flag
setup_admin_css_symlink
fi
# Start watch processes
if run_owner; then
cargo leptos watch -p chattyness-owner $release_flag &
OWNER_PID=$!
fi
if run_app; then
cargo leptos watch -p chattyness-app --split $release_flag &
APP_PID=$!
fi
print_server_info "Watch Mode"
wait_for_processes
}
do_build() {
local release_flag=""
local target_dir="debug"
if [ "$RELEASE" = "true" ]; then
release_flag="--release"
target_dir="release"
fi
# Build phase
if run_owner; then
echo "Running: cargo leptos build -p chattyness-owner $release_flag"
cargo leptos build -p chattyness-owner $release_flag
setup_admin_css_symlink
fi
if run_app; then
echo ""
echo "Running: cargo leptos build -p chattyness-app --split $release_flag"
cargo leptos build -p chattyness-app --split $release_flag
fi
# Verify binaries exist
if run_owner && [ ! -x "./target/$target_dir/chattyness-owner" ]; then
echo "Error: ./target/$target_dir/chattyness-owner not found or not executable"
exit 1
fi
if run_app && [ ! -x "./target/$target_dir/chattyness-app" ]; then
echo "Error: ./target/$target_dir/chattyness-app not found or not executable"
exit 1
fi
# Start servers
if run_owner; then
echo "Starting Owner Server on :$OWNER_PORT..."
./target/$target_dir/chattyness-owner &
OWNER_PID=$!
fi
if run_app; then
echo "Starting App Server on :$APP_PORT..."
./target/$target_dir/chattyness-app &
APP_PID=$!
fi
print_server_info "Servers"
wait_for_processes
}
# =============================================================================
# Main Entry Point
# =============================================================================
# Parse arguments
LEPTOS_CMD="build"
TARGET="both"
FORCE="false"
KILL_EXISTING="false"
CHECK_STATUS="false"
RELEASE="false"
for arg in "$@"; do
case $arg in
build | watch)
LEPTOS_CMD="$arg"
;;
--user-app)
TARGET="app"
;;
--owner-app)
TARGET="owner"
;;
--both)
TARGET="both"
;;
-f | --force)
FORCE="true"
;;
-k | --kill)
KILL_EXISTING="true"
;;
-s | --status)
CHECK_STATUS="true"
;;
-r | --release)
RELEASE="true"
;;
--help | -h)
usage
;;
*)
echo "Unknown option: $arg"
usage
;;
esac
done
# Handle special flags first (before acquiring lock)
[ "$CHECK_STATUS" = "true" ] && handle_status
[ "$KILL_EXISTING" = "true" ] && handle_kill
[ "$FORCE" = "true" ] && handle_force
# Acquire lock and store PID
acquire_lock
# Set up cleanup trap
trap cleanup EXIT INT TERM
# Load and validate environment
load_env
check_env
# Run the requested command
case "$LEPTOS_CMD" in
watch) do_watch ;;
build) do_build ;;
esac