Files
MeshTerm/.github/workflows/release.yml
T
Jean-Pierre MartineauandClaude Opus 5 59eccba0a0 Every build runs on 3.14
JP builds with 3.14 now, so the workflows do too. The lint job moves from 3.10, the
packaging build job and the release's site rebuild and PyPI build from 3.12, and the
installers from 3.10. The test matrix is untouched: it still runs every version from
3.10 up, because 3.10 is still the floor.

The installers' comment said the oldest Python was chosen on purpose, for reach. The
reach that matters on Linux comes from glibc, and that comes from the runner, so the
Linux legs stay on ubuntu-22.04 and the comment now says that. The binaries carry 3.14
from the next release; 0.9.0's were built on 3.10.

The changelog gains the 3.14 entry JP wrote in another session, plus a line for the
downloads.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 21:14:52 -04:00

100 lines
3.3 KiB
YAML

# Publishing to PyPI. Deliberately hard to fire.
#
# There is no `push: tags` trigger here, and that absence is the point. A release
# workflow that publishes on a `v*` tag would mean tagging v0.9.0 on launch day
# silently claims a distribution name on a public index, permanently, as a side
# effect of an unrelated action. PyPI has no rename and no true delete, so that
# side effect is not one you can take back.
#
# So: a human runs this, from the Actions tab, on purpose, and types the version
# to prove they meant this version. Three gates before anything leaves the runner:
#
# 1. no automatic trigger at all
# 2. the typed version must equal the version actually built
# 3. the `pypi` environment, which can require a reviewer in repo settings
#
# It publishes whatever version is in the tree. There is no version argument, so
# this cannot publish something other than what is committed.
name: Release
on:
workflow_dispatch:
inputs:
confirm_version:
description: "Version to publish, exactly (e.g. 0.9.0). A mismatch aborts."
required: true
type: string
permissions:
contents: read
jobs:
build:
name: build and check
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.14"
- run: pip install build twine
- run: python -m build
# Renders the README the way PyPI will, and fails on metadata PyPI would reject.
- run: twine check dist/*
# The version the build actually produced, read off the artifact rather than
# trusted from anywhere else.
- id: built
shell: bash
run: |
version=$(ls dist/*.tar.gz | sed -E 's/.*-([0-9][^-]*)\.tar\.gz/\1/')
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "built version: $version"
- name: Confirm the typed version matches what was built
shell: bash
env:
TYPED: ${{ inputs.confirm_version }}
BUILT: ${{ steps.built.outputs.version }}
run: |
if [ "$TYPED" != "$BUILT" ]; then
echo "::error::You typed '$TYPED' but the tree builds '$BUILT'. Nothing published."
exit 1
fi
echo "confirmed: $BUILT"
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
publish:
name: publish to PyPI
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
# Configure this environment in repo settings with a required reviewer, and it
# becomes a fourth gate: the job waits for an approval before it runs. The name
# must also match the environment set on the PyPI trusted publisher, or the OIDC
# exchange fails with an unhelpful error. If the publisher has no environment
# configured, delete this line.
environment:
name: pypi
url: https://pypi.org/p/mesh-term
permissions:
# Trusted Publishing mints a short-lived OIDC token instead of storing an API
# token in the repository. This is the only thing that needs write here.
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1