name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write id-token: write
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
test:
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
os: ubuntu-latest
- arch: aarch64
os: ubuntu-24.04-arm
runs-on: ${{ matrix.os }}
timeout-minutes: 360
steps:
- uses: actions/checkout@v7
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- name: Install musl-tools
run: sudo apt-get update && sudo apt-get install -y musl-tools
- name: Build all artifacts
run: cargo build --workspace
- name: Run integration tests
run: |
set -o pipefail
cargo test --workspace --no-fail-fast 2>&1 | tee /tmp/integration-tests.log
- name: Report failed tests
if: failure()
run: |
if [ -f /tmp/integration-tests.log ]; then
grep -E '^test .* FAILED|FAILED|panicked at|error: test failed' /tmp/integration-tests.log | tail -80 | while IFS= read -r line; do
echo "::error::$line"
done
fi
macro-parity:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- name: Install cross compilers
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu g++-aarch64-linux-gnu g++-riscv64-linux-gnu make curl
- name: Cache musl 1.2.6 headers
uses: actions/cache@v6
with:
path: ~/.cache/crabc
key: musl-1.2.6-headers-v1
- name: Check macro expansion parity
run: ./scripts/macro-parity.sh
- name: Check headers against Rust constants
run: python3 tests/header_rust_consistency.py --include include
publish:
needs: [test, macro-parity]
runs-on: ubuntu-latest
environment: release
steps:
- uses: actions/checkout@v7
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- name: Verify tag matches workspace versions
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
cargo metadata --no-deps --format-version 1 > /tmp/metadata.json
for pkg in crabc crabc-libc crabc-ldso crabc-utils; do
got=$(jq -r --arg p "$pkg" '.packages[] | select(.name == $p) | .version' /tmp/metadata.json)
if [ "$got" != "$version" ]; then
echo "::error::$pkg is version $got but tag $GITHUB_REF_NAME expects $version"
exit 1
fi
done
echo "Publishing version $version"
- name: Install musl, cross toolchains and targets
run: |
sudo apt-get update
sudo apt-get install -y musl-tools gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu
rustup target add \
x86_64-unknown-linux-musl \
aarch64-unknown-linux-musl \
aarch64-unknown-linux-gnu \
riscv64gc-unknown-linux-musl
- name: Build release artifacts
run: |
set -eux
cargo build --release -p crabc-libc -p crabc-ldso --target x86_64-unknown-linux-gnu
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc \
cargo build --release -p crabc --target x86_64-unknown-linux-musl
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
cargo build --release -p crabc-libc -p crabc-ldso --target aarch64-unknown-linux-gnu
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc \
cargo build --release -p crabc --target aarch64-unknown-linux-musl
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_LINKER=riscv64-linux-gnu-gcc \
cargo build --release --workspace --target riscv64gc-unknown-linux-musl
lld="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n 's/^host: //p')/bin/rust-lld"
RUSTFLAGS="-C target-feature=+crt-static -C link-self-contained=yes -C linker=$lld" \
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_MUSL_LINKER="$lld" \
cargo build --release -p crabc --target riscv64gc-unknown-linux-musl
- name: Build Rust CRT objects and crabc-utils
run: |
set -o pipefail
./scripts/build-crt.sh > /tmp/crt-build.log 2>&1 || {
tail -40 /tmp/crt-build.log | sed 's/^/::error::/'
exit 1
}
./scripts/build-utils.sh > /tmp/utils-build.log 2>&1 || {
tail -10 /tmp/utils-build.log | sed 's/^/::error::/'
exit 1
}
- name: Build Alpine packages
run: ./scripts/package-apk.sh
- name: Smoke-test crabc-utils
env:
VERSION: ${{ github.ref_name }}
run: |
set -eux
version="${VERSION#v}"
docker run --rm -v "$PWD/dist:/dist:ro" alpine:3.23 sh -c "
apk add --allow-untrusted \
/dist/crabc-${version}-r0.x86_64.apk \
/dist/crabc-utils-${version}-r0.x86_64.apk
crabc-utils getconf PAGESIZE >/dev/null
ldd /bin/busybox >/dev/null
"
- name: Smoke-test the packaged libc
env:
VERSION: ${{ github.ref_name }}
run: |
set -eux
version="${VERSION#v}"
musl-gcc -fPIE -pie -D_GNU_SOURCE -I include \
-Wl,--dynamic-linker=/usr/lib/crabc/libldso.so \
-L target/x86_64-unknown-linux-gnu/release \
tests/fixtures/compat_test.c -Wl,--allow-shlib-undefined -lpthread -lc \
-o /tmp/compat_apk
docker run --rm --network=none \
-v "$PWD/dist:/dist:ro" -v "/tmp:/work:ro" alpine:3.23 sh -c "
apk --no-network add --allow-untrusted /dist/crabc-${version}-r0.x86_64.apk
mkdir -p /tmp/crabtest && touch /tmp/crabtest/seed
LD_LIBRARY_PATH=/usr/lib/crabc /work/compat_apk
"
- name: Authenticate with crates.io
id: auth
uses: rust-lang/crates-io-auth-action@v1
- name: Publish crabc-libc
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
code=$(curl -s -o /dev/null -w '%{http_code}' \
-A 'crabc-release (+https://github.com/mengzhuo/crabc)' \
"https://crates.io/api/v1/crates/crabc-libc/$version")
case "$code" in
200) echo "crabc-libc $version is already published; skipping" ;;
404) RUSTFLAGS='-C panic=abort' cargo publish -p crabc-libc --registry crates-io ;;
*) echo "::error::unexpected crates.io status $code"; exit 1 ;;
esac
- name: Publish crabc-ldso
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
code=$(curl -s -o /dev/null -w '%{http_code}' \
-A 'crabc-release (+https://github.com/mengzhuo/crabc)' \
"https://crates.io/api/v1/crates/crabc-ldso/$version")
case "$code" in
200) echo "crabc-ldso $version is already published; skipping" ;;
404) RUSTFLAGS='-C panic=abort' cargo publish -p crabc-ldso --registry crates-io ;;
*) echo "::error::unexpected crates.io status $code"; exit 1 ;;
esac
- name: Publish crabc
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: |
set -euo pipefail
version="${GITHUB_REF_NAME#v}"
code=$(curl -s -o /dev/null -w '%{http_code}' \
-A 'crabc-release (+https://github.com/mengzhuo/crabc)' \
"https://crates.io/api/v1/crates/crabc/$version")
case "$code" in
200) echo "crabc $version is already published; skipping" ;;
404) cargo publish -p crabc --registry crates-io ;;
*) echo "::error::unexpected crates.io status $code"; exit 1 ;;
esac
- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 || \
gh release create "$GITHUB_REF_NAME" --generate-notes --verify-tag
gh release upload "$GITHUB_REF_NAME" dist/*.apk dist/SHA256SUMS --clobber