fix(release): make the x86_64 musl binary actually static
Routing the x86_64-unknown-linux-musl link through Ubuntu's musl-gcc wrapper overrode Rust's crt-static default and produced a dynamic binary with an interpreter of /lib/ld-musl-x86_64.so.1. That path exists on approximately no machine outside Alpine, so the shipped binary died at exec time with "required file not found" everywhere — the opposite of what a musl build is for. aarch64-unknown-linux-musl never used musl-gcc and was static all along; only x86_64 was broken. Drop musl-tools and the linker override and let Rust link with its own bundled musl libc.a, which yields a static-pie executable. Add a post-build check that fails the release if a musl binary has an INTERP segment, so this can't silently ship again. Also correct the asset-URL comment: Gitea serves /releases/download/latest/<name>, not GitHub's /releases/latest/download/<name>. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011J8C6eRmWEZsh28vkEMD9H
This commit is contained in:
@@ -133,8 +133,15 @@ jobs:
|
||||
# crti.o, and friends needed at link time.
|
||||
apt: "gcc-aarch64-linux-gnu libc6-dev-arm64-cross"
|
||||
rustflags: "-D warnings"
|
||||
# No musl-tools / musl-gcc here on purpose. Rust ships a
|
||||
# self-contained musl libc.a plus the crt objects for this
|
||||
# target, and the default link mode is static-pie. Routing the
|
||||
# link through Ubuntu's musl-gcc wrapper instead produces a
|
||||
# *dynamic* binary needing /lib/ld-musl-x86_64.so.1, which
|
||||
# exists on approximately no machine outside Alpine — the
|
||||
# opposite of what a musl build is for.
|
||||
- target: x86_64-unknown-linux-musl
|
||||
apt: "musl-tools"
|
||||
apt: ""
|
||||
rustflags: "-D warnings"
|
||||
- target: aarch64-unknown-linux-musl
|
||||
apt: ""
|
||||
@@ -168,13 +175,29 @@ jobs:
|
||||
- name: Build nuwiki-ls
|
||||
env:
|
||||
# Per-target linker overrides. Cargo ignores the entries that
|
||||
# don't match the current target, so setting all of them here
|
||||
# keeps the matrix declarative.
|
||||
# don't match the current target, so setting them here keeps
|
||||
# the matrix declarative. Only add an override for a target
|
||||
# whose default linker genuinely can't do the job.
|
||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
|
||||
RUSTFLAGS: ${{ matrix.rustflags }}
|
||||
run: cargo build --release --target ${{ matrix.target }} -p nuwiki-ls
|
||||
|
||||
# A musl build only earns its keep if it is actually static. A
|
||||
# dynamic one silently fails at exec time with "no such file or
|
||||
# directory" on every non-Alpine host, so fail the release here
|
||||
# rather than shipping it.
|
||||
- name: Verify musl binary is statically linked
|
||||
if: contains(matrix.target, 'musl')
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bin="target/${{ matrix.target }}/release/nuwiki-ls"
|
||||
if readelf -l "$bin" | grep -q INTERP; then
|
||||
echo "::error::$bin is dynamically linked but should be static:"
|
||||
readelf -l "$bin" | grep -A2 INTERP
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: $bin has no INTERP segment (statically linked)."
|
||||
|
||||
- name: Package archive
|
||||
id: package
|
||||
env:
|
||||
@@ -183,7 +206,9 @@ jobs:
|
||||
set -euo pipefail
|
||||
archive="nuwiki-ls-${VERSION}-${{ matrix.target }}.tar.gz"
|
||||
tar -czf "$archive" -C "target/${{ matrix.target }}/release" nuwiki-ls
|
||||
# Use a stable name without version so /releases/latest/download/nuwiki-ls-{target}.tar.gz always resolves.
|
||||
# Use a stable name without version so the download URL always
|
||||
# resolves. Note the Gitea shape is /releases/download/latest/<name>,
|
||||
# not GitHub's /releases/latest/download/<name> — the latter 404s.
|
||||
stable="nuwiki-ls-${{ matrix.target }}.tar.gz"
|
||||
mv "$archive" "$stable"
|
||||
echo "archive=$stable" >> "$GITHUB_OUTPUT"
|
||||
|
||||
Reference in New Issue
Block a user