mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-06-09 01:35:18 +02:00
Two related fixes uncovered while recovering the v0.5.0 publish. 1. crates.io API requires a User-Agent header. The `publish_if_new` skip check was doing a bare `curl -fsSL https://crates.io/api/...` which crates.io rejects with HTTP 403. With `-f` curl exits non-zero, the pipeline returns empty, the script doesn't recognize already-published crates, and we fall through to a real publish attempt. On a re-run that means cargo publish errors with "already exists on crates.io index" for crates that DID publish successfully on the previous run. Fix: send a `User-Agent: ModernRelay-omnigraph-ci (URL)` header. 2. Defense in depth: even with the UA, the API could hiccup. If the skip check misses an existing version and cargo publish errors with "already exists on crates.io index", treat as success instead of failing the whole run. This makes the workflow re-runnable after any partial publish without needing manual intervention. Both fixes are required to recover from the v0.5.0 partial publish where omnigraph-compiler@0.5.0 made it through but the run failed before omnigraph-policy / engine / server / cli — re-triggering the workflow now succeeds end-to-end. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
124 lines
4.5 KiB
YAML
124 lines
4.5 KiB
YAML
name: Publish to crates.io
|
|
|
|
# Publishes the four workspace crates to crates.io in dependency order.
|
|
#
|
|
# Triggers:
|
|
# - push of any v* tag (future releases auto-publish alongside release.yml)
|
|
# - workflow_dispatch with an explicit tag input (catch-up runs for past tags)
|
|
#
|
|
# Idempotent: each crate's current crates.io version is checked before publish,
|
|
# so a partial failure can be re-run without "crate version already exists" errors.
|
|
#
|
|
# Prerequisite: repo secret CARGO_REGISTRY_TOKEN. The job exits cleanly if absent.
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- "v*"
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: "Tag to publish (e.g. v0.4.1). Required for manual dispatches."
|
|
required: true
|
|
type: string
|
|
|
|
jobs:
|
|
publish_crates:
|
|
name: Publish crates
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
|
CARGO_TERM_COLOR: always
|
|
steps:
|
|
- name: Skip if CARGO_REGISTRY_TOKEN is not configured
|
|
if: env.CARGO_REGISTRY_TOKEN == ''
|
|
run: |
|
|
echo "CARGO_REGISTRY_TOKEN is not set; skipping crates.io publish."
|
|
echo "CARGO_PUBLISH_SKIP=1" >> "$GITHUB_ENV"
|
|
|
|
- name: Resolve ref
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
id: ref
|
|
run: |
|
|
ref="${{ inputs.tag || github.ref_name }}"
|
|
echo "ref=${ref}" >> "$GITHUB_OUTPUT"
|
|
echo "Publishing from ref: ${ref}"
|
|
|
|
- name: Checkout source at tag
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
uses: actions/checkout@v5.0.1
|
|
with:
|
|
ref: ${{ steps.ref.outputs.ref }}
|
|
|
|
- name: Install Linux dependencies
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y protobuf-compiler libprotobuf-dev
|
|
|
|
- name: Install Rust stable
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
toolchain: stable
|
|
|
|
- name: Cache Rust build data
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: |
|
|
. -> target
|
|
|
|
- name: Publish crates in dependency order
|
|
if: env.CARGO_PUBLISH_SKIP != '1'
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
publish_if_new() {
|
|
local crate="$1"
|
|
local version
|
|
version=$(cargo metadata --format-version=1 --no-deps \
|
|
| jq -r --arg c "$crate" '.packages[] | select(.name==$c) | .version')
|
|
|
|
# crates.io API requires a User-Agent header — without it the
|
|
# API responds 403 and the skip check below would silently
|
|
# fall through to a real publish attempt that errors with
|
|
# "already exists on crates.io index" when re-running after a
|
|
# partial publish. Send a UA naming the workflow.
|
|
local current
|
|
current=$(curl -fsSL \
|
|
-A 'ModernRelay-omnigraph-ci (https://github.com/ModernRelay/omnigraph)' \
|
|
"https://crates.io/api/v1/crates/${crate}" \
|
|
| jq -r '.crate.max_version' || echo "")
|
|
|
|
if [[ "$current" == "$version" ]]; then
|
|
echo "==> ${crate} ${version} already on crates.io, skipping"
|
|
return 0
|
|
fi
|
|
|
|
echo "==> publishing ${crate} ${version} (current crates.io: ${current:-none})"
|
|
# Defense in depth: if the skip check missed an existing
|
|
# version (e.g. crates.io API hiccup), cargo publish errors
|
|
# with "already exists on crates.io index". Treat that as
|
|
# success so the workflow can be re-run idempotently.
|
|
local output
|
|
if ! output=$(cargo publish -p "$crate" --locked 2>&1); then
|
|
echo "$output"
|
|
if echo "$output" | grep -q "already exists on crates.io"; then
|
|
echo "==> ${crate} ${version} was already published; treating as success"
|
|
return 0
|
|
fi
|
|
return 1
|
|
fi
|
|
echo "$output"
|
|
}
|
|
|
|
# Order matters: each crate must precede anything that depends on it.
|
|
# omnigraph-compiler and omnigraph-policy have no internal deps;
|
|
# omnigraph-engine depends on both; server depends on engine + the
|
|
# two leaf crates; cli depends on everything.
|
|
publish_if_new omnigraph-compiler
|
|
publish_if_new omnigraph-policy
|
|
publish_if_new omnigraph-engine
|
|
publish_if_new omnigraph-server
|
|
publish_if_new omnigraph-cli
|