Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/python-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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*"
Expand Down Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion backends/cuvs_26_02/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backends/cuvs_26_02/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <dev@lance.org>"]
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion backends/cuvs_26_02/pyproject.toml
Original file line number Diff line number Diff line change
@@ -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" }
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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" }
Expand Down
151 changes: 151 additions & 0 deletions tools/apply_release_version.py
Original file line number Diff line number Diff line change
@@ -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<base>\d+\.\d+\.\d+)(?:(?P<phase>a|b|rc)(?P<number>\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())
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading