publish/github/.github/workflows/pypi.yml
# Publish facett + facett-demo to PyPI with OIDC TRUSTED PUBLISHING.
#
# This workflow BUILDS NOTHING. The wheels (and facett-demo's sdist) are built on the
# owner's build box by facett's build thing, in the pinned `nordisk-pip-toolchain`
# container with the network off (`cargo ops publish-pip`), verified there against
# their own RECORD and against what PyPI will check (`cargo ops publish-pypi
# --dry-run`), and attached to a GitHub release of THIS repository as assets together
# with their SHA256SUMS files. This job downloads those assets, checks them against
# the SHA256SUMS that came with them, and hands them to PyPI's official publish
# action. GitHub's runner never compiles, packages or rewrites a byte (the law: no
# host builds).
#
# Kept in the facett repo at publish/github/.github/workflows/pypi.yml. To run, it must
# sit at the ROOT .github/workflows/pypi.yml of the GitHub mirror Ignalina/facett —
# and because ~/mirror-ignalina/mirror-to-github.sh force-pushes every branch from
# Codeberg, it gets there by being committed at the root on Codeberg main (an owner
# step; Codeberg has Actions disabled for this repo, so it is inert there).
# See publish/github/README.md and .nornir/pypi-publish.md.
#
# Triggers:
# * a GitHub release is RELEASED (published, not a pre-release) -> pypi.org
# (environment `pypi`; protect it with a required reviewer so a release is a decision)
# * Actions -> pypi -> Run workflow -> choose the tag and `testpypi` or `pypi`
# (rehearse on a PRE-release: it does not trigger the `released` event)
name: pypi
on:
release:
types: [released]
workflow_dispatch:
inputs:
tag:
description: "Release tag whose assets to publish (e.g. v0.2.0)"
required: true
index:
description: "Where to publish"
required: true
default: testpypi
type: choice
options: [testpypi, pypi]
# Nothing by default; the job asks for exactly what it needs.
permissions: {}
jobs:
publish:
# Only on GitHub: the file is inert anywhere else (Codeberg's Forgejo would read a
# root .github/workflows/ only if Actions were enabled on the repo, and they are not).
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
# The environment NAME is part of the trusted-publisher identity PyPI checks
# (pending publisher: owner `Ignalina`, repo `facett`, workflow `pypi.yml`, environment).
environment:
name: ${{ (github.event_name == 'release' || inputs.index == 'pypi') && 'pypi' || 'testpypi' }}
permissions:
id-token: write # the OIDC token PyPI exchanges for a short-lived upload token
contents: read # to download this repository's release assets
env:
TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.tag }}
INDEX: ${{ (github.event_name == 'release' || inputs.index == 'pypi') && 'pypi' || 'testpypi' }}
steps:
- name: Download the already-built release assets (nothing is built here)
env:
GH_TOKEN: ${{ github.token }}
run: |
set -eu
mkdir -p dist sums
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --dir dist \
--pattern '*.whl' --pattern '*.tar.gz' --pattern 'SHA256SUMS*'
mv dist/SHA256SUMS* sums/
ls -l dist sums
- name: Check every file against the SHA256SUMS built beside it
run: |
set -eu
cd dist
# Each release's SHA256SUMS lists its own files by bare name.
for s in ../sums/SHA256SUMS*; do sha256sum -c "$s"; done
# Every file to publish must be listed in some SHA256SUMS: nothing unchecked goes up.
for f in *; do
grep -q " $f\$" ../sums/SHA256SUMS* || { echo "::error::$f is not in any SHA256SUMS"; exit 1; }
done
- name: Refuse anything that is not the two facett releases
run: |
set -eu
cd dist
for f in *; do
case "$f" in
facett-[0-9]*-py3-none-any.whl|facett_demo-[0-9]*-py3-none-any.whl|facett_demo-[0-9]*.tar.gz) ;;
*) echo "::error::unexpected file $f"; exit 1 ;;
esac
done
# The demo pins the components' release line: never publish it alone.
ls facett-[0-9]*-py3-none-any.whl facett_demo-[0-9]*-py3-none-any.whl >/dev/null
- name: Publish to TestPyPI
if: env.INDEX == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/
repository-url: https://test.pypi.org/legacy/
- name: Publish to PyPI
if: env.INDEX == 'pypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist/