Files
qdrant/.github/workflows/edge-py-release.yml
dependabot[bot] ac58036e20 build(deps): bump astral-sh/setup-uv from 8.3.2 to 9.0.0 (#9993)
Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 8.3.2 to 9.0.0.
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](11f9893b08...c771a70e62)

---
updated-dependencies:
- dependency-name: astral-sh/setup-uv
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-27 16:53:15 -04:00

447 lines
17 KiB
YAML

name: Qdrant Edge Release Build
on:
workflow_dispatch:
inputs:
publish:
description: 'Publish to PyPI'
type: boolean
default: false
required: true
push:
branches: [ dev ]
permissions:
contents: read
concurrency:
group: edge-py-release-${{ github.ref }}
cancel-in-progress: false
jobs:
edge-py-linux:
name: Build Python wheels [Linux]
runs-on: ubuntu-latest
strategy:
matrix:
# On `push` to `dev`, only build the single most popular flavor:
# x86_64 manylinux_2_17 (widest glibc compatibility). Thanks to
# abi3, that one wheel covers every CPython >= 3.10, including the
# latest. The full matrix (aarch64, manylinux_2_28, musl, PyPy)
# only runs on manual `workflow_dispatch` releases.
target: ${{ github.event_name == 'push' && fromJSON('["x86_64"]') || fromJSON('["x86_64", "aarch64"]') }}
manylinux: ${{ github.event_name == 'push' && fromJSON('["manylinux_2_17"]') || fromJSON('["manylinux_2_17", "manylinux_2_28"]') }}
python: ${{ github.event_name == 'push' && fromJSON('["python3.10"]') || fromJSON('["python3.10", "pypy3.11"]') }}
include: >-
${{ github.event_name == 'push' && fromJSON('[]') || fromJSON('[
{"arch": "x86_64", "target": "x86_64-unknown-linux-musl", "manylinux": "musllinux_1_2", "python": "python3.10"},
{"arch": "aarch64", "target": "aarch64-unknown-linux-musl", "manylinux": "musllinux_1_2", "python": "python3.10"}
]') }}
exclude:
- target: aarch64
manylinux: manylinux_2_17
steps:
- name: Checkout Qdrant
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set pre-release version
# Bump the patch component and append a `-dev<run_number>` SemVer
# pre-release suffix so the dev version sorts strictly ABOVE the
# latest published stable (e.g. Cargo.toml `0.7.2` -> Cargo.toml
# `0.7.3-dev137` -> wheel `0.7.3.dev137`). Cargo rejects PEP 440
# syntax `.dev` because it isn't valid SemVer, so we write SemVer
# here and let maturin translate to PEP 440 when building the wheel.
# Combined with `pip install --pre`, this lets consumers pull "the
# current dev" without knowing the run number, and prevents pip
# from silently preferring the older stable release.
if: github.event_name == 'push'
shell: bash
working-directory: lib/edge/python
run: |
awk -v run="${GITHUB_RUN_NUMBER}" '
!patched && /^version = "/ {
# Assumes SemVer X.Y.Z inside the quotes; bump patch, append -devN.
split($0, quoted, "\"")
split(quoted[2], parts, ".")
parts[3] = parts[3] + 1
print "version = \"" parts[1] "." parts[2] "." parts[3] "-dev" run "\""
patched = 1
next
}
{ print }
' Cargo.toml > Cargo.toml.new
mv Cargo.toml.new Cargo.toml
grep '^version' Cargo.toml
- name: Build Python wheel
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
command: build
args: --release --features abi3 --interpreter ${{ matrix.python }} --out dist
working-directory: lib/edge/python
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
- name: Upload Python wheel
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: qdrant-edge-${{ matrix.manylinux }}-${{ matrix.arch || matrix.target }}-${{ matrix.python }}
path: lib/edge/python/dist
edge-py-macos:
name: Build Python wheels [macOS]
runs-on: macos-latest
# Dev pushes only build Linux x86_64; the full platform matrix is
# reserved for manual `workflow_dispatch` releases.
if: github.event_name != 'push'
strategy:
matrix:
target: [x86_64, aarch64]
python:
- label: python3.10
version: '3.10'
- pypy3.11
steps:
- name: Install Rust
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
- name: Install Python
id: setup-python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python.version || matrix.python }}
- name: Checkout Qdrant
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set pre-release version
# See the identical step on the Linux job for the rationale (SemVer
# `-dev<N>` + patch bump so maturin emits PEP 440 `X.Y.(Z+1).dev<N>`).
if: github.event_name == 'push'
shell: bash
working-directory: lib/edge/python
run: |
awk -v run="${GITHUB_RUN_NUMBER}" '
!patched && /^version = "/ {
split($0, quoted, "\"")
split(quoted[2], parts, ".")
parts[3] = parts[3] + 1
print "version = \"" parts[1] "." parts[2] "." parts[3] "-dev" run "\""
patched = 1
next
}
{ print }
' Cargo.toml > Cargo.toml.new
mv Cargo.toml.new Cargo.toml
grep '^version' Cargo.toml
- name: Build Python wheel
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
command: build
args: --release --features abi3 --interpreter ${{ steps.setup-python.outputs.python-path }} --out dist
working-directory: lib/edge/python
target: ${{ matrix.target }}
- name: Upload Python wheel
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: qdrant-edge-macos-${{ matrix.target }}-${{ matrix.python.label || matrix.python }}
path: lib/edge/python/dist
edge-py-windows:
name: Build Python wheels [Windows]
runs-on: windows-latest
# Dev pushes only build Linux x86_64; the full platform matrix is
# reserved for manual `workflow_dispatch` releases.
if: github.event_name != 'push'
strategy:
matrix:
target: [x86_64]
python:
- label: python3.10
version: '3.10'
- pypy3.11
steps:
- name: Install Rust
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
- name: Install Python
id: setup-python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python.version || matrix.python }}
- name: Checkout Qdrant
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set pre-release version
# See the identical step on the Linux job for the rationale (SemVer
# `-dev<N>` + patch bump so maturin emits PEP 440 `X.Y.(Z+1).dev<N>`).
if: github.event_name == 'push'
shell: bash
working-directory: lib/edge/python
run: |
awk -v run="${GITHUB_RUN_NUMBER}" '
!patched && /^version = "/ {
split($0, quoted, "\"")
split(quoted[2], parts, ".")
parts[3] = parts[3] + 1
print "version = \"" parts[1] "." parts[2] "." parts[3] "-dev" run "\""
patched = 1
next
}
{ print }
' Cargo.toml > Cargo.toml.new
mv Cargo.toml.new Cargo.toml
grep '^version' Cargo.toml
- name: Build Python wheel
uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1.51.0
with:
command: build
args: --release --features abi3 --interpreter ${{ steps.setup-python.outputs.python-path }} --out dist
working-directory: lib/edge/python
target: ${{ matrix.target }}
- name: Upload Python wheel
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: qdrant-edge-windows-${{ matrix.target }}-${{ matrix.python.label || matrix.python }}
path: lib/edge/python/dist
edge-py-test:
name: Test Python wheels [${{ matrix.os }}, ${{ matrix.python }}]
runs-on: ${{ matrix.os }}
needs:
- edge-py-linux
- edge-py-macos
- edge-py-windows
# macOS/Windows builds are intentionally skipped on `push`, which by
# default would skip this job too — override with `!cancelled()` and
# only require the jobs that were supposed to run.
if: >-
${{ !cancelled()
&& needs.edge-py-linux.result == 'success'
&& (github.event_name == 'push' || (needs.edge-py-macos.result == 'success' && needs.edge-py-windows.result == 'success')) }}
strategy:
fail-fast: false
matrix:
# Dev pushes test the single x86_64 Linux wheel on the latest
# stable Python only. Production releases (workflow_dispatch)
# verify the abi3 wheel against every stable CPython plus PyPy.
os: ${{ github.event_name == 'push' && fromJSON('["ubuntu-latest"]') || fromJSON('["macos-latest", "ubuntu-24.04-arm", "ubuntu-latest", "windows-latest"]') }}
python: ${{ github.event_name == 'push' && fromJSON('["3.14"]') || fromJSON('["3.10", "3.11", "3.12", "3.13", "3.14", "pypy3.11"]') }}
steps:
- name: Checkout Qdrant
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Just
uses: ./.github/actions/setup-just
- name: Install uv
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
- name: Install Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python }}
- name: Download wheels
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: qdrant-edge-*
merge-multiple: true
path: lib/edge/python/dist
- name: Setup Qdrant
uses: ./.github/actions/setup-qdrant
- name: Install and test wheel
working-directory: lib/edge/python
run: |
uv venv --allow-existing --python ${{ matrix.python }}
uv pip install --no-index --find-links dist/ qdrant-edge-py
uv pip install requests
just py-examples
merge-artifacts:
name: Merge release artifacts
runs-on: ubuntu-latest
needs:
- edge-py-linux
- edge-py-macos
- edge-py-windows
- edge-py-test
# `!cancelled()` because macOS/Windows builds are skipped on `push`;
# `edge-py-test` succeeding already implies every required build passed.
if: ${{ !cancelled() && needs.edge-py-test.result == 'success' && (inputs.publish || github.event_name == 'push') }}
steps:
- name: Merge release artifacts
uses: actions/upload-artifact/merge@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: qdrant-edge
pattern: qdrant-edge-*
delete-merged: false
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs:
- merge-artifacts
# Stable releases go to PyPI via manual workflow_dispatch.
# Dev pre-releases from `push` to `dev` go to GCS instead (see publish-gcs).
if: inputs.publish
steps:
- name: Install Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
- name: Download release wheels
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: qdrant-edge
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN_EDGE }}
run: |
pip install --upgrade twine
twine upload --skip-existing *
publish-gcs:
name: Publish dev wheels to GCS
runs-on: ubuntu-latest
needs:
- merge-artifacts
# Only publish dev pre-releases (from push to `dev`) to the GCS bucket.
# Manual workflow_dispatch runs target PyPI via the `publish` job above.
if: github.event_name == 'push'
env:
GCS_BUCKET: qdrant-debug
GCS_ENDPOINT: https://storage.googleapis.com
# Sub-folder inside the same bucket used by debug-tools.yml
GCS_PREFIX: edge-py-dev
# GCS interop ignores the region, but the AWS CLI requires one to be set.
AWS_REGION: auto
steps:
- name: Download release wheels
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: qdrant-edge
path: dist
- name: Upload wheels and regenerate index
env:
# GCS HMAC interoperability keys (AWS-style), stored as repo secrets.
AWS_ACCESS_KEY_ID: ${{ secrets.GCS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.GCS_SECRET_ACCESS_KEY }}
# Recent AWS CLI adds default CRC32 integrity checksums that GCS's
# S3-compatible UploadPart rejects (SignatureDoesNotMatch). Opt out so
# multipart uploads of the larger wheels work.
AWS_REQUEST_CHECKSUM_CALCULATION: when_required
AWS_RESPONSE_CHECKSUM_VALIDATION: when_required
run: |
# Extract the current version from a freshly built wheel filename.
# Wheel filename format (PEP 427):
# {distribution}-{version}-{python-tag}-{abi-tag}-{platform-tag}.whl
# PEP 440 versions never contain `-`, so a simple field split is safe.
current_version=$(ls dist/*.whl | head -1 | sed 's|.*/qdrant_edge_py-\([^-]*\)-.*|\1|')
echo "Publishing qdrant-edge-py ${current_version}"
# Upload the freshly built wheels. Object overwrite is a no-op for the
# same content; different content would replace (unlike PyPI's immutable
# filenames), but our `.dev<run_number>` versioning gives every push a
# unique filename so collisions shouldn't happen.
aws s3 cp dist/ "s3://${GCS_BUCKET}/${GCS_PREFIX}/wheels/" \
--recursive --exclude '*' --include '*.whl' \
--endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION"
# Delete every wheel that doesn't belong to the current version, so
# only the latest dev release is kept in the bucket. Done AFTER upload
# so there's never a window with zero wheels available.
aws s3 ls "s3://${GCS_BUCKET}/${GCS_PREFIX}/wheels/" \
--endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION" \
| awk '{print $NF}' \
| grep '\.whl$' \
| grep -vF "qdrant_edge_py-${current_version}-" \
| while read -r stale; do
echo "Removing stale wheel: ${stale}"
aws s3 rm "s3://${GCS_BUCKET}/${GCS_PREFIX}/wheels/${stale}" \
--endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION"
done
# Regenerate the pip-compatible HTML index. After the cleanup above,
# only the current version's wheels remain in the bucket.
base_url="${GCS_ENDPOINT}/${GCS_BUCKET}/${GCS_PREFIX}/wheels"
{
echo '<!DOCTYPE html>'
echo '<html><body>'
aws s3 ls "s3://${GCS_BUCKET}/${GCS_PREFIX}/wheels/" \
--endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION" \
| awk '{print $NF}' \
| grep '\.whl$' \
| sort \
| while read -r wheel; do
echo "<a href=\"${base_url}/${wheel}\">${wheel}</a><br>"
done
echo '</body></html>'
} > qdrant-edge-py.html
aws s3 cp qdrant-edge-py.html "s3://${GCS_BUCKET}/${GCS_PREFIX}/qdrant-edge-py.html" \
--endpoint-url "$GCS_ENDPOINT" --region "$AWS_REGION" \
--content-type text/html
# Surface the install command in the job summary
index_url="${GCS_ENDPOINT}/${GCS_BUCKET}/${GCS_PREFIX}/qdrant-edge-py.html"
{
echo "## qdrant-edge-py dev wheels uploaded"
echo ""
echo "Published version: ${current_version}"
echo ""
echo "Index: <${index_url}>"
echo ""
echo "Install the current dev build (no version pin needed):"
echo '```'
echo "pip install --find-links ${index_url} qdrant-edge-py --pre"
echo '```'
echo ""
echo "Pin a specific dev version:"
echo '```'
echo "pip install --find-links ${index_url} qdrant-edge-py==${current_version}"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"