mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-18 20:18:37 +00:00
ff50e698cf
Standard GitHub-hosted runners are free on public repos, so all Actions spend here is on the `*-8x-*` / `4x` larger runners. Measured over 30 days at current (post-Jan-2026) larger-runner rates, that is ~$1,400/mo, and `npm-publish` is ~70% of it. ## Changes **Fat LTO was forcing builds onto large runners.** `[profile.release]` in `.cargo/config.toml` sets `lto = "fat"` with `codegen-units = 1`, which is single-threaded and the peak-memory step. The macOS `npm-publish` build was 111 of its 113 minutes in one `napi build` step, making it the critical path of the whole publish pipeline. The ThinLTO override already applied to Windows now covers macOS too, and both Windows builds move from `windows-2025-8x-x64` to the free standard `windows-2025`. **The npm-publish cargo cache never existed.** There are zero caches with its key prefix. The key was static, so `actions/cache` (which only writes on a miss) could never refresh it, and a multi-GB release `target/` per target could never fit the repo's 10 GB budget anyway. Now caches only the crate registry, keyed on `Cargo.lock`. The docker builds also mounted `.cargo/registry/*` while the cache saved `.cargo-cache`, so containers re-downloaded the registry every run. **Cache eviction thrash.** Repo cache usage is 10.4 GB against GitHub's 10 GB cap, so every PR run evicted main's warm entries. `rust.yml` and `nodejs.yml` now restore everywhere but only save from `main`. **npm-publish moves to nightly + tags** instead of every push to main (~90/month). The cross-compiled targets do need watching, so `report-failure` now fires on scheduled runs, and dedupes onto an existing open issue rather than filing one per night. **rust.yml aarch64-pc-windows-msvc** cross-compiled its tests and then skipped them, paying full codegen and link cost for a compile check. `windows-11-arm` is now GA and free on public repos, so it builds and tests natively. Its test step also passes `--target` — without it cargo used `target/ci/` rather than `target/<triple>/ci/` and rebuilt the entire dependency graph a second time. **pypi-publish.yml had no concurrency group**, so force-pushes left a ~74 minute Windows job running. ## What is cost vs. wall-clock | Change | Cost | Wall-clock | |---|---|---| | Windows npm-publish → free runners | **−$570/mo** | slower per job (8→4 cores) | | npm-publish nightly | **−$125/mo** | — | | pypi-publish concurrency | small | — | | macOS ThinLTO | $0 (already free) | **−~50 min** per release | | rust aarch64 Windows native | $0 (already free) | **−~25 min** | | rust `--target` on test step | $0 | large, avoids a second full build | | rust-cache `save-if` | small | faster via real cache hits | ## Risks - The two Windows builds now have 4 cores instead of 8 and ~14 GB of free disk. If they fail, it is most likely disk rather than memory; fallback is `windows-2025-4x-x64`, which still halves that line. - `windows-11-arm` has a thinner toolset (choco/vcpkg/protoc under emulation) and this enables a test step that has never run, so it may surface real aarch64 failures. That is the point, but it is the change most likely to need iteration. - ThinLTO applies to published macOS and Windows binaries, typically within a few percent of fat LTO. Linux release builds are untouched. ## Follow-ups - `python.yml` `pydantic1x` (37 min) and `Doctest` (33 min) each rebuild the extension from source via `pip install -e .` with no Rust cache; they should consume the wheel the `linux` job already builds. Worth ~$235/mo and ~70 min of compute per run. Separate PR. - The three `ubuntu-2404-8x-x64` npm-publish builds (~$420/mo at the old cadence) are the remaining large-runner spend; `aarch64-unknown-linux-gnu` could run natively on free `ubuntu-24.04-arm`. Worth doing after this lands so the ThinLTO change can be validated first. - The wheel composite actions declare `python-minor-version` as required but never use it, and every caller omits it (actionlint warns). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
213 lines
7.6 KiB
YAML
213 lines
7.6 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
|
|
- 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 ~74 minute 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"
|
|
- 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:
|
|
timeout-minutes: 90
|
|
runs-on: windows-latest
|
|
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
|
|
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 ~74 minutes 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
|
|
publish:
|
|
name: Publish wheels
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
needs: [linux, mac, windows]
|
|
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: List wheels
|
|
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)
|
|
if [[ ${#WHEELS[@]} -eq 0 ]]; then
|
|
echo "No wheels found in target/wheels/" >&2
|
|
exit 1
|
|
fi
|
|
for WHEEL in "${WHEELS[@]}"; do
|
|
echo "Uploading $WHEEL to Fury"
|
|
curl -f -F package=@"$WHEEL" "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, 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 }}
|