scripts cleanup & better all-in-one deploy
build flutter ui -> deploy backend -> uv sync -> deploy ui
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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" "$@"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user