Skip to content

fix(EXT-7): bundle musl NIF for Linux Burrito releases and container - #163

Merged
bougyman merged 3 commits into
mainfrom
EXT-7-musl-nif
Aug 20, 2026
Merged

fix(EXT-7): bundle musl NIF for Linux Burrito releases and container#163
bougyman merged 3 commits into
mainfrom
EXT-7-musl-nif

Conversation

@bougyman

Copy link
Copy Markdown
Member

Summary

  • Pre-compile mdex_native with TARGET_ABI=musl before mix release on Linux targets so the musl precompiled NIF is bundled instead of the glibc one detected from Ubuntu runners
  • Same pre-compile step added to the container job (which also builds on Ubuntu but runs on Alpine)
  • Add an Alpine/musl container smoke test (podman run ... lc version) after building the image to verify the musl NIF loads before publishing

Background

rustler_precompiled resolves which precompiled NIF to bundle at compile time from :erlang.system_info(:system_architecture). Ubuntu runners report a glibc triplet, so the glibc mdex_native NIF was being baked into Linux Burrito releases and the container image. The container runs Alpine 3.22 (musl), so the glibc NIF fails to load at runtime.

mdex_native v0.2.7 ships both x86_64-unknown-linux-gnu and x86_64-unknown-linux-musl (and aarch64 equivalents). Setting TARGET_ABI=musl before compiling mdex_native selects the musl variant. mix release then sees mdex_native already compiled in _build/prod/ and skips recompilation, bundling the musl .so.

TARGET_ABI is scoped to the mix deps.compile mdex_native step rather than set globally on mix release because cc_precompiler (used by exqlite) also reads that env var and produces a malformed triplet (--musl) when TARGET_ARCH/TARGET_OS are absent.

CRY-40 (v1.5.1) fixed the cross-OS NIF mismatch (Linux NIF going into macOS/Windows releases). This PR fixes the musl/glibc mismatch within Linux.

Test plan

  • YAML validates syntactically (python3 yaml.safe_load)
  • mix format --check-formatted clean
  • mix credo --strict clean
  • Tests: same 7 pre-existing GitTest failures (require a specific git remote state), 300/307 pass — unchanged from main
  • CI: burrito-build Linux legs pre-compile mdex_native with musl NIF then build
  • CI: container smoke test runs podman run ... lc version on Alpine and exits 0

Closes EXT-7

🤖 Generated with Claude Code

rustler_precompiled resolves the precompiled NIF target at compile time
from the build host's system_architecture. Ubuntu runners report a glibc
triplet, so the glibc mdex_native NIF was being bundled into Linux Burrito
releases and the container image. The container runs Alpine (musl), and
Burrito's own musl runtime is also musl-based, so the glibc NIF would fail
to load at runtime on musl systems.

Fix: pre-compile mdex_native with TARGET_ABI=musl before running mix
release on each Linux target. mix release then sees mdex_native already
compiled in _build/prod and skips it, picking up the musl .so. TARGET_ABI
is scoped to just the mdex_native compile step because cc_precompiler
(used by exqlite) also reads that env var but behaves incorrectly when
only TARGET_ABI is set without TARGET_ARCH and TARGET_OS.

Also add an Alpine/musl container smoke test after building the image to
verify the musl NIF actually loads before publishing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 20, 2026 15:46

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

- Add rustler_precompiled to the pre-compile commands: mix deps.compile
  only compiles the named deps (not their transitive deps), so mdex_native's
  compile-time dependency on RustlerPrecompiled must be listed explicitly.
- Add -e LINEAR_CLI_DAEMON=false to the container smoke test: the image
  bakes in LINEAR_CLI_DAEMON=true, which causes the app to start as a
  daemon (ignoring the version arg and never exiting), timing out the step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bougyman

Copy link
Copy Markdown
Member Author

Rework: fixed two bugs in the original commit

What changed: Two correctness issues identified during self-review and fixed in the follow-up commit (4e9bdfa):

  1. Pre-compile dep order (mix deps.compile mdex_nativemix deps.compile rustler_precompiled mdex_native): mix deps.compile with explicit dep names only compiles the named deps, not their transitive deps. mdex_native/native.ex uses use RustlerPrecompiled at compile time, so rustler_precompiled must be available in the Erlang code path before mdex_native is compiled. On a fresh CI runner (_build/ is empty), the original single-name command would fail with a CompileError: module RustlerPrecompiled is not loaded. Applied to both the burrito-build and container pre-compile steps.

  2. Smoke test daemon mode (added -e LINEAR_CLI_DAEMON=false): The container image bakes in ENV LINEAR_CLI_DAEMON=true (intentional — that's what the daemon CMD needs). Without overriding it, podman run ... lc version starts the app in daemon mode, which ignores the version arg and stays alive indefinitely. The timeout-minutes: 1 guard would then kill it as a failure on every run. LINEAR_CLI_DAEMON=false routes through start_interactive()CLI.main(["version"]) → prints version, exits 0.

Trade-offs: None. Both are straight correctness fixes.

The comment claimed cc_precompiler only activates its TARGET_ABI override
when all three of TARGET_ARCH, TARGET_OS, TARGET_ABI are set. That is wrong:
its current_target_from_env fires when any of the three is set. The step is
actually safe because (a) env: blocks are step-scoped so mix release does not
inherit TARGET_ABI, and (b) mix deps.compile only compiles the named deps so
cc_precompiler/exqlite never run during this step.

Flagged by code review.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bougyman
bougyman merged commit 3172d4e into main Aug 20, 2026
2 checks passed
@bougyman
bougyman deleted the EXT-7-musl-nif branch August 20, 2026 15:56
bougyman pushed a commit that referenced this pull request Aug 20, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.16.0](v1.15.2...v1.16.0)
(2026-08-20)


### Features

* **EXT-5:** show workflow status in compact and full issue listings
([#162](#162))
([acf35e5](acf35e5))


### Bug Fixes

* **EXT-4:** handle GraphQL error and partial-data responses explicitly
([#160](#160))
([31359ef](31359ef))
* **EXT-7:** bundle musl NIF for Linux Burrito releases and container
([#163](#163))
([3172d4e](3172d4e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

2 participants