Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,17 @@ that imports `VCRUNTIME140.dll` fails to load on a machine without the VC++ Redi
a Windows exe imports the runtime again. Don't set a `RUSTFLAGS` env var in those workflows: it
replaces the config rustflags instead of adding to them.

The Linux binaries have a glibc 2.17 floor, the same as the PyPI wheels. A plain `cargo build`
links against the runner's glibc, and v1.7.6 built on a newer `ubuntu-latest` needed GLIBC_2.43,
so it would not start on Ubuntu 24.04 (#272). `release-github`, `release-npm` and `release-nuget`
build their Linux rows with `packaging/linux/build-manylinux.sh`, which runs `cargo build` inside
`quay.io/pypa/manylinux2014_*` (the image release-pypi's maturin already uses), then run
`packaging/linux/check-glibc.sh` to fail the job if the binary needs anything above 2.17. The
runner image no longer matters for Linux. `check-linux-glibc.yml` builds x86_64 the same way on
pull requests that touch `packaging/linux/` and runs the result on `centos:7` and `debian:12`.
Don't move the Linux builds back to a bare `cargo build` or a job-level `container:` (the
actions' Node needs glibc 2.28, which CentOS 7 lacks).

Every release asset now carries a `.sha256` sidecar, not just the macOS Homebrew tarball. Scoop,
winget, and the AUR PKGBUILD each need one, and they consume the raw binaries rather than the
tarball. `release-github` also builds a `.deb` and `.rpm` per Linux target with nfpm
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/check-linux-glibc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI Linux glibc floor

# The release workflows build the Linux binaries with packaging/linux/build-manylinux.sh so they
# run on glibc 2.17 and later. A pull request that touches that script, or this file, builds
# x86_64 the same way, checks the glibc floor, and starts the binary on the oldest and a current
# glibc, so a broken build script shows up before a release does.
on:
pull_request:
paths:
- packaging/linux/**
- .github/workflows/check-linux-glibc.yml

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
TARGET: x86_64-unknown-linux-gnu

jobs:
build:
name: Build and check (x86_64)
runs-on: ubuntu-24.04
timeout-minutes: 60

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
key: manylinux-${{ env.TARGET }}

- name: Build Linux binary (glibc 2.17)
shell: bash
run: ./packaging/linux/build-manylinux.sh "${TARGET}"

- name: Check Linux binary against the glibc 2.17 floor
shell: bash
run: ./packaging/linux/check-glibc.sh "target/${TARGET}/release/sigit" 2.17

# centos:7 is glibc 2.17, the floor itself; debian:12 is a current distro.
- name: Run the binary on old and current glibc
shell: bash
run: |
for image in centos:7 debian:12; do
echo "== ${image}"
docker run --rm -v "${PWD}/target/${TARGET}/release/sigit:/usr/local/bin/sigit:ro" \
"${image}" sh -c 'ldd --version | sed -n 1p && sigit --version'
done
20 changes: 17 additions & 3 deletions .github/workflows/release-github.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ jobs:
matrix:
include:
- name: linux-amd64
# Pinned, not ubuntu-latest: the binary links against the build host's
# glibc, and v1.7.6 built on a newer image needed GLIBC_2.43, which
# Ubuntu 24.04 (2.39, the assurance runner image's base) doesn't have.
# Pinned since v1.7.6, which built on a newer ubuntu-latest and needed
# GLIBC_2.43. The Linux binaries are now built in manylinux2014 (glibc
# 2.17, see packaging/linux/build-manylinux.sh), so the runner's own
# glibc no longer reaches the binary.
runner: ubuntu-24.04
target: x86_64-unknown-linux-gnu
- name: linux-arm64
Expand Down Expand Up @@ -96,9 +97,22 @@ jobs:
key: github-${{ matrix.target }}

- name: Build binary
if: ${{ !contains(matrix.target, 'linux') }}
shell: bash
run: cargo build --locked --release -p sigit --target ${{ matrix.target }}

# Built in manylinux2014 so the binary runs on glibc 2.17 and later, whatever the runner
# has. See packaging/linux/build-manylinux.sh.
- name: Build Linux binary (glibc 2.17)
if: contains(matrix.target, 'linux')
shell: bash
run: ./packaging/linux/build-manylinux.sh ${{ matrix.target }}

- name: Check Linux binary against the glibc 2.17 floor
if: contains(matrix.target, 'linux')
shell: bash
run: ./packaging/linux/check-glibc.sh target/${{ matrix.target }}/release/sigit 2.17

- name: Check Windows binary has no VC++ runtime dependency
if: contains(matrix.target, 'windows')
shell: pwsh
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/release-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,22 @@ jobs:
key: npm-${{ matrix.build.TARGET }}

- name: Build binary
if: ${{ !contains(matrix.build.TARGET, 'linux') }}
shell: bash
run: cargo build --locked --release -p sigit --target ${{ matrix.build.TARGET }}

# Built in manylinux2014 so the binary runs on glibc 2.17 and later, whatever the runner
# has. See packaging/linux/build-manylinux.sh.
- name: Build Linux binary (glibc 2.17)
if: contains(matrix.build.TARGET, 'linux')
shell: bash
run: ./packaging/linux/build-manylinux.sh ${{ matrix.build.TARGET }}

- name: Check Linux binary against the glibc 2.17 floor
if: contains(matrix.build.TARGET, 'linux')
shell: bash
run: ./packaging/linux/check-glibc.sh target/${{ matrix.build.TARGET }}/release/sigit 2.17

- name: Install Node
uses: actions/setup-node@v6
with:
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/release-nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,22 @@ jobs:
key: nuget-${{ matrix.build.TARGET }}

- name: Build binary
if: ${{ !contains(matrix.build.TARGET, 'linux') }}
shell: bash
run: cargo build --locked --release -p sigit --target ${{ matrix.build.TARGET }}

# Built in manylinux2014 so the binary runs on glibc 2.17 and later, whatever the runner
# has. See packaging/linux/build-manylinux.sh.
- name: Build Linux binary (glibc 2.17)
if: contains(matrix.build.TARGET, 'linux')
shell: bash
run: ./packaging/linux/build-manylinux.sh ${{ matrix.build.TARGET }}

- name: Check Linux binary against the glibc 2.17 floor
if: contains(matrix.build.TARGET, 'linux')
shell: bash
run: ./packaging/linux/check-glibc.sh target/${{ matrix.build.TARGET }}/release/sigit 2.17

- name: Prepare artifact
shell: bash
run: |
Expand Down
62 changes: 62 additions & 0 deletions packaging/linux/build-manylinux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Builds the release `sigit` for a Linux target inside the manylinux2014 image, so the binary
# needs nothing newer than glibc 2.17.
#
# packaging/linux/build-manylinux.sh x86_64-unknown-linux-gnu
#
# A plain `cargo build` links against the build host's glibc. v1.7.6 was built on a newer
# ubuntu-latest and needed GLIBC_2.43, so it would not start on Ubuntu 24.04 (2.39), the base of
# the Code Assurance runner image (#272). 2.17 is the same floor as the PyPI wheels, which
# release-pypi already builds in quay.io/pypa/manylinux2014_* through maturin, so this image is
# known to handle aws-lc-sys, onig_sys and esaxx-rs.
#
# This is a script and not a job-level `container:` because actions/checkout and the other
# JavaScript actions run Node inside the job container, and that Node needs glibc 2.28, which
# CentOS 7 does not have. cargo-zigbuild (`--target x86_64-unknown-linux-gnu.2.17`) would avoid
# Docker, but it swaps the C toolchain for every native dependency, while this image is the one
# PyPI has been building with since v1.7.4.
#
# The output lands at target/<target>/release/sigit, the same place `cargo build --target` puts
# it, so the packaging steps after it don't change. The host's ~/.cargo/{registry,git} are
# mounted so Swatinem/rust-cache still restores and saves them. Check the result with
# packaging/linux/check-glibc.sh.
set -euo pipefail

target=${1:?usage: build-manylinux.sh <rust-target>}
arch=$(uname -m)
case "${target}" in
"${arch}"-unknown-linux-gnu) ;;
*) echo "build-manylinux.sh: ${target} does not match this ${arch} host" >&2; exit 1 ;;
esac

repo=$(cd "$(dirname "$0")/../.." && pwd)
cargo_home=${CARGO_HOME:-$HOME/.cargo}
toolchain=$(sed -n 's/^channel = "\(.*\)"/\1/p' "${repo}/rust-toolchain.toml" | head -n 1)
[ -n "${toolchain}" ] || { echo "build-manylinux.sh: no channel in rust-toolchain.toml" >&2; exit 1; }
image=quay.io/pypa/manylinux2014_${arch}

mkdir -p "${cargo_home}/registry" "${cargo_home}/git"

# The container runs as root. Files it creates under target/ and the cargo cache are handed
# back to the runner user, which the later steps and rust-cache's post step need to write to.
docker run --rm \
-v "${repo}:/io" \
-v "${cargo_home}/registry:/cargo/registry" \
-v "${cargo_home}/git:/cargo/git" \
-w /io \
-e CARGO_HOME=/cargo \
-e RUSTUP_HOME=/rustup \
-e CARGO_TERM_COLOR="${CARGO_TERM_COLOR:-auto}" \
-e TOOLCHAIN="${toolchain}" \
-e TARGET="${target}" \
-e OWNER="$(id -u):$(id -g)" \
"${image}" \
bash -euo pipefail -c '
trap "chown -R ${OWNER} /io/target /cargo/registry /cargo/git 2>/dev/null || true" EXIT
# Same before-script-linux as release-pypi: aws-lc-sys needs a full Perl.
yum install -y -q perl-core
curl -sSf https://sh.rustup.rs | sh -s -- -y -q --profile minimal --default-toolchain "${TOOLCHAIN}"
export PATH=/cargo/bin:$PATH
ldd --version | sed -n 1p
cargo build --locked --release -p sigit --target "${TARGET}"
'
24 changes: 24 additions & 0 deletions packaging/linux/check-glibc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Fails if a Linux binary needs a newer glibc than the release floor.
#
# packaging/linux/check-glibc.sh target/x86_64-unknown-linux-gnu/release/sigit [2.17]
#
# The Linux releases are built in manylinux2014 (packaging/linux/build-manylinux.sh) so they run
# on glibc 2.17 and later. If a build slips back onto the runner's own toolchain, the binary still
# builds and passes tests there, then fails to start on an older distro with "GLIBC_2.xx not
# found". This check catches it before release, the way check-static-crt.ps1 does for Windows.
set -euo pipefail

binary=${1:?usage: check-glibc.sh <binary> [max-glibc]}
max=${2:-2.17}

top=$(objdump -T "${binary}" | grep -o 'GLIBC_[0-9.]*' | sed 's/GLIBC_//' | sort -uV | tail -n 1)
if [ -z "${top}" ]; then
echo "::error::objdump found no GLIBC_ symbols in ${binary}"
exit 1
fi
if [ "$(printf '%s\n%s\n' "${top}" "${max}" | sort -V | tail -n 1)" != "${max}" ]; then
echo "::error::${binary} needs GLIBC_${top}, above the ${max} floor"
exit 1
fi
echo "${binary} needs at most GLIBC_${top} (floor ${max})."