Skip to content

Publish Crates

Publish Crates #12

name: Publish Crates
on:
workflow_dispatch:
inputs:
crate_list:
description: Space-separated crate package names to publish in order
required: true
type: string
dry_run:
description: Run cargo publish --dry-run only
required: false
default: "true"
type: choice
options: ["true", "false"]
env:
CARGO_TERM_COLOR: always
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Strip private-registry refs for crates.io publish
run: |
python3 - <<'PY'
import re, pathlib
# The Gitea `terraphim` registry mirrors crates.io for these deps.
# crates.io rejects manifests whose deps pin a non-default registry,
# so drop the `registry = "terraphim"` key everywhere and remove any
# [patch.crates-io] entry that pins to it (a patch needs a source).
root = pathlib.Path('Cargo.toml')
lines = root.read_text().splitlines()
out, inp = [], False
for ln in lines:
if ln.strip() == '[patch.crates-io]':
inp = True; out.append(ln); continue
if ln.startswith('['):
inp = False
if inp and 'registry = "terraphim"' in ln:
continue
out.append(ln)
root.write_text("\n".join(out) + "\n")
for f in pathlib.Path('.').rglob('Cargo.toml'):
s = f.read_text()
s = re.sub(r',\s*registry = "terraphim"', '', s)
s = re.sub(r'registry = "terraphim",\s*', '', s)
f.write_text(s)
leftover = [str(p) for p in pathlib.Path('.').rglob('Cargo.toml')
if 'registry = "terraphim"' in p.read_text()]
assert not leftover, f"registry refs remain: {leftover}"
print("Stripped terraphim-registry refs")
PY
- name: Publish crates in dependency order
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
CRATE_LIST: ${{ inputs.crate_list }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
set -euo pipefail
for crate in $CRATE_LIST; do
manifest="crates/$crate/Cargo.toml"
if [ ! -f "$manifest" ]; then
echo "Missing manifest for $crate at $manifest" >&2
exit 1
fi
# Resolve the real version via cargo metadata so that
# `version.workspace = true` is expanded to the workspace value.
# (A naive `grep '^version'` yields the literal "true".)
version=$(cargo metadata --no-deps --format-version=1 \
| jq -r --arg manifest "$manifest" \
'.packages[] | select(.manifest_path | endswith($manifest)) | .version')
if [ -z "$version" ]; then
echo "ERROR: could not resolve version for $crate from $manifest" >&2
exit 1
fi
# Check crates.io with auth to avoid rate limits
if curl -fsS -H "Authorization: ${CARGO_REGISTRY_TOKEN}" "https://crates.io/api/v1/crates/$crate/$version" >/dev/null 2>&1; then
echo "$crate $version already exists on crates.io; skipping"
continue
fi
if [ "$DRY_RUN" = "true" ]; then
cargo publish --manifest-path "$manifest" --dry-run --allow-dirty
else
set +e
output=$(cargo publish --manifest-path "$manifest" --allow-dirty 2>&1)
exit_code=$?
set -e
if [ "$exit_code" -ne 0 ]; then
if echo "$output" | grep -qi "already exists"; then
echo "$crate $version already exists on crates.io; skipping"
continue
fi
echo "$output" >&2
echo "ERROR: Failed to publish $crate" >&2
exit 1
fi
fi
done