name: Publish to crates.io
on:
push:
tags:
- "v*.*.*" workflow_dispatch: {}
permissions:
contents: read
jobs:
crate:
name: Publish to crates.io
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Install jq (for JSON parsing)
run: sudo apt-get update && sudo apt-get install -y jq
- name: Read crate name & version from Cargo.toml
id: crate
run: |
meta=$(cargo metadata --no-deps --format-version=1)
name=$(echo "$meta" | jq -r '.packages[0].name')
version=$(echo "$meta" | jq -r '.packages[0].version')
echo "name=$name" >> $GITHUB_OUTPUT
echo "version=$version" >> $GITHUB_OUTPUT
- name: Ensure tag matches Cargo.toml version
if: startsWith(github.ref, 'refs/tags/')
run: |
tag="${GITHUB_REF#refs/tags/}"
expected="v${{ steps.crate.outputs.version }}"
if [ "$tag" != "$expected" ]; then
echo "Tag ($tag) does not match Cargo.toml version ($expected)."
exit 1
fi
- name: Skip if this version is already published
id: already
run: |
name='${{ steps.crate.outputs.name }}'
version='${{ steps.crate.outputs.version }}'
# Query crates.io for existing versions
if curl -fsSL "https://crates.io/api/v1/crates/$name/versions" | \
jq -e ".versions[] | select(.num==\"$version\")" > /dev/null; then
echo "exists=yes" >> $GITHUB_OUTPUT
echo "Version $version already published for $name. Skipping."
else
echo "exists=no" >> $GITHUB_OUTPUT
fi
- name: Publish dry run
if: steps.already.outputs.exists == 'no'
run: cargo publish --locked --all-features --dry-run
- name: Publish to crates.io
if: steps.already.outputs.exists == 'no'
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --locked --all-features