From 7a1e6b06c19b134141b57bf2c933316f37e14c59 Mon Sep 17 00:00:00 2001 From: Nils Briggen Date: Sat, 13 Jun 2026 23:59:08 +0200 Subject: [PATCH] fix(android): working signed release APK - docker/android.Dockerfile: install nodejs (image ships Bun; ci-android-sign.sh patches build.gradle with a Node script). - buffer polyfill: whisper.rn -> safe-buffer requires Node's `buffer`, absent in Metro/RN. Add `buffer` dep, alias it in metro.config.js, set the global in _layout. Web export unaffected (verified). Validated locally end-to-end: assembleRelease BUILD SUCCESSFUL, 117MB APK across all 4 ABIs with librnwhisper.so, signed (CN=Wisp, O=briggen.dev). Co-Authored-By: Claude Opus 4.8 --- bun.lock | 5 +++++ docker/android.Dockerfile | 4 ++++ metro.config.js | 14 ++++++++++++++ package.json | 1 + src/app/_layout.tsx | 7 +++++++ 5 files changed, 31 insertions(+) create mode 100644 metro.config.js diff --git a/bun.lock b/bun.lock index 2b57c28..e24fd1f 100644 --- a/bun.lock +++ b/bun.lock @@ -7,6 +7,7 @@ "dependencies": { "@expo/ui": "~56.0.17", "@huggingface/transformers": "^4.2.0", + "buffer": "^6.0.3", "dexie": "^4.4.3", "expo": "~56.0.11", "expo-constants": "~56.0.18", @@ -595,6 +596,8 @@ "bser": ["bser@2.1.1", "", { "dependencies": { "node-int64": "^0.4.0" } }, "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ=="], + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -839,6 +842,8 @@ "hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], "image-size": ["image-size@1.2.1", "", { "dependencies": { "queue": "6.0.2" }, "bin": { "image-size": "bin/image-size.js" } }, "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw=="], diff --git a/docker/android.Dockerfile b/docker/android.Dockerfile index db4c4bf..42df6d3 100644 --- a/docker/android.Dockerfile +++ b/docker/android.Dockerfile @@ -81,6 +81,10 @@ COPY . . # Generate the native android/ project (autolinks whisper.rn, reanimated, etc.). RUN bunx expo prebuild --platform android --no-install +# Node is required by scripts/ci-android-sign.sh (this image ships Bun, not Node). +# Installed AFTER prebuild so the cached toolchain + prebuild layers are reused. +RUN apt-get update && apt-get install -y --no-install-recommends nodejs && rm -rf /var/lib/apt/lists/* + # Decode the keystore + wire release signing, then assemble the signed APK. # Secrets are mounted as files under /run/secrets and exported only for this RUN. RUN --mount=type=secret,id=ks_b64 \ diff --git a/metro.config.js b/metro.config.js new file mode 100644 index 0000000..fd7215a --- /dev/null +++ b/metro.config.js @@ -0,0 +1,14 @@ +// Extends Expo's default Metro config. The only customization is a `buffer` +// polyfill: whisper.rn (native) pulls in `safe-buffer`, which `require('buffer')` +// — a Node core module Metro doesn't ship for React Native. Aliasing it to the +// userland `buffer` package lets the native bundle resolve. Harmless on web. +const { getDefaultConfig } = require('expo/metro-config'); + +const config = getDefaultConfig(__dirname); + +config.resolver.extraNodeModules = { + ...(config.resolver.extraNodeModules || {}), + buffer: require.resolve('buffer/'), +}; + +module.exports = config; diff --git a/package.json b/package.json index 6a5684f..7669fb6 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "@expo/ui": "~56.0.17", "@huggingface/transformers": "^4.2.0", + "buffer": "^6.0.3", "dexie": "^4.4.3", "expo": "~56.0.11", "expo-constants": "~56.0.18", diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 1e1e2e3..a03fa4c 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -1,3 +1,10 @@ +// Polyfill the Buffer global early — some native deps (whisper.rn -> safe-buffer) +// expect it. Harmless on web, where Buffer simply isn't used. +import { Buffer } from 'buffer'; +if (typeof (globalThis as { Buffer?: unknown }).Buffer === 'undefined') { + (globalThis as { Buffer?: unknown }).Buffer = Buffer; +} + import { DarkTheme, DefaultTheme, Stack, ThemeProvider } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { useColorScheme } from 'react-native';