Skip to content

fix(ci): build Linux natives against a pinned glibc floor - #24

Merged
soloturn merged 5 commits into
MovingBlocks:masterfrom
SiliconSaga:fix/pin-linux-glibc-floor
Aug 12, 2026
Merged

fix(ci): build Linux natives against a pinned glibc floor#24
soloturn merged 5 commits into
MovingBlocks:masterfrom
SiliconSaga:fix/pin-linux-glibc-floor

Conversation

@agent-refr

@agent-refr agent-refr commented Aug 11, 2026

Copy link
Copy Markdown

AI-assisted change proposal. Filed by agent driven by @Cervator via GDD.

Summary

  • Builds the Linux natives inside ubuntu:22.04 instead of directly on the runner, so their glibc floor is a pinned decision rather than a side effect of whichever runner image GitHub currently offers.
  • Adds a step that reads the symbol versions back off the built .so and fails if any exceeds the declared floor — the regression this fixes was only discovered downstream, in someone else's CI.
  • Three smaller fixes that containerising surfaced: container jobs default to sh rather than bash; ${{ github.workspace }} resolves to the host path inside a container while $GITHUB_WORKSPACE resolves per environment; SWIG cache keys need a version component because the cached binary is linked against the environment it was built in.

Why

CMakeLists.txt already links libgcc and libstdc++ statically, so glibc is the only remaining dynamic dependency of the published .so. That makes the build host's glibc the minimum supported Linux for every consumer — and Terasology copies these natives straight into its player distribution (facades/PC/build.gradle.kts).

#23 moved the Linux matrix from ubuntu-20.04 to ubuntu-24.04 to replace runner images GitHub had retired. That was necessary, but it raised the floor from glibc 2.31 to 2.39 as a side effect, and nothing in the build declares or checks a floor. The result cannot load on anything older:

UnsatisfiedLinkError: .../libbullet-linux-amd64.so:
  /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.38' not found

That is MovingBlocks/Terasology#5359, where every engine test fails on Debian-based build agents once the static initialiser dies. It would equally affect any player below Ubuntu 24.04.

A container rather than an older runner label, for two reasons: there is no ubuntu-20.04-arm or equivalent, so the linux_aarch64 target added in #23 cannot reach a low floor via runs-on at all; and pinning the image is what stops the next runner retirement from moving the floor again silently.

ubuntu:22.04 specifically, rather than something older, because LLVM_MINGW_VERSION is pinned to an ubuntu-22.04 build of llvm-mingw which runs in the same job. Going lower would mean splitting the Windows cross-compile targets into a separate job.

Note the image's glibc and the resulting floor are different numbers. The image supplies 2.35, but the linker only ends up referencing symbols up to 2.34 (the release that folded libpthread and libdl into libc), and 2.34 is what the check asserts — so the natives load on Debian 12, Ubuntu 22.04, and RHEL 9. It does drop Ubuntu 20.04 and Debian 11 relative to 1.0.4 — both past end of standard support — so if you would rather keep those, say so and I will split the job and drop the image to ubuntu:20.04.

Test plan

  • Full workflow green on a fork, all four platforms, on both the original and the post-review revision: https://github.com/SiliconSaga/JNBullet/actions/runs/31535955686
  • The check reports the actual floor rather than assuming it — objdump -T on the built natives yields a highest requirement of 2.34 on both amd64 and aarch64, exactly the declared floor.
  • Verified the check fails closed: it compares against the highest observed symbol version and treats "no versions found" as unverified rather than passing. With GLIBC_FLOOR now at the measured value there is deliberately zero headroom, so any increase fails the job.
  • Needs a merge to master to republish 1.0.5-SNAPSHOT; the publish job is gated on github.ref, so a green PR build alone does not refresh the artifact.
  • After that, re-run build: bump gestalt to 8.0.2-SNAPSHOT, JNBullet to 1.0.5-SNAPSHOT; add Windows/ARM64 LWJGL natives Terasology#5359 — no version change needed there, since 1.0.5-SNAPSHOT is a changing module and the agents start cold.

Related

Cervator and others added 4 commits August 11, 2026 11:21
CMakeLists.txt already links libgcc and libstdc++ statically, so glibc is the only dynamic dependency left in the published `.so` — which makes the build host's glibc the minimum supported Linux for every consumer, and Terasology ships these natives to players.

MovingBlocks#23 moved the Linux matrix from `ubuntu-20.04` to `ubuntu-24.04` to replace retired runner images, raising that floor from glibc 2.31 to 2.39 as a side effect. The result fails to load on anything older: `libm.so.6: version 'GLIBC_2.38' not found`, seen on Terasology's Debian-based build agents (MovingBlocks/Terasology#5359) and on any player distro below Ubuntu 24.04.

Building inside `ubuntu:22.04` decouples the floor from whichever runner image GitHub currently offers, so the next retirement can't move it silently. There is no `ubuntu-20.04-arm` runner, so a container is also the only way to give the new `linux_aarch64` target the same floor as amd64.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Container jobs default to `sh` rather than `bash`, since GitHub cannot assume bash exists in an arbitrary image. The OS-dispatch steps use `==`, which dash rejects with `[: Linux: unexpected operator` — and the `else` branch then exits 1 claiming the OS is unsupported, which reads as a platform problem rather than a shell one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`${{ github.workspace }}` resolves to the host path even in a container job, so the PATH entry pointed at `/home/runner/work/...` while the container only has `/__w/...` — `swig: command not found` despite a reported cache hit. `actions/cache` maps the two itself, which is why its `path:` input works and this step did not.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…bc floor

The container pins the floor; nothing yet proves the output honours it. This reads the symbol versions back off the built `.so` and fails the job if any exceeds the declared floor, so the next base-image change surfaces here rather than as an `UnsatisfiedLinkError` on a user's machine — which is how MovingBlocks#23 was found.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Build & Release
    • Standardized Linux builds and publishing on Ubuntu 22.04.
    • Enforced compatibility with systems using glibc 2.34 or newer?
    • Improved SWIG cache versioning and dependency setup for more reliable builds.
    • Added validation to prevent publishing Linux binaries requiring glibc symbols newer than 2.34.

Walkthrough

The GitHub Actions workflow now runs Linux jobs in Ubuntu 22.04 containers, versions the SWIG cache, uses container workspace paths, and rejects Linux native artifacts that require glibc newer than 2.34.

Changes

Linux CI portability

Layer / File(s) Summary
Containerized Linux jobs
.github/workflows/allInOne.yml
The workflow sets Bash as the default shell, documents the glibc floor, runs Linux SWIG, native build, and publish jobs in Ubuntu 22.04 containers, provisions container dependencies, and versions the SWIG cache.
glibc compatibility validation
.github/workflows/allInOne.yml
The native Linux build extracts required GLIBC symbol versions, rejects missing or unverifiable symbol data, and fails when the highest requirement exceeds 2.34.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: soloturn

Poem

A rabbit checks the build trail,
Bash and SWIG caches stay bright.
Ubuntu holds the tools in place,
glibc guards the version limit.
Hop, build, publish—clean and right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main CI change: building Linux natives against a pinned glibc compatibility floor.
Description check ✅ Passed The description accurately explains the containerized Linux builds, glibc validation, cache changes, and related testing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/allInOne.yml:
- Around line 48-50: Update the SWIG cache comment near the ubuntu-24.04 matrix
entry to state that the cached binary runs in the Linux build and publish
containers, removing the outdated reference to a plain runner.
- Around line 12-14: Update the support comment near GLIBC_FLOOR so it matches
the enforced glibc requirement: remove RHEL 9 from the supported-distribution
list, or lower GLIBC_FLOOR to 2.34 if RHEL 9 must remain supported.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 725f8220-1fde-47e7-a9ba-c47aef31c026

📥 Commits

Reviewing files that changed from the base of the PR and between 1ab4805 and 3a0a26a.

📒 Files selected for processing (1)
  • .github/workflows/allInOne.yml

Comment thread .github/workflows/allInOne.yml Outdated
Comment thread .github/workflows/allInOne.yml Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Pins the Linux native build environment by moving Linux builds into an ubuntu:22.04 container, so the produced .so files have an intentional, stable glibc compatibility floor instead of inheriting whatever glibc version the GitHub runner image happens to ship.

Changes:

  • Run the swig, build, and publish jobs in an ubuntu:22.04 container for Linux targets, with a bootstrapping step to install missing base tooling.
  • Version SWIG cache keys to avoid restoring a SWIG binary built against a different libc/PCRE environment.
  • Add a post-build check that inspects required GLIBC_* symbol versions and fails if they exceed a declared floor.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/allInOne.yml Outdated
Comment thread .github/workflows/allInOne.yml Outdated
Review round 1, CodeRabbit and Copilot both landing on the same conflation: the header comment used the container's glibc (2.35) as the compatibility claim, then listed RHEL 9 as covered — and RHEL 9 ships 2.34, so the claim contradicted itself.

Copilot had the sharper framing: the consumer floor is the highest `GLIBC_*` symbol the linker references, which can be lower than the image's glibc, and here is 2.34. So `GLIBC_FLOOR` drops to that measured value. This makes the check strictly stronger — at 2.35 a change that started referencing 2.35 symbols would have passed while silently dropping RHEL 9.

Also per Copilot: the check ran under `-e -o pipefail`, so a `grep` with no matches killed the step with no diagnostic. It now distinguishes "no natives", "objdump failed", and "no versions found" — the last being unverified rather than satisfied, which is the one that could have masked a regression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@soloturn
soloturn merged commit 972db6e into MovingBlocks:master Aug 12, 2026
1 check passed
@Cervator
Cervator deleted the fix/pin-linux-glibc-floor branch August 12, 2026 15:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants