softgpu 0.8.0

Developer-oriented GPU emulation, testing, debugging, and sanitization runtime
Documentation
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"