mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-18 12:08:35 +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
6.6 KiB
YAML
213 lines
6.6 KiB
YAML
name: NodeJS (NAPI)
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
paths:
|
|
- Cargo.toml
|
|
- Cargo.lock
|
|
- rust-toolchain.toml
|
|
- nodejs/**
|
|
- rust/**
|
|
- docs/src/js/**
|
|
- .github/workflows/nodejs.yml
|
|
- docker-compose.yml
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
RUST_BACKTRACE: "1"
|
|
|
|
jobs:
|
|
lint:
|
|
name: Lint
|
|
runs-on: ubuntu-22.04
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
working-directory: nodejs
|
|
env:
|
|
# Need up-to-date compilers for kernels
|
|
CC: gcc-12
|
|
CXX: g++-12
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
lfs: true
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: 11.1.1
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
|
# in October. The library itself still supports Node >= 18
|
|
# (see test matrix below).
|
|
node-version: 24
|
|
cache: 'pnpm'
|
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
|
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
with:
|
|
components: rustfmt, clippy
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt update
|
|
sudo apt install -y protobuf-compiler libssl-dev
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
# Restore everywhere, but only save from main. Per-PR saves are
|
|
# unreadable outside their own branch anyway, since GitHub scopes
|
|
# caches to the creating ref.
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Format Rust
|
|
run: cargo fmt --all -- --check
|
|
- name: Lint Rust
|
|
run: cargo clippy --profile ci --all --all-features -- -D warnings
|
|
- name: Lint Typescript
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
pnpm lint-ci
|
|
- name: Lint examples
|
|
working-directory: nodejs/examples
|
|
# The `@lancedb/lancedb` dep points at file:../dist; pnpm errors if
|
|
# that dir is missing, so create an empty one for lint-only runs.
|
|
run: mkdir -p ../dist && pnpm install --frozen-lockfile && pnpm lint-ci
|
|
linux:
|
|
name: Linux (NodeJS ${{ matrix.node-version }})
|
|
timeout-minutes: 30
|
|
strategy:
|
|
matrix:
|
|
node-version: [ "18", "20" ]
|
|
runs-on: "ubuntu-22.04"
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
working-directory: nodejs
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
lfs: true
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: 11.1.1
|
|
- uses: actions/setup-node@v6
|
|
name: Setup Node.js 24 for build
|
|
with:
|
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
|
# in October. Build/install runs on Node 24; tests run on the
|
|
# matrix version below using direct jest invocation.
|
|
node-version: 24
|
|
cache: 'pnpm'
|
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
# Restore everywhere, but only save from main. Per-PR saves are
|
|
# unreadable outside their own branch anyway, since GitHub scopes
|
|
# caches to the creating ref.
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt update
|
|
sudo apt install -y protobuf-compiler libssl-dev
|
|
- name: Build
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
# No `--` separator: pnpm forwards it literally, which would
|
|
# make napi-rs treat `--profile ci` as a cargo passthrough arg.
|
|
pnpm build:debug --profile ci
|
|
pnpm tsc
|
|
- name: Setup examples
|
|
working-directory: nodejs/examples
|
|
run: pnpm install --frozen-lockfile
|
|
- name: Check docs
|
|
run: |
|
|
# We run this as part of the job because the binary needs to be built
|
|
# first to export the types of the native code.
|
|
set -e
|
|
# `pnpm docs` would invoke pnpm's built-in `docs` command, not
|
|
# the script — use `pnpm run docs`.
|
|
pnpm run docs
|
|
if ! git diff --exit-code -- ../ ':(exclude)Cargo.lock'; then
|
|
echo "Docs need to be updated"
|
|
echo "Run 'pnpm run docs', fix any warnings, and commit the changes."
|
|
exit 1
|
|
fi
|
|
- uses: actions/setup-node@v6
|
|
name: Setup Node.js ${{ matrix.node-version }} for test
|
|
with:
|
|
node-version: ${{ matrix.node-version }}
|
|
- name: Setup localstack
|
|
working-directory: .
|
|
run: docker compose up --detach --wait
|
|
- name: Test
|
|
env:
|
|
S3_TEST: "1"
|
|
# Newer @smithy/core uses dynamic ESM imports.
|
|
NODE_OPTIONS: "--experimental-vm-modules"
|
|
# Invoke jest directly because pnpm 11 itself requires Node 22+
|
|
# while the matrix tests on older Node versions.
|
|
run: npx jest --verbose
|
|
- name: Test examples
|
|
working-directory: ./
|
|
env:
|
|
OPENAI_API_KEY: test
|
|
OPENAI_BASE_URL: http://0.0.0.0:8000
|
|
NODE_OPTIONS: "--experimental-vm-modules"
|
|
run: |
|
|
python ci/mock_openai.py &
|
|
cd nodejs/examples
|
|
npx jest --testEnvironment jest-environment-node-single-context --verbose
|
|
macos:
|
|
timeout-minutes: 30
|
|
# macos-15 ships a newer linker; the older macos-14 linker fails to insert
|
|
# branch islands when the debug cdylib's __text section exceeds the 128 MB
|
|
# AArch64 B/BL branch range.
|
|
runs-on: "macos-15"
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
working-directory: nodejs
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
lfs: true
|
|
- uses: pnpm/action-setup@v6
|
|
with:
|
|
version: 11.1.1
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
# pnpm 11 requires Node >= 22.13; use 24 since 22 hits EOL
|
|
# in October.
|
|
node-version: 24
|
|
cache: 'pnpm'
|
|
cache-dependency-path: nodejs/pnpm-lock.yaml
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
# Restore everywhere, but only save from main. Per-PR saves are
|
|
# unreadable outside their own branch anyway, since GitHub scopes
|
|
# caches to the creating ref.
|
|
save-if: ${{ github.ref == 'refs/heads/main' }}
|
|
- name: Install dependencies
|
|
run: |
|
|
brew install protobuf
|
|
- name: Build
|
|
run: |
|
|
pnpm install --frozen-lockfile
|
|
# No `--` separator: pnpm forwards it literally, which would
|
|
# make napi-rs treat `--profile ci` as a cargo passthrough arg.
|
|
pnpm build:debug --profile ci
|
|
pnpm tsc
|
|
- name: Test
|
|
run: |
|
|
pnpm test
|