2026-09-18 12:03:43 -04:00
|
|
|
name: release
|
|
|
|
|
|
|
|
|
|
# Tag-driven. Cargo.toml carries the version; the tag only points at it, and the
|
2026-09-18 12:50:56 -04:00
|
|
|
# first step refuses to go on if the two disagree — a release tagged v0.3.0 that
|
|
|
|
|
# publishes 0.2.0 cannot be undone, because PyPI will not let a version be reused
|
|
|
|
|
# even after it is yanked.
|
2026-09-18 12:03:43 -04:00
|
|
|
on:
|
|
|
|
|
push:
|
|
|
|
|
tags: ['v*']
|
|
|
|
|
|
2026-09-18 12:50:56 -04:00
|
|
|
# One job on purpose. Splitting build from publish would mean passing wheels
|
|
|
|
|
# between jobs through actions/upload-artifact, which is a JavaScript action: the
|
|
|
|
|
# runner executes it with `node` inside this container, and a Rust image has none.
|
|
|
|
|
# Everything a release needs fits in one job, so nothing has to be handed over.
|
2026-09-18 12:03:43 -04:00
|
|
|
jobs:
|
2026-09-18 12:50:56 -04:00
|
|
|
publish:
|
2026-09-18 12:03:43 -04:00
|
|
|
runs-on: docker
|
|
|
|
|
container:
|
|
|
|
|
image: rust:1.98-bookworm
|
|
|
|
|
steps:
|
2026-09-18 12:50:56 -04:00
|
|
|
- name: Checkout
|
|
|
|
|
run: |
|
|
|
|
|
git init -q .
|
|
|
|
|
git remote add origin "$GITHUB_SERVER_URL/$GITHUB_REPOSITORY"
|
|
|
|
|
git fetch -q --depth 1 origin "$GITHUB_SHA"
|
|
|
|
|
git checkout -q FETCH_HEAD
|
|
|
|
|
|
2026-09-18 12:03:43 -04:00
|
|
|
- name: Tag must match Cargo.toml
|
|
|
|
|
run: |
|
|
|
|
|
tag="${GITHUB_REF_NAME#v}"
|
|
|
|
|
crate=$(grep -m1 '^version = ' Cargo.toml | cut -d'"' -f2)
|
|
|
|
|
echo "tag=$tag crate=$crate"
|
|
|
|
|
[ "$tag" = "$crate" ] || { echo "::error::tag $tag != Cargo.toml $crate"; exit 1; }
|
2026-09-18 12:50:56 -04:00
|
|
|
|
2026-09-18 12:03:43 -04:00
|
|
|
- name: Tests
|
|
|
|
|
run: cargo test
|
|
|
|
|
|
2026-09-18 12:50:56 -04:00
|
|
|
- name: Build wheels and sdist
|
2026-09-18 12:03:43 -04:00
|
|
|
run: |
|
|
|
|
|
apt-get update && apt-get install -y --no-install-recommends python3 python3-venv
|
|
|
|
|
python3 -m venv /tmp/venv
|
2026-09-18 12:50:56 -04:00
|
|
|
/tmp/venv/bin/pip install --quiet 'maturin[zig]' twine
|
|
|
|
|
# --zig cross-compiles against an older glibc than this image ships, so
|
|
|
|
|
# the wheel is not silently pinned to bookworm's.
|
|
|
|
|
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
|
|
|
|
|
rustup target add "$target"
|
|
|
|
|
/tmp/venv/bin/maturin build --release --zig --target "$target" --out dist
|
|
|
|
|
done
|
2026-09-18 12:03:43 -04:00
|
|
|
/tmp/venv/bin/maturin sdist --out dist
|
2026-09-18 12:50:56 -04:00
|
|
|
ls -l dist
|
2026-09-18 12:03:43 -04:00
|
|
|
|
|
|
|
|
- name: Publish
|
|
|
|
|
env:
|
2026-09-18 12:50:56 -04:00
|
|
|
# A project-scoped token, not the account-wide one. PyPI's trusted
|
2026-09-18 12:03:43 -04:00
|
|
|
# publishing is OIDC and supports GitHub, GitLab, Google Cloud Build and
|
|
|
|
|
# ActiveState — not Forgejo — so this forge has to hold a long-lived
|
|
|
|
|
# credential. Scoping it to this one project is the whole of the
|
2026-09-18 12:50:56 -04:00
|
|
|
# mitigation available: if the forge is compromised, what is lost is the
|
|
|
|
|
# ability to publish typikon rather than every package the account owns.
|
2026-09-18 12:03:43 -04:00
|
|
|
TWINE_USERNAME: __token__
|
|
|
|
|
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
|
|
|
|
run: |
|
2026-09-18 12:50:56 -04:00
|
|
|
/tmp/venv/bin/twine check dist/*
|
|
|
|
|
/tmp/venv/bin/twine upload --non-interactive dist/*
|