Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-plants-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@use-voltra/android-client': patch
---

Respect Android ABI selections and avoid bundling duplicate Hermes libraries.
89 changes: 89 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,92 @@ jobs:

- name: Check Kotlin formatting
run: pnpm run format:kotlin:check

android-tarball-build:
name: '[Android] Tarball + all-ABI native build'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 11.5.0
run_install: false

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '24.14.0'
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

# build/** must exist so the packed tarball is complete.
- name: Build monorepo
run: pnpm run build:all

# Packaging integrity: pack android-client and install the *tarball* into
# the example app instead of the workspace symlink. `pnpm pack` rewrites
# the `workspace:` deps to real version ranges that match the sibling
# packages, so pnpm links those from the workspace while android-client
# itself is consumed exactly as it would ship to npm. A file missing from
# the tarball (the v2.1.0 CMakeLists.txt incident) then breaks prebuild or
# gradle configure below.
- name: Pack android-client tarball
working-directory: packages/android-client
run: |
mkdir -p "$RUNNER_TEMP/tarballs"
pnpm pack --pack-destination "$RUNNER_TEMP/tarballs"
TARBALL=$(ls "$RUNNER_TEMP/tarballs"/*.tgz | head -n1)
echo "ANDROID_CLIENT_TARBALL=$TARBALL" >> "$GITHUB_ENV"

# `pnpm pack` rewrites the tarball's `workspace:^` deps to real semver
# ranges, which pnpm would otherwise resolve from the npm registry —
# tripping minimumReleaseAge for freshly published versions and testing
# published siblings instead of this branch. Override every @use-voltra
# sibling back to the workspace so only android-client itself is consumed
# from the tarball (it must NOT be overridden, or the tarball install —
# the whole point of this job — would be replaced by a workspace link).
- name: Resolve sibling packages from the workspace
run: |
{
echo ""
echo "overrides:"
for f in packages/*/package.json; do
name=$(node -p "require('./$f').name")
if [ "$name" != "@use-voltra/android-client" ]; then
echo " \"$name\": workspace:*"
fi
done
} >> pnpm-workspace.yaml

- name: Install android-client from tarball into example app
run: pnpm --filter voltra-example add "file:$ANDROID_CLIENT_TARBALL"

- name: Generate Android native project
working-directory: example
run: CI=1 pnpm exec expo prebuild --platform android

# All-ABI native build (issue #209): a targeted external-native-build task
# keeps CI fast while compiling libvoltra_js_renderer.so and linking
# ReactAndroid::jsi / hermes-engine::hermesvm / fbjni::fbjni for every ABI,
# so a per-ABI (e.g. armeabi-v7a) link regression fails CI.
- name: Build native library for all ABIs
working-directory: example/android
run: >-
./gradlew :use-voltra_android-client:externalNativeBuildDebug
-PreactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
12 changes: 12 additions & 0 deletions packages/android-client/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ def safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

// Mirror the app's ABI selection (React Native template convention) so the
// standalone Hermes runtime is only built for the architectures the app ships.
def reactNativeArchitectures() {
def value = rootProject.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}

// Read version from package.json for BuildConfig
def packageJsonFile = new File(project.projectDir.parentFile, "package.json")
def packageJson = new groovy.json.JsonSlurper().parseText(packageJsonFile.text)
Expand All @@ -48,6 +55,10 @@ android {
arguments "-DANDROID_STL=c++_shared"
}
}

ndk {
abiFilters(*reactNativeArchitectures())
}
}

externalNativeBuild {
Expand All @@ -71,6 +82,7 @@ android {
"**/libfbjni.so",
"**/libjsi.so",
"**/libhermes.so",
"**/libhermesvm.so",
]
}

Expand Down
Loading