name: Release
on:
push:
tags:
- "v*.*.*"
permissions:
contents: write
jobs:
publish:
name: crates.io + GitHub Release
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- name: Install Rust toolchain (rust-toolchain.toml)
run: rustup show
- name: Verify tag matches Cargo workspace version
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
VER="${TAG#v}"
CARGO_VER="$(awk '/^\[workspace\.package\]/{p=1; next} p && /^version = /{gsub(/"/, "", $3); print $3; exit}' Cargo.toml)"
if [[ "$VER" != "$CARGO_VER" ]]; then
echo "ERROR: tag $TAG does not match workspace version $CARGO_VER" >&2
exit 1
fi
echo "Publishing SoftGPU $VER"
echo "SOFTGPU_VER=$VER" >> "$GITHUB_ENV"
- name: Dry-run publish order
run: |
set -euo pipefail
# Query crates.io only — bare `cargo info pkg@ver` resolves the local
# workspace member and falsely reports unpublished SoftGPU versions.
crate_on_crates_io() {
cargo info "$1@$2" --registry crates-io >/dev/null 2>&1
}
# Full dry-run (pack + verify) for crates with no SoftGPU sibling deps.
# Dependents need softgpu-*@$SOFTGPU_VER on crates.io; that version is
# not uploaded until the real publish step, so dry-run cannot resolve them.
cargo publish -p softgpu-amd-code-object --locked --dry-run
cargo publish -p softgpu-amd-isa --locked --dry-run
cargo publish -p softgpu-functional --locked --dry-run
dry_run_if_deps_published() {
local pkg="$1"
shift
local dep
for dep in "$@"; do
if ! crate_on_crates_io "$dep" "$SOFTGPU_VER"; then
echo "skip dry-run: ${pkg} (waiting for ${dep}@${SOFTGPU_VER} on crates.io; validated by ordered publish)"
return 0
fi
done
cargo publish -p "$pkg" --locked --dry-run
}
# softgpu-core gained SoftGPU sibling deps in v0.8 (amd-code-object, amd-isa).
dry_run_if_deps_published softgpu-core softgpu-amd-code-object softgpu-amd-isa
dry_run_if_deps_published softgpu-hsa softgpu-core
dry_run_if_deps_published softgpu softgpu-core softgpu-amd-code-object softgpu-amd-isa softgpu-functional
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
set -euo pipefail
if [[ -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then
echo "ERROR: repository secret CARGO_REGISTRY_TOKEN is not set" >&2
exit 1
fi
crate_on_crates_io() {
cargo info "$1@$2" --registry crates-io >/dev/null 2>&1
}
wait_for_crate() {
local pkg="$1"
local ver="$2"
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
if crate_on_crates_io "$pkg" "$ver"; then
echo "ok: ${pkg}@${ver} visible on crates.io"
return 0
fi
echo "waiting for ${pkg}@${ver} ($i)..."
sleep 15
done
echo "ERROR: timed out waiting for ${pkg}@${ver}" >&2
return 1
}
publish_crate() {
local pkg="$1"
local ver="$2"
if crate_on_crates_io "$pkg" "$ver"; then
echo "skip: ${pkg}@${ver} already published"
return 0
fi
cargo publish -p "$pkg" --locked
wait_for_crate "$pkg" "$ver"
}
# Leaves first, then dependents (softgpu-core now needs amd-* siblings).
# Order: amd-code-object → amd-isa → functional → core → hsa → softgpu CLI
publish_crate softgpu-amd-code-object "$SOFTGPU_VER"
publish_crate softgpu-amd-isa "$SOFTGPU_VER"
publish_crate softgpu-functional "$SOFTGPU_VER"
publish_crate softgpu-core "$SOFTGPU_VER"
publish_crate softgpu-hsa "$SOFTGPU_VER"
publish_crate softgpu "$SOFTGPU_VER"
- name: Create GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
if gh release view "$TAG" >/dev/null 2>&1; then
echo "skip: GitHub release $TAG already exists"
exit 0
fi
NOTES="SoftGPU ${TAG}
See CHANGELOG.md for details.
Crates:
- https://crates.io/crates/softgpu
- https://crates.io/crates/softgpu-core
- https://crates.io/crates/softgpu-amd-code-object
- https://crates.io/crates/softgpu-amd-isa
- https://crates.io/crates/softgpu-functional
- https://crates.io/crates/softgpu-hsa
"
gh release create "$TAG" --title "SoftGPU $TAG" --notes "$NOTES"