From aecef0905d4b22862f449d2cc20d2c3740ca0f2a Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Tue, 7 Apr 2026 10:00:49 +0400 Subject: [PATCH] feat: fire-and-forget build script with ntfy + rustypaste - Uploads build script to remote, runs in tmux (survives SSH drop) - Builds Rust + APK in Docker - Validates both .so files present before APK build - Uploads APK to rustypaste - Sends ntfy.sh/wzp notification with download URL - --install flag: waits + downloads + adb installs locally - --rust flag: force clean Rust rebuild - --pull flag: git pull before building Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/build-and-notify.sh | 147 ++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100755 scripts/build-and-notify.sh diff --git a/scripts/build-and-notify.sh b/scripts/build-and-notify.sh new file mode 100755 index 0000000..c46e259 --- /dev/null +++ b/scripts/build-and-notify.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Build Android APK via Docker on SepehrHomeserverdk, upload to rustypaste, +# notify via ntfy.sh/wzp. Fire and forget. +# +# Usage: +# ./scripts/build-and-notify.sh Build + upload + notify +# ./scripts/build-and-notify.sh --rust Force Rust rebuild +# ./scripts/build-and-notify.sh --pull Git pull before building +# ./scripts/build-and-notify.sh --install Also download + adb install locally + +REMOTE_HOST="SepehrHomeserverdk" +BASE_DIR="/mnt/storage/manBuilder" +NTFY_TOPIC="https://ntfy.sh/wzp" +LOCAL_OUTPUT="target/android-apk" +SSH_OPTS="-o ConnectTimeout=15 -o ServerAliveInterval=15 -o ServerAliveCountMax=4 -o LogLevel=ERROR" + +REBUILD_RUST=0 +DO_PULL=0 +DO_INSTALL=0 +for arg in "$@"; do + case "$arg" in + --rust) REBUILD_RUST=1 ;; + --pull) DO_PULL=1 ;; + --install) DO_INSTALL=1 ;; + esac +done + +log() { echo -e "\033[1;36m>>> $*\033[0m"; } + +ssh_cmd() { ssh -A $SSH_OPTS "$REMOTE_HOST" "$@"; } + +# Upload the remote build script +log "Uploading build script to remote..." +ssh_cmd "cat > /tmp/wzp-docker-build.sh" <<'REMOTE_SCRIPT' +#!/usr/bin/env bash +set -euo pipefail + +BASE_DIR="/mnt/storage/manBuilder" +NTFY_TOPIC="https://ntfy.sh/wzp" +REBUILD_RUST="${1:-0}" +DO_PULL="${2:-0}" + +notify() { curl -s -d "$1" "$NTFY_TOPIC" > /dev/null 2>&1 || true; } + +# Pull if requested +if [ "$DO_PULL" = "1" ]; then + echo ">>> Pulling latest..." + cd "$BASE_DIR/data/source" + git checkout -- . 2>/dev/null || true + git pull origin feat/android-voip-client 2>&1 | tail -3 +fi + +# Clean Rust if requested +if [ "$REBUILD_RUST" = "1" ]; then + echo ">>> Cleaning Rust target..." + rm -rf "$BASE_DIR/data/cache/target/aarch64-linux-android/release" +fi + +# Fix perms +find "$BASE_DIR/data/source" "$BASE_DIR/data/cache" \ + ! -user 1000 -o ! -group 1000 2>/dev/null | \ + xargs -r chown 1000:1000 2>/dev/null || true + +# Clean jniLibs +rm -rf "$BASE_DIR/data/source/android/app/src/main/jniLibs/arm64-v8a" + +notify "WZP build started..." + +echo ">>> Building in Docker..." +docker run --rm --user 1000:1000 \ + -v "$BASE_DIR/data/source:/build/source" \ + -v "$BASE_DIR/data/cache/cargo-registry:/home/builder/.cargo/registry" \ + -v "$BASE_DIR/data/cache/cargo-git:/home/builder/.cargo/git" \ + -v "$BASE_DIR/data/cache/target:/build/source/target" \ + -v "$BASE_DIR/data/cache/gradle:/home/builder/.gradle" \ + wzp-android-builder bash -c ' +set -euo pipefail +cd /build/source + +echo ">>> Rust build..." +cargo ndk -t arm64-v8a -o android/app/src/main/jniLibs build --release -p wzp-android 2>&1 | tail -5 + +echo ">>> Checking .so files..." +ls -lh android/app/src/main/jniLibs/arm64-v8a/ +[ -f android/app/src/main/jniLibs/arm64-v8a/libwzp_android.so ] || { echo "ERROR: libwzp_android.so missing!"; exit 1; } +[ -f android/app/src/main/jniLibs/arm64-v8a/libc++_shared.so ] || { echo "ERROR: libc++_shared.so missing!"; exit 1; } + +echo ">>> APK build..." +cd android && chmod +x gradlew +./gradlew clean assembleDebug --no-daemon --warning-mode=none 2>&1 | tail -3 +echo "APK_BUILT" +' + +# Upload to rustypaste +echo ">>> Uploading to rustypaste..." +source "$BASE_DIR/.env" +APK=$(find "$BASE_DIR/data/source/android" -name "app-debug*.apk" -path "*/outputs/apk/*" | head -1) +if [ -n "$APK" ]; then + URL=$(curl -s -F "file=@$APK" -H "Authorization: $rusty_auth_token" "$rusty_address") + echo "UPLOAD_URL=$URL" + notify "WZP build done! APK: $URL" + echo ">>> Done! APK at: $URL" +else + notify "WZP build FAILED - no APK" + echo "ERROR: No APK found" + exit 1 +fi +REMOTE_SCRIPT + +ssh_cmd "chmod +x /tmp/wzp-docker-build.sh" + +# Run in tmux +log "Starting build in tmux..." +ssh_cmd "tmux kill-session -t wzp-build 2>/dev/null; true" +ssh_cmd "tmux new-session -d -s wzp-build '/tmp/wzp-docker-build.sh $REBUILD_RUST $DO_PULL 2>&1 | tee /tmp/wzp-build.log'" + +log "Build running! You'll get a notification on ntfy.sh/wzp with the download URL." +echo "" +echo " Monitor: ssh $REMOTE_HOST 'tail -f /tmp/wzp-build.log'" +echo " Status: ssh $REMOTE_HOST 'tail -5 /tmp/wzp-build.log'" +echo "" + +# Optionally wait and install locally +if [ "$DO_INSTALL" = "1" ]; then + log "Waiting for build to finish..." + while true; do + sleep 15 + if ssh_cmd "grep -q 'UPLOAD_URL\|ERROR' /tmp/wzp-build.log 2>/dev/null"; then + break + fi + done + + URL=$(ssh_cmd "grep UPLOAD_URL /tmp/wzp-build.log | tail -1 | cut -d= -f2") + if [ -n "$URL" ]; then + log "Downloading APK..." + mkdir -p "$LOCAL_OUTPUT" + curl -s -o "$LOCAL_OUTPUT/wzp-debug.apk" "$URL" + log "Installing..." + adb uninstall com.wzp.phone 2>/dev/null || true + adb install "$LOCAL_OUTPUT/wzp-debug.apk" + log "Done!" + else + err "Build failed" + fi +fi