From 50934e2ac61a1dca9516161d122ab83b0d358c17 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Thu, 13 Aug 2026 07:47:43 +0200 Subject: [PATCH] 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 --- Dockerfile.builder | 16 ++++++++++++++++ scripts/sync-android-sources.sh | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/Dockerfile.builder b/Dockerfile.builder index a685950b..8371546d 100644 --- a/Dockerfile.builder +++ b/Dockerfile.builder @@ -87,6 +87,22 @@ RUN $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --sdk_root=$ANDROID_HOME \ # Set NDK environment variable ENV NDK_HOME=$ANDROID_HOME/ndk/$NDK_VERSION +# Gradle distribution. `tauri android init` regenerates gen/android with a +# wrapper pointing at services.gradle.org, so every Android job would otherwise +# download ~130MB of Gradle at build time — slow, and a hard failure when the +# CDN hiccups ("Unexpected end of file from server"). Ship the distribution in +# the image instead; scripts/sync-android-sources.sh repoints the regenerated +# wrapper at this local copy. Keep GRADLE_VERSION in sync with the version +# Tauri's generated wrapper requests. +ENV GRADLE_VERSION=8.14.3 \ + GRADLE_HOME=/opt/gradle/gradle-8.14.3 +RUN mkdir -p /opt/gradle/dist && \ + wget -q "https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip" \ + -O "/opt/gradle/dist/gradle-${GRADLE_VERSION}-bin.zip" && \ + unzip -q "/opt/gradle/dist/gradle-${GRADLE_VERSION}-bin.zip" -d /opt/gradle && \ + "$GRADLE_HOME/bin/gradle" --version +ENV PATH="$GRADLE_HOME/bin:$PATH" + # --------------------------------------------------------------------------- # Desktop packaging tools — kept in a trailing layer ON PURPOSE so that adding # or changing a packaging tool doesn't invalidate the expensive apt/rust/Android diff --git a/scripts/sync-android-sources.sh b/scripts/sync-android-sources.sh index 1a00c822..47095242 100755 --- a/scripts/sync-android-sources.sh +++ b/scripts/sync-android-sources.sh @@ -118,4 +118,26 @@ if [ -d "$RES_SRC" ]; then "$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"