Skip to content

pypi-publish

pypi-publish #11

Workflow file for this run

name: pypi-publish
# Publishing is deliberate and manual, and it is the exact sibling of npm-publish.yml: same
# ref-proof contract, same package, other ecosystem. It refuses to run against anything but
# the exact current tip of refs/heads/main, and it checks out the statically trusted
# github.sha rather than an indirect ref.
#
# Authentication is PyPI Trusted Publishing (OIDC) -- no token, nothing to rotate or leak.
# The `pypi-production` environment scopes which GitHub identity PyPI will accept: its
# Trusted Publisher must be registered with owner AetherAI3, repo agent-browser, workflow
# pypi-publish.yml, and environment name "pypi-production" (Manage project -> Publishing on
# pypi.org).
on:
workflow_dispatch:
inputs:
expected_sha:
description: Exact 40-character commit SHA currently at refs/heads/main
required: true
type: string
dry_run:
description: Build and verify without publishing
required: false
default: true
type: boolean
permissions:
contents: read
concurrency:
group: pypi-publish-main
cancel-in-progress: false
jobs:
ref-proof:
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
sha: ${{ steps.proof.outputs.sha }}
steps:
- name: Checkout current main without retaining credentials
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/heads/main
fetch-depth: 1
persist-credentials: false
- name: Prove exact current main commit
id: proof
shell: bash
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
EVENT_REF: ${{ github.ref }}
EVENT_SHA: ${{ github.sha }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
run: |
set -Eeuo pipefail
test "$DEFAULT_BRANCH" = "main"
test "$EVENT_REF" = "refs/heads/main"
test "${#EVENT_SHA}" -eq 40
test "${#EXPECTED_SHA}" -eq 40
case "$EVENT_SHA" in (*[!0-9a-f]*) exit 1 ;; esac
case "$EXPECTED_SHA" in (*[!0-9a-f]*) exit 1 ;; esac
test "$EXPECTED_SHA" = "$EVENT_SHA"
test "$(git rev-parse --verify HEAD^{commit})" = "$EVENT_SHA"
printf 'sha=%s\n' "$EVENT_SHA" >> "$GITHUB_OUTPUT"
publish:
needs: ref-proof
runs-on: ubuntu-24.04
timeout-minutes: 15
environment: pypi-production
permissions:
contents: read
id-token: write # PyPI Trusted Publishing (OIDC)
steps:
- name: Checkout proved commit
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# ref-proof has already asserted that github.sha is the exact refs/heads/main tip
# and equals the supplied expected_sha. Use github.sha here rather than the job
# output: it is the identical commit by construction, but it is a statically
# trusted ref, so a privileged run can never execute an unproved tree. The step
# below still cross-checks HEAD against the proved SHA.
ref: ${{ github.sha }}
persist-credentials: false
- name: Verify exact checkout
shell: bash
env:
TRUSTED_SHA: ${{ needs.ref-proof.outputs.sha }}
run: test "$(git rev-parse --verify HEAD^{commit})" = "$TRUSTED_SHA"
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Refuse runtime dependencies
shell: bash
working-directory: clients/python
run: grep -qx 'dependencies = \[\]' pyproject.toml
- name: Unit tests
working-directory: clients/python
env:
PYTHONPATH: src
run: python -m unittest discover -s tests -v
- name: Build sdist and wheel
id: build
shell: bash
working-directory: clients/python
run: |
set -Eeuo pipefail
python -m pip install --upgrade pip build twine
python -m build
python -m twine check --strict dist/*
version="$(python -c 'import tomllib,pathlib; print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
printf 'version=%s\n' "$version" >> "$GITHUB_OUTPUT"
test -f "dist/aether_browser-${version}.tar.gz"
test -f "dist/aether_browser-${version}-py3-none-any.whl"
sha256sum dist/*
- name: Publish to PyPI
if: ${{ !inputs.dry_run }}
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
packages-dir: clients/python/dist
- name: Dry run summary
if: ${{ inputs.dry_run }}
shell: bash
run: |
set -Eeuo pipefail
echo "Dry run only. Built aether-browser ${{ steps.build.outputs.version }}."
echo "Re-run with dry_run=false to publish."