From aabca4915c62ac7d55c3d4d9ce527d96a34b93b5 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Sat, 24 Jan 2026 23:18:37 +0900 Subject: [PATCH] Debian trixie-based build-compile-deploy workflow --- .gitignore | 8 +- README.md | 14 +-- pi/ui/pubspec.lock | 205 ++++++++++++++++++++++++++++++++++ scripts/build.sh | 54 ++++++++- scripts/deploy.sh | 23 ++-- scripts/deploy_target.json | 3 +- scripts/pi_setup.sh | 28 +++-- scripts/smartserow-ui.service | 12 +- 8 files changed, 311 insertions(+), 36 deletions(-) create mode 100644 pi/ui/pubspec.lock diff --git a/.gitignore b/.gitignore index 7d62138..cc44677 100644 --- a/.gitignore +++ b/.gitignore @@ -32,16 +32,20 @@ Thumbs.db # Logs *.log -# Flutter platforms we don't use (Pi project - elinux only) +# Flutter platform folders (all generated by flutter-elinux create) pi/ui/android/ pi/ui/ios/ pi/ui/macos/ pi/ui/web/ pi/ui/windows/ +pi/ui/linux/ +pi/ui/elinux/ +pi/ui/test/ -# Flutter generated files we don't need in repo +# Flutter generated files pi/ui/.metadata pi/ui/README.md +pi/ui/analysis_options.yaml # Pi (aarch64) dynamic linkers and stuff pi_sysroot/ \ No newline at end of file diff --git a/README.md b/README.md index 365aef6..90c7621 100644 --- a/README.md +++ b/README.md @@ -24,14 +24,15 @@ smart-serow/ │ └── ui/ # Flutter app (elinux target) │ ├── lib/main.dart │ ├── pubspec.yaml -│ └── elinux/ # Platform-specific build config +│ └── elinux/ # Generated by flutter-elinux (gitignored) ├── scripts/ │ ├── build.sh # Cross-compile for ARM64 │ ├── deploy.sh # Push to Pi via rsync │ ├── deploy_target.json │ ├── pi_setup.sh # One-time Pi setup │ └── smartserow-ui.service -└── pi_sysroot/ # Pi libraries for cross-linking (gitignored) +├── pi_sysroot/ # Pi libraries for cross-linking (gitignored) +└── LICENSE # MIT ``` --- @@ -145,11 +146,10 @@ Flutter-elinux supports multiple backends. We use **GBM** (DRM/KMS direct). | `x11` | Debug | Needs X server, mouse/keyboard friendly | | `wayland` | - | Requires compositor, more dependencies | -The service runs with `-b drm`. For X11 debugging on Pi: -```bash -startx & -./smartserow_ui -b x11 -``` +The backend is compiled in (we build with `--target-backend-type=gbm`). +The `-b` flag is for bundle path, not backend selection. + +For X11 debugging, you'd need to rebuild with `--target-backend-type=x11`. --- diff --git a/pi/ui/pubspec.lock b/pi/ui/pubspec.lock new file mode 100644 index 0000000..5928308 --- /dev/null +++ b/pi/ui/pubspec.lock @@ -0,0 +1,205 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + url: "https://pub.dev" + source: hosted + version: "2.12.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + url: "https://pub.dev" + source: hosted + version: "1.3.2" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + url: "https://pub.dev" + source: hosted + version: "14.3.1" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/scripts/build.sh b/scripts/build.sh index bf33186..4de7eb1 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -11,16 +11,62 @@ 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" -# Clean previous build (optional, comment out for faster incremental builds) -# flutter-elinux clean +# 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)..." -flutter-elinux build elinux --target-arch=arm64 --release +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" diff --git a/scripts/deploy.sh b/scripts/deploy.sh index b5d8d2b..f4b84eb 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -14,14 +14,21 @@ if [ ! -f "$CONFIG_FILE" ]; then exit 1 fi -HOST=$(jq -r '.host' "$CONFIG_FILE") -REMOTE_PATH=$(jq -r '.remote_path' "$CONFIG_FILE") -SERVICE_NAME=$(jq -r '.service_name' "$CONFIG_FILE") +# 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" echo "=== Smart Serow Deploy ===" -echo "Target: $HOST:$REMOTE_PATH" +echo "Target: $SSH_TARGET:$REMOTE_PATH" echo "Source: $BUILD_DIR" if [ ! -d "$BUILD_DIR" ]; then @@ -34,20 +41,20 @@ echo "" echo "Syncing files..." rsync -avz --delete \ "$BUILD_DIR/" \ - "$HOST:$REMOTE_PATH/bundle/" + "$SSH_TARGET:$REMOTE_PATH/bundle/" # Restart service if requested RESTART="${1:-}" if [ "$RESTART" = "--restart" ] || [ "$RESTART" = "-r" ]; then echo "" echo "Restarting service: $SERVICE_NAME" - ssh "$HOST" "sudo systemctl restart $SERVICE_NAME" + ssh "$SSH_TARGET" "sudo systemctl restart $SERVICE_NAME" sleep 2 - ssh "$HOST" "systemctl status $SERVICE_NAME --no-pager" + ssh "$SSH_TARGET" "systemctl status $SERVICE_NAME --no-pager" else echo "" echo "Deploy complete. To restart service, run:" - echo " ssh $HOST 'sudo systemctl restart $SERVICE_NAME'" + echo " ssh $SSH_TARGET 'sudo systemctl restart $SERVICE_NAME'" echo "" echo "Or run this script with --restart flag" fi diff --git a/scripts/deploy_target.json b/scripts/deploy_target.json index 2cbcfc5..eb3fd56 100644 --- a/scripts/deploy_target.json +++ b/scripts/deploy_target.json @@ -1,5 +1,6 @@ { - "host": "pi@smartserow.local", + "user": "mikkeli", + "host": "192.168.2.102", "remote_path": "/opt/smartserow", "service_name": "smartserow-ui" } diff --git a/scripts/pi_setup.sh b/scripts/pi_setup.sh index e6cdb79..c071cd0 100644 --- a/scripts/pi_setup.sh +++ b/scripts/pi_setup.sh @@ -6,6 +6,11 @@ set -e echo "=== Smart Serow Pi Setup ===" +# Use current user (whoever is running this script on the Pi) +PI_USER="${USER:-$(whoami)}" +PI_UID=$(id -u "$PI_USER") +echo "Setting up for user: $PI_USER (uid: $PI_UID)" + # Check if running on Pi (arm architecture) ARCH=$(uname -m) if [[ "$ARCH" != "aarch64" && "$ARCH" != "armv7l" ]]; then @@ -15,11 +20,12 @@ fi APP_DIR="/opt/smartserow" SERVICE_FILE="/etc/systemd/system/smartserow-ui.service" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Create app directory echo "Creating app directory: $APP_DIR" sudo mkdir -p "$APP_DIR/bundle" -sudo chown -R pi:pi "$APP_DIR" +sudo chown -R "$PI_USER:$PI_USER" "$APP_DIR" # Install runtime dependencies for flutter-elinux echo "Installing runtime dependencies..." @@ -41,11 +47,15 @@ sudo apt-get install -y \ libx11-6 \ libxkbcommon-x11-0 -# Copy systemd service +# Generate systemd service with correct user echo "Installing systemd service..." -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ -f "$SCRIPT_DIR/smartserow-ui.service" ]; then - sudo cp "$SCRIPT_DIR/smartserow-ui.service" "$SERVICE_FILE" + # Patch the template with actual user values + sed -e "s/User=.*/User=$PI_USER/" \ + -e "s/Group=.*/Group=$PI_USER/" \ + -e "s|HOME=/home/.*|HOME=/home/$PI_USER|" \ + -e "s|XDG_RUNTIME_DIR=/run/user/.*|XDG_RUNTIME_DIR=/run/user/$PI_UID|" \ + "$SCRIPT_DIR/smartserow-ui.service" | sudo tee "$SERVICE_FILE" > /dev/null else echo "WARNING: Service file not found at $SCRIPT_DIR/smartserow-ui.service" echo "Copy it manually to $SERVICE_FILE" @@ -56,11 +66,11 @@ echo "Enabling service..." sudo systemctl daemon-reload sudo systemctl enable smartserow-ui -# Add pi user to required groups for DRM/KMS access -echo "Setting up permissions..." -sudo usermod -aG video pi -sudo usermod -aG input pi -sudo usermod -aG render pi 2>/dev/null || true +# Add user to required groups for DRM/KMS access +echo "Setting up permissions for $PI_USER..." +sudo usermod -aG video "$PI_USER" +sudo usermod -aG input "$PI_USER" +sudo usermod -aG render "$PI_USER" 2>/dev/null || true echo "" echo "=== Setup Complete ===" diff --git a/scripts/smartserow-ui.service b/scripts/smartserow-ui.service index 7f02d44..8ecefa2 100644 --- a/scripts/smartserow-ui.service +++ b/scripts/smartserow-ui.service @@ -5,12 +5,13 @@ Wants=multi-user.target [Service] Type=simple -User=pi -Group=pi +User=mikkeli +Group=mikkeli WorkingDirectory=/opt/smartserow/bundle -# DRM/KMS backend for direct framebuffer (no X11 needed) -ExecStart=/opt/smartserow/bundle/smartserow_ui -b drm +# GBM backend is compiled in. Just specify bundle path. +# Scale factor 2.5 for 5.5" 1080p screen (high DPI) +ExecStart=/opt/smartserow/bundle/smartserow_ui --bundle=/opt/smartserow/bundle --force-scale-factor=2.5 # Restart on crash Restart=always @@ -18,7 +19,8 @@ RestartSec=3 # Environment for DRM/KMS access Environment=XDG_RUNTIME_DIR=/run/user/1000 -Environment=HOME=/home/pi +Environment=HOME=/home/mikkeli +Environment=LD_LIBRARY_PATH=/opt/smartserow/bundle/lib # Give time for GPU to initialize on boot ExecStartPre=/bin/sleep 2