From e073533c62ded581ff37dff6bfcb98608c4c23f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Fri, 7 Aug 2026 15:20:44 -0300 Subject: [PATCH] fix(release): make the x86_64 musl binary actually static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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/, not GitHub's /releases/latest/download/. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011J8C6eRmWEZsh28vkEMD9H --- .gitea/workflows/release.yaml | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/release.yaml b/.gitea/workflows/release.yaml index 4f8c58e..7df7bd7 100644 --- a/.gitea/workflows/release.yaml +++ b/.gitea/workflows/release.yaml @@ -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/, + # not GitHub's /releases/latest/download/ — the latter 404s. stable="nuwiki-ls-${{ matrix.target }}.tar.gz" mv "$archive" "$stable" echo "archive=$stable" >> "$GITHUB_OUTPUT"