macp-runtime 0.5.0

MACP reference runtime: a coordination kernel and gRPC server enforcing session boundaries, message validation, append-only history, modes, and governance policy.
Documentation
name: Publish Crates

# Publishes the workspace crates to crates.io in dependency order when a
# version tag (e.g. v0.4.0) is pushed. Can also be run manually for a dry run.
on:
  push:
    tags: ["v*"]
  workflow_dispatch:
    inputs:
      dry_run:
        description: "Package and verify without uploading"
        type: boolean
        default: true

env:
  CARGO_TERM_COLOR: always
  # Workspace publishing (`cargo publish --workspace`) resolves internal path
  # deps locally, so a dry-run validates the whole graph before anything is on
  # the index. It needs a toolchain new enough to have the flag stabilized.
  RUST_TOOLCHAIN: "1.95.0"
  # The repo pins the MSRV via rust-toolchain.toml (channel = "1.89.0"), and
  # that file wins over the installed default for plain `cargo`. RUSTUP_TOOLCHAIN
  # has higher precedence than the file, so it forces this job's cargo to the
  # newer toolchain installed above without touching the repo-wide MSRV pin.
  RUSTUP_TOOLCHAIN: "1.95.0"

jobs:
  publish:
    name: Publish to crates.io
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@master
        with:
          toolchain: ${{ env.RUST_TOOLCHAIN }}

      - name: Install protoc
        run: |
          sudo apt-get update
          sudo apt-get install -y protobuf-compiler

      # Guard against tagging a release whose version doesn't match the
      # workspace. The tag drives the release; the manifest must agree.
      - name: Verify tag matches workspace version
        if: github.ref_type == 'tag'
        run: |
          tag="${GITHUB_REF_NAME#v}"
          ws=$(cargo metadata --format-version 1 --no-deps \
            | jq -r '.packages[] | select(.name=="macp-runtime") | .version')
          echo "tag=$tag workspace=$ws"
          if [ "$tag" != "$ws" ]; then
            echo "::error::tag $GITHUB_REF_NAME does not match workspace version $ws"
            exit 1
          fi

      # `cargo publish --workspace` computes the dependency order itself, builds
      # each crate's verify step against its sibling path deps (so a dry-run
      # validates the whole graph without anything on the index yet), and waits
      # for each upload to appear on the index before publishing its dependents.
      #
      # Re-run safety: a crate whose current version is already live is passed
      # via --exclude, so re-running after a mid-release failure skips the
      # already-published members instead of erroring on "already exists".
      - name: Publish workspace to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
          DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run }}
        run: |
          set -euo pipefail

          is_published() {
            local name="$1" ver="$2" code
            code=$(curl -s -o /dev/null -w '%{http_code}' \
              -A "macp-runtime-release (https://github.com/multiagentcoordinationprotocol/macp-runtime)" \
              "https://crates.io/api/v1/crates/$name/$ver")
            [ "$code" = "200" ]
          }

          total=0
          exclude_args=()
          while read -r name ver; do
            total=$((total + 1))
            if is_published "$name" "$ver"; then
              echo "==> skip $name@$ver (already on crates.io)"
              exclude_args+=(--exclude "$name")
            fi
          done < <(cargo metadata --format-version 1 --no-deps \
            | jq -r '.packages[] | "\(.name) \(.version)"')

          if [ "${#exclude_args[@]}" -eq $((total * 2)) ]; then
            echo "All workspace crates already published; nothing to do."
            exit 0
          fi

          # ${arr[@]+...} guards against an empty array tripping `set -u`.
          if [ "${DRY_RUN:-false}" = "true" ]; then
            echo "==> dry-run publish workspace"
            cargo publish --workspace --dry-run --locked ${exclude_args[@]+"${exclude_args[@]}"}
          else
            echo "==> publish workspace"
            cargo publish --workspace --locked ${exclude_args[@]+"${exclude_args[@]}"}
          fi