name: Release
on:
release:
types: [published]
workflow_dispatch:
permissions: {}
jobs:
crates-io:
name: Publish to crates.io
runs-on: ubuntu-latest
environment: crates.io
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Verify versions match tag
env:
TAG: ${{ github.ref_name }}
run: |
TAG_VERSION="${TAG#v}"
META=$(cargo metadata --format-version 1 --no-deps)
for pkg in ruststream-macros ruststream; do
VERSION=$(echo "$META" | jq -r --arg p "$pkg" '.packages[] | select(.name == $p) | .version')
if [ "$TAG_VERSION" != "$VERSION" ]; then
echo "Tag '${TAG}' does not match $pkg version '$VERSION'"
exit 1
fi
echo "$pkg $VERSION verified"
done
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Authenticate to crates.io via trusted publishing
uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: cargo publish (dependency order)
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: |
# Publish a crate only if its current version is not already on crates.io,
# so re-running a release after a partial failure is safe. ruststream-macros
# is a proc-macro crate and a dependency of ruststream, so it goes first.
publish() {
name="$1"; shift
version=$(cargo metadata --format-version 1 --no-deps \
| jq -r --arg n "$name" '.packages[] | select(.name == $n) | .version')
index="https://index.crates.io/${name:0:2}/${name:2:2}/${name}"
if curl -fsSL "$index" 2>/dev/null | grep -qF "\"vers\":\"${version}\""; then
echo "$name $version already on crates.io, skipping"
else
cargo publish -p "$name" "$@"
fi
}
publish ruststream-macros
publish ruststream --all-features
- name: Generate release notes
if: github.event_name == 'release'
env:
TAG: ${{ github.ref_name }}
GH_TOKEN: ${{ github.token }}
run: |
NOTES=$(gh api "repos/$GITHUB_REPOSITORY/releases/generate-notes" \
-f tag_name="$TAG" -q .body)
gh release edit "$TAG" --notes "$NOTES" --repo "$GITHUB_REPOSITORY"