Skip to content

fix(android): AAB upload + exclude x86 from release to fix 100 MB APK limit (#3783)#3784

Merged
grimen merged 2 commits into
mainfrom
fix/play-store-aab-3783
May 28, 2026
Merged

fix(android): AAB upload + exclude x86 from release to fix 100 MB APK limit (#3783)#3784
grimen merged 2 commits into
mainfrom
fix/play-store-aab-3783

Conversation

@grimen
Copy link
Copy Markdown
Contributor

@grimen grimen commented May 26, 2026

Summary

Two related Android release-distribution fixes:

  1. Switch the Play Store upload from multi-APK to an Android App Bundle (AAB) — the universal APK (~240 MB) exceeds Google Play's hard 100 MB single-APK limit:

    [!] Google Api Error: Invalid request - APK of size 251620080 is too large.
    

    Play generates and signs optimized per-device APKs server-side from the AAB, so the single-APK ceiling no longer applies and users get smaller downloads.

  2. Exclude x86 / x86_64 from release builds — those ABIs exist only for emulators; every real Android device is ARM. Dropping them shrinks the AAB and the universal APK (240 MB → 119 MB) and removes dead weight from what ships. A CI guard prevents silent re-introduction.

Closes #3783.

Changes

android/fastlane/Fastfile

  • build lane: add gradle(task: "bundle", build_type: "Release") to produce app/build/outputs/bundle/release/app-release.aab. The existing assemble step is kept, so the universal/split APKs are still built (see below).
  • build lane: pass reactNativeArchitectures = "armeabi-v7a,arm64-v8a" to the release assemble + bundle gradle calls. This skips compiling the unused x86 native libs (faster CI) and stops emitting the orphan app-x86*-release.apk split APKs.
  • play_store_upload lane: upload aab: instead of apk_paths:. The old multi-APK call is kept commented out and flagged as legacy for reference, with an inline rationale.

android/app/build.gradle

  • Add ndk { abiFilters "armeabi-v7a", "arm64-v8a" } to the release buildType. This is the hard packaging guarantee: it strips x86 from the universal APK and the AAB even if a third-party AAR ships prebuilt x86 .so — something the reactNativeArchitectures property alone cannot catch. debug is untouched, so it keeps all 4 ABIs for emulator e2e.

ci/tasks/build.sh

  • After the fastlane build, add a guard that fails the release if app-release.aab or app-universal-release.apk contains any lib/x86. It piggybacks on the build that already runs (≈ two unzip | greps), and guards against a silent regression — e.g. abiFilters dropped, x86 re-added to reactNativeArchitectures, or the legacy apk_paths upload re-enabled — quietly putting x86 back into the Play AAB.

Why the APKs are still built

The universal/split APKs are consumed elsewhere and must not be removed:

  • e2e checks/uploads the universal APK — ci/tasks/e2e-test-android.sh:21
  • pipeline resource regexp — ci/pipeline.yml
  • Huawei lane (huawei_store_upload) uploads the universal APK

ci/tasks/build.sh already copies all of app/build/outputs/*, so the AAB is archived to GCS automatically — no extra copy step needed.

Why x86 exclusion is scoped to release only

x86/x86_64 are exactly what the emulator-based e2e tests need, and they load the debug APK:

  • e2e/config/wdio.conf.js — Appium on generic_x86, loads app-universal-debug.apk
  • .detoxrc.js — Detox on the Pixel_API_34 AVD (x86_64), loads app-universal-debug.apk

The release artifacts all run on ARM hardware (Play devices, Huawei devices, BrowserStack real devices), so they don't need x86. Debug builds are left with all 4 ABIs, so emulator e2e is unaffected.

Verification

Ran a real local release build (assembleRelease + bundleRelease, RN 0.76.9, new arch on) and inspected the lib/ ABIs of every artifact with unzip -l:

Artifact Consumed by ABIs x86 present?
app-release.aab Play Store arm64-v8a, armeabi-v7a ❌ none
app-universal-release.apk (119 MB) Huawei, BrowserStack e2e arm64-v8a, armeabi-v7a ❌ none
app-arm64-v8a-release.apk arm64-v8a ❌ none
app-armeabi-v7a-release.apk armeabi-v7a ❌ none
app-x86-release.apk not produced
app-x86_64-release.apk not produced
  • With reactNativeArchitectures arm-only, no x86 split APKs are produced at all.
  • The CI guard was tested both ways: it passes on the ARM-only AAB + universal APK, and its pattern correctly rejects base/lib/x86/, base/lib/x86_64/, and lib/x86/ while allowing arm64-v8a / armeabi-v7a.
  • Note: the ARM-only universal APK is still 119 MB (> 100 MB), confirming x86 exclusion alone would not have fixed the Play upload — the AAB switch is the actual fix; x86 exclusion is a complementary size/build-time win.

Notes / follow-ups for the maintainer

  • Play App Signing must be enabled for the app (required for AAB uploads). Almost certainly already is, since we upload to a track. (owner-verified)
  • versionCode (verify before first upload): the AAB uses the plain build-number code (it has abi == null, so it skips the per-ABI × 10_000_000 multiplier the split APKs use). Google Play version codes are permanent. Since the oversized-APK upload has been failing and rolling the whole edit back, those inflated codes were most likely never registered and the plain code should be free. Please confirm the highest internal-track versionCode in the Play Console is below the AAB's base code before the first AAB upload; if not, bump build-number-android (ops action, no code change).
  • The legacy per-ABI versionCode multiplier in build.gradle is now obsolete for Play (those APKs no longer go there). Retiring it in favor of a single monotonic versionCode is good future cleanup but is intentionally out of scope here.

Acceptance criteria

  • Release build produces app/build/outputs/bundle/release/app-release.aab
  • play_store_upload uploads the .aab (no apk_paths)
  • Universal/split ARM APKs still built (no e2e/Huawei regression)
  • Release AAB + universal APK are ARM-only (verified: no lib/x86)
  • No x86 split APKs produced; debug retains all 4 ABIs for emulator e2e
  • CI guard fails the release on any lib/x86 in the AAB/universal APK
  • Promotion lanes (promote_to_*, public_phased_percent) unchanged — they promote tracks only, upload no binary
  • A build publishes to the Play Store internal track from CI (owner-verified)
  • Play App Signing confirmed enabled (owner-verified)

🤖 Generated with Claude Code

grimen and others added 2 commits May 26, 2026 14:55
The play_store_upload lane uploaded five APKs including the universal APK
(~240 MB), which exceeds Google Play's hard 100 MB single-APK limit and
failed the whole upload edit:

    Google Api Error: Invalid request - APK of size 251620080 is too large.

Build an Android App Bundle (AAB) and upload that to Play instead. Play
generates/signs optimized per-device APKs server-side, so the single-APK
ceiling no longer applies. The build lane still runs `assemble`, so the
universal/split APKs remain available to e2e (BrowserStack), Huawei
AppGallery, and the GCS archive. The old multi-APK upload is kept
commented out and flagged as legacy.

Closes #3783

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real Android devices are ARM-only; x86/x86_64 exist only for emulators.
Strip them from release builds to shrink the Play AAB and the universal
APK consumed by Huawei + BrowserStack.

- build.gradle: ndk.abiFilters on the release buildType (hard packaging
  guarantee; strips x86 even from 3rd-party prebuilt .so)
- Fastfile: reactNativeArchitectures=armeabi-v7a,arm64-v8a on the release
  assemble+bundle lanes (skips compiling x86 native libs, drops the orphan
  x86 split APKs)
- ci/tasks/build.sh: post-build guard fails the release if the AAB or
  universal APK ships any lib/x86

Debug builds keep all 4 ABIs for emulator e2e (Detox Pixel_API_34,
Appium generic_x86).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@grimen grimen changed the title fix(android): switch Play Store upload to AAB to fix 100 MB APK limit (#3783) fix(android): AAB upload + exclude x86 from release to fix 100 MB APK limit (#3783) May 27, 2026
@grimen grimen requested review from esaugomez31 and openoms May 27, 2026 22:42
@grimen grimen merged commit 197c1e7 into main May 28, 2026
6 of 8 checks passed
@grimen grimen deleted the fix/play-store-aab-3783 branch May 28, 2026 06:59
@grimen
Copy link
Copy Markdown
Contributor Author

grimen commented May 28, 2026

⚠️ Follow-up: this needed a companion change in blink-mobile-deployments (the "no CI script changes needed" note was incorrect).

play_store_upload runs from the blink-mobile-deployments pipeline, whose upload-to-play-store task downloaded only the apk/release/ dir (download_build_apk). After this PR merged, the post-merge deploy failed:

Driving the lane 'android play_store_upload' 🚀
Could not find aab file at path './app/build/outputs/bundle/release/app-release.aab'

Fix: blinkbitcoin/blink-mobile-deployments#496 — adds download_build_aab (the AAB is already archived to GCS next to the APK) + a gsutil stat fail-fast guard.

Ordering note: the current latest prod build (v2.4.46) predates this PR (still has x86 splits, no AAB in GCS), so a green Play deploy needs: merge #496 → run a fresh prod-build on current main → the next upload-to-play-store will find and upload the AAB.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Play Store upload fails: universal APK exceeds 100 MB limit — switch to AAB

2 participants