Debian trixie-based build-compile-deploy workflow
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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/
|
||||
14
README.md
14
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`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
205
pi/ui/pubspec.lock
Normal file
205
pi/ui/pubspec.lock
Normal file
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"host": "pi@smartserow.local",
|
||||
"user": "mikkeli",
|
||||
"host": "192.168.2.102",
|
||||
"remote_path": "/opt/smartserow",
|
||||
"service_name": "smartserow-ui"
|
||||
}
|
||||
|
||||
@@ -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 ==="
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user