OverviewFilesCommitsIssuesDocsReleasesPeers

favela-android commits / 6f02805

0.0.8: verify the ABIs we ship, not whatever directories are lying around
The bare-dns bump broke the build, and the build was right to fail loudly and wrong about what it failed on: DEMANDED BUT NOT STAGED — these would fail to dlopen on device: armeabi-v7a/libbare-dns.2.2.0.so x86/libbare-dns.2.2.0.so x86_64/libbare-dns.2.2.0.so This APK is arm64-v8a ONLY (abiFilters, app/build.gradle:116 — the other three were 351 MB of a 527 MB APK). arm64 staged 2.2.0 correctly and passed. The staging dir lives in node_modules, which CI copies wholesale from the live checkout, and it still holds armeabi-v7a/x86/x86_64 from before the arm64-only decision — 62 .so each. bare-link only refreshes the hosts it is given (arm64), so those three kept whatever they had. Dormant, until a demanded addon's VERSION changed: prebuilds are named lib<name>.<version>.so, so 2.1.4 → 2.2.0 renamed the file and three stale directories suddenly failed a check about a build they are not part of. The loop iterated `fs.readdirSync(out)` — every directory that happens to exist — while staging is scoped to `hosts`. Now the verification is scoped the same way: non-shipped ABIs are still REPORTED (marked "NOT SHIPPED (abiFilters), not verified") and no longer fail the build. abiFilters keeps them out of the APK, so a missing .so there cannot dlopen-fail on any device we ship to, which is the only thing this assertion protects. Also closes a hole the old loop had: a shipped ABI with NO directory passed silently, because the loop iterated over what existed rather than over what is required. An empty staging dir is now a failure. The emulator path documented at build.gradle:115 keeps working — `--hosts android-x64` verifies x86_64 instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6f02805e2afc8d50cbf02c4e7ebb327b5d9dec67
by plankatron <fleeky@gmail.com> · 2026-08-25T14:51:29.000Z

Changed files (2) +32 -3

package.json +1 -1
……@@ -1,6 +1,6 @@
11{
22 "name": "favela-android",
3 "version": "0.0.7",
3 "version": "0.0.8",
44 "description": "favela-android — a React Native client over a bare-kit worklet running a FULL favela node (hyper-proto-peer + appview + hyperswarm) on the phone. favela-web is reference for the logic, NOT the rendering: this is a different rendering surface. See spec/architecture.md.",
55 "private": true,
66 "type": "module",
scripts/stage-addons.mjs +31 -2
……@@ -78,10 +78,37 @@ console.log(`bundle demands ${want.size} addon(s)`)
7878
7979for await (const _ of link(root, { hosts, out })) { /* bare-link stages the superset */ }
8080
81// ⛔ VERIFY THE ABIs WE SHIP, NOT WHATEVER DIRECTORIES EXIST. This loop used to check every directory
82// under `out`, and `out` lives in node_modules, which the CI copies wholesale from the live checkout —
83// so it still holds armeabi-v7a/x86/x86_64 from before the arm64-only decision (62 .so each). Staging
84// only refreshes arm64, so the moment a demanded addon's version CHANGED, the stale dirs no longer had
85// the file under its new name and the build failed on three ABIs this APK does not contain:
86//
87// DEMANDED BUT NOT STAGED: armeabi-v7a/libbare-dns.2.2.0.so, x86/…, x86_64/…
88//
89// (bare-link names prebuilds lib<name>.<version>.so, so a dependency bump renames the file — which is
90// what turned dormant leftovers into a build failure on the bare-dns 2.1.4 → 2.2.0 upgrade.)
91//
92// abiFilters keeps those ABIs out of the APK regardless, so a missing .so there cannot dlopen-fail on
93// any device we ship to. Checking them was asserting on leftovers, not on the build.
94const ABI_FOR_HOST = {
95 'android-arm64': 'arm64-v8a',
96 'android-arm': 'armeabi-v7a',
97 'android-ia32': 'x86',
98 'android-x64': 'x86_64'
99}
100const shipped = new Set(hosts.map((h) => ABI_FOR_HOST[h] || h))
101
81102// Report the split per ABI and verify the contract. Only .so files are considered — bare-kit also
82103// consumes *.classes.jar from this dir.
83104const missing = []
84105
106// A shipped ABI with no directory at all is the failure this check exists for, and an empty `out`
107// would otherwise pass silently — the loop below would simply iterate over nothing.
108for (const abi of shipped) {
109 if (!fs.existsSync(path.join(out, abi))) missing.push(`${abi}/ (nothing staged for a shipped ABI)`)
110}
111
85112for (const abi of fs.readdirSync(out)) {
86113 const abiDir = path.join(out, abi)
87114 if (!fs.statSync(abiDir).isDirectory()) continue
……@@ -102,11 +129,13 @@ for (const abi of fs.readdirSync(out)) {
102129 // arm64 APK entries. Quote the APK delta, not this, when reporting size wins.
103130 console.log(
104131 ` ${abi}: ${present.size}/${want.size} demanded staged, ` +
105 `${excess} excess excluded at packaging (${(excessBytes / 1048576).toFixed(1)} MB on disk, pre-strip)`
132 `${excess} excess excluded at packaging (${(excessBytes / 1048576).toFixed(1)} MB on disk, pre-strip)` +
133 (shipped.has(abi) ? '' : ' — NOT SHIPPED (abiFilters), not verified')
106134 )
107135
108136 // A demanded addon with no staged .so is a dlopen failure at runtime, on a device, with no
109 // build-time signal. Fail here instead.
137 // build-time signal. Fail here instead — but only for an ABI that reaches a device.
138 if (!shipped.has(abi)) continue
110139 for (const w of want) if (!present.has(w)) missing.push(`${abi}/${w}`)
111140}
112141