From dce8748ff71cdbfa7ae3c77667a519e85ba926ef Mon Sep 17 00:00:00 2001 From: NCIA-AlphaSparse Date: Mon, 28 Sep 2026 19:52:10 +0800 Subject: [PATCH 1/2] version ci --- .github/workflows/gpu-benchmark.yml | 2 +- .github/workflows/gpu-ci.yml | 2 +- .github/workflows/triton-smoke.yml | 2 +- Makefile | 2 +- tests/ci/test_installed_wheel.py | 5 ++++- tests/ci/test_package_smoke.py | 10 +++++++++- tools/ci/check_installed_wheel.py | 30 ++++++++++++++++++++++++++++- 7 files changed, 46 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gpu-benchmark.yml b/.github/workflows/gpu-benchmark.yml index 2cc32de..a376ce7 100644 --- a/.github/workflows/gpu-benchmark.yml +++ b/.github/workflows/gpu-benchmark.yml @@ -101,7 +101,7 @@ jobs: run: python3.12 -m pip install --force-reinstall --no-deps dist/*.whl - name: Validate installed wheel import - run: python3.12 tools/ci/check_installed_wheel.py --expected-version 0.4.0 + run: python3.12 tools/ci/check_installed_wheel.py --expected-version auto - name: Run GPU performance benchmarks shell: bash diff --git a/.github/workflows/gpu-ci.yml b/.github/workflows/gpu-ci.yml index 189bb90..d37c1c6 100644 --- a/.github/workflows/gpu-ci.yml +++ b/.github/workflows/gpu-ci.yml @@ -90,7 +90,7 @@ jobs: run: python3.12 -m pip install --force-reinstall --no-deps dist/*.whl - name: Validate installed wheel import - run: python3.12 tools/ci/check_installed_wheel.py --expected-version 0.4.0 + run: python3.12 tools/ci/check_installed_wheel.py --expected-version auto - name: Run GPU accuracy tests shell: bash diff --git a/.github/workflows/triton-smoke.yml b/.github/workflows/triton-smoke.yml index 4f7556c..c73b217 100644 --- a/.github/workflows/triton-smoke.yml +++ b/.github/workflows/triton-smoke.yml @@ -46,7 +46,7 @@ jobs: run: python -m pip install --force-reinstall --no-deps dist/*.whl - name: Validate installed wheel import - run: python tools/ci/check_installed_wheel.py --expected-version 0.4.0 + run: python tools/ci/check_installed_wheel.py --expected-version auto - name: Run triton smoke tests run: FLAGSPARSE_TRITON_SMOKE=1 pytest tests/ci -q diff --git a/Makefile b/Makefile index 244c61d..13f150a 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ PYTHON ?= python DIST_DIR ?= dist -EXPECTED_VERSION ?= 0.4.0 +EXPECTED_VERSION ?= auto .PHONY: help ci check ci-deps compile format-check lint lint-src pre-commit-check build install-wheel validate-wheel test-ci smoke triton-smoke triton-deps gpu-env-check gpu-benchmark release-check release clean diff --git a/tests/ci/test_installed_wheel.py b/tests/ci/test_installed_wheel.py index 65ef20a..d9e0230 100644 --- a/tests/ci/test_installed_wheel.py +++ b/tests/ci/test_installed_wheel.py @@ -21,6 +21,9 @@ from tempfile import TemporaryDirectory PROJECT_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(PROJECT_ROOT)) + +from tools.ci.check_installed_wheel import project_version # noqa: E402 def test_installed_wheel_import_resolves_outside_repo_tree(): @@ -44,7 +47,7 @@ def test_installed_wheel_import_resolves_outside_repo_tree(): lines = [line.strip() for line in (proc.stdout or "").splitlines() if line.strip()] assert lines, proc.stdout - assert lines[0] == "0.4.0" + assert lines[0] == project_version(PROJECT_ROOT) module_path = Path(lines[1]).resolve() assert PROJECT_ROOT not in module_path.parents assert module_path.is_file() diff --git a/tests/ci/test_package_smoke.py b/tests/ci/test_package_smoke.py index 56fd462..a0fee3b 100644 --- a/tests/ci/test_package_smoke.py +++ b/tests/ci/test_package_smoke.py @@ -14,11 +14,19 @@ """CPU-only smoke tests for CI packaging and public exports.""" +import re + import flagsparse def test_package_version_is_exposed(): - assert flagsparse.__version__ == "0.4.0" + # __version__ comes from the installed metadata. The exact value is checked + # against pyproject.toml by tools/ci/check_installed_wheel.py on the wheel; here + # it only has to be a real, parseable version -- "0.0.0" means metadata was not found. + assert flagsparse.__version__ != "0.0.0" + assert re.fullmatch( + r"\d+(\.\d+)+((a|b|rc|\.dev|\.post)\d+)*", flagsparse.__version__ + ) def test_public_exports_are_listed(): diff --git a/tools/ci/check_installed_wheel.py b/tools/ci/check_installed_wheel.py index 22cc6d8..25c4af1 100644 --- a/tools/ci/check_installed_wheel.py +++ b/tools/ci/check_installed_wheel.py @@ -19,6 +19,7 @@ import argparse import os import pathlib +import re import subprocess import sys from dataclasses import dataclass @@ -34,6 +35,27 @@ class WheelImportCheck: module_path: pathlib.Path +def project_version(project_root: Optional[pathlib.Path] = None) -> str: + """The version pyproject.toml declares, in the PEP 440 form a wheel reports. + + ``0.4.0-dev1`` is written that way in pyproject.toml but installs as + ``0.4.0.dev1``; comparing the raw text is what broke the wheel check when the + dev version landed. pyproject.toml stays the only place the version lives. + """ + root = project_root or pathlib.Path(__file__).resolve().parents[2] + text = (root / "pyproject.toml").read_text(encoding="utf-8") + match = re.search(r'^\[project\]\s.*?^version\s*=\s*"([^"]+)"', text, re.S | re.M) + if match is None: + raise AssertionError("no [project] version in pyproject.toml") + raw = match.group(1) + try: + from packaging.version import Version + + return str(Version(raw)) + except ImportError: # packaging is not a declared dependency + return re.sub(r"[-_.]?(dev|a|b|rc)(\d+)", r".\1\2", raw) + + def validate_installed_wheel( expected_version: Optional[str] = None, ) -> WheelImportCheck: @@ -62,6 +84,8 @@ def validate_installed_wheel( raise AssertionError(proc.stdout) version = lines[0] + if expected_version == "auto": + expected_version = project_version(project_root) if expected_version is not None and version != expected_version: raise AssertionError(f"expected version {expected_version!r}, got {version!r}") @@ -76,7 +100,11 @@ def validate_installed_wheel( def main(argv: Optional[List[str]] = None) -> int: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("--expected-version", default=None) + parser.add_argument( + "--expected-version", + default=None, + help="version the installed wheel must report, or 'auto' for pyproject.toml's", + ) args = parser.parse_args(argv) result = validate_installed_wheel(expected_version=args.expected_version) From 77519f2a62df570416723dea7165f4961d49eb0c Mon Sep 17 00:00:00 2001 From: NCIA-AlphaSparse Date: Mon, 28 Sep 2026 20:00:27 +0800 Subject: [PATCH 2/2] ci again --- tests/ci/test_packaging_metadata.py | 35 ++++++++++++++++++++++++++--- tools/ci/check_installed_wheel.py | 28 ++++++++++++++--------- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/tests/ci/test_packaging_metadata.py b/tests/ci/test_packaging_metadata.py index ef04629..a0d4537 100644 --- a/tests/ci/test_packaging_metadata.py +++ b/tests/ci/test_packaging_metadata.py @@ -15,11 +15,17 @@ """CPU-only packaging metadata checks.""" import re +import sys from pathlib import Path +import pytest + import flagsparse PROJECT_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(PROJECT_ROOT)) + +from tools.ci.check_installed_wheel import normalize_version # noqa: E402 def _read_text(path): @@ -57,9 +63,11 @@ def _extract_python_requires_from_setup_py(): def test_package_version_matches_metadata(): - version = flagsparse.__version__ - assert version == _extract_version_from_pyproject() - assert version == _extract_version_from_setup_py() + declared = _extract_version_from_pyproject() + # The two declarations are compared as written; the installed package reports + # the PEP 440 form of them (0.4.0-dev1 is installed as 0.4.0.dev1). + assert declared == _extract_version_from_setup_py() + assert flagsparse.__version__ == normalize_version(declared) def test_python_requires_matches_metadata(): @@ -75,3 +83,24 @@ def test_license_metadata_is_apache_2(): assert 'license = "Apache-2.0"' in pyproject assert 'license-files = ["LICENSE"]' in pyproject assert "Apache (Version 2.0)" in _read_text("README.md") + + +@pytest.mark.parametrize( + "raw,expected", + [ + ("0.4.0", "0.4.0"), + ("0.4.0-dev1", "0.4.0.dev1"), + ("0.4.0.dev1", "0.4.0.dev1"), + ("1.2.3rc1", "1.2.3rc1"), + ], +) +def test_declared_versions_normalize_like_a_built_wheel(raw, expected): + assert normalize_version(raw) == expected + + +def test_normalization_survives_a_missing_packaging_module(monkeypatch): + # packaging is not a declared dependency; the fallback must agree for the common case. + monkeypatch.setitem(sys.modules, "packaging", None) + monkeypatch.setitem(sys.modules, "packaging.version", None) + assert normalize_version("0.4.0-dev1") == "0.4.0.dev1" + assert normalize_version("0.4.0") == "0.4.0" diff --git a/tools/ci/check_installed_wheel.py b/tools/ci/check_installed_wheel.py index 25c4af1..a927bff 100644 --- a/tools/ci/check_installed_wheel.py +++ b/tools/ci/check_installed_wheel.py @@ -35,19 +35,12 @@ class WheelImportCheck: module_path: pathlib.Path -def project_version(project_root: Optional[pathlib.Path] = None) -> str: - """The version pyproject.toml declares, in the PEP 440 form a wheel reports. +def normalize_version(raw: str) -> str: + """The PEP 440 form a built wheel reports for a version written in pyproject.toml. - ``0.4.0-dev1`` is written that way in pyproject.toml but installs as - ``0.4.0.dev1``; comparing the raw text is what broke the wheel check when the - dev version landed. pyproject.toml stays the only place the version lives. + ``0.4.0-dev1`` is written that way but installs as ``0.4.0.dev1``; comparing the + raw text is what broke the wheel check when the dev version landed. """ - root = project_root or pathlib.Path(__file__).resolve().parents[2] - text = (root / "pyproject.toml").read_text(encoding="utf-8") - match = re.search(r'^\[project\]\s.*?^version\s*=\s*"([^"]+)"', text, re.S | re.M) - if match is None: - raise AssertionError("no [project] version in pyproject.toml") - raw = match.group(1) try: from packaging.version import Version @@ -56,6 +49,19 @@ def project_version(project_root: Optional[pathlib.Path] = None) -> str: return re.sub(r"[-_.]?(dev|a|b|rc)(\d+)", r".\1\2", raw) +def project_version(project_root: Optional[pathlib.Path] = None) -> str: + """The version pyproject.toml declares, normalized as a wheel reports it. + + pyproject.toml stays the only place the version lives. + """ + root = project_root or pathlib.Path(__file__).resolve().parents[2] + text = (root / "pyproject.toml").read_text(encoding="utf-8") + match = re.search(r'^\[project\]\s.*?^version\s*=\s*"([^"]+)"', text, re.S | re.M) + if match is None: + raise AssertionError("no [project] version in pyproject.toml") + return normalize_version(match.group(1)) + + def validate_installed_wheel( expected_version: Optional[str] = None, ) -> WheelImportCheck: