From 69ca572db01ffbca7bcbe388dc1efe0b5b8ef34e Mon Sep 17 00:00:00 2001 From: Cody Lee Date: Thu, 7 May 2026 16:12:41 -0500 Subject: [PATCH] fix(release): build Linux binaries as static musl to restore glibc 2.35 compatibility Switches Linux release and CI builds from `*-unknown-linux-gnu` to `*-unknown-linux-musl` so the published binaries are fully statically linked and have no glibc dependency. Resolves the GLIBC_2.38/2.39 errors reported on Ubuntu 22.04 (jammy, glibc 2.35). - .goreleaser-linux.yaml: target the musl triples - .github/workflows/release.yml: install musl-tools + the messense aarch64-unknown-linux-musl cross toolchain, set per-target CC and linker env vars, build with `-C target-feature=+crt-static` - .github/workflows/ci.yml: mirror the release setup; build x86_64 and aarch64 musl sequentially and verify each binary with `file` (statically linked) and `ldd` (not a dynamic executable) - docs/ARCHITECTURE.md, docs/TROUBLESHOOTING.md: document the static linking and the glibc upgrade guidance for affected users Co-Authored-By: Claude Opus 4.7 (1M context) --- .../actions/install-musl-toolchain/action.yml | 33 +++++++++++ .github/workflows/ci.yml | 59 ++++++++----------- .github/workflows/release.yml | 13 +--- .goreleaser-linux.yaml | 4 +- docs/ARCHITECTURE.md | 11 ++++ docs/TROUBLESHOOTING.md | 26 ++++++++ 6 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 .github/actions/install-musl-toolchain/action.yml diff --git a/.github/actions/install-musl-toolchain/action.yml b/.github/actions/install-musl-toolchain/action.yml new file mode 100644 index 00000000..08fddcf2 --- /dev/null +++ b/.github/actions/install-musl-toolchain/action.yml @@ -0,0 +1,33 @@ +name: Install musl toolchain +description: Install x86_64 musl-tools (apt) and the cached aarch64 musl cross compiler from cross-tools/musl-cross. + +runs: + using: composite + steps: + - name: Cache aarch64 musl cross compiler + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ~/musl-cross + key: aarch64-unknown-linux-musl-cross-tools-20260430 + - name: Install musl toolchains + shell: bash + run: | + for i in 1 2 3; do + sudo apt-get update && sudo apt-get install -y musl-tools && break + [ "$i" = 3 ] && exit 1 + sleep 5 + done + if [ ! -x "$HOME/musl-cross/aarch64-unknown-linux-musl/bin/aarch64-unknown-linux-musl-gcc" ]; then + mkdir -p "$HOME/musl-cross" + curl -fL "https://github.com/cross-tools/musl-cross/releases/download/20260430/aarch64-unknown-linux-musl.tar.xz" \ + | tar -xJ -C "$HOME/musl-cross" + fi + { + echo "$HOME/musl-cross/aarch64-unknown-linux-musl/bin" + } >> "$GITHUB_PATH" + { + echo "CC_x86_64_unknown_linux_musl=musl-gcc" + echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc" + echo "CC_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-gcc" + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-unknown-linux-musl-gcc" + } >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b201cdb..12b3d7af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,10 +61,14 @@ jobs: name: Cross Compile (Linux) runs-on: ubuntu-latest env: - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc + RUSTFLAGS: "-C target-feature=+crt-static" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Install Rust + run: | + rustup toolchain install stable --profile minimal + rustup default stable + rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: | @@ -72,42 +76,31 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: cargo-ubuntu-cross-linux-${{ hashFiles('**/Cargo.lock') }} + key: cargo-ubuntu-cross-linux-musl-${{ hashFiles('**/Cargo.lock') }} restore-keys: cargo-ubuntu- - - name: Install Rust + - uses: ./.github/actions/install-musl-toolchain + - name: Build x86_64 musl + run: cargo build --release --target x86_64-unknown-linux-musl --features vendored-openssl + - name: Verify x86_64 static linkage run: | - rustup toolchain install stable --profile minimal - rustup default stable - rustup target add aarch64-unknown-linux-gnu - - name: Install aarch64 cross toolchain + file target/x86_64-unknown-linux-musl/release/pup + if readelf -d target/x86_64-unknown-linux-musl/release/pup | grep -q "(NEEDED)"; then + echo "ERROR: binary has dynamic NEEDED entries" + exit 1 + fi + - name: Build aarch64 musl + run: cargo build --release --target aarch64-unknown-linux-musl --features vendored-openssl + - name: Verify aarch64 static linkage run: | - for i in 1 2 3; do - sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu && exit 0 - sleep 5 - done - exit 1 - - name: Build all targets - run: | - PIDS=() - for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do - (set -o pipefail - echo "==> Starting ${target}" - cargo build --release --target "${target}" --features vendored-openssl 2>&1 \ - | tee "/tmp/${target}.log" - ) & - PIDS+=($!) - done - status=0 - for pid in "${PIDS[@]}"; do - wait "$pid" || status=1 - done - exit $status + file target/aarch64-unknown-linux-musl/release/pup + if readelf -d target/aarch64-unknown-linux-musl/release/pup | grep -q "(NEEDED)"; then + echo "ERROR: binary has dynamic NEEDED entries" + exit 1 + fi - name: Report sizes run: | - for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do - echo "${target}:" - ls -lh "target/${target}/release/pup" - done + ls -lh target/x86_64-unknown-linux-musl/release/pup + ls -lh target/aarch64-unknown-linux-musl/release/pup cross-compile-macos: name: Cross Compile (macOS) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cd38fe63..6a9ae6ac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,8 +18,7 @@ jobs: name: Build Linux runs-on: ubuntu-latest env: - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc - CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc + RUSTFLAGS: "-C target-feature=+crt-static" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -30,7 +29,7 @@ jobs: run: | rustup toolchain install stable --profile minimal rustup default stable - rustup target add aarch64-unknown-linux-gnu + rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl - name: Cache Rust dependencies uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: @@ -42,13 +41,7 @@ jobs: key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }} restore-keys: | ${{ runner.os }}-cargo-release- - - name: Install aarch64 cross toolchain - run: | - for i in 1 2 3; do - sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu && exit 0 - sleep 5 - done - exit 1 + - uses: ./.github/actions/install-musl-toolchain - name: Install syft uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 - uses: goreleaser/goreleaser-action@1a80836c5c9d9e5755a25cb59ec6f45a3b5f41a8 # v7.2.1 diff --git a/.goreleaser-linux.yaml b/.goreleaser-linux.yaml index 48b02899..6f408a3f 100644 --- a/.goreleaser-linux.yaml +++ b/.goreleaser-linux.yaml @@ -20,8 +20,8 @@ builds: binary: pup command: build targets: - - x86_64-unknown-linux-gnu - - aarch64-unknown-linux-gnu + - x86_64-unknown-linux-musl + - aarch64-unknown-linux-musl flags: - --release - --features=vendored-openssl diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6e35825c..75ba48ba 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -13,6 +13,17 @@ Technical architecture and design rationale for Pup CLI. 4. **Concurrency** - Async/await with tokio runtime 5. **Strong type system** - Catch errors at compile time with rich enums +### Static Linking for Linux + +Linux release binaries are statically linked using musl libc: +- **No glibc dependency** - Works across all Linux distributions, including older systems like Ubuntu 22.04 (Jammy) with glibc 2.35 +- **Fully self-contained** - No library compatibility issues +- **Portable** - Same binary works on Debian, Ubuntu, Alpine, CentOS, etc. + +Build targets: +- `x86_64-unknown-linux-musl` - Intel/AMD 64-bit processors +- `aarch64-unknown-linux-musl` - ARM 64-bit processors + **Tradeoffs:** - Steeper learning curve than Go - Longer compile times diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index ad2e46c6..b310217d 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -2,6 +2,32 @@ Common issues and solutions for Pup CLI. +## Compatibility Issues + +### GLIBC version error on Linux + +**Symptoms:** +``` +pup: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found (required by pup) +pup: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.39' not found (required by pup) +``` + +**Solution:** +Upgrade to **pup 0.58.4 or later**. Starting with version 0.58.4, Linux binaries are statically linked with musl libc and have **no glibc dependency**. + +```bash +# Update to the latest version +brew upgrade pup + +# Or download the latest release manually +curl -L https://github.com/DataDog/pup/releases/latest/download/pup_Linux_x86_64.tar.gz | tar xz +``` + +**Technical details:** +- Versions before 0.58.4 were dynamically linked and required glibc ≥ 2.38 +- Versions 0.58.4+ are static musl binaries that work on any Linux distribution +- No action required after upgrading — the new binary "just works" + ## Authentication Issues ### OAuth2 Login Fails