mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
UI changes to update and support auto updating (#6075)
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
919f0ade99
commit
256d1a86d2
@@ -0,0 +1,35 @@
|
||||
# Auto-Update Testing
|
||||
|
||||
## One command (automated)
|
||||
|
||||
```bash
|
||||
# First time only:
|
||||
npm run tauri:setup-dev-update
|
||||
|
||||
# Run tests (builds JRE + JAR + signed bundle, starts server + app, runs checks):
|
||||
npm run tauri:test-update-e2e
|
||||
|
||||
# Full install test (downloads + installs the update):
|
||||
npm run tauri:test-update-e2e:install
|
||||
|
||||
# Skip rebuild if bundle already exists:
|
||||
bash scripts/dev-update-test/test-update-e2e.sh --skip-build
|
||||
```
|
||||
|
||||
## Manual testing
|
||||
|
||||
```bash
|
||||
# Terminal 1 - serve the signed v99.0.0 update:
|
||||
npm run tauri:serve-dev-update
|
||||
|
||||
# Terminal 2 - run the app at v0.0.1:
|
||||
npm run tauri:dev-with-update
|
||||
```
|
||||
|
||||
Go to Settings > General > Software Updates. Click "Check for Updates" then "Install Now".
|
||||
|
||||
## Requires
|
||||
|
||||
- Java 21+ JDK (with `jlink`)
|
||||
- Node.js, Python 3 (with `pip install websockets`)
|
||||
- First-time setup generates signing keys + config override
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds a signed update bundle at version 99.0.0 using the dev key pair.
|
||||
# After this runs, start the server with: npm run tauri:serve-dev-update
|
||||
# The server will automatically serve the real bundle instead of the placeholder.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KEYS_DIR="$SCRIPT_DIR/.keys"
|
||||
FRONTEND_DIR="$SCRIPT_DIR/../.."
|
||||
OUTPUT_DIR="$SCRIPT_DIR/.update-dist"
|
||||
|
||||
# ── pre-flight ────────────────────────────────────────────────────────────────
|
||||
if [ ! -f "$KEYS_DIR/dev-update-key" ]; then
|
||||
echo "Error: dev key not found."
|
||||
echo "Run setup first: npm run tauri:setup-dev-update"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PRIVATE_KEY="$(cat "$KEYS_DIR/dev-update-key")"
|
||||
VERSION="99.0.0"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ── detect platform ───────────────────────────────────────────────────────────
|
||||
OS="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
|
||||
case "$OS" in
|
||||
Linux)
|
||||
BUNDLES="appimage"
|
||||
TAURI_PLATFORM="linux-x86_64"
|
||||
BUNDLE_GLOB="*.AppImage.tar.gz"
|
||||
;;
|
||||
Darwin)
|
||||
BUNDLES="app"
|
||||
TAURI_PLATFORM="darwin-$([ "$ARCH" = "arm64" ] && echo aarch64 || echo x86_64)"
|
||||
BUNDLE_GLOB="*.app.tar.gz"
|
||||
;;
|
||||
*)
|
||||
echo "Use build-dev-update.ps1 on Windows."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# ── build ─────────────────────────────────────────────────────────────────────
|
||||
echo "Building signed update bundle (version $VERSION, platform $TAURI_PLATFORM)..."
|
||||
echo "This takes a few minutes — Rust is compiling."
|
||||
echo ""
|
||||
|
||||
cd "$FRONTEND_DIR/editor"
|
||||
|
||||
# Prepare desktop env/assets before building
|
||||
npx tsx scripts/setup-env.mts --desktop && node scripts/generate-icons.js
|
||||
# Build the Windows installer provisioner + thumbnail-handler (no-op on macOS/Linux).
|
||||
# The WiX fragment references these binaries, so light.exe fails to bind without them.
|
||||
node scripts/build-provisioner.mjs
|
||||
|
||||
TAURI_SIGNING_PRIVATE_KEY="$PRIVATE_KEY" \
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" \
|
||||
npx tauri build \
|
||||
--config "{\"version\":\"$VERSION\"}" \
|
||||
--bundles "$BUNDLES"
|
||||
|
||||
# ── locate outputs ────────────────────────────────────────────────────────────
|
||||
BUNDLE="$(find src-tauri/target -name "$BUNDLE_GLOB" -not -name "*.sig" 2>/dev/null | sort | tail -1)"
|
||||
SIG="$(find src-tauri/target -name "${BUNDLE_GLOB}.sig" 2>/dev/null | sort | tail -1)"
|
||||
|
||||
if [ -z "$BUNDLE" ]; then
|
||||
echo "Error: bundle matching '$BUNDLE_GLOB' not found in src-tauri/target."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$SIG" ]; then
|
||||
echo "Error: .sig file not found alongside bundle."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BUNDLE_FILENAME="$(basename "$BUNDLE")"
|
||||
SIGNATURE="$(cat "$SIG")"
|
||||
PUB_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
|
||||
# ── assemble serve directory ──────────────────────────────────────────────────
|
||||
cp "$BUNDLE" "$OUTPUT_DIR/$BUNDLE_FILENAME"
|
||||
|
||||
cat > "$OUTPUT_DIR/latest.json" << EOF
|
||||
{
|
||||
"version": "$VERSION",
|
||||
"notes": "Dev test update v$VERSION (local signed build)",
|
||||
"pub_date": "$PUB_DATE",
|
||||
"platforms": {
|
||||
"$TAURI_PLATFORM": {
|
||||
"signature": "$SIGNATURE",
|
||||
"url": "http://localhost:8090/$BUNDLE_FILENAME"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# ── done ──────────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "Build complete."
|
||||
echo " bundle : $OUTPUT_DIR/$BUNDLE_FILENAME"
|
||||
echo " manifest: $OUTPUT_DIR/latest.json"
|
||||
echo ""
|
||||
echo "Start the server: npm run tauri:serve-dev-update"
|
||||
echo "Run the app: npm run tauri:dev-with-update"
|
||||
echo ""
|
||||
echo "The running app (v0.0.1) will see v$VERSION as an available update."
|
||||
echo "Clicking 'Install' will download and verify the real signed bundle."
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Starts a local HTTP server on port 8090 serving an update manifest.
|
||||
#
|
||||
# If a real signed build exists in .update-dist/ (produced by build-dev-update.sh),
|
||||
# it is served automatically — enabling the full download + install flow.
|
||||
# Otherwise a placeholder manifest is served, which is enough to test the UI only.
|
||||
set -euo pipefail
|
||||
|
||||
PORT=8090
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DIST_DIR="$SCRIPT_DIR/.update-dist"
|
||||
|
||||
if [ -f "$DIST_DIR/latest.json" ]; then
|
||||
# ── real signed build ─────────────────────────────────────────────────────
|
||||
SERVE_DIR="$DIST_DIR"
|
||||
VERSION="$(python3 -c "import json; print(json.load(open('$DIST_DIR/latest.json'))['version'])" 2>/dev/null || grep -o '"version":"[^"]*"' "$DIST_DIR/latest.json" | head -1 | cut -d'"' -f4)"
|
||||
echo "Serving real signed build from .update-dist/"
|
||||
echo " version : $VERSION"
|
||||
echo " manifest: http://localhost:$PORT/latest.json"
|
||||
echo " install : full download + verify flow will work"
|
||||
else
|
||||
# ── placeholder (UI testing only) ─────────────────────────────────────────
|
||||
SERVE_DIR="$(mktemp -d)"
|
||||
VERSION="99.0.0"
|
||||
|
||||
OS="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
case "$OS" in
|
||||
Linux) TAURI_PLATFORM="linux-x86_64" ;;
|
||||
Darwin)
|
||||
case "$ARCH" in
|
||||
arm64) TAURI_PLATFORM="darwin-aarch64" ;;
|
||||
*) TAURI_PLATFORM="darwin-x86_64" ;;
|
||||
esac ;;
|
||||
*) TAURI_PLATFORM="windows-x86_64" ;;
|
||||
esac
|
||||
|
||||
PUB_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
|
||||
cat > "$SERVE_DIR/latest.json" << EOF
|
||||
{
|
||||
"version": "$VERSION",
|
||||
"notes": "Dev test update v$VERSION — placeholder (UI testing only)",
|
||||
"pub_date": "$PUB_DATE",
|
||||
"platforms": {
|
||||
"$TAURI_PLATFORM": {
|
||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHBsYWNlaG9sZGVyIHNpZyBmb3IgZGV2IHRlc3Rpbmcgb25seQ==",
|
||||
"url": "http://localhost:$PORT/dummy-update-$VERSION.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
echo "No real build found in .update-dist/ — serving placeholder manifest."
|
||||
echo " version : $VERSION (placeholder)"
|
||||
echo " manifest: http://localhost:$PORT/latest.json"
|
||||
echo " install : will fail signature verification (expected for UI testing)"
|
||||
echo ""
|
||||
echo " To test the full install flow, run first:"
|
||||
echo " npm run tauri:build-dev-update"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop."
|
||||
echo ""
|
||||
|
||||
cd "$SERVE_DIR" && python3 -m http.server "$PORT"
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sets up a dev/local Tauri signing key pair and config override for update testing.
|
||||
# Run once before using tauri:dev-with-update.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
KEYS_DIR="$SCRIPT_DIR/.keys"
|
||||
FRONTEND_DIR="$SCRIPT_DIR/../.."
|
||||
TAURI_CONF_OVERRIDE="$FRONTEND_DIR/editor/src-tauri/tauri.conf.dev-update.json"
|
||||
|
||||
mkdir -p "$KEYS_DIR"
|
||||
|
||||
echo "Generating Tauri Ed25519 signing key pair for dev update testing..."
|
||||
|
||||
# Generate key pair — private key goes to .keys/dev-update-key, public key to .keys/dev-update-key.pub
|
||||
(cd "$FRONTEND_DIR/editor" && npx tauri signer generate -w "$KEYS_DIR/dev-update-key" --ci -p "")
|
||||
|
||||
PUBKEY=$(cat "$KEYS_DIR/dev-update-key.pub")
|
||||
echo ""
|
||||
echo "Public key: $PUBKEY"
|
||||
echo ""
|
||||
|
||||
# Write the override config — sets version to 0.0.1 so any "available" version looks newer,
|
||||
# and points the updater endpoint to the local dev server on port 8090.
|
||||
cat > "$TAURI_CONF_OVERRIDE" << EOF
|
||||
{
|
||||
"version": "0.0.1",
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "$PUBKEY",
|
||||
"endpoints": [
|
||||
"http://localhost:8090/latest.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Created: editor/src-tauri/tauri.conf.dev-update.json"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. In a separate terminal: npm run tauri:serve-dev-update"
|
||||
echo " 2. Then run: npm run tauri:dev-with-update"
|
||||
echo ""
|
||||
echo "The private key is stored at:"
|
||||
echo " $KEYS_DIR/dev-update-key"
|
||||
echo "Keep it secret — it is gitignored."
|
||||
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env bash
|
||||
# End-to-end auto-update test for macOS/Linux.
|
||||
#
|
||||
# Builds JRE + JAR + signed update bundle, starts server + app,
|
||||
# runs CDP tests against the WebView, then cleans up.
|
||||
#
|
||||
# Usage:
|
||||
# bash scripts/dev-update-test/test-update-e2e.sh # check-only
|
||||
# bash scripts/dev-update-test/test-update-e2e.sh --install # full install
|
||||
# bash scripts/dev-update-test/test-update-e2e.sh --skip-build # reuse existing MSI
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Java 21+ JDK (with jlink)
|
||||
# - Node.js + npm
|
||||
# - Python 3 (for HTTP server + CDP tests)
|
||||
# - First-time: bash scripts/dev-update-test/setup-dev-updater.sh
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL=false
|
||||
SKIP_BUILD=false
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--install) INSTALL=true ;;
|
||||
--skip-build) SKIP_BUILD=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FRONTEND_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
REPO_ROOT="$(cd "$FRONTEND_DIR/.." && pwd)"
|
||||
TAURI_DIR="$FRONTEND_DIR/editor/src-tauri"
|
||||
OUTPUT_DIR="$SCRIPT_DIR/.update-dist"
|
||||
KEYS_DIR="$SCRIPT_DIR/.keys"
|
||||
PORT=8090
|
||||
DEBUG_PORT=9222
|
||||
|
||||
# PIDs to clean up
|
||||
SERVER_PID=""
|
||||
APP_PID=""
|
||||
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "=== Cleanup ==="
|
||||
[ -n "$APP_PID" ] && kill "$APP_PID" 2>/dev/null || true
|
||||
[ -n "$SERVER_PID" ] && kill "$SERVER_PID" 2>/dev/null || true
|
||||
pkill -f "stirling-pdf" 2>/dev/null || true
|
||||
echo " Done"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── pre-flight ────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "=== Pre-flight checks ==="
|
||||
|
||||
if [ ! -f "$KEYS_DIR/dev-update-key" ]; then
|
||||
echo " Running first-time setup..."
|
||||
bash "$SCRIPT_DIR/setup-dev-updater.sh"
|
||||
fi
|
||||
echo " Signing keys: OK"
|
||||
|
||||
JAVA_VER=$(java -version 2>&1 | head -1 | tr -d '\r"' | awk '{print $3}' | cut -d. -f1)
|
||||
if [ -z "$JAVA_VER" ] || [ "$JAVA_VER" -lt 21 ]; then
|
||||
echo " Error: Java 21+ required, found Java $JAVA_VER"
|
||||
exit 1
|
||||
fi
|
||||
echo " Java $JAVA_VER: OK"
|
||||
|
||||
# Detect python command (python3 on macOS/Linux, python on Windows Git Bash)
|
||||
PYTHON="python3"
|
||||
$PYTHON --version >/dev/null 2>&1 || PYTHON="python"
|
||||
$PYTHON --version >/dev/null 2>&1 || { echo " Error: Python 3 required"; exit 1; }
|
||||
$PYTHON -c "import websockets" 2>/dev/null || {
|
||||
echo " Installing Python websockets..."
|
||||
$PYTHON -m pip install websockets --quiet
|
||||
}
|
||||
echo " Python: OK"
|
||||
|
||||
# ── detect platform ───────────────────────────────────────────────────────────
|
||||
OS="$(uname -s)"
|
||||
ARCH="$(uname -m)"
|
||||
case "$OS" in
|
||||
Darwin)
|
||||
BUNDLES="app"
|
||||
TAURI_PLATFORM="darwin-$([ "$ARCH" = "arm64" ] && echo aarch64 || echo x86_64)"
|
||||
BUNDLE_GLOB="*.app.tar.gz"
|
||||
WEBVIEW_DEBUG_ENV=""
|
||||
;;
|
||||
Linux)
|
||||
BUNDLES="appimage"
|
||||
TAURI_PLATFORM="linux-x86_64"
|
||||
BUNDLE_GLOB="*.AppImage.tar.gz"
|
||||
WEBVIEW_DEBUG_ENV=""
|
||||
;;
|
||||
MINGW*|MSYS*|CYGWIN*)
|
||||
BUNDLES="msi"
|
||||
TAURI_PLATFORM="windows-x86_64"
|
||||
BUNDLE_GLOB="*.msi"
|
||||
WEBVIEW_DEBUG_ENV="WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--remote-debugging-port=$DEBUG_PORT"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported OS: $OS"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# ── Step 1: Build JRE + JAR ──────────────────────────────────────────────────
|
||||
JRE_JAVA="$TAURI_DIR/runtime/jre/bin/java"
|
||||
JAR_GLOB="$TAURI_DIR/libs/stirling-pdf-*.jar"
|
||||
|
||||
if [ "$SKIP_BUILD" = false ] || ! ls $JAR_GLOB >/dev/null 2>&1 || [ ! -f "$JRE_JAVA" ]; then
|
||||
echo ""
|
||||
echo "=== Building backend JAR ==="
|
||||
cd "$REPO_ROOT"
|
||||
DISABLE_ADDITIONAL_FEATURES=true ./gradlew bootJar -x test --no-daemon 2>&1 | tail -3
|
||||
|
||||
BUILT_JAR=$(ls -t app/core/build/libs/stirling-pdf-*.jar 2>/dev/null | head -1 || true)
|
||||
if [ -z "$BUILT_JAR" ]; then echo "Error: JAR not found"; exit 1; fi
|
||||
|
||||
mkdir -p "$TAURI_DIR/libs"
|
||||
# Remove any dummy jars
|
||||
find "$TAURI_DIR/libs" -name "*.jar" -size -1k -delete 2>/dev/null || true
|
||||
cp "$BUILT_JAR" "$TAURI_DIR/libs/"
|
||||
echo " JAR: $(basename "$BUILT_JAR") ($(du -h "$BUILT_JAR" | cut -f1))"
|
||||
|
||||
if [ ! -f "$JRE_JAVA" ]; then
|
||||
echo ""
|
||||
echo "=== Building JRE with jlink ==="
|
||||
rm -rf "$TAURI_DIR/runtime/jre"
|
||||
MODULES="java.base,java.compiler,java.desktop,java.instrument,java.logging,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.security.sasl,java.sql,java.transaction.xa,java.xml,java.xml.crypto,jdk.crypto.ec,jdk.crypto.cryptoki,jdk.unsupported"
|
||||
jlink \
|
||||
--add-modules "$MODULES" \
|
||||
--strip-debug \
|
||||
--no-header-files \
|
||||
--no-man-pages \
|
||||
--output "$TAURI_DIR/runtime/jre"
|
||||
echo " JRE: OK"
|
||||
else
|
||||
echo " JRE: already exists"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Step 2: Build signed update bundle ────────────────────────────────────────
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
BUNDLE_FILE=$(ls "$OUTPUT_DIR"/$BUNDLE_GLOB 2>/dev/null | head -1 || true)
|
||||
|
||||
if [ "$SKIP_BUILD" = false ] || [ -z "$BUNDLE_FILE" ]; then
|
||||
echo ""
|
||||
echo "=== Building signed v99.0.0 update bundle ==="
|
||||
echo " This takes a few minutes (Rust release build)..."
|
||||
|
||||
PRIVATE_KEY="$(cat "$KEYS_DIR/dev-update-key")"
|
||||
|
||||
cd "$FRONTEND_DIR/editor"
|
||||
# Build the Windows installer provisioner + thumbnail-handler (no-op on macOS/Linux).
|
||||
# The WiX fragment references these binaries, so light.exe fails to bind without them.
|
||||
node scripts/build-provisioner.mjs
|
||||
TAURI_SIGNING_PRIVATE_KEY="$PRIVATE_KEY" \
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" \
|
||||
npx tauri build \
|
||||
--config '{"version":"99.0.0"}' \
|
||||
--bundles "$BUNDLES" 2>&1 | tail -5
|
||||
|
||||
# Find and copy bundle (search release/bundle first, then broader)
|
||||
BUILT_BUNDLE=$(find "$TAURI_DIR/target/release/bundle" -name "$BUNDLE_GLOB" -not -name "*.sig" 2>/dev/null | sort | tail -1)
|
||||
[ -z "$BUILT_BUNDLE" ] && BUILT_BUNDLE=$(find "$TAURI_DIR/target" -maxdepth 5 -name "$BUNDLE_GLOB" -not -name "*.sig" 2>/dev/null | sort | tail -1)
|
||||
BUILT_SIG="${BUILT_BUNDLE}.sig"
|
||||
|
||||
if [ -z "$BUILT_BUNDLE" ] || [ ! -f "$BUILT_SIG" ]; then
|
||||
# Sign manually if .sig missing
|
||||
if [ -n "$BUILT_BUNDLE" ]; then
|
||||
TAURI_SIGNING_PRIVATE_KEY="$PRIVATE_KEY" \
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" \
|
||||
npx tauri signer sign "$BUILT_BUNDLE"
|
||||
BUILT_SIG="${BUILT_BUNDLE}.sig"
|
||||
else
|
||||
echo "Error: bundle not found"; exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
BUNDLE_FILENAME="$(basename "$BUILT_BUNDLE")"
|
||||
SIGNATURE="$(cat "$BUILT_SIG")"
|
||||
PUB_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
|
||||
cp "$BUILT_BUNDLE" "$OUTPUT_DIR/$BUNDLE_FILENAME"
|
||||
|
||||
cat > "$OUTPUT_DIR/latest.json" << EOF
|
||||
{
|
||||
"version": "99.0.0",
|
||||
"notes": "Test update v99.0.0 - built with real JRE + backend",
|
||||
"pub_date": "$PUB_DATE",
|
||||
"platforms": {
|
||||
"$TAURI_PLATFORM": {
|
||||
"signature": "$SIGNATURE",
|
||||
"url": "http://localhost:$PORT/$BUNDLE_FILENAME"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
BUNDLE_SIZE=$(du -h "$OUTPUT_DIR/$BUNDLE_FILENAME" | cut -f1)
|
||||
echo " Bundle: ${BUNDLE_SIZE}, signed and ready"
|
||||
else
|
||||
echo ""
|
||||
echo "=== Skipping build (use without --skip-build to rebuild) ==="
|
||||
fi
|
||||
|
||||
# ── Step 3: Start HTTP server ─────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "=== Starting update server on port $PORT ==="
|
||||
cd "$OUTPUT_DIR"
|
||||
$PYTHON -m http.server "$PORT" &>/dev/null &
|
||||
SERVER_PID=$!
|
||||
sleep 2
|
||||
|
||||
curl -sf "http://localhost:$PORT/latest.json" >/dev/null || { echo " Error: server not responding"; exit 1; }
|
||||
echo " Server: OK (PID $SERVER_PID)"
|
||||
|
||||
# ── Step 4: Start Tauri app ──────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "=== Starting Tauri app (v0.0.1) ==="
|
||||
cd "$FRONTEND_DIR/editor"
|
||||
|
||||
# Enable WebView remote debugging (platform-specific)
|
||||
if [ -n "$WEBVIEW_DEBUG_ENV" ]; then
|
||||
export ${WEBVIEW_DEBUG_ENV}
|
||||
else
|
||||
export WEBKIT_INSPECTOR_SERVER="127.0.0.1:$DEBUG_PORT"
|
||||
fi
|
||||
|
||||
npx tauri dev --config src-tauri/tauri.conf.dev-update.json &>"$OUTPUT_DIR/tauri-dev.log" &
|
||||
APP_PID=$!
|
||||
|
||||
# ── Step 5: CDP tests ────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "=== Running CDP tests ==="
|
||||
|
||||
INSTALL_FLAG=""
|
||||
if [ "$INSTALL" = true ]; then INSTALL_FLAG="--install"; fi
|
||||
|
||||
$PYTHON - $INSTALL_FLAG << 'PYEOF'
|
||||
import json, asyncio, websockets, base64, sys, urllib.request, time
|
||||
|
||||
INSTALL = '--install' in sys.argv
|
||||
PORT = 9222
|
||||
|
||||
async def wait_for_app(timeout=180):
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
try:
|
||||
data = urllib.request.urlopen(f'http://localhost:{PORT}/json', timeout=2).read()
|
||||
targets = json.loads(data)
|
||||
page = next((t for t in targets if t['type'] == 'page'), None)
|
||||
if page: return page
|
||||
except: pass
|
||||
await asyncio.sleep(3)
|
||||
raise TimeoutError('App did not start')
|
||||
|
||||
async def run():
|
||||
print(' Waiting for app to start...')
|
||||
page = await wait_for_app()
|
||||
print(f' Connected: {page["url"]}')
|
||||
await asyncio.sleep(8)
|
||||
|
||||
uri = page['webSocketDebuggerUrl']
|
||||
async with websockets.connect(uri) as ws:
|
||||
await ws.send(json.dumps({'id': 0, 'method': 'Runtime.enable'}))
|
||||
for _ in range(30):
|
||||
try: await asyncio.wait_for(ws.recv(), timeout=0.3)
|
||||
except: break
|
||||
|
||||
async def ev(expr, mid, t=30):
|
||||
await ws.send(json.dumps({'id': mid, 'method': 'Runtime.evaluate', 'params': {'expression': expr, 'awaitPromise': True}}))
|
||||
while True:
|
||||
msg = json.loads(await asyncio.wait_for(ws.recv(), timeout=t))
|
||||
if msg.get('id') == mid:
|
||||
err = msg.get('result', {}).get('exceptionDetails')
|
||||
if err: return 'EX: ' + str(err.get('exception', {}).get('description', ''))[:200]
|
||||
r = msg.get('result', {}).get('result', {})
|
||||
return r.get('value', r.get('description', str(r)))
|
||||
|
||||
# Test 1: Rust updater check
|
||||
print('\n Test 1: check_for_update via Tauri IPC')
|
||||
r = await ev('(async()=>{const r=await window.__TAURI_INTERNALS__.invoke("check_for_update");return JSON.stringify(r)})()', 10)
|
||||
if r.startswith('EX:'): print(f' FAIL: {r}'); sys.exit(1)
|
||||
data = json.loads(r)
|
||||
if not data: print(' FAIL: no update found'); sys.exit(1)
|
||||
print(f' PASS: {data["currentVersion"]} -> {data["version"]}')
|
||||
|
||||
# Test 2: get_app_version
|
||||
r = await ev('window.__TAURI_INTERNALS__.invoke("get_app_version")', 11)
|
||||
print(f'\n Test 2: App version = {r}')
|
||||
assert r == '0.0.1', f'Expected 0.0.1, got {r}'
|
||||
print(' PASS')
|
||||
|
||||
# Test 3: Download + install
|
||||
if INSTALL:
|
||||
print('\n Test 3: download_and_install_update')
|
||||
print(' Downloading update (this may take a minute)...')
|
||||
try:
|
||||
r = await ev('(async()=>{await window.__TAURI_INTERNALS__.invoke("download_and_install_update");return"OK"})()', 20, t=300)
|
||||
print(f' Result: {r}')
|
||||
except Exception:
|
||||
print(f' App exited (installer took over) - SUCCESS')
|
||||
else:
|
||||
print('\n Test 3: Skipped (use --install for full install)')
|
||||
|
||||
print('\n ALL TESTS PASSED\n')
|
||||
|
||||
asyncio.run(run())
|
||||
PYEOF
|
||||
Reference in New Issue
Block a user