Skip to content

Release to PyPI

Release to PyPI #38

Workflow file for this run

name: Release to PyPI
on:
release:
types: [published]
workflow_dispatch:
inputs:
test_pypi_only:
description: 'Publish to TestPyPI only'
required: false
type: boolean
default: false
test_docker:
description: 'Build & push the Docker image (tagged :test) without running PyPI publish — use to verify Docker Hub / GHCR credentials'
required: false
type: boolean
default: false
permissions:
contents: read
jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Build package
run: uv build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
publish-to-testpypi:
name: Publish to TestPyPI
if: github.event_name == 'workflow_dispatch' && inputs.test_pypi_only
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/cocoindex-code
permissions:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
publish-to-pypi:
name: Publish to PyPI
if: github.event_name == 'release'
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/cocoindex-code
permissions:
id-token: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
github-release:
name: Upload distribution to GitHub Release
needs:
- publish-to-pypi
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/
- name: Upload distributions to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload
'${{ github.ref_name }}' dist/*
--repo '${{ github.repository }}'
publish-docker:
name: Build & push Docker image (${{ matrix.variant }})
# Runs on real releases, and on manual dispatch with `test_docker=true`
# for verifying registry credentials before the first release.
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.test_docker)
runs-on: ubuntu-latest
environment:
name: docker-hub
url: https://hub.docker.com/r/cocoindex/cocoindex-code
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
# slim (default) — LiteLLM-only, ~300 MB. Publishes as `:latest`.
- variant: slim
install_spec: /ccc-src
# full — bundles sentence-transformers + torch + baked model,
# ~2 GB. Publishes as `:full`.
- variant: full
install_spec: /ccc-src[full]
steps:
- uses: actions/checkout@v4
# QEMU lets buildx cross-compile linux/arm64 on the x86_64 runner.
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Compute image tags
id: tags
# Tag scheme:
# slim on release: :latest, :<version>
# full on release: :full, :<version>-full
# slim on dispatch: :test
# full on dispatch: :test-full
# Dispatched tags stay out of the `:latest` / `:<version>` namespace
# so manual test runs don't clobber what users pull.
run: |
variant="${{ matrix.variant }}"
if [ "$variant" = "slim" ]; then
slim_suffix=""
else
slim_suffix="-$variant"
fi
if [ "${{ github.event_name }}" = "release" ]; then
version="${{ github.ref_name }}"
if [ "$variant" = "slim" ]; then
latest_tag="latest"
else
latest_tag="$variant"
fi
{
echo "tags<<EOF"
echo "cocoindex/cocoindex-code:${latest_tag}"
echo "cocoindex/cocoindex-code:${version}${slim_suffix}"
echo "ghcr.io/cocoindex-io/cocoindex-code:${latest_tag}"
echo "ghcr.io/cocoindex-io/cocoindex-code:${version}${slim_suffix}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
test_tag="test${slim_suffix}"
{
echo "tags<<EOF"
echo "cocoindex/cocoindex-code:${test_tag}"
echo "ghcr.io/cocoindex-io/cocoindex-code:${test_tag}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi
- name: Build and push to both registries
uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
push: true
# Native amd64 for Linux servers + Intel Macs; arm64 (cross-compiled
# via QEMU) for Apple Silicon Macs and arm64 Linux hosts.
platforms: linux/amd64,linux/arm64
# Install cocoindex-code from the checked-out source tree, not PyPI.
# Avoids a race where a just-published version hasn't propagated to
# PyPI's CDN yet (which happened on v0.2.24 release), and ensures
# the image matches the tagged commit byte-for-byte.
build-args: |
CCC_VARIANT=${{ matrix.variant }}
CCC_INSTALL_SPEC=${{ matrix.install_spec }}
tags: ${{ steps.tags.outputs.tags }}
# Per-variant BuildKit cache so slim and full don't evict each
# other's layers. The heavy `deps` layer (torch + friends for
# full; empty for slim) reuses across releases.
cache-from: type=gha,scope=${{ matrix.variant }}
cache-to: type=gha,mode=max,scope=${{ matrix.variant }}