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))
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
from build import build
|
from build import build
|
||||||
from deploy import deploy
|
from deploy import deploy
|
||||||
|
from deploy_backend import deploy as deploy_backend
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -39,21 +40,44 @@ def main():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Only deploy, don't build",
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Build
|
# Default: both UI and backend if neither flag specified
|
||||||
if not args.deploy_only:
|
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()
|
print()
|
||||||
if not build(clean=args.clean):
|
if not build(clean=args.clean):
|
||||||
print("Build failed!")
|
print("UI build failed!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Deploy
|
# Deploy backend FIRST (no build step needed - it's Python)
|
||||||
if not args.build_only:
|
# 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()
|
print()
|
||||||
restart = not args.no_restart
|
|
||||||
if not deploy(restart=restart):
|
if not deploy(restart=restart):
|
||||||
print("Deploy failed!")
|
print("UI deploy failed!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print()
|
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}/",
|
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
|
# Restart service if requested
|
||||||
if restart:
|
if restart:
|
||||||
print()
|
print()
|
||||||
@@ -91,11 +103,9 @@ def deploy(restart: bool = False) -> bool:
|
|||||||
print("Or run this script with --restart flag")
|
print("Or run this script with --restart flag")
|
||||||
|
|
||||||
print()
|
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" ssh {ssh_target}")
|
||||||
print(f" cd {remote_path}")
|
|
||||||
print(" curl -LsSf https://astral.sh/uv/install.sh | sh")
|
print(" curl -LsSf https://astral.sh/uv/install.sh | sh")
|
||||||
print(" uv sync")
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user