ci: add the publish.yaml workflow file (completes d3af1c4)
d3af1c4 described the manual publish workflow and updated ADR-0056, but
`git commit -am` doesn't stage new untracked files, so publish.yaml
itself was left out. Add it here.
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
# Manual publication workflow (workflow_dispatch) — the outward, irreversible
|
||||||
|
# release steps a human triggers AFTER the automated `release.yaml` build has
|
||||||
|
# produced downloadable assets (and they've been eyeballed as good).
|
||||||
|
#
|
||||||
|
# Why manual + separate from release.yaml:
|
||||||
|
# * Publishing to a public registry is irreversible (crates.io versions can
|
||||||
|
# only be *yanked*, never deleted) — a human pulls this lever, and the
|
||||||
|
# registry token never sits on every tag push.
|
||||||
|
# * Our release is split (Linux/Windows on the tag, macOS dispatched), so a
|
||||||
|
# human is the natural "all assets are up — go" gate. crates.io publish
|
||||||
|
# reads SOURCE so it doesn't strictly need the release, but binstall's
|
||||||
|
# metadata points at the release assets — hence run this once builds exist.
|
||||||
|
#
|
||||||
|
# Structure: each registry is its OWN job with NO inter-job `needs`, so jobs run
|
||||||
|
# independently and one failing (or a newly-added one) never breaks another.
|
||||||
|
# Every job is IDEMPOTENT — re-dispatching when a target is already published is
|
||||||
|
# a clean no-op. Add Scoop / Homebrew / winget as sibling jobs here later.
|
||||||
|
name: publish
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag to publish (e.g. v0.2.0)'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
crates-io:
|
||||||
|
runs-on: ci-public
|
||||||
|
container:
|
||||||
|
image: git.lazyeval.net/oli/rdbms-playground-ci:latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.tag }}
|
||||||
|
|
||||||
|
- name: publish to crates.io (idempotent)
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
TAG: ${{ inputs.tag }}
|
||||||
|
# A crate-scoped, publish-update crates.io token, stored as a Gitea
|
||||||
|
# Actions secret. `cargo publish` reads CARGO_REGISTRY_TOKEN from env.
|
||||||
|
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Source of truth = the [package] version at the checked-out tag
|
||||||
|
# (toolchain-free read; same approach as release.yaml's guard, which
|
||||||
|
# avoids the flake devShell's stdout banner corrupting a parse).
|
||||||
|
VER=$(grep -m1 '^version = ' Cargo.toml | sed -E 's/^version = "(.*)"/\1/')
|
||||||
|
[ -n "$VER" ] || { echo "ERROR: could not read version from Cargo.toml" >&2; exit 1; }
|
||||||
|
if [ "$TAG" != "v$VER" ]; then
|
||||||
|
echo "ERROR: dispatch tag '$TAG' != 'v$VER' (Cargo.toml at that tag)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Idempotency: if this version is already on crates.io, no-op.
|
||||||
|
# (crates.io requires a descriptive User-Agent per its data policy;
|
||||||
|
# without one the API returns 403.) Only an explicit 200 means
|
||||||
|
# "already there" — anything else proceeds, and `cargo publish` is the
|
||||||
|
# final backstop (it refuses to overwrite an existing version).
|
||||||
|
UA="rdbms-playground-release-ci (oliver@sturmnet.org)"
|
||||||
|
code=$(curl -sS -o /dev/null -w '%{http_code}' -A "$UA" \
|
||||||
|
"https://crates.io/api/v1/crates/rdbms-playground/$VER" || echo 000)
|
||||||
|
if [ "$code" = "200" ]; then
|
||||||
|
echo "rdbms-playground $VER is already on crates.io — nothing to do."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "crates.io returned HTTP $code for $VER (not 200) — proceeding to publish."
|
||||||
|
|
||||||
|
echo "publishing rdbms-playground $VER to crates.io ..."
|
||||||
|
nix develop -c cargo publish --locked
|
||||||
|
echo "published rdbms-playground $VER to crates.io."
|
||||||
|
|
||||||
|
# Future manual publication targets go here as independent, idempotent jobs,
|
||||||
|
# e.g.:
|
||||||
|
# scoop-bucket: { ... update the lazyeval Scoop bucket manifest ... }
|
||||||
|
# homebrew-tap: { ... update the lazyeval Homebrew formula ... }
|
||||||
|
# winget: { ... komac submit, or a manual PR helper ... }
|
||||||
|
# No `needs:` between them — each checks the target and skips if already done.
|
||||||
Reference in New Issue
Block a user