---
name: Publish to crates.io
"on":
push:
tags:
- "v*.*.*"
workflow_dispatch:
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
jobs:
publish:
name: Publish to crates.io
runs-on: ubuntu-latest
environment: copilot
permissions:
contents: read
id-token: write
attestations: write
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 with:
toolchain: stable
- name: Install cargo-deny
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 with:
tool: cargo-deny
- name: Verify package
run: |
cargo package --list
cargo package --allow-dirty
- name: Run pre-publish checks
run: |
cargo fmt -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
cargo doc --no-deps --all-features
cargo deny check
- name: Dry run publish
run: cargo publish --dry-run --allow-dirty
- name: Authenticate with crates.io
id: auth
if: startsWith(github.ref, 'refs/tags/v')
uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe
- name: Publish to crates.io
if: startsWith(github.ref, 'refs/tags/v')
run: cargo publish --token "$CARGO_REGISTRY_TOKEN"
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
- name: Download published crate from registry
id: crate
if: startsWith(github.ref, 'refs/tags/v')
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
CRATE="rlm-cli-${VERSION}.crate"
URL="https://static.crates.io/crates/rlm-cli/${CRATE}"
downloaded=0
for attempt in 1 2 3 4 5 6; do
if curl -fsSL -o "${CRATE}" "$URL"; then
downloaded=1
break
fi
echo "crate not yet on CDN (attempt ${attempt}); retrying"
sleep 10
done
if [ "$downloaded" -ne 1 ] || [ ! -s "${CRATE}" ]; then
echo "::error::failed to download ${URL} after 6 attempts"
exit 1
fi
LOCAL="target/package/${CRATE}"
if [ ! -f "$LOCAL" ]; then
echo "::error::locally packaged crate missing: ${LOCAL}"
exit 1
fi
sha_remote=$(sha256sum "${CRATE}" | cut -d' ' -f1)
sha_local=$(sha256sum "$LOCAL" | cut -d' ' -f1)
if [ "$sha_remote" != "$sha_local" ]; then
echo "::error::registry crate bytes differ from local package"
exit 1
fi
echo "crate-path=${CRATE}" >> "$GITHUB_OUTPUT"
- name: Attest crate provenance
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 with:
subject-path: ${{ steps.crate.outputs.crate-path }}