Skip to content
Latchkey

Publish Python 🐍 distributions πŸ“¦ to PyPI and TestPyPI workflow (graphistry/pygraphistry)

The Publish Python 🐍 distributions πŸ“¦ to PyPI and TestPyPI workflow from graphistry/pygraphistry, explained and optimized by Latchkey.

F

CI health: F - at risk

Point runs-on at Latchkey and get caching, run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: graphistry/pygraphistry.github/workflows/publish-pypi.ymlLicense BSD-3-ClauseView source

What it does

This is the Publish Python 🐍 distributions πŸ“¦ to PyPI and TestPyPI workflow from the graphistry/pygraphistry repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-3-Clause license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Publish Python 🐍 distributions πŸ“¦ to PyPI and TestPyPI

on:
  push:
    tags:
      - '*'
  workflow_dispatch:
    inputs:
      release_mode:
        description: "Release mode: evidence (no publish), test (TestPyPI), or release (TestPyPI + PyPI)"
        required: true
        type: choice
        options:
          - evidence
          - test
          - release
        default: evidence

env:
  UV_EXCLUDE_NEWER: "6 days"
  EXPECTED_REPOSITORY: graphistry/pygraphistry
  EXPECTED_WORKFLOW_PATH: .github/workflows/publish-pypi.yml

jobs:
  build-evidence:
    name: Build release artifacts and evidence
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch')
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      attestations: write

    steps:
      - uses: actions/checkout@v4
        with:  # fetch tag for versioneer
          fetch-depth: 0
          persist-credentials: false

      - name: Verify trusted release trigger
        run: |
          set -euo pipefail
          expected_workflow_ref="${EXPECTED_REPOSITORY}/${EXPECTED_WORKFLOW_PATH}@${GITHUB_REF}"

          if [[ "${GITHUB_REPOSITORY}" != "${EXPECTED_REPOSITORY}" ]]; then
            echo "::error::Unexpected repository ${GITHUB_REPOSITORY}; expected ${EXPECTED_REPOSITORY}."
            exit 1
          fi
          if [[ -z "${GITHUB_WORKFLOW_REF:-}" || "${GITHUB_WORKFLOW_REF}" != "${expected_workflow_ref}" ]]; then
            echo "::error::Unexpected workflow ref ${GITHUB_WORKFLOW_REF:-<unset>}; expected ${expected_workflow_ref}."
            exit 1
          fi

          if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
            tag="${GITHUB_REF#refs/tags/}"
            if [[ ! "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9._-]*)?$ ]]; then
              echo "::error::Tag ${tag} is not a supported release tag format (expected X.Y.Z[extra])."
              exit 1
            fi
            # Refuse tag publishes that do not point to commits in master history.
            git fetch --no-tags origin master
            if ! git merge-base --is-ancestor "${GITHUB_SHA}" "origin/master"; then
              echo "::error::Ref ${GITHUB_REF} (${GITHUB_SHA}) is not in origin/master history; refusing publish."
              exit 1
            fi
          elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
            # Dispatch-specific publish restrictions are enforced at job/step `if` guards.
            true
          else
            echo "::error::Unexpected event ${GITHUB_EVENT_NAME}; refusing publish."
            exit 1
          fi

      - name: Set up Python 3.8
        uses: actions/setup-python@v5
        with:
          python-version: '3.8'

      - name: Normalize version for TestPyPI dispatch publishes
        if: >-
          github.event_name == 'workflow_dispatch' &&
          github.event.inputs.release_mode == 'test'
        run: |
          set -euo pipefail
          synthetic_version="0.0.dev${GITHUB_RUN_ID}"
          git tag -f "${synthetic_version}" "${GITHUB_SHA}"
          echo "Using synthetic TestPyPI version: ${synthetic_version}"
          git describe --tags --dirty --always --long

      - name: Install uv (pinned)
        run: |
          set -euo pipefail
          python -m pip install --disable-pip-version-check --no-deps "uv==0.11.3"

      - name: Install pypa/build
        run: >-
          UV_SYSTEM_PYTHON=1 uv pip install -e .[build]

      - name: Build a binary wheel and a source tarball
        run: >-
          ./bin/build.sh

      - name: Generate SBOM (CycloneDX JSON)
        run: |
          set -euo pipefail
          UV_SYSTEM_PYTHON=1 uv pip install cyclonedx-bom
          mkdir -p evidence
          cyclonedx-py environment --of JSON -o evidence/sbom-cyclonedx.json

      - name: Attest built distributions provenance
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: |
            dist/*.whl
            dist/*.tar.gz

      - name: Upload built distributions
        uses: actions/upload-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: |
            dist/*.whl
            dist/*.tar.gz
          if-no-files-found: error

      - name: Upload release evidence artifacts
        uses: actions/upload-artifact@v4
        with:
          name: release-evidence-${{ github.run_id }}
          path: |
            evidence/sbom-cyclonedx.json
          if-no-files-found: error

  publish-testpypi:
    name: Publish distribution πŸ“¦ to Test PyPI
    needs: build-evidence
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch' &&
       (github.event.inputs.release_mode == 'test' || github.event.inputs.release_mode == 'release'))
    runs-on: ubuntu-latest
    permissions:
      id-token: write

    steps:
      - name: Download release evidence artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: dist

      - name: Verify dist files for TestPyPI publish
        run: |
          set -euo pipefail
          ls -al dist
          ls -1 dist/*.whl dist/*.tar.gz >/dev/null

      - name: Publish distribution πŸ“¦ to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          attestations: true

  publish-pypi:
    name: Publish distribution πŸ“¦ to PyPI
    needs:
      - build-evidence
      - publish-testpypi
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch' &&
       github.event.inputs.release_mode == 'release' &&
       github.ref == 'refs/heads/master')
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    # Configure required reviewers on the `pypi-release` environment in repository settings.
    # This creates a human approval gate before PyPI publish can mint OIDC creds.
    environment:
      name: pypi-release

    steps:
      - name: Download release evidence artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: dist

      - name: Verify dist files for PyPI publish
        run: |
          set -euo pipefail
          ls -al dist
          ls -1 dist/*.whl dist/*.tar.gz >/dev/null

      - name: Publish distribution πŸ“¦ to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          attestations: true

The same workflow, on Latchkey

Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.

name: Publish Python 🐍 distributions πŸ“¦ to PyPI and TestPyPI
 
on:
  push:
    tags:
      - '*'
  workflow_dispatch:
    inputs:
      release_mode:
        description: "Release mode: evidence (no publish), test (TestPyPI), or release (TestPyPI + PyPI)"
        required: true
        type: choice
        options:
          - evidence
          - test
          - release
        default: evidence
 
env:
  UV_EXCLUDE_NEWER: "6 days"
  EXPECTED_REPOSITORY: graphistry/pygraphistry
  EXPECTED_WORKFLOW_PATH: .github/workflows/publish-pypi.yml
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  build-evidence:
    timeout-minutes: 30
    name: Build release artifacts and evidence
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch')
    runs-on: latchkey-small
    permissions:
      contents: read
      id-token: write
      attestations: write
 
    steps:
      - uses: actions/checkout@v4
        with:  # fetch tag for versioneer
          fetch-depth: 0
          persist-credentials: false
 
      - name: Verify trusted release trigger
        run: |
          set -euo pipefail
          expected_workflow_ref="${EXPECTED_REPOSITORY}/${EXPECTED_WORKFLOW_PATH}@${GITHUB_REF}"
 
          if [[ "${GITHUB_REPOSITORY}" != "${EXPECTED_REPOSITORY}" ]]; then
            echo "::error::Unexpected repository ${GITHUB_REPOSITORY}; expected ${EXPECTED_REPOSITORY}."
            exit 1
          fi
          if [[ -z "${GITHUB_WORKFLOW_REF:-}" || "${GITHUB_WORKFLOW_REF}" != "${expected_workflow_ref}" ]]; then
            echo "::error::Unexpected workflow ref ${GITHUB_WORKFLOW_REF:-<unset>}; expected ${expected_workflow_ref}."
            exit 1
          fi
 
          if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
            tag="${GITHUB_REF#refs/tags/}"
            if [[ ! "${tag}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9._-]*)?$ ]]; then
              echo "::error::Tag ${tag} is not a supported release tag format (expected X.Y.Z[extra])."
              exit 1
            fi
            # Refuse tag publishes that do not point to commits in master history.
            git fetch --no-tags origin master
            if ! git merge-base --is-ancestor "${GITHUB_SHA}" "origin/master"; then
              echo "::error::Ref ${GITHUB_REF} (${GITHUB_SHA}) is not in origin/master history; refusing publish."
              exit 1
            fi
          elif [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
            # Dispatch-specific publish restrictions are enforced at job/step `if` guards.
            true
          else
            echo "::error::Unexpected event ${GITHUB_EVENT_NAME}; refusing publish."
            exit 1
          fi
 
      - name: Set up Python 3.8
        uses: actions/setup-python@v5
        with:
          cache: 'pip'
          python-version: '3.8'
 
      - name: Normalize version for TestPyPI dispatch publishes
        if: >-
          github.event_name == 'workflow_dispatch' &&
          github.event.inputs.release_mode == 'test'
        run: |
          set -euo pipefail
          synthetic_version="0.0.dev${GITHUB_RUN_ID}"
          git tag -f "${synthetic_version}" "${GITHUB_SHA}"
          echo "Using synthetic TestPyPI version: ${synthetic_version}"
          git describe --tags --dirty --always --long
 
      - name: Install uv (pinned)
        run: |
          set -euo pipefail
          python -m pip install --disable-pip-version-check --no-deps "uv==0.11.3"
 
      - name: Install pypa/build
        run: >-
          UV_SYSTEM_PYTHON=1 uv pip install -e .[build]
 
      - name: Build a binary wheel and a source tarball
        run: >-
          ./bin/build.sh
 
      - name: Generate SBOM (CycloneDX JSON)
        run: |
          set -euo pipefail
          UV_SYSTEM_PYTHON=1 uv pip install cyclonedx-bom
          mkdir -p evidence
          cyclonedx-py environment --of JSON -o evidence/sbom-cyclonedx.json
 
      - name: Attest built distributions provenance
        uses: actions/attest-build-provenance@v2
        with:
          subject-path: |
            dist/*.whl
            dist/*.tar.gz
 
      - name: Upload built distributions
        uses: actions/upload-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: |
            dist/*.whl
            dist/*.tar.gz
          if-no-files-found: error
 
      - name: Upload release evidence artifacts
        uses: actions/upload-artifact@v4
        with:
          name: release-evidence-${{ github.run_id }}
          path: |
            evidence/sbom-cyclonedx.json
          if-no-files-found: error
 
  publish-testpypi:
    timeout-minutes: 30
    name: Publish distribution πŸ“¦ to Test PyPI
    needs: build-evidence
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch' &&
       (github.event.inputs.release_mode == 'test' || github.event.inputs.release_mode == 'release'))
    runs-on: latchkey-small
    permissions:
      id-token: write
 
    steps:
      - name: Download release evidence artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: dist
 
      - name: Verify dist files for TestPyPI publish
        run: |
          set -euo pipefail
          ls -al dist
          ls -1 dist/*.whl dist/*.tar.gz >/dev/null
 
      - name: Publish distribution πŸ“¦ to Test PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          attestations: true
 
  publish-pypi:
    timeout-minutes: 30
    name: Publish distribution πŸ“¦ to PyPI
    needs:
      - build-evidence
      - publish-testpypi
    if: >-
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) ||
      (github.event_name == 'workflow_dispatch' &&
       github.event.inputs.release_mode == 'release' &&
       github.ref == 'refs/heads/master')
    runs-on: latchkey-small
    permissions:
      id-token: write
    # Configure required reviewers on the `pypi-release` environment in repository settings.
    # This creates a human approval gate before PyPI publish can mint OIDC creds.
    environment:
      name: pypi-release
 
    steps:
      - name: Download release evidence artifacts
        uses: actions/download-artifact@v4
        with:
          name: release-dists-${{ github.run_id }}
          path: dist
 
      - name: Verify dist files for PyPI publish
        run: |
          set -euo pipefail
          ls -al dist
          ls -1 dist/*.whl dist/*.tar.gz >/dev/null
 
      - name: Publish distribution πŸ“¦ to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          attestations: true
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 3 jobs per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow