Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

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/