Skip to content
Latchkey

Upload Python Package workflow (PINTO0309/onnx2tf)

The Upload Python Package workflow from PINTO0309/onnx2tf, explained and optimized by Latchkey.

A

CI health: A - excellent

Point runs-on at Latchkey and get job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: PINTO0309/onnx2tf.github/workflows/python-publish.ymlLicense MITView source

What it does

This is the Upload Python Package workflow from the PINTO0309/onnx2tf repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

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

The workflow

workflow (.yml)
name: Upload Python Package

on:
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  DOCKER_REGISTRY: docker.io
  DOCKER_REGISTRY_USER: pinto0309
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-python-dist:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
    - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
    - name: Install uv
      uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
      with:
        python-version: "3.12"
    - name: Build
      run: uv build
    - name: Upload Python distributions
      uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
      with:
        name: python-distributions
        path: dist/*
        if-no-files-found: error
        retention-days: 1

  pypi-publish:
    needs: build-python-dist
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    environment:
      name: pypi
      url: https://pypi.org/p/onnx2tf
    steps:
    - name: Download Python distributions
      uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
      with:
        name: python-distributions
        path: dist/
    - name: Publish a Python distribution to PyPI
      uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
      with:
        packages-dir: dist/

  docker-deploy:
    needs: pypi-publish
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false  # do not cancel even if any platform fails.
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
    runs-on: ${{ matrix.runner }}
    steps:
      - name: Git checkout
        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
      - name: Enable buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd

      - name: Prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

      - name: Repository to lowercase
        id: lower-repo-1
        run: |
          echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT

      # For GitHub Container Registry
      - name: Log in to GHCR
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ github.token }}
      - name: Extract metadata (tags, labels) for GHCR
        id: meta_gh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }}
      - name: Build and push by digest to GHCR
        id: build_gh
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
        with:
          context: .
          platforms: ${{ matrix.platform }}
          build-args: BUILD_ARCH=${{ matrix.platform }}
          push: true
          labels: ${{ steps.meta_gh.outputs.labels }}
          outputs: type=image,name=${{ env.REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }},push-by-digest=true,name-canonical=true,push=true
          cache-from: type=gha
          cache-to: type=gha,mode=max

      # For Docker Hub
      - name: Log in to Docker Hub
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.DOCKER_REGISTRY }}
          username: ${{ env.DOCKER_REGISTRY_USER }}
          password: ${{ secrets.DH_ACCESS_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker Hub
        id: meta_dh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }}
      - name: Build and push by digest to Docker Hub
        id: build_dh
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
        with:
          context: .
          platforms: ${{ matrix.platform }}
          build-args: BUILD_ARCH=${{ matrix.platform }}
          push: true
          labels: ${{ steps.meta_gh.outputs.labels }}
          outputs: type=image,name=${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }},push-by-digest=true,name-canonical=true,push=true
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Export digest
        run: |
          mkdir -p /tmp/digests/gh
          digest="${{ steps.build_gh.outputs.digest }}"
          touch "/tmp/digests/gh/${digest#sha256:}"
          mkdir -p /tmp/digests/dh
          digest="${{ steps.build_dh.outputs.digest }}"
          touch "/tmp/digests/dh/${digest#sha256:}"

      - name: Upload digest
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
        with:
          name: digests-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    runs-on: ubuntu-latest
    needs:
      - docker-deploy
    permissions:
      contents: read
      packages: write
    steps:
      - name: Download digests
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd

      - name: Repository to lowercase
        id: lower-repo-2
        run: |
          echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT

      # For GitHub Container Registry
      - name: Log in to GHCR
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ github.token }}
      - name: Extract metadata (tags, labels) for GHCR
        id: meta_gh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}
      - name: Create manifest list and push to GHCR
        working-directory: /tmp/digests/gh
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
          $(printf '${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}@sha256:%s ' *)
      - name: Inspect image on GHCR
        run: |
          docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}:${{ steps.meta_gh.outputs.version }}

      # For Docker Hub
      - name: Log in to the Docker Hub
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.DOCKER_REGISTRY }}
          username: ${{ env.DOCKER_REGISTRY_USER }}
          password: ${{ secrets.DH_ACCESS_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker Hub
        id: meta_dh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}
      - name: Create manifest list and push to Docker Hub
        working-directory: /tmp/digests/dh
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
          $(printf '${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}@sha256:%s ' *)
      - name: Inspect image on Docker Hub
        run: |
          docker buildx imagetools inspect ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}:${{ steps.meta_dh.outputs.version }}

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Upload Python Package
 
on:
  release:
    types: [published]
 
env:
  REGISTRY: ghcr.io
  DOCKER_REGISTRY: docker.io
  DOCKER_REGISTRY_USER: pinto0309
  IMAGE_NAME: ${{ github.repository }}
 
jobs:
  build-python-dist:
    timeout-minutes: 30
    runs-on: latchkey-small
    permissions:
      contents: read
    steps:
    - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
    - name: Install uv
      uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78
      with:
        python-version: "3.12"
    - name: Build
      run: uv build
    - name: Upload Python distributions
      uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
      with:
        name: python-distributions
        path: dist/*
        if-no-files-found: error
        retention-days: 1
 
  pypi-publish:
    timeout-minutes: 30
    needs: build-python-dist
    runs-on: latchkey-small
    permissions:
      id-token: write
    environment:
      name: pypi
      url: https://pypi.org/p/onnx2tf
    steps:
    - name: Download Python distributions
      uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
      with:
        name: python-distributions
        path: dist/
    - name: Publish a Python distribution to PyPI
      uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
      with:
        packages-dir: dist/
 
  docker-deploy:
    timeout-minutes: 30
    needs: pypi-publish
    permissions:
      contents: read
      packages: write
    strategy:
      fail-fast: false  # do not cancel even if any platform fails.
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
    runs-on: ${{ matrix.runner }}
    steps:
      - name: Git checkout
        uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd
      - name: Enable buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd
 
      - name: Prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
 
      - name: Repository to lowercase
        id: lower-repo-1
        run: |
          echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
 
      # For GitHub Container Registry
      - name: Log in to GHCR
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ github.token }}
      - name: Extract metadata (tags, labels) for GHCR
        id: meta_gh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }}
      - name: Build and push by digest to GHCR
        id: build_gh
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
        with:
          context: .
          platforms: ${{ matrix.platform }}
          build-args: BUILD_ARCH=${{ matrix.platform }}
          push: true
          labels: ${{ steps.meta_gh.outputs.labels }}
          outputs: type=image,name=${{ env.REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }},push-by-digest=true,name-canonical=true,push=true
          cache-from: type=gha
          cache-to: type=gha,mode=max
 
      # For Docker Hub
      - name: Log in to Docker Hub
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.DOCKER_REGISTRY }}
          username: ${{ env.DOCKER_REGISTRY_USER }}
          password: ${{ secrets.DH_ACCESS_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker Hub
        id: meta_dh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }}
      - name: Build and push by digest to Docker Hub
        id: build_dh
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294
        with:
          context: .
          platforms: ${{ matrix.platform }}
          build-args: BUILD_ARCH=${{ matrix.platform }}
          push: true
          labels: ${{ steps.meta_gh.outputs.labels }}
          outputs: type=image,name=${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-1.outputs.repository }},push-by-digest=true,name-canonical=true,push=true
          cache-from: type=gha
          cache-to: type=gha,mode=max
 
      - name: Export digest
        run: |
          mkdir -p /tmp/digests/gh
          digest="${{ steps.build_gh.outputs.digest }}"
          touch "/tmp/digests/gh/${digest#sha256:}"
          mkdir -p /tmp/digests/dh
          digest="${{ steps.build_dh.outputs.digest }}"
          touch "/tmp/digests/dh/${digest#sha256:}"
 
      - name: Upload digest
        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
        with:
          name: digests-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1
 
  merge:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs:
      - docker-deploy
    permissions:
      contents: read
      packages: write
    steps:
      - name: Download digests
        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true
 
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd
 
      - name: Repository to lowercase
        id: lower-repo-2
        run: |
          echo "repository=${GITHUB_REPOSITORY@L}" >> $GITHUB_OUTPUT
 
      # For GitHub Container Registry
      - name: Log in to GHCR
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ github.token }}
      - name: Extract metadata (tags, labels) for GHCR
        id: meta_gh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}
      - name: Create manifest list and push to GHCR
        working-directory: /tmp/digests/gh
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
          $(printf '${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}@sha256:%s ' *)
      - name: Inspect image on GHCR
        run: |
          docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}:${{ steps.meta_gh.outputs.version }}
 
      # For Docker Hub
      - name: Log in to the Docker Hub
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
        with:
          registry: ${{ env.DOCKER_REGISTRY }}
          username: ${{ env.DOCKER_REGISTRY_USER }}
          password: ${{ secrets.DH_ACCESS_TOKEN }}
      - name: Extract metadata (tags, labels) for Docker Hub
        id: meta_dh
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
        with:
          images: ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}
      - name: Create manifest list and push to Docker Hub
        working-directory: /tmp/digests/dh
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
          $(printf '${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}@sha256:%s ' *)
      - name: Inspect image on Docker Hub
        run: |
          docker buildx imagetools inspect ${{ env.DOCKER_REGISTRY }}/${{ steps.lower-repo-2.outputs.repository }}:${{ steps.meta_dh.outputs.version }}
 

What changed

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 4 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