Files
lancedb/.github/workflows/pypi-publish.yml
T
Jack YeandLance Release 197403e891 fix: honor downstream Lance dependency overrides (#4671)
Consumers that provide a local Lance checkout cannot resolve the
dependency graph because the SDK directly selects a vendored
`lance-arrow`, producing two path packages with the same name and
version. The Rust core now declares its upstream Git dependency
separately so the consuming workspace can override that source
consistently. Python continues inheriting the vendored workspace
dependency, keeping the sliced-struct validity fix in Maturin source
distributions. Standalone SDK builds still select that same fix through
the existing root patch.

Adds a downstream consumer CI regression that resolves a local Lance
override and checks that exactly one canonical `lance-arrow` package is
selected. The Lance version updater also updates the core declaration in
stable, preview and local modes. Public APIs and query behavior are
unchanged.

The workspace declaration lint excludes path dependencies because the
Python binding intentionally uses the bundled source. Duplicate and
unused external declarations remain errors; advisory, license,
banned-package and source checks continue covering the full dependency
graph.

---------

Co-authored-by: Lance Release <lance-dev@lancedb.com>
2026-10-25 07:19:55 -07:00

326 lines
12 KiB
YAML

name: PyPI Publish
on:
push:
tags:
- 'v*'
pull_request:
# This should trigger a dry run (we skip the final publish step)
paths:
- .github/workflows/pypi-publish.yml
- .github/workflows/build_linux_wheel/action.yml
- .github/workflows/build_mac_wheel/action.yml
- .github/workflows/build_windows_wheel/action.yml
- .github/actions/setup-protoc/action.yml
- ci/check_cargo_lock.py
- ci/check_sdist.py
- python/pyproject.toml
- python/Cargo.toml
- python/vendor/**
- rust/lancedb/Cargo.toml
- Cargo.toml # Change in dependency frequently breaks builds
- Cargo.lock
env:
PIP_EXTRA_INDEX_URL: "https://pypi.fury.io/lance-format/ https://pypi.fury.io/lancedb/"
permissions:
contents: read
# Without this, a force-push to a PR leaves the previous run going -- including
# a long fat-LTO Windows job and a billed arm64 wheel build.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
linux:
name: Python ${{ matrix.config.package_name }} ${{ matrix.config.platform }} manylinux${{ matrix.config.manylinux }}
timeout-minutes: 60
strategy:
matrix:
config:
- platform: x86_64
manylinux: "2_28"
extra_args: "--features fp16kernels"
runner: ubuntu-22.04
package_name: "lancedb"
rustflags: ""
# For successful fat LTO builds, we need a large runner to avoid OOM errors.
- platform: aarch64
manylinux: "2_28"
extra_args: "--features fp16kernels"
runner: ubuntu-2404-8x-arm64
package_name: "lancedb"
rustflags: ""
# `lancedb-compat`: pre-Haswell-friendly variant for x86_64 hosts
# without AVX2 (Sandy Bridge / Ivy Bridge / Westmere on Intel,
# Bulldozer / Piledriver / Steamroller on AMD). Compiled at the
# `x86-64-v2` baseline; runtime SIMD dispatch in lance-linalg
# picks the appropriate tier (scalar / AVX / AVX+FMA / AVX2+FMA
# / AVX-512) at load time. Same import as `lancedb` -- conflicts
# at install time, so users pick one.
- platform: x86_64
manylinux: "2_28"
extra_args: ""
runner: ubuntu-22.04
package_name: "lancedb-compat"
rustflags: "-Ctarget-cpu=x86-64-v2"
runs-on: ${{ matrix.config.runner }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.10"
- name: Add swap for Arm fat LTO
if: matrix.config.platform == 'aarch64'
shell: bash
run: |
swap_file="$RUNNER_TEMP/lancedb-swap"
sudo fallocate --length 16G "$swap_file"
sudo chmod 600 "$swap_file"
sudo mkswap "$swap_file"
sudo swapon "$swap_file"
free -h
- uses: ./.github/workflows/build_linux_wheel
with:
python-minor-version: 10
args: "--release --strip ${{ matrix.config.extra_args }}"
arm-build: ${{ matrix.config.platform == 'aarch64' }}
manylinux: ${{ matrix.config.manylinux }}
package-name: ${{ matrix.config.package_name }}
rustflags: ${{ matrix.config.rustflags }}
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-linux-${{ matrix.config.package_name }}-${{ matrix.config.platform }}-${{ matrix.config.manylinux }}
path: target/wheels/*.whl
if-no-files-found: error
mac:
timeout-minutes: 90
runs-on: ${{ matrix.config.runner }}
strategy:
matrix:
config:
- target: aarch64-apple-darwin
runner: warp-macos-14-arm64-6x
env:
MACOSX_DEPLOYMENT_TARGET: 10.15
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- uses: ./.github/workflows/build_mac_wheel
with:
python-minor-version: 10
args: "--release --strip --target ${{ matrix.config.target }} --features fp16kernels"
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-mac-${{ matrix.config.target }}
path: target/wheels/lancedb-*.whl
if-no-files-found: error
windows:
# The fat-LTO build of the `lancedb` crate does not finish within 90 minutes
# on the 4-core standard runner, so use the 8-core larger runner with extra
# headroom.
timeout-minutes: 150
runs-on: windows-2025-8x-x64
env:
# link.exe is single-threaded and the long pole on Windows builds. Use
# rustc's bundled lld-link instead.
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER: rust-lld
# Keep the repository's fat LTO / 1 codegen unit release profile to leave
# headroom below PyPI's file-size limit for Windows wheels.
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
# NOTE: caching cargo here would be a no-op. This workflow only runs on
# tags and PRs, and GitHub only lets a run restore caches from its own ref
# or the default branch -- so with no run on main there is nothing that
# can populate an entry the release build would be allowed to read. Fixing
# this needs a main/nightly trigger (which would also catch wheel-build
# breakage before a release); the build time here is otherwise dominated
# by the fat-LTO link, which no cache avoids.
- uses: ./.github/workflows/build_windows_wheel
with:
python-minor-version: 10
args: "--release --strip"
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/v')
with:
name: wheels-windows
path: target/wheels/lancedb-*.whl
if-no-files-found: error
sdist:
name: Python sdist
timeout-minutes: 60
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
maturin-version: "1.12.4"
command: sdist
working-directory: python
args: --out dist
# maturin ships the nodejs-only pins in Cargo.lock. Remove this step when
# the pinned maturin fixes https://github.com/PyO3/maturin/issues/2609.
- name: Sync Cargo.lock to the packaged workspace
shell: bash
run: |
# A repo lock in sync with every manifest means the prune only drops pins.
cargo metadata --locked --format-version 1 > /dev/null
mkdir "$RUNNER_TEMP/sdist-src"
tar -xzf python/dist/lancedb-*.tar.gz -C "$RUNNER_TEMP/sdist-src"
lock=$(echo "$RUNNER_TEMP"/sdist-src/lancedb-*/Cargo.lock)
cp "$lock" "$RUNNER_TEMP/Cargo.lock.before"
cargo metadata --format-version 1 \
--manifest-path "$RUNNER_TEMP"/sdist-src/lancedb-*/Cargo.toml > /dev/null
python ci/check_cargo_lock.py "$RUNNER_TEMP/Cargo.lock.before" "$lock"
tar -czf python/dist/lancedb-*.tar.gz -C "$RUNNER_TEMP/sdist-src" \
"$(basename python/dist/lancedb-*.tar.gz .tar.gz)"
- name: Install sdist check tools
shell: bash
run: python -m pip install twine==7.0.0 pydistcheck==0.11.3
- name: Check sdist size
shell: bash
run: |
pydistcheck --select distro-too-large-compressed \
--max-allowed-size-compressed 100M python/dist/lancedb-*.tar.gz
- name: Check sdist contents
shell: bash
run: python ci/check_sdist.py python/dist/lancedb-*.tar.gz
- name: Check sdist with twine
shell: bash
run: python -m twine check --strict python/dist/lancedb-*.tar.gz
- uses: ./.github/actions/setup-protoc
# Build the way `pip install lancedb` does when no wheel matches. The dev
# profile keeps the build within the runner's disk.
- name: Build wheel from sdist
shell: bash
env:
MATURIN_PEP517_ARGS: "--profile dev --locked"
CARGO_PROFILE_DEV_DEBUG: "0"
run: |
python -m pip wheel --no-deps --wheel-dir "$RUNNER_TEMP/sdist-wheel" \
python/dist/lancedb-*.tar.gz
- name: Smoke test wheel built from sdist
shell: bash
working-directory: ${{ runner.temp }}
run: |
python -m venv sdist-venv
sdist-venv/bin/pip install sdist-wheel/lancedb-*.whl
sdist-venv/bin/python -c '
import tempfile
import lancedb
db = lancedb.connect(tempfile.mkdtemp())
table = db.create_table("t", [{"vector": [1.0, 2.0], "id": 1}, {"vector": [3.0, 4.0], "id": 2}])
assert table.search([1.0, 2.0]).limit(1).to_list()[0]["id"] == 1
print("lancedb", lancedb.__version__, "built from sdist OK")
'
- uses: actions/upload-artifact@v7
if: startsWith(github.ref, 'refs/tags/v')
with:
name: sdist
path: python/dist/lancedb-*.tar.gz
if-no-files-found: error
publish:
name: Publish wheels and sdist
if: startsWith(github.ref, 'refs/tags/v')
needs: [linux, mac, windows, sdist]
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- name: Download wheel artifacts
uses: actions/download-artifact@v8
with:
pattern: wheels-*
path: target/wheels
merge-multiple: true
- name: Download sdist artifact
uses: actions/download-artifact@v8
with:
name: sdist
path: target/wheels
- name: List packages
run: ls -la target/wheels
- name: Choose repo
id: choose_repo
run: |
if [[ ${{ github.ref }} == *beta* ]]; then
echo "repo=fury" >> $GITHUB_OUTPUT
else
echo "repo=pypi" >> $GITHUB_OUTPUT
fi
- name: Publish to Fury
if: steps.choose_repo.outputs.repo == 'fury'
env:
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
run: |
shopt -s nullglob
WHEELS=(target/wheels/*.whl)
SDISTS=(target/wheels/*.tar.gz)
if [[ ${#WHEELS[@]} -eq 0 ]]; then
echo "No wheels found in target/wheels/" >&2
exit 1
fi
if [[ ${#SDISTS[@]} -ne 1 ]]; then
echo "Expected one sdist in target/wheels/, found ${#SDISTS[@]}" >&2
exit 1
fi
for PACKAGE in "${WHEELS[@]}" "${SDISTS[@]}"; do
echo "Uploading $PACKAGE to Fury"
curl -f -F package=@"$PACKAGE" "https://$FURY_TOKEN@push.fury.io/lancedb/"
done
# NOTE: pypa/gh-action-pypi-publish must be invoked directly from a
# workflow file, not from inside a composite action. When called from a
# composite, `github.action_repository` is empty (actions/runner#2473)
# and the action falls back to `github.repository`, producing a bogus
# `docker://ghcr.io/<repo>:<ref>` image reference that GHA tries to pull.
- name: Publish to PyPI
if: steps.choose_repo.outputs.repo == 'pypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: target/wheels/
report-failure:
name: Report Workflow Failure
runs-on: ubuntu-latest
needs: [linux, mac, windows, sdist, publish]
permissions:
contents: read
issues: write
if: always() && failure() && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/create-failure-issue
with:
job-results: ${{ toJSON(needs) }}
workflow-name: ${{ github.workflow }}