#!/usr/bin/env bash # # Build a portable Linux distribution of LedGrab. # Produces a self-contained tarball with virtualenv and launcher script. # # Usage: # ./build-dist.sh [VERSION] # ./build-dist.sh v0.1.0-alpha.1 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" BUILD_DIR="$SCRIPT_DIR/build" DIST_NAME="LedGrab" DIST_DIR="$BUILD_DIR/$DIST_NAME" SERVER_DIR="$SCRIPT_DIR/server" VENV_DIR="$DIST_DIR/venv" APP_DIR="$DIST_DIR/app" # ── Version detection ──────────────────────────────────────── VERSION="${1:-}" if [ -z "$VERSION" ]; then VERSION=$(git describe --tags --exact-match 2>/dev/null || true) fi if [ -z "$VERSION" ]; then VERSION="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}" fi if [ -z "$VERSION" ]; then VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' "$SERVER_DIR/src/wled_controller/__init__.py" 2>/dev/null || echo "0.0.0") fi VERSION_CLEAN="${VERSION#v}" TAR_NAME="LedGrab-v${VERSION_CLEAN}-linux-x64.tar.gz" echo "=== Building LedGrab v${VERSION_CLEAN} (Linux) ===" echo " Output: build/$TAR_NAME" echo "" # ── Clean ──────────────────────────────────────────────────── if [ -d "$DIST_DIR" ]; then echo "[1/7] Cleaning previous build..." rm -rf "$DIST_DIR" fi mkdir -p "$DIST_DIR" # ── Create virtualenv ──────────────────────────────────────── echo "[2/7] Creating virtualenv..." python3 -m venv "$VENV_DIR" source "$VENV_DIR/bin/activate" pip install --upgrade pip --quiet # ── Install dependencies ───────────────────────────────────── echo "[3/7] Installing dependencies..." pip install --quiet "${SERVER_DIR}[camera,notifications]" 2>&1 | { grep -i 'error\|failed' || true } # Remove the installed wled_controller package (PYTHONPATH handles app code) SITE_PACKAGES="$VENV_DIR/lib/python*/site-packages" rm -rf $SITE_PACKAGES/wled_controller* $SITE_PACKAGES/wled*.dist-info 2>/dev/null || true # Clean up caches find "$VENV_DIR" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find "$VENV_DIR" -type d -name tests -exec rm -rf {} + 2>/dev/null || true find "$VENV_DIR" -type d -name test -exec rm -rf {} + 2>/dev/null || true # ── Build frontend ─────────────────────────────────────────── echo "[4/7] Building frontend bundle..." (cd "$SERVER_DIR" && npm ci --loglevel error && npm run build) 2>&1 | { grep -v 'RemoteException' || true } # ── Copy application files ─────────────────────────────────── echo "[5/7] Copying application files..." mkdir -p "$APP_DIR" cp -r "$SERVER_DIR/src" "$APP_DIR/src" cp -r "$SERVER_DIR/config" "$APP_DIR/config" mkdir -p "$DIST_DIR/data" "$DIST_DIR/logs" # Clean up source maps and __pycache__ find "$APP_DIR" -name "*.map" -delete 2>/dev/null || true find "$APP_DIR" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true # ── Create launcher ────────────────────────────────────────── echo "[6/7] Creating launcher..." cat > "$DIST_DIR/run.sh" << 'LAUNCHER' #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" export PYTHONPATH="$SCRIPT_DIR/app/src" export WLED_CONFIG_PATH="$SCRIPT_DIR/app/config/default_config.yaml" mkdir -p "$SCRIPT_DIR/data" "$SCRIPT_DIR/logs" echo "" echo " =============================================" echo " LedGrab vVERSION_PLACEHOLDER" echo " Open http://localhost:8080 in your browser" echo " =============================================" echo "" source "$SCRIPT_DIR/venv/bin/activate" exec python -m uvicorn wled_controller.main:app --host 0.0.0.0 --port 8080 LAUNCHER sed -i "s/VERSION_PLACEHOLDER/${VERSION_CLEAN}/" "$DIST_DIR/run.sh" chmod +x "$DIST_DIR/run.sh" # ── Create tarball ─────────────────────────────────────────── echo "[7/7] Creating $TAR_NAME..." deactivate 2>/dev/null || true TAR_PATH="$BUILD_DIR/$TAR_NAME" (cd "$BUILD_DIR" && tar -czf "$TAR_NAME" "$DIST_NAME") TAR_SIZE=$(du -h "$TAR_PATH" | cut -f1) echo "" echo "=== Build complete ===" echo " Archive: $TAR_PATH" echo " Size: $TAR_SIZE" echo ""