name: Release
on:
push:
tags:
- 'v*'
env:
CARGO_TERM_COLOR: never
jobs:
create-release:
name: Create Release
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.VERSION }}
steps:
- uses: actions/checkout@v6
- name: Get version from tag
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Read changelog
id: changelog
run: |
# Extract the latest version's changelog
VERSION="${{ steps.get_version.outputs.VERSION }}"
VERSION_NO_V="${VERSION#v}"
CHANGELOG=$(awk -v ver="## \\[$VERSION_NO_V\\]" '
$0 ~ ver {p=1; next}
/^## \[/ && p {exit}
p {print}
' CHANGELOG.md)
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.get_version.outputs.VERSION }}
RELEASE_NOTES: ${{ steps.changelog.outputs.CHANGELOG }}
run: |
# Check if release already exists
if gh release view "$VERSION" &>/dev/null; then
echo "Release $VERSION already exists, skipping creation"
echo "RELEASE_EXISTS=true" >> $GITHUB_OUTPUT
else
gh release create "$VERSION" \
--title "$VERSION" \
--notes "$RELEASE_NOTES" \
--verify-tag
echo "RELEASE_EXISTS=false" >> $GITHUB_OUTPUT
fi
publish-crates:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: create-release
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-publish-${{ hashFiles('**/Cargo.lock') }}
- name: Publish pmcp-widget-utils
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing pmcp-widget-utils..."
OUTPUT=$(cargo publish -p pmcp-widget-utils 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "pmcp-widget-utils already published, continuing..."
else
echo "::error::Failed to publish pmcp-widget-utils"
exit 1
fi
}
- name: Wait for crates.io to index pmcp-widget-utils
run: sleep 30
- name: Publish pmcp-macros
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing pmcp-macros..."
OUTPUT=$(cargo publish -p pmcp-macros 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "pmcp-macros already published, continuing..."
else
echo "::error::Failed to publish pmcp-macros"
exit 1
fi
}
- name: Wait for crates.io to index pmcp-macros
run: sleep 30
- name: Publish pmcp (core SDK)
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing pmcp..."
OUTPUT=$(cargo publish -p pmcp 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "pmcp already published, continuing..."
else
echo "::error::Failed to publish pmcp"
exit 1
fi
}
- name: Wait for crates.io to index pmcp
run: sleep 30
- name: Publish mcp-tester
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing mcp-tester..."
OUTPUT=$(cargo publish -p mcp-tester 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "mcp-tester already published, continuing..."
else
echo "::error::Failed to publish mcp-tester"
exit 1
fi
}
- name: Publish mcp-preview
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing mcp-preview..."
OUTPUT=$(cargo publish -p mcp-preview 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "mcp-preview already published, continuing..."
else
echo "::error::Failed to publish mcp-preview"
exit 1
fi
}
- name: Publish pmcp-server
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing pmcp-server..."
OUTPUT=$(cargo publish -p pmcp-server 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "pmcp-server already published, continuing..."
else
echo "::error::Failed to publish pmcp-server"
exit 1
fi
}
- name: Wait for crates.io to index mcp-tester, mcp-preview, and pmcp-server
run: sleep 30
- name: Publish cargo-pmcp
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
echo "Publishing cargo-pmcp..."
OUTPUT=$(cargo publish -p cargo-pmcp 2>&1) && echo "$OUTPUT" || {
echo "$OUTPUT"
if echo "$OUTPUT" | grep -q "already exists"; then
echo "cargo-pmcp already published, continuing..."
else
echo "::error::Failed to publish cargo-pmcp"
exit 1
fi
}
publish-mcp:
name: Publish to MCP Registry
runs-on: ubuntu-latest
needs: publish-crates
permissions:
id-token: write contents: read
steps:
- uses: actions/checkout@v6
- name: Wait for crates.io availability
run: |
echo "Waiting 60 seconds for crates.io to index all packages..."
sleep 60
- name: Install MCP Publisher
run: |
PLATFORM=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -sL "https://github.com/modelcontextprotocol/registry/releases/download/v1.5.0/mcp-publisher_${PLATFORM}_${ARCH}.tar.gz" | tar xz
chmod +x mcp-publisher
- name: Login to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Publish to MCP Registry
run: ./mcp-publisher publish
build-tester:
name: Build Tester Binaries
needs: create-release
uses: ./.github/workflows/release-binary.yml
with:
tag_name: ${{ needs.create-release.outputs.version }}
package_name: mcp-tester
secrets: inherit
build-pmcp-server:
name: Build PMCP Server Binaries
needs: create-release
uses: ./.github/workflows/release-binary.yml
with:
tag_name: ${{ needs.create-release.outputs.version }}
package_name: pmcp-server
secrets: inherit