From 8a986a4bc2cb1e712155ac0239549d1d39515ca1 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Tue, 14 Apr 2026 20:42:35 +0800 Subject: [PATCH] Derive release versions from tags --- .github/workflows/python-release.yml | 10 ++ Justfile | 6 ++ README.md | 4 +- backends/cuvs_26_02/Cargo.lock | 2 +- backends/cuvs_26_02/Cargo.toml | 2 +- backends/cuvs_26_02/pyproject.toml | 2 +- pyproject.toml | 2 +- tools/apply_release_version.py | 151 +++++++++++++++++++++++++++ uv.lock | 2 +- 9 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 tools/apply_release_version.py diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml index 7b901d7..13b3128 100644 --- a/.github/workflows/python-release.yml +++ b/.github/workflows/python-release.yml @@ -2,6 +2,11 @@ name: Python Release on: workflow_dispatch: + inputs: + release_tag: + description: Release tag to publish, for example v0.1.0b3 + required: false + type: string push: tags: - "v*" @@ -40,6 +45,11 @@ jobs: workspaces: | backends/cuvs_26_02 -> target + - name: Apply release version from tag + env: + RELEASE_TAG: ${{ inputs.release_tag || github.ref_name }} + run: python tools/apply_release_version.py --tag "$RELEASE_TAG" + - name: Build release wheels run: just build-wheels diff --git a/Justfile b/Justfile index 8615d79..e4f18ed 100644 --- a/Justfile +++ b/Justfile @@ -10,6 +10,12 @@ backend_wheel_compatibility := "manylinux_2_28" default: @just --list +apply-release-version tag: + {{uv_project}} python tools/apply_release_version.py --tag {{tag}} + +show-release-version tag: + {{uv_project}} python tools/apply_release_version.py --tag {{tag}} --dry-run + sync-dev: uv sync --group dev diff --git a/README.md b/README.md index f8cb72f..115ed99 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,6 @@ `pylance-cuvs` provides cuVS-backed IVF_PQ training and artifact building for Lance datasets. -The current release line is `0.1.0b2`. - It covers one narrow part of the indexing pipeline: 1. Train an IVF_PQ model with cuVS. @@ -34,6 +32,8 @@ same Python environment: Published beta releases are wheel-only. Source distributions are not supported. +Release versions are injected from the git tag during the publish workflow. + If you are working from this repository, the shortest local setup is: ```bash diff --git a/backends/cuvs_26_02/Cargo.lock b/backends/cuvs_26_02/Cargo.lock index 89b57d1..b25228a 100644 --- a/backends/cuvs_26_02/Cargo.lock +++ b/backends/cuvs_26_02/Cargo.lock @@ -4639,7 +4639,7 @@ dependencies = [ [[package]] name = "pylance-cuvs-cu12" -version = "0.1.0-beta.2" +version = "0.0.0-dev" dependencies = [ "arrow", "arrow-array", diff --git a/backends/cuvs_26_02/Cargo.toml b/backends/cuvs_26_02/Cargo.toml index 7386326..b3cd29f 100644 --- a/backends/cuvs_26_02/Cargo.toml +++ b/backends/cuvs_26_02/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pylance-cuvs-cu12" -version = "0.1.0-beta.2" +version = "0.0.0-dev" edition = "2024" authors = ["Lance Devs "] license = "Apache-2.0" diff --git a/backends/cuvs_26_02/pyproject.toml b/backends/cuvs_26_02/pyproject.toml index 6307c57..011e852 100644 --- a/backends/cuvs_26_02/pyproject.toml +++ b/backends/cuvs_26_02/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylance-cuvs-cu12" -version = "0.1.0b2" +version = "0.0.0.dev0" description = "CUDA 12 runtime backend for pylance-cuvs" authors = [{ name = "Lance Devs", email = "dev@lance.org" }] license = { text = "Apache-2.0" } diff --git a/pyproject.toml b/pyproject.toml index 8206ad5..8fcdfc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylance-cuvs" -version = "0.1.0b2" +version = "0.0.0.dev0" description = "Version-dispatched cuVS backend loader for Lance" authors = [{ name = "Lance Devs", email = "dev@lance.org" }] license = { file = "LICENSE" } diff --git a/tools/apply_release_version.py b/tools/apply_release_version.py new file mode 100644 index 0000000..e2ba6ac --- /dev/null +++ b/tools/apply_release_version.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import re +import sys +from dataclasses import dataclass +from pathlib import Path + +REPO_PYTHON_VERSION = "0.0.0.dev0" +REPO_CARGO_VERSION = "0.0.0-dev" + +TAG_PATTERN = re.compile( + r"^v(?P\d+\.\d+\.\d+)(?:(?Pa|b|rc)(?P\d+))?$" +) + + +@dataclass(frozen=True) +class ReleaseVersion: + python: str + cargo: str + + +def parse_release_tag(tag: str) -> ReleaseVersion: + match = TAG_PATTERN.fullmatch(tag) + if not match: + raise ValueError( + f"unsupported release tag {tag!r}; expected forms like v0.1.0, v0.1.0b2, or v0.1.0rc1" + ) + + base = match.group("base") + phase = match.group("phase") + number = match.group("number") + python = tag.removeprefix("v") + + if phase is None: + return ReleaseVersion(python=python, cargo=base) + + cargo_phase = {"a": "alpha", "b": "beta", "rc": "rc"}[phase] + return ReleaseVersion(python=python, cargo=f"{base}-{cargo_phase}.{number}") + + +def replace_once(content: str, pattern: str, replacement: str, path: Path) -> str: + updated, count = re.subn(pattern, replacement, content, count=1, flags=re.MULTILINE) + if count != 1: + raise ValueError(f"failed to update {path}") + return updated + + +def replace_package_version( + content: str, package_name: str, version: str, path: Path +) -> str: + pattern = ( + rf'(\[\[package\]\]\nname = "{re.escape(package_name)}"\nversion = ")' + rf'([^"\n]+)(")' + ) + updated, count = re.subn(pattern, rf"\g<1>{version}\3", content, count=1) + if count != 1: + raise ValueError(f"failed to update {package_name} version in {path}") + return updated + + +def update_file(path: Path, transform) -> None: + original = path.read_text() + updated = transform(original) + if updated != original: + path.write_text(updated) + + +def apply_versions(root: Path, version: ReleaseVersion) -> None: + update_file( + root / "pyproject.toml", + lambda content: replace_once( + content, + r'^version = "[^"\n]+"$', + f'version = "{version.python}"', + root / "pyproject.toml", + ), + ) + update_file( + root / "backends/cuvs_26_02/pyproject.toml", + lambda content: replace_once( + content, + r'^version = "[^"\n]+"$', + f'version = "{version.python}"', + root / "backends/cuvs_26_02/pyproject.toml", + ), + ) + update_file( + root / "backends/cuvs_26_02/Cargo.toml", + lambda content: replace_once( + content, + r'^version = "[^"\n]+"$', + f'version = "{version.cargo}"', + root / "backends/cuvs_26_02/Cargo.toml", + ), + ) + update_file( + root / "uv.lock", + lambda content: replace_package_version( + content, "pylance-cuvs", version.python, root / "uv.lock" + ), + ) + update_file( + root / "backends/cuvs_26_02/Cargo.lock", + lambda content: replace_package_version( + content, + "pylance-cuvs-cu12", + version.cargo, + root / "backends/cuvs_26_02/Cargo.lock", + ), + ) + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Apply a release version derived from a git tag to local package metadata." + ) + parser.add_argument("--tag", required=True, help="Git tag like v0.1.0b3") + parser.add_argument( + "--root", + default=Path(__file__).resolve().parent.parent, + type=Path, + help="Repository root containing the package manifests.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Validate the tag and print derived versions without mutating files.", + ) + args = parser.parse_args() + + try: + version = parse_release_tag(args.tag) + except ValueError as err: + print(str(err), file=sys.stderr) + return 1 + + print(f"tag={args.tag}") + print(f"python_version={version.python}") + print(f"cargo_version={version.cargo}") + + if not args.dry_run: + apply_versions(args.root.resolve(), version) + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/uv.lock b/uv.lock index 8ffe886..6dab49b 100644 --- a/uv.lock +++ b/uv.lock @@ -454,7 +454,7 @@ wheels = [ [[package]] name = "pylance-cuvs" -version = "0.1.0b2" +version = "0.0.0.dev0" source = { editable = "." } dependencies = [ { name = "pyarrow" },