tessl 0.2.0

Metal 4 GEMM and encode runtime for Apple silicon, built on Metal Performance Primitives TensorOps
Documentation
name: Release

# Tag-triggered publish. Manual `cargo publish` without this workflow used to
# leave GitHub Releases empty while crates.io moved — Status links in the
# CHANGELOG footers then pointed at missing release pages. This job is the
# single path that keeps tag, crates.io, and GitHub Release in lockstep.
on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      dry_run:
        description: 'cargo publish --dry-run only'
        type: boolean
        default: true

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  release:
    name: verify + publish
    runs-on: macos-26
    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: Install the Metal Toolchain
        run: |
          if xcrun --find metal >/dev/null 2>&1; then
            echo "Metal compiler already present: $(xcrun --find metal)"
          else
            xcodebuild -downloadComponent MetalToolchain
          fi
          xcrun metal --version

      - name: Tag matches Cargo.toml version
        run: |
          set -euo pipefail
          VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)"
          if [[ "${{ github.event_name }}" == "push" ]]; then
            TAG="${GITHUB_REF_NAME#v}"
            if [[ "$TAG" != "$VERSION" ]]; then
              echo "error: tag v$TAG does not match Cargo.toml $VERSION" >&2
              exit 1
            fi
          fi
          echo "VERSION=$VERSION" >> "$GITHUB_ENV"

      - name: Release readiness (version triad + package denylist)
        run: |
          chmod +x scripts/check_release_ready.sh
          ./scripts/check_release_ready.sh

      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          set -euo pipefail
          if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.dry_run }}" == "true" ]]; then
            cargo publish --locked --dry-run
            exit 0
          fi
          # Prefer the Actions secret; fall back to a no-op when the crate is
          # already on crates.io (local `cargo publish` then tag-push).
          if [[ -z "${CARGO_REGISTRY_TOKEN:-}" ]]; then
            if curl -fsSLA 'tessl-release/1.0' "https://crates.io/api/v1/crates/tessl/${VERSION}" >/dev/null; then
              echo "CARGO_REGISTRY_TOKEN unset, but tessl ${VERSION} is already on crates.io — skipping upload"
              exit 0
            fi
            echo "error: CARGO_REGISTRY_TOKEN secret is not set and ${VERSION} is not on crates.io" >&2
            exit 1
          fi
          if cargo publish --locked 2>&1 | tee /tmp/publish.log; then
            exit 0
          fi
          if rg -q 'already uploaded|crate version .* already exists' /tmp/publish.log; then
            echo "version already on crates.io — treating as success"
            exit 0
          fi
          exit 1

      - name: Create GitHub Release from CHANGELOG
        if: ${{ github.event_name == 'push' || inputs.dry_run == false }}
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail
          TAG="v${VERSION}"
          BODY_FILE="$(mktemp)"
          VERSION="$VERSION" python3 - <<'PY' >"$BODY_FILE"
          import os, pathlib, re, sys
          text = pathlib.Path("CHANGELOG.md").read_text()
          version = os.environ["VERSION"]
          pat = rf"## \[{re.escape(version)}\][^\n]*\n(.*?)(?=\n## \[|\Z)"
          m = re.search(pat, text, re.S)
          if not m:
              sys.exit(f"CHANGELOG section for {version} not found")
          print(m.group(1).strip())
          PY
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "GitHub Release $TAG already exists; leaving it"
          else
            gh release create "$TAG" --title "tessl ${VERSION}" --notes-file "$BODY_FILE"
          fi