Compare commits

2 Commits

Author SHA1 Message Date
eea6f4f7ed fix: bake the NDK into the image so debugSymbolLevel works
All checks were successful
build-android-builder-image / build-image (push) Successful in 3m15s
ndk { debugSymbolLevel = "SYMBOL_TABLE" } needs the NDK's llvm-objcopy
to extract native symbol tables into the .aab's BUNDLE-METADATA. The
image shipped no NDK, so AGP silently skipped the step and Play warned
"no debug symbols" on every bundle with native code (confirmed by
inspecting Dade's 0.0.2 .aab: onnxruntime .so present, debugsymbols
metadata dir absent).

Install ndk;27.0.12077973 (AGP 8.13's default, so app repos need no
android.ndkVersion), export ANDROID_NDK_ROOT/HOME, and add llvm-objcopy
to the image sanity check so an arch/version mismatch fails the image
build instead of silently dropping symbols downstream.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 09:09:36 -04:00
3eadbb01a2 Merge pull request 'Add reusable Heluca Google Play publishing guide' (#2) from docs-play-publishing into main
All checks were successful
build-android-builder-image / build-image (push) Successful in 1m46s
Reviewed-on: #2
2026-07-21 17:56:01 +00:00
2 changed files with 25 additions and 4 deletions

View File

@@ -12,6 +12,12 @@ FROM eclipse-temurin:21-jdk-noble
ARG CMDLINE_TOOLS_VERSION=13114758 ARG CMDLINE_TOOLS_VERSION=13114758
ARG BUILD_TOOLS_VERSION=36.0.0 ARG BUILD_TOOLS_VERSION=36.0.0
ARG PLATFORM_VERSION=android-36 ARG PLATFORM_VERSION=android-36
# NDK: required for `ndk { debugSymbolLevel }` — AGP shells out to the NDK's
# llvm-objcopy to extract native symbol tables into the .aab's BUNDLE-METADATA.
# Without it AGP silently skips the step and Play warns "no debug symbols". Pin
# to AGP's default NDK (r27 for AGP 8.13) so app repos need no `ndkVersion`.
# Check the default at https://developer.android.com/build/releases/gradle-plugin
ARG NDK_VERSION=27.0.12077973
ENV ANDROID_SDK_ROOT=/opt/android-sdk \ ENV ANDROID_SDK_ROOT=/opt/android-sdk \
ANDROID_HOME=/opt/android-sdk \ ANDROID_HOME=/opt/android-sdk \
@@ -48,14 +54,25 @@ RUN yes | sdkmanager --licenses >/dev/null && \
sdkmanager --install \ sdkmanager --install \
"platform-tools" \ "platform-tools" \
"platforms;${PLATFORM_VERSION}" \ "platforms;${PLATFORM_VERSION}" \
"build-tools;${BUILD_TOOLS_VERSION}" && \ "build-tools;${BUILD_TOOLS_VERSION}" \
"ndk;${NDK_VERSION}" && \
rm -rf ${ANDROID_SDK_ROOT}/.android rm -rf ${ANDROID_SDK_ROOT}/.android
# Point AGP at the baked-in NDK. AGP auto-discovers ndk;<ver> under the SDK
# root, but exporting these makes it explicit and lets the debugSymbolLevel
# step resolve llvm-objcopy without an app-side android.ndkVersion.
ENV ANDROID_NDK_ROOT=${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION} \
ANDROID_NDK_HOME=${ANDROID_SDK_ROOT}/ndk/${NDK_VERSION}
# Sanity: fail the image build if the toolchain isn't actually usable. # Sanity: fail the image build if the toolchain isn't actually usable.
# Execute aapt2 (not just --version a tool) so an arch mismatch in the # Execute aapt2 (not just --version a tool) so an arch mismatch in the
# build-tools binaries fails the image build here, not in every app build. # build-tools binaries fails the image build here, not in every app build.
# Execute the NDK's llvm-objcopy too — it is the exact binary the bundle's
# debug-symbol extraction shells out to, so an arch/version mismatch fails
# here rather than silently dropping symbols from every app's .aab.
RUN java -version && sdkmanager --version && node --version && \ RUN java -version && sdkmanager --version && node --version && \
"${ANDROID_SDK_ROOT}/build-tools/${BUILD_TOOLS_VERSION}/aapt2" version && \ "${ANDROID_SDK_ROOT}/build-tools/${BUILD_TOOLS_VERSION}/aapt2" version && \
test -d "${ANDROID_SDK_ROOT}/platforms/${PLATFORM_VERSION}" test -d "${ANDROID_SDK_ROOT}/platforms/${PLATFORM_VERSION}" && \
"${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-objcopy" --version
WORKDIR /workspace WORKDIR /workspace

View File

@@ -22,8 +22,11 @@ so building on amd64 costs nothing on the output side.
## What's in the image ## What's in the image
- Eclipse Temurin JDK 21 (native amd64) - Eclipse Temurin JDK 21 (native amd64)
- Android cmdline-tools, platform, and build-tools — **baked in**, so prod - Android cmdline-tools, platform, build-tools, and **NDK****baked in**, so
builds don't depend on Google's download endpoint at job time prod builds don't depend on Google's download endpoint at job time. The NDK is
what makes `ndk { debugSymbolLevel }` work: AGP shells out to its
`llvm-objcopy` to embed native symbol tables in the `.aab`, so Play can
symbolicate native crashes. Without it AGP skips that step silently.
- `git`, `curl`, `unzip` - `git`, `curl`, `unzip`
- Node.js 24 — so Gitea Actions JS actions (`actions/checkout`, - Node.js 24 — so Gitea Actions JS actions (`actions/checkout`,
`upload-artifact`, …) run when this image is used as a job `container` `upload-artifact`, …) run when this image is used as a job `container`
@@ -35,6 +38,7 @@ Pinned versions live as `ARG`s at the top of the `Dockerfile`:
| `CMDLINE_TOOLS_VERSION` | `13114758` | https://developer.android.com/studio#command-line-tools-only | | `CMDLINE_TOOLS_VERSION` | `13114758` | https://developer.android.com/studio#command-line-tools-only |
| `BUILD_TOOLS_VERSION` | `36.0.0` | SDK Manager / release notes | | `BUILD_TOOLS_VERSION` | `36.0.0` | SDK Manager / release notes |
| `PLATFORM_VERSION` | `android-36`| your app's `compileSdk` | | `PLATFORM_VERSION` | `android-36`| your app's `compileSdk` |
| `NDK_VERSION` | `27.0.12077973` | AGP's default NDK: https://developer.android.com/build/releases/gradle-plugin |
The build-tools binaries (`aapt2`, `d8`) are x86_64 ELF — this is why the The build-tools binaries (`aapt2`, `d8`) are x86_64 ELF — this is why the
image is amd64 (see "Why amd64" above). image is amd64 (see "Why amd64" above).