From 62eaaff88e16477ce96439fda27cbe6109aa6111 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Mon, 26 Jan 2026 16:50:21 +0900 Subject: [PATCH] scripts cleanup & better all-in-one deploy build flutter ui -> deploy backend -> uv sync -> deploy ui --- scripts/build-deploy.py | 38 +++++++++++++---- scripts/build-deploy.sh | 6 --- scripts/build.sh | 81 ------------------------------------- scripts/deploy.sh | 85 --------------------------------------- scripts/deploy_backend.py | 16 ++++++-- 5 files changed, 44 insertions(+), 182 deletions(-) delete mode 100644 scripts/build-deploy.sh delete mode 100644 scripts/build.sh delete mode 100644 scripts/deploy.sh diff --git a/scripts/build-deploy.py b/scripts/build-deploy.py index 943633d..14ce632 100644 --- a/scripts/build-deploy.py +++ b/scripts/build-deploy.py @@ -13,6 +13,7 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) from build import build from deploy import deploy +from deploy_backend import deploy as deploy_backend def main(): @@ -39,21 +40,44 @@ def main(): action="store_true", help="Only deploy, don't build", ) + parser.add_argument( + "--ui", + action="store_true", + help="Build/deploy UI only (no backend)", + ) + parser.add_argument( + "--backend", + action="store_true", + help="Deploy backend only (no UI, no build)", + ) args = parser.parse_args() - # Build - if not args.deploy_only: + # Default: both UI and backend if neither flag specified + do_ui = args.ui or not args.backend + do_backend = args.backend or not args.ui + + restart = not args.no_restart + + # Build UI (only if doing UI and not deploy-only) + if do_ui and not args.deploy_only: print() if not build(clean=args.clean): - print("Build failed!") + print("UI build failed!") sys.exit(1) - # Deploy - if not args.build_only: + # Deploy backend FIRST (no build step needed - it's Python) + # Backend must be up before UI connects to WebSocket + if do_backend and not args.build_only: + print() + if not deploy_backend(restart=restart): + print("Backend deploy failed!") + sys.exit(1) + + # Deploy UI after backend is ready + if do_ui and not args.build_only: print() - restart = not args.no_restart if not deploy(restart=restart): - print("Deploy failed!") + print("UI deploy failed!") sys.exit(1) print() diff --git a/scripts/build-deploy.sh b/scripts/build-deploy.sh deleted file mode 100644 index 311db76..0000000 --- a/scripts/build-deploy.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -# Wrapper for build-deploy.py -# Usage: ./build-deploy.sh [options] - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -exec python3 "$SCRIPT_DIR/build-deploy.py" "$@" diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100644 index 4de7eb1..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash -# Build script for Smart Serow Flutter UI -# Run this in WSL2 with flutter-elinux installed - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -UI_DIR="$PROJECT_ROOT/pi/ui" - -echo "=== Smart Serow Build ===" -echo "Project: $UI_DIR" - -# Check for flutter-elinux -if ! command -v flutter-elinux &> /dev/null; then - echo "ERROR: flutter-elinux not found in PATH" - echo "Install it or check your PATH" - echo "" - echo "Current PATH: $PATH" - which flutter 2>/dev/null && echo "Found flutter at: $(which flutter)" - exit 1 -fi - -echo "Using: $(which flutter-elinux)" - -# Cross-compilation toolchain for ARM64 -export CC=aarch64-linux-gnu-gcc -export CXX=aarch64-linux-gnu-g++ -export AR=aarch64-linux-gnu-ar -export LD=aarch64-linux-gnu-ld -# CMake-specific vars -export CMAKE_C_COMPILER=aarch64-linux-gnu-gcc -export CMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ - -echo "Cross-compiler: $CXX" - -cd "$UI_DIR" - -# Initialize elinux project if not already configured -if [ ! -d "elinux" ]; then - echo "Initializing elinux project structure..." - flutter-elinux create . --project-name smartserow_ui --org com.smartserow -fi - -# Clean CMake cache on --clean flag -# (CMake caches compiler choice, so stale cache = wrong linker) -if [ "${1:-}" = "--clean" ]; then - echo "Cleaning CMake cache..." - rm -rf build/elinux/arm64 -fi - -echo "Fetching dependencies..." -flutter-elinux pub get - -echo "Building for ARM64 (elinux) with DRM-GBM backend..." - -# Use Pi sysroot if available (for proper cross-linking) -SYSROOT_FLAG="" -if [ -d "$PROJECT_ROOT/pi_sysroot" ]; then - echo "Using Pi sysroot: $PROJECT_ROOT/pi_sysroot" - SYSROOT_FLAG="--target-sysroot=$PROJECT_ROOT/pi_sysroot" -fi - -flutter-elinux build elinux \ - --target-arch=arm64 \ - --target-backend-type=gbm \ - --target-compiler-triple=aarch64-linux-gnu \ - $SYSROOT_FLAG \ - --release - -BUILD_OUTPUT="$UI_DIR/build/elinux/arm64/release/bundle" - -if [ -d "$BUILD_OUTPUT" ]; then - echo "" - echo "=== Build Complete ===" - echo "Output: $BUILD_OUTPUT" - ls -lh "$BUILD_OUTPUT" -else - echo "ERROR: Build output not found at $BUILD_OUTPUT" - exit 1 -fi diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100644 index c9991bd..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/bash -# Deploy script for Smart Serow Flutter UI -# Pushes build bundle to Pi and optionally restarts service - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -CONFIG_FILE="$SCRIPT_DIR/deploy_target.json" -# You'll need to create this file based on deploy_target.sample.json - -# Parse config -if [ ! -f "$CONFIG_FILE" ]; then - echo "ERROR: Config file not found: $CONFIG_FILE" - exit 1 -fi - -# Parse JSON with Python (more universal than jq) -read_json() { - python3 -c "import json; print(json.load(open('$CONFIG_FILE'))['$1'])" -} - -PI_USER=$(read_json user) -PI_HOST=$(read_json host) -REMOTE_PATH=$(read_json remote_path) -SERVICE_NAME=$(read_json service_name) - -SSH_TARGET="$PI_USER@$PI_HOST" -BUILD_DIR="$PROJECT_ROOT/pi/ui/build/elinux/arm64/release/bundle" -CONFIG_SRC="$PROJECT_ROOT/pi/ui/config.json" -IMAGES_SRC="$PROJECT_ROOT/extra/images" - -echo "=== Smart Serow Deploy ===" -echo "Target: $SSH_TARGET:$REMOTE_PATH" -echo "Source: $BUILD_DIR" - -if [ ! -d "$BUILD_DIR" ]; then - echo "ERROR: Build directory not found. Run build.sh first." - exit 1 -fi - -# Sync build to Pi -echo "" -echo "Syncing files..." -rsync -avz --delete \ - "$BUILD_DIR/" \ - "$SSH_TARGET:$REMOTE_PATH/bundle/" - -# Sync config.json (sits next to executable in bundle) -if [ -f "$CONFIG_SRC" ]; then - echo "" - echo "Syncing config.json..." - rsync -avz "$CONFIG_SRC" "$SSH_TARGET:$REMOTE_PATH/bundle/config.json" -else - echo "" - echo "Note: No config.json found, using defaults" -fi - -# Sync images to assets path -if [ -d "$IMAGES_SRC" ]; then - # Read assets_path from config, fall back to default - ASSETS_PATH=$(python3 -c "import json; print(json.load(open('$CONFIG_FILE')).get('assets_path', '$REMOTE_PATH/assets'))" 2>/dev/null || echo "$REMOTE_PATH/assets") - echo "" - echo "Syncing images to $ASSETS_PATH..." - rsync -avz "$IMAGES_SRC/" "$SSH_TARGET:$ASSETS_PATH/" -else - echo "" - echo "Note: No extra/images folder found, skipping image sync" -fi - -# Restart service if requested -RESTART="${1:-}" -if [ "$RESTART" = "--restart" ] || [ "$RESTART" = "-r" ]; then - echo "" - echo "Restarting service: $SERVICE_NAME" - ssh "$SSH_TARGET" "sudo systemctl restart $SERVICE_NAME" - sleep 2 - ssh "$SSH_TARGET" "systemctl status $SERVICE_NAME --no-pager" -else - echo "" - echo "Deploy complete. To restart service, run:" - echo " ssh $SSH_TARGET 'sudo systemctl restart $SERVICE_NAME'" - echo "" - echo "Or run this script with --restart flag" -fi diff --git a/scripts/deploy_backend.py b/scripts/deploy_backend.py index de7f1d4..09702ed 100644 --- a/scripts/deploy_backend.py +++ b/scripts/deploy_backend.py @@ -76,6 +76,18 @@ def deploy(restart: bool = False) -> bool: f"{ssh_target}:{remote_path}/", ]) + # Run uv sync to install/update dependencies + # Use full path since non-interactive SSH doesn't load .bashrc + print() + print("Running uv sync...") + result = run( + ["ssh", ssh_target, f"cd {remote_path} && ~/.local/bin/uv sync"], + check=False, + ) + if result.returncode != 0: + print("WARNING: uv sync failed - dependencies may be out of date") + print("Make sure uv is installed on Pi: curl -LsSf https://astral.sh/uv/install.sh | sh") + # Restart service if requested if restart: print() @@ -91,11 +103,9 @@ def deploy(restart: bool = False) -> bool: print("Or run this script with --restart flag") print() - print("Note: First-time setup on Pi requires:") + print("Note: First-time setup on Pi requires uv to be installed:") print(f" ssh {ssh_target}") - print(f" cd {remote_path}") print(" curl -LsSf https://astral.sh/uv/install.sh | sh") - print(" uv sync") return True