name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
fetch-depth: 0
- name: Verify tag belongs to main
shell: bash
run: |
git fetch --no-tags origin '+refs/heads/main:refs/remotes/origin/main'
git merge-base --is-ancestor "${GITHUB_SHA}" refs/remotes/origin/main
- uses: dtolnay/rust-toolchain@4e529fb27e59237866a6523e61ab248308c068b4 with:
components: clippy, rustfmt
- name: Verify release metadata
shell: bash
run: |
crate_version="$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)"
test "${GITHUB_REF_NAME}" = "v${crate_version}"
bash scripts/release-notes.sh "${GITHUB_REF_NAME}" >/dev/null
- run: cargo fmt --check
- run: cargo clippy --locked --all-targets -- -D warnings
- run: cargo test --locked
- run: cargo package --locked
- run: cargo check --locked
- run: cargo install cargo-audit --version 0.22.2 --locked
- run: cargo audit
platform-tests:
needs: verify
timeout-minutes: 30
strategy:
matrix:
os: [macos-14, windows-2022]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - uses: dtolnay/rust-toolchain@4e529fb27e59237866a6523e61ab248308c068b4 - run: cargo test --locked
build:
needs: [verify, platform-tests]
environment: release
timeout-minutes: 45
strategy:
matrix:
include:
- target: x86_64-apple-darwin
os: macos-14
kind: macos
name: sunox-x86_64-apple-darwin
- target: aarch64-apple-darwin
os: macos-14
kind: macos
name: sunox-aarch64-apple-darwin
- target: x86_64-unknown-linux-gnu
zig_target: x86_64-unknown-linux-gnu.2.28
os: ubuntu-24.04
kind: linux
name: sunox-x86_64-unknown-linux-gnu
- target: aarch64-unknown-linux-gnu
zig_target: aarch64-unknown-linux-gnu.2.28
os: ubuntu-24.04
kind: linux
name: sunox-aarch64-unknown-linux-gnu
- target: x86_64-pc-windows-msvc
os: windows-2022
kind: windows
name: sunox-x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
- name: Install Rust
uses: dtolnay/rust-toolchain@4e529fb27e59237866a6523e61ab248308c068b4 with:
targets: ${{ matrix.target }}
- name: Install reproducible Linux cross toolchain
if: matrix.kind == 'linux'
shell: bash
run: |
cargo install cargo-zigbuild --version 0.23.0 --locked
python3 -m venv "${RUNNER_TEMP}/zig-venv"
"${RUNNER_TEMP}/zig-venv/bin/pip" install --disable-pip-version-check 'ziglang==0.15.2'
"${RUNNER_TEMP}/zig-venv/bin/python" - <<'PY' >> "${GITHUB_PATH}"
import pathlib, ziglang
print(pathlib.Path(ziglang.__file__).parent)
PY
- name: Build Linux against glibc 2.28
if: matrix.kind == 'linux'
shell: bash
run: cargo zigbuild --release --locked --target '${{ matrix.zig_target }}'
- name: Build macOS
if: matrix.kind == 'macos'
shell: bash
run: cargo build --release --locked --target '${{ matrix.target }}'
- name: Build Windows with the static CRT
if: matrix.kind == 'windows'
shell: pwsh
env:
RUSTFLAGS: -C target-feature=+crt-static
run: cargo build --release --locked --target '${{ matrix.target }}'
- name: Verify Linux compatibility and binary
if: matrix.kind == 'linux'
shell: bash
run: |
binary="target/${{ matrix.target }}/release/sunox"
file "${binary}"
max_glibc="$(readelf --version-info "${binary}" | grep -o 'GLIBC_[0-9.]*' | sort -V | tail -1)"
test -n "${max_glibc}"
test "$(printf '%s\n' GLIBC_2.28 "${max_glibc}" | sort -V | tail -1)" = 'GLIBC_2.28'
if [ '${{ matrix.target }}' = 'x86_64-unknown-linux-gnu' ]; then
"${binary}" --version
fi
- name: Verify macOS binary
if: matrix.kind == 'macos'
shell: bash
run: |
binary="target/${{ matrix.target }}/release/sunox"
"${binary}" --version
- name: Verify Windows binary and static CRT
if: matrix.kind == 'windows'
shell: pwsh
run: |
$binary = 'target/${{ matrix.target }}/release/sunox.exe'
& $binary --version
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$install = & $vswhere -latest -products * -property installationPath
$dumpbin = Get-ChildItem "$install\VC\Tools\MSVC\*\bin\Hostx64\x64\dumpbin.exe" | Sort-Object FullName -Descending | Select-Object -First 1
$dependencies = (& $dumpbin.FullName /dependents $binary) -join "`n"
if ($dependencies -match 'VCRUNTIME|MSVCP') { throw 'release binary still depends on the dynamic Visual C++ runtime' }
- name: Package Unix artifact
if: matrix.kind != 'windows'
shell: bash
run: tar -C 'target/${{ matrix.target }}/release' -czf '${{ matrix.name }}.tar.gz' sunox
- name: Package Windows artifact
if: matrix.kind == 'windows'
shell: pwsh
run: |
Compress-Archive -LiteralPath 'target/${{ matrix.target }}/release/sunox.exe' -DestinationPath '${{ matrix.name }}.zip' -CompressionLevel NoCompression -Force
- name: Upload artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: ${{ matrix.name }}
path: ${{ matrix.name }}.*
if-no-files-found: error
release:
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 20
environment: release
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with:
fetch-depth: 0
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
path: artifacts
- name: Generate CycloneDX SBOM
shell: bash
run: |
cargo install cargo-cyclonedx --version 0.5.9 --locked
cargo cyclonedx --format json --all --override-filename sunox.cdx
mv sunox.cdx.json artifacts/sunox.cdx.json
- name: Generate release notes
shell: bash
run: bash scripts/release-notes.sh "${GITHUB_REF_NAME}" > release-notes.md
- name: Generate checksums
shell: bash
run: |
checksum_file="${RUNNER_TEMP}/SHA256SUMS"
while IFS= read -r file; do
printf '%s %s\n' "$(sha256sum "${file}" | cut -d' ' -f1)" "$(basename "${file}")"
done < <(find artifacts -type f | sort) > "${checksum_file}"
mv "${checksum_file}" artifacts/SHA256SUMS
- name: Attest release artifacts
uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a with:
subject-path: artifacts/**/*
- name: Create unsigned release
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b with:
body_path: release-notes.md
files: artifacts/**/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-crate:
needs: release
runs-on: ubuntu-24.04
timeout-minutes: 15
environment: release
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 - uses: dtolnay/rust-toolchain@4e529fb27e59237866a6523e61ab248308c068b4 - name: Authenticate with crates.io
id: auth
uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 - name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: cargo publish --locked