Files
jellytau/scripts/sync-android-sources.sh
dtourolleandClaude Opus 5 50934e2ac6
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 10m7s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 5m28s
Traceability Validation / Check Requirement Traces (push) Successful in 19s
Build & Release / Run Tests (push) Successful in 7m35s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 3m2s
Build & Release / Build Linux (push) Successful in 20m0s
Build & Release / Build Windows (push) Successful in 8m36s
Build & Release / Build Android (push) Successful in 30m30s
Build & Release / Create Release (push) Successful in 21s
ci(android): ship Gradle in the builder image instead of downloading it
The release APK job died at the Gradle wrapper step, after the 11-minute
Rust compile had already succeeded:

    Downloading https://services.gradle.org/distributions/gradle-8.14.3-bin.zip
    java.net.SocketException: Unexpected end of file from server

`tauri android init` regenerates gen/android with a wrapper pointing at
services.gradle.org, so every Android job re-downloaded ~130MB of Gradle at
build time. That is slow on a good day and a hard build failure when the CDN
drops the connection mid-transfer. It was also a standing violation of the
rule that every build tool must already live in the builder image.

Dockerfile.builder installs Gradle 8.14.3, keeping both the unpacked
distribution (on PATH) and the original zip under /opt/gradle/dist. A
`gradle --version` smoke-test fails the image build on a bad version rather
than letting CI discover it.

sync-android-sources.sh then repoints the regenerated wrapper at that local
zip, which is the established place for fixing up the generated project.
It parses the version the wrapper actually requests, so a future Tauri Gradle
bump logs "not in image, will download" instead of pointing at a missing
file. On dev machines /opt/gradle/dist does not exist and the properties file
is left untouched.

Verified by running the project's own wrapper jar inside a network namespace
with no connectivity: it resolved and unpacked the local zip to 100% and
proceeded into build-script evaluation.

Note: this is inert until the builder image is rebuilt and pushed
(scripts/build-builder-image.sh).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 07:47:43 +02:00

144 lines
6.7 KiB
Bash
Executable File

#!/bin/bash
# Sync Android source files from src-tauri/android to src-tauri/gen/android
# This ensures the generated build directory has the latest source files
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
SOURCE_DIR="$PROJECT_ROOT/src-tauri/android/src/main/java/com/dtourolle/jellytau"
TARGET_DIR="$PROJECT_ROOT/src-tauri/gen/android/app/src/main/java/com/dtourolle/jellytau"
echo "Syncing Android sources..."
echo " From: $SOURCE_DIR"
echo " To: $TARGET_DIR"
# Create target directory if it doesn't exist
mkdir -p "$TARGET_DIR"
# Remove old copies of player and security directories
rm -rf "$TARGET_DIR/player" "$TARGET_DIR/security"
# Copy the directories
cp -r "$SOURCE_DIR/player" "$TARGET_DIR/"
cp -r "$SOURCE_DIR/security" "$TARGET_DIR/"
# Copy individual Kotlin files (like VideoOverlayManager.kt)
for kt_file in "$SOURCE_DIR"/*.kt; do
if [ -f "$kt_file" ]; then
cp "$kt_file" "$TARGET_DIR/"
echo " Copied: $(basename "$kt_file")"
fi
done
# Restore the app module build.gradle.kts (media3 deps + release signing config).
# gen/android is regenerated by `tauri android init`, so this tracked template is
# the source of truth and must be copied back after any (re)generation.
APP_GRADLE_SRC="$PROJECT_ROOT/src-tauri/android/app/build.gradle.kts"
APP_GRADLE_DST="$PROJECT_ROOT/src-tauri/gen/android/app/build.gradle.kts"
if [ -f "$APP_GRADLE_SRC" ]; then
cp "$APP_GRADLE_SRC" "$APP_GRADLE_DST"
echo " Copied: app/build.gradle.kts"
fi
# AndroidManifest.xml. `tauri android init` regenerates gen/android from
# tauri.conf.json and would drop our hand-maintained entries (media playback
# service + permissions, hardware acceleration, picture-in-picture attributes
# on MainActivity), so this tracked copy is the source of truth and must be
# restored after a regen. Gradle reads ONLY the gen/ copy - there is no
# manifest-merger hook here, so this must be the complete manifest.
MANIFEST_SRC="$PROJECT_ROOT/src-tauri/android/src/main/AndroidManifest.xml"
MANIFEST_DST="$PROJECT_ROOT/src-tauri/gen/android/app/src/main/AndroidManifest.xml"
if [ -f "$MANIFEST_SRC" ]; then
cp "$MANIFEST_SRC" "$MANIFEST_DST"
echo " Copied: app/src/main/AndroidManifest.xml"
fi
# Custom ProGuard/R8 keep rules. Required for minified release builds:
# the player/ and security/ Kotlin classes are loaded by name via JNI from
# Rust, so R8 can't see the references and would strip them without this.
# build.gradle.kts globs **/*.pro, so dropping it in app/ is enough.
PROGUARD_SRC="$PROJECT_ROOT/src-tauri/android/app/proguard-jellytau.pro"
PROGUARD_DST="$PROJECT_ROOT/src-tauri/gen/android/app/proguard-jellytau.pro"
if [ -f "$PROGUARD_SRC" ]; then
cp "$PROGUARD_SRC" "$PROGUARD_DST"
echo " Copied: app/proguard-jellytau.pro"
fi
# Launcher icons / adaptive-icon mipmaps. `tauri android init` generates
# low-quality launcher icons from tauri.conf.json (which has no high-res
# Android source), so overwrite them with the real committed mipmaps.
RES_SRC="$PROJECT_ROOT/src-tauri/android/src/main/res"
RES_DST="$PROJECT_ROOT/src-tauri/gen/android/app/src/main/res"
if [ -d "$RES_SRC" ]; then
for dir in "$RES_SRC"/mipmap-*; do
[ -d "$dir" ] || continue
name="$(basename "$dir")"
mkdir -p "$RES_DST/$name"
cp "$dir"/* "$RES_DST/$name/"
echo " Copied res: $name"
done
# values/ (themes.xml): status-bar styling that `tauri android init` does
# not generate. Previously this directory was tracked but never copied, so
# the theme customizations below never reached a build.
if [ -d "$RES_SRC/values" ]; then
mkdir -p "$RES_DST/values"
cp "$RES_SRC"/values/*.xml "$RES_DST/values/"
echo " Copied res: values"
fi
# xml/ (network_security_config.xml): referenced from the manifest, so a
# missing copy fails the resource link rather than degrading quietly.
# Merged into Tauri's generated xml/ (which holds file_paths.xml) rather
# than replacing it.
if [ -d "$RES_SRC/xml" ]; then
mkdir -p "$RES_DST/xml"
cp "$RES_SRC"/xml/*.xml "$RES_DST/xml/"
echo " Copied res: xml"
fi
# We ship only the color adaptive icon (background + foreground). Drop any
# monochrome layer Tauri may generate: the themed-icon monochrome doesn't
# render well, and our adaptive-icon xml no longer references it, so a stray
# ic_launcher_monochrome.png would just be dead weight.
rm -f "$RES_DST"/mipmap-*/ic_launcher_monochrome.png
# `tauri android init` also emits the Android Studio DEFAULT adaptive icon
# as API-qualified VECTOR drawables:
# drawable/ic_launcher_background.xml (solid #3DDC84 green)
# drawable-v24/ic_launcher_foreground.xml (the Android robot)
# Because drawable-v24 is a more specific match than our unqualified
# mipmap-*/ic_launcher_*.png, on API 24+ the vector WINS and the app ships
# the green square robot instead of our jellyfish. Remove them so the
# adaptive-icon xml resolves @mipmap/ic_launcher_{background,foreground}
# to the real committed PNGs.
rm -f "$RES_DST"/drawable/ic_launcher_background.xml \
"$RES_DST"/drawable-v24/ic_launcher_foreground.xml \
"$RES_DST"/drawable*/ic_launcher_foreground.xml \
"$RES_DST"/drawable*/ic_launcher_background.xml
fi
# Gradle wrapper distribution. `tauri android init` regenerates the wrapper
# pointing at services.gradle.org, so each build downloads ~130MB of Gradle —
# slow, and a hard failure when the CDN drops the connection mid-transfer
# ("Unexpected end of file from server"), which is what broke the release APK
# job. The builder image ships the matching distribution under /opt/gradle/dist,
# so when it's present repoint the wrapper at that local zip and build offline.
# Outside the image (dev machines) the properties file is left untouched and the
# wrapper downloads as usual.
WRAPPER_PROPS="$PROJECT_ROOT/src-tauri/gen/android/gradle/wrapper/gradle-wrapper.properties"
if [ -f "$WRAPPER_PROPS" ]; then
WANTED_VERSION="$(sed -n 's#.*/gradle-\([0-9.]*\)-\(bin\|all\)\.zip.*#\1#p' "$WRAPPER_PROPS")"
LOCAL_DIST="/opt/gradle/dist/gradle-${WANTED_VERSION}-bin.zip"
if [ -n "$WANTED_VERSION" ] && [ -f "$LOCAL_DIST" ]; then
# distributionUrl is a java.util.Properties value: ':' must stay escaped.
sed -i "s#^distributionUrl=.*#distributionUrl=file\\\\:///opt/gradle/dist/gradle-${WANTED_VERSION}-bin.zip#" \
"$WRAPPER_PROPS"
echo " Gradle wrapper -> local distribution ($WANTED_VERSION, offline)"
elif [ -n "$WANTED_VERSION" ]; then
echo " Gradle wrapper: $WANTED_VERSION not in image, will download"
fi
fi
echo "✓ Android sources synced successfully"