macula-rust 0.2.4

Rust port of macula's SDK (client/leaf) wire protocol — mobile first, not mobile-only. See plans/PLAN_WIRE_PROTOCOL.md.
Documentation
name: Release to crates.io

on:
  push:
    tags: ["v*"]

jobs:
  publish:
    name: publish to crates.io
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable

      - name: Extract version from tag
        id: version
        run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

      - name: Validate version matches Cargo.toml
        run: |
          PACKAGE_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1)
          if [ "$PACKAGE_VERSION" != "${{ steps.version.outputs.VERSION }}" ]; then
            echo "::error::Tag version (${{ steps.version.outputs.VERSION }}) doesn't match Cargo.toml version ($PACKAGE_VERSION)"
            exit 1
          fi

      # Rebuilds and re-runs the offline suite here rather than trusting
      # an artifact from ci.yml's own run -- this workflow triggers off a
      # tag push, a separate event from the branch push ci.yml already
      # validated, so there's no prior run's output to reuse. Deliberately
      # NOT --locked anywhere below: this repo doesn't commit Cargo.lock
      # (library crate; a committed lock silently caps what a loose
      # constraint resolves to on every later run -- see the workspace's
      # own lockfile-hygiene convention), so a fresh checkout has no lock
      # to be strict against.
      - run: cargo build --workspace --all-targets
      - run: cargo test --workspace --all-features
      - run: cargo clippy --workspace --all-targets --all-features -- -D warnings
      - run: cargo fmt --all -- --check

      # Dry-run needs no registry auth -- it only verifies metadata,
      # compresses the package, and checks the result, never uploads.
      - name: Dry-run publish (catches packaging issues before the real one)
        run: cargo publish --dry-run

      # CRATES_IO_TOKEN (a plain crates.io API token, cargo login-style) --
      # NOT OIDC Trusted Publishing, unlike this org's npm (macula-mcp)
      # and NuGet (macula-dotnet) release workflows. crates.io does
      # support Trusted Publishing (rust-lang/crates-io-auth-action, same
      # shape as those two), but it has no PyPI-style "pending publisher"
      # for a crate that doesn't exist yet -- a Trusted Publisher can only
      # be configured on crates.io AFTER a crate's first release already
      # exists there. A static token is the only mechanism that can
      # actually perform that first release from CI at all.
      #
      # macula-rust v0.2.3 published this way 2026-09-05 -- the migration
      # path once someone wants to spend the time on it: add a Trusted
      # Publisher for macula-io/macula-rust + this workflow file in the
      # crate's crates.io settings, replace this step with
      # `rust-lang/crates-io-auth-action@v1` (permissions:
      # id-token: write) feeding its `token` output to
      # CARGO_REGISTRY_TOKEN the same way, then delete the CRATES_IO_TOKEN
      # repo secret -- no code reason to keep a long-lived token around
      # once the OIDC path is available.
      - name: Publish to crates.io
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}