fix(android): working signed release APK
CI / test (push) Successful in 22s
CI / deploy-web (push) Successful in 34s
CI / build-apk (push) Failing after 3h1m42s

- 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 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 23:59:08 +02:00
parent 74a436b5ee
commit 7a1e6b06c1
5 changed files with 31 additions and 0 deletions
+5
View File
@@ -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=="],
+4
View File
@@ -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 \
+14
View File
@@ -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;
+1
View File
@@ -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",
+7
View File
@@ -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';