From c44205826f7a869159d40e09d3f31a29a3114146 Mon Sep 17 00:00:00 2001 From: Adam Poulemanos Date: Sun, 26 Jul 2026 07:30:59 -0400 Subject: [PATCH] ci(release): measure static linkage on dry runs instead of inferring it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The musl targets exist so submod ships a fully static Linux binary, but a dry run built the archive and discarded it, so that property was only ever argued from curl-sys/openssl-sys build-script logic — never observed. Adds two workflow_dispatch-only steps to the build job: one that extracts the archive and asserts musl binaries are static (accepting both "statically linked" and the "static-pie linked" form Rust actually produces for musl, and failing loudly on anything dynamic), and one that keeps the archive as an artifact for 7 days so it can be inspected by hand. Both are gated on workflow_dispatch, so a v* tag release is unchanged. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XocU6NjgZricrRWMLyLXDn --- .github/workflows/release.yml | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f844f11..41a5843 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -91,6 +91,46 @@ jobs: features: git2/vendored-libgit2,git2/vendored-openssl dry-run: ${{ github.event_name == 'workflow_dispatch' }} + # A dry run builds the archive and then throws it away, which left the whole + # point of the musl targets — that the binary is actually static — argued + # from build-script logic rather than measured. These two steps only run on + # workflow_dispatch, so a real tag release is unaffected. + - name: Verify Linux binary linkage + if: github.event_name == 'workflow_dispatch' && contains(matrix.target, 'linux') + env: + TARGET: ${{ matrix.target }} + run: | + set -euo pipefail + mkdir -p /tmp/linkcheck + tar xzf "submod-${TARGET}.tar.gz" -C /tmp/linkcheck + desc=$(file /tmp/linkcheck/submod) + echo "$desc" + case "$TARGET" in + *musl*) + # Rust links musl targets as static-pie by default, which `file` + # reports as "static-pie linked" rather than "statically linked". + # Accept either; reject anything dynamically linked. + case "$desc" in + *"statically linked"*|*"static-pie linked"*) + echo "OK: ${TARGET} is statically linked" ;; + *) + echo "::error::${TARGET} is not statically linked: ${desc}" + exit 1 ;; + esac + ;; + *) + echo "note: ${TARGET} is a glibc target; dynamic linking is expected" ;; + esac + + - name: Upload dry-run archive for inspection + if: github.event_name == 'workflow_dispatch' + uses: actions/upload-artifact@v4 + with: + name: dry-run-${{ matrix.target }} + path: submod-${{ matrix.target }}.* + retention-days: 7 + if-no-files-found: error + publish: name: Publish to crates.io needs: [build]