Skip to content

ci: fail when an ABI split ships fewer native libraries than its siblings - #7013

Merged
jamesarich merged 1 commit into
fix/maplibre-missing-native-guardfrom
ci/native-abi-parity
Sep 2, 2026
Merged

ci: fail when an ABI split ships fewer native libraries than its siblings#7013
jamesarich merged 1 commit into
fix/maplibre-missing-native-guardfrom
ci/native-abi-parity

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Why

A dependency published for only some of our ABIs builds and installs cleanly on the rest, then dies with UnsatisfiedLinkError the first time it is touched — and nothing at build time says a word. That is #7001: maplibre-native-ffi 0.202608.3 ships arm64-v8a and x86_64 only, so the F-Droid armeabi-v7a split had no map engine and opening the map took the app down. The two shipped v2.8.2-closed.1 splits were 32.5 MB and 18.2 MB and CI was green.

Stacked on #7005, which makes the crash a message. This makes the gap visible, and makes closing it verified.

Relates to #7001.

🧹 Chores

  • scripts/lib/abi-parity.sh — the classifier. The reference set is the union of native libs across a flavor's split APKs; every split must carry all of it unless a known-gap line says otherwise.
  • scripts/verify-abi-parity.sh — runs in android-check right after the debug assemble, where every PR already builds the splits. Fails on a missing lib with the three ways out (fix the dependency, drop the ABI, record a known gap with its reason).
  • scripts/verify-abi-parity-selftest.sh — fixture self-test in lint-check, mirroring verify-rb-selftest.sh. The real check only ever sees what the current dependencies happen to ship, so both failure modes would otherwise go unexercised until they bit.

The part that matters for review: known gaps are checked both ways

The two lines recorded now are the MapLibre gap on fdroid/armeabi-v7a. An entry must be absent from its split — and the check fails once the library turns up, so an entry cannot outlive its reason.

That closes the hole in the obvious alternative. Renovate tracks the 8 org.maplibre.compose artifacts in the catalog and will bump maplibre-compose when a release with the 32-bit build lands (upstream merged it 2026-08-24: maplibre-native-ffi #658, #659, #660; no release yet). Without this, that bump lands green and nothing marks #7001 closed. With it, the bump PR goes red with "the gap has closed, delete those lines", and deleting them is the proof the v7a APK really has an engine.

Why not the snapshot pin (#7003)

Closed in favour of this. Verified against it directly:

  • The rolling 0.1.0-SNAPSHOT was re-published overnight — yesterday's bytes were 20260825.201433-1, today's are 20260902.064417-1. Same version string, different engine.
  • Pinning a timestamped snapshot instead does not work either: the timestamps are per artifact (-android is 064417-1, -runtime-opengl-android is 064601-1), and Central's snapshot retention is 90 days, so a merged pin would 404 for any rebuild of that tag after late November.
  • rb-check would not have caught any of that: it assembles the APK once and never compares two builds.

Testing Performed

Run against three real APK sets, not just fixtures:

input result
shipped v2.8.2-closed.1 fdroid splits + google splits ✅ google clean; ✅ fdroid "splits match apart from 2 recorded known gap(s)"
the #7003 snapshot build, where v7a has the engine ❌ "known-gap entries whose library is now present" — the self-healing signal, exit 1
shipped splits with the two lines deleted ❌ "native libraries missing from a split: armeabi-v7a/libjniMaplibreNativeC.so armeabi-v7a/libmaplibre-native-c.so", exit 1
  • Self-test: 15 checks green, including the wrong-ABI and wrong-flavor entries that must not excuse a gap.
  • shellcheck -x over scripts/ (the lint-check invocation) clean; actionlint on both workflows clean.

🤖 Generated with Claude Code

…ings

A dependency published for only some of our ABIs builds and installs cleanly
on the rest and dies with UnsatisfiedLinkError the first time it is touched.
Nothing at build time says a word. That is #7001: maplibre-native-ffi
0.202608.3 ships arm64-v8a and x86_64 only, so the F-Droid armeabi-v7a split
had no map engine and opening the map took the app down.

Compare the split APKs in android-check, where every PR already builds them.
The reference set is the union across splits — a library any ABI ships is
one the app expects to load — and each split must carry all of it unless a
known-gap line in scripts/lib/abi-parity.sh says otherwise.

Known gaps are checked both ways. The library must be absent, and the check
fails once it turns up, so an entry cannot outlive its reason: the
maplibre-compose bump that closes this one goes red until its two lines are
deleted with it. Without that, Renovate would land the bump and nothing
would mark #7001 closed.

The two lines recorded now are the MapLibre gap on fdroid/armeabi-v7a. The
32-bit build upstream merged on 2026-08-24 (maplibre-native-ffi #658, #659,
#660) has no release yet; on those devices the app shows a message instead
of a map meanwhile.

A fixture self-test runs in lint-check, mirroring verify-rb-selftest.sh: the
real check only ever sees what the current dependencies happen to ship, so
both failure modes would otherwise go unexercised until they bit.
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@jamesarich
jamesarich merged commit 86ca854 into fix/maplibre-missing-native-guard Sep 2, 2026
3 checks passed
@jamesarich
jamesarich deleted the ci/native-abi-parity branch September 2, 2026 20:57
@github-actions github-actions Bot added ci Pertains to ci/cd actions or workflows repo Repository maintenance labels Sep 2, 2026
@jamesarich

Copy link
Copy Markdown
Collaborator Author

Rolled into #7005 rather than stacked: pull-request.yml only triggers for PRs targeting main or release/**, so a PR based on a feature branch gets no CI here, and the parity check has to run in android-check to prove itself. GitHub marked this merged when the fast-forward put its commit into the base branch.

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

Labels

ci Pertains to ci/cd actions or workflows repo Repository maintenance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant