Files
2026-07-13 12:24:33 +08:00

532 lines
22 KiB
YAML

name: Build and Publish to PyPI
on:
# Trigger the workflow on push to the dev and any release branches,
# and on release tag pushes / GitHub release publication.
# Per-PR verification lives in test.yml, code_quality_checks.yml, and
# pr_full_build.yml — this workflow is the release pipeline.
push:
branches:
- dev
- "release-**"
tags:
- "v*"
release:
types:
- published
env:
LC_ALL: en_US.UTF-8
# Cancel superseded runs on the same ref, but never abort a tag/release publish.
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') && github.event_name != 'release' }}
defaults:
run:
shell: bash
permissions:
contents: read
pull-requests: read
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
# Skip the whole pipeline for operator releases (tags like `operator-v*`).
# operator_release.yml handles those; this workflow is lmcache-only.
lmcache: ${{ !startsWith(github.ref_name, 'operator-') && (github.event_name == 'release' || startsWith(github.ref, 'refs/tags/') || steps.filter.outputs.lmcache == 'true') }}
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
with:
fetch-depth: 2
- uses: dorny/paths-filter@v3
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
id: filter
with:
filters: |
lmcache:
- '**.py'
- 'pyproject.toml'
- 'pyproject_cli.toml'
- 'requirements/**.txt'
- 'docker/**'
- '.github/workflows/test.yml'
- '.github/workflows/code_quality_checks.yml'
- '.github/workflows/publish.yml'
- '.github/workflows/build_main_artifacts.yml'
- '.github/workflows/build_cli_artifacts.yml'
- '.github/workflows/build_cu129_artifacts.yml'
- '!operator/**'
# Build each artifact group in its own reusable workflow so that a
# failure in one (e.g. cu129) does not block publishing of the others.
build-main:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build main
uses: ./.github/workflows/build_main_artifacts.yml
build-cli:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build CLI
uses: ./.github/workflows/build_cli_artifacts.yml
build-cu129:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Build cu129
uses: ./.github/workflows/build_cu129_artifacts.yml
# DockerHub creds needed for the cu12.9 manylinux image pull;
# reusable workflows do not auto-inherit secrets.
secrets: inherit
# Run tests and code quality checks before publishing
test:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Run tests
uses: ./.github/workflows/test.yml
code-quality:
needs: changes
if: needs.changes.outputs.lmcache == 'true'
name: Run code quality checks
uses: ./.github/workflows/code_quality_checks.yml
# Push to Test PyPI when:
# - a new GitHub release is published
# - a commit lands on dev (PR merged)
publish-test-pypi:
name: Publish packages to test.pypi.org
# Require upstream jobs to have succeeded so a cancelled/skipped build
# doesn't trigger a publish that fails on the missing artifact.
if: |
needs.build-main.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' &&
github.event_name == 'push' && github.ref == 'refs/heads/dev'
permissions:
contents: read
# see https://docs.pypi.org/trusted-publishers/
id-token: write
runs-on: ubuntu-latest
needs: [changes, build-main, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-artifacts
path: dist
- name: Upload to Test PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
publish-cli-test-pypi:
name: Publish lmcache-cli packages to test.pypi.org
if: |
needs.build-cli.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' &&
github.event_name == 'push' && github.ref == 'refs/heads/dev'
permissions:
contents: read
id-token: write
runs-on: ubuntu-latest
needs: [changes, build-cli, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch CLI release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cli-artifacts
path: dist
- name: Upload lmcache-cli to Test PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
repository-url: https://test.pypi.org/legacy/
verbose: true
# Push to PyPI (production) when:
# - a new GitHub release is created
publish-pypi:
name: Publish release to pypi.org
if: |
needs.build-main.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
# see https://docs.pypi.org/trusted-publishers/
id-token: write
# allow gh release upload
contents: write
runs-on: ubuntu-latest
needs: [changes, build-main, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-artifacts
path: dist
- name: Upload release artifacts to GitHub release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload '${{ github.ref_name }}' dist/* --repo '${{ github.repository }}'
- name: Upload to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
verbose: true
publish-cli-pypi:
name: Publish lmcache-cli release to pypi.org
if: |
needs.build-cli.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
id-token: write
contents: write
runs-on: ubuntu-latest
needs: [changes, build-cli, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch CLI release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cli-artifacts
path: dist
- name: Upload lmcache-cli release artifacts to GitHub release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release upload '${{ github.ref_name }}' dist/* --repo '${{ github.repository }}'
- name: Upload lmcache-cli to PyPI
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
verbose: true
# Publish cu12.9 wheel to GitHub Release (not PyPI) when a release is published
publish-cu129-github-release:
name: Publish cu129 wheel to GitHub Release
if: |
needs.build-cu129.result == 'success' &&
needs.test.result == 'success' &&
needs.code-quality.result == 'success' &&
needs.changes.outputs.lmcache == 'true' &&
github.repository_owner == 'LMCache' && github.event.action == 'published'
permissions:
contents: write
runs-on: ubuntu-latest
needs: [changes, build-cu129, test, code-quality]
steps:
- name: Harden Runner
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Fetch cu12.9 release artifacts
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: release-cu129-artifacts
path: dist
- name: Publish cu12.9 wheel to dedicated GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
gh release create "${{ github.ref_name }}-cu129" \
--repo ${{ github.repository }} \
--latest=false \
--title "Release ${{ github.ref_name }} · CUDA 12.9" \
--notes "CUDA 12.9 wheel for LMCache ${{ github.ref_name }}.
\`\`\`
VERSION=${{ github.ref_name }}
uv pip install lmcache==${VERSION#v} \\
--extra-index-url https://download.pytorch.org/whl/cu129 \\
--find-links https://github.com/${{ github.repository }}/releases/expanded_assets/${VERSION}-cu129 \\
--index-strategy unsafe-best-match
\`\`\`" \
dist/*.whl
# Build container image and push to DockerHub when:
# - a new stable GitHub release is created (non-RC)
publish-image:
name: Publish image to DockerHub
if: |
needs.publish-pypi.result == 'success' &&
!cancelled() &&
github.repository_owner == 'LMCache' && github.event.action == 'published' &&
!contains(github.ref_name, 'rc')
runs-on: ubuntu-latest
needs: [publish-pypi, publish-cu129-github-release]
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# for setuptools-scm
fetch-depth: 0
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Resolve the release tag that triggered this run
run: |
echo "LATEST_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV"
- name: Build lmcache/vllm-openai container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_TAG }} \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target image-release \
--tag lmcache/vllm-openai:latest --tag lmcache/vllm-openai:${{ env.LATEST_TAG }} \
--file docker/Dockerfile .
# `lmcache --help` exercises eager CLI subcommand discovery,
# so a missing runtime dep fails the build before the image ships.
- name: Smoke test lmcache/vllm-openai (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }} --help
- name: Push lmcache/vllm-openai container image to DockerHub
run: |
docker push lmcache/vllm-openai:latest
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/vllm-openai:lightweight
run: |
docker build \
--tag lmcache/vllm-openai:lightweight --tag lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight \
--file docker/Dockerfile.lightweight .
- name: Smoke test lmcache/vllm-openai:lightweight
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight --help
- name: Push lmcache/vllm-openai:lightweight image to DockerHub
run: |
docker push lmcache/vllm-openai:lightweight
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}-lightweight
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/standalone container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target lmcache-final \
--tag lmcache/standalone:latest --tag lmcache/standalone:${{ env.LATEST_TAG }} \
--tag lmcache/standalone:latest-cu130 --tag lmcache/standalone:${{ env.LATEST_TAG }}-cu130 \
--file docker/Dockerfile.standalone .
- name: Smoke test lmcache/standalone (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/standalone:${{ env.LATEST_TAG }} --help
- name: Push lmcache/standalone container image to DockerHub
run: |
docker push lmcache/standalone:latest
docker push lmcache/standalone:${{ env.LATEST_TAG }}
docker push lmcache/standalone:latest-cu130
docker push lmcache/standalone:${{ env.LATEST_TAG }}-cu130
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/vllm-openai container image (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_TAG }} \
--target image-release-cu129 \
--tag lmcache/vllm-openai:latest-cu129 --tag lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129 \
--file docker/Dockerfile .
- name: Smoke test lmcache/vllm-openai (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129 --help
- name: Push lmcache/vllm-openai cu129 container image to DockerHub
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker push lmcache/vllm-openai:latest-cu129
docker push lmcache/vllm-openai:${{ env.LATEST_TAG }}-cu129
- name: Clean up Docker layers and cache
run: |
docker system prune -f --all
docker builder prune -af
- name: Build lmcache/standalone container image (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.05-cuda12.9-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=12.9 \
--target lmcache-final \
--tag lmcache/standalone:latest-cu129 --tag lmcache/standalone:${{ env.LATEST_TAG }}-cu129 \
--file docker/Dockerfile.standalone .
- name: Smoke test lmcache/standalone (cu12.9)
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker run --rm --entrypoint lmcache lmcache/standalone:${{ env.LATEST_TAG }}-cu129 --help
- name: Push lmcache/standalone cu129 container image to DockerHub
if: needs.publish-cu129-github-release.result == 'success'
run: |
docker push lmcache/standalone:latest-cu129
docker push lmcache/standalone:${{ env.LATEST_TAG }}-cu129
# Build RC container image and push to DockerHub when:
# - a new RC GitHub release is created (e.g. v0.4.8rc1)
# Only pushes lmcache/vllm-openai:<tag> — does not touch latest or other image variants.
publish-image-rc:
name: Publish RC image to DockerHub
if: |
needs.publish-pypi.result == 'success' &&
!cancelled() &&
github.repository_owner == 'LMCache' && github.event.action == 'published' &&
contains(github.ref_name, 'rc')
runs-on: ubuntu-latest
needs: [publish-pypi]
steps:
- name: "Harden Runner"
uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0
with:
disable-sudo-and-containers: false
egress-policy: audit
- name: Login to DockerHub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- name: Free disk space
uses: ./.github/actions/free-disk-space
- name: Get the release tag
run: |
REF="${{ github.ref_name }}"
[[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+rc[0-9]+$ ]] && echo "LATEST_RC_TAG=$REF" >> "$GITHUB_ENV"
- name: Build lmcache/vllm-openai container image (cu13)
run: |
docker build \
--build-arg BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:25.09-cuda13.0-devel-ubuntu24.04 \
--build-arg CUDA_VERSION=13.0 \
--build-arg LMCACHE_VERSION=${{ env.LATEST_RC_TAG }} \
--build-arg torch_cuda_arch_list='7.5 8.0 8.6 8.9 9.0 10.0 12.0+PTX' \
--target image-release \
--tag lmcache/vllm-openai:${{ env.LATEST_RC_TAG }} \
--file docker/Dockerfile .
- name: Smoke test lmcache/vllm-openai (cu13)
run: |
docker run --rm --entrypoint lmcache lmcache/vllm-openai:${{ env.LATEST_RC_TAG }} --help
- name: Push lmcache/vllm-openai RC image to DockerHub
run: |
docker push lmcache/vllm-openai:${{ env.LATEST_RC_TAG }}