mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-20 18:52:28 +00:00
Closes #4246. lancedb publishes no source distribution, so a platform without a prebuilt wheel (#4104, #4239) has nothing to install. This adds an sdist to the Python release. ## The sdist job The release workflow builds an sdist with the maturin version of the wheel jobs. On a tag, the publish job uploads it with the wheels: to PyPI, and to Fury for beta tags. The job also runs on the pull request dry run, which now starts on a change to `python/pyproject.toml`. ## Checks - The job builds a wheel from the sdist with `--locked` and smoke-tests it in a fresh venv. - `ci/check_sdist.py` fails if a tracked file of the Python package is missing from the tarball. - `twine check --strict` checks that the metadata renders. `pydistcheck` enforces PyPI's 100 MiB size limit. - The unit tests of both check scripts run in the CI scripts workflow. ## Cargo.lock maturin ships the repository lockfile, which pins crates that only the dropped `nodejs` member uses, so `cargo --locked` fails (PyO3/maturin#2609). The job first runs `cargo metadata --locked` on the repository workspace, so a lockfile that has drifted from `Cargo.toml` fails there. It then removes the nodejs-only pins and repacks the sdist. `ci/check_cargo_lock.py` fails if the prune changes any package it keeps, or any dependency of one. The published file is the repacked one. ## Behavior change After the first release with an sdist, pip on a platform with no current wheel builds the newest version from source, which needs a Rust toolchain and `protoc`. Before, pip installed the last version with a wheel for that platform.
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""Fail if pruning Cargo.lock did more than remove packages and the edges to them.
|
|
|
|
Usage: python ci/check_cargo_lock.py <lock-before> <lock-after>
|
|
"""
|
|
|
|
import sys
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
|
|
def key(package: dict) -> tuple:
|
|
return tuple(package.get(k) for k in ("name", "version", "source", "checksum"))
|
|
|
|
|
|
def graph(lock: dict) -> dict[tuple, set[tuple]]:
|
|
"""Map each package to the packages its dependency strings resolve to."""
|
|
by_name = {}
|
|
for p in lock["package"]:
|
|
by_name.setdefault(p["name"], []).append(p)
|
|
|
|
def resolve(dep: str) -> tuple:
|
|
# Cargo writes "name", "name version" or "name version (source)".
|
|
name, *rest = dep.split(" ", 2)
|
|
(match,) = [
|
|
p
|
|
for p in by_name[name]
|
|
if (not rest or p["version"] == rest[0])
|
|
and (len(rest) < 2 or rest[1] == f"({p.get('source')})")
|
|
]
|
|
return key(match)
|
|
|
|
return {
|
|
key(p): {resolve(d) for d in p.get("dependencies", [])} for p in lock["package"]
|
|
}
|
|
|
|
|
|
def main() -> None:
|
|
before, after = (tomllib.loads(Path(p).read_text()) for p in sys.argv[1:])
|
|
errors = []
|
|
for field in (before.keys() | after.keys()) - {"package"}:
|
|
if before.get(field) != after.get(field):
|
|
errors.append(f"top-level {field!r} changed")
|
|
edges_before, edges_after = graph(before), graph(after)
|
|
removed = edges_before.keys() - edges_after.keys()
|
|
for package, deps in edges_after.items():
|
|
if package not in edges_before:
|
|
errors.append(f"added or changed {package}")
|
|
elif deps != edges_before[package] - removed:
|
|
errors.append(f"dependencies of {package[:2]} changed")
|
|
if errors:
|
|
sys.exit("error: the prune changed Cargo.lock:\n" + "\n".join(errors))
|
|
print(f"Cargo.lock prune only removed {len(removed)} packages")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|