exclude docs from wheel; harden bundle item handling; single-source version

This commit is contained in:
ykcheungab
2026-09-12 16:19:34 +08:00
parent 57bef85e32
commit 35af74f639
24 changed files with 508 additions and 157 deletions
-1
View File
@@ -8,7 +8,6 @@ Flow:
4. Feed the rectified frame into Sensor.get_img(..., already_processed=True)
5. Compute deformation/contact and visualize
SDK version: 0.5.2
"""
from __future__ import annotations
+15 -2
View File
@@ -28,14 +28,25 @@ def test_pyproject_dependencies(packaging_utils):
def test_pyproject_dynamic_version(packaging_utils):
"""The dynamic version must resolve to a valid version from one source.
Deliberately does NOT assert a literal version: pinning it here means every
release has to edit this file too. ``src/orisys/__init__.py::__version__`` is
the single source of truth, and build/release/version.py checks that the
wheels agree with it.
"""
import re
import sys
build_dir = REPO_ROOT / "build"
if str(build_dir) not in sys.path:
sys.path.insert(0, str(build_dir))
from common.pyproject_utils import get_version
from release.version import dynamic_version_attr
assert get_version(REPO_ROOT) == "0.5.2"
version = get_version(REPO_ROOT)
assert re.fullmatch(r"\d+(?:\.\d+)+(?:[-+][0-9A-Za-z.]+)?", version), version
assert dynamic_version_attr(REPO_ROOT) == "orisys.__version__"
def test_pyproject_bundle_manifest(packaging_utils):
@@ -48,7 +59,9 @@ def test_pyproject_bundle_manifest(packaging_utils):
items = bundle_items(REPO_ROOT)
names = {item["name"] for item in items}
assert names == {"configs", "examples", "docs", "sensors"}
# docs/ is intentionally not bundled: it ships with the GitHub repo and the
# PyPI project page only, so doc edits do not invalidate built wheels.
assert names == {"configs", "examples", "sensors"}
data_files = dict(bundle_data_files(REPO_ROOT))
assert "orisys/configs" in data_files
+115 -47
View File
@@ -30,7 +30,7 @@ version = {attr = "orisys.__version__"}
""".lstrip()
def _make_repo(tmp_path: Path, *, version: str = "0.5.2", with_git: bool = False) -> Path:
def _make_repo(tmp_path: Path, *, version: str = "1.2.3", with_git: bool = False) -> Path:
(tmp_path / "pyproject.toml").write_text(PYPROJECT, encoding="utf-8")
package = tmp_path / "src" / "orisys"
package.mkdir(parents=True)
@@ -63,16 +63,16 @@ class _FakeResponse:
def test_wheel_version_parsing():
assert version_mod.wheel_version(Path("orisys-0.5.2-cp310-cp310-win_amd64.whl")) == "0.5.2"
assert version_mod.wheel_version(Path("orisys-1.2.3-cp310-cp310-win_amd64.whl")) == "1.2.3"
assert version_mod.wheel_version(Path("orisys-1.0.0-1-cp312-abi3-manylinux_2_28_x86_64.whl")) == "1.0.0"
assert version_mod.wheel_version(Path("something-else.whl")) is None
def test_sdk_versions_are_numerically_sorted(tmp_path):
for name in ("0.9.0", "0.4.10", "0.5.2"):
for name in ("0.9.0", "0.4.10", "1.2.3"):
(tmp_path / "SDK" / name).mkdir(parents=True)
(tmp_path / "SDK" / "not-a-version").mkdir()
assert version_mod.sdk_versions(tmp_path)[:3] == ["0.4.10", "0.5.2", "0.9.0"]
assert version_mod.sdk_versions(tmp_path)[:3] == ["0.4.10", "0.9.0", "1.2.3"]
def test_sdk_versions_empty_without_sdk_dir(tmp_path):
@@ -81,10 +81,10 @@ def test_sdk_versions_empty_without_sdk_dir(tmp_path):
def test_sdk_wheels_lists_only_that_version(tmp_path):
repo = _make_repo(tmp_path)
_stage_wheel(repo, "0.5.2")
_stage_wheel(repo, "1.2.3")
_stage_wheel(repo, "0.4.3")
wheels = version_mod.sdk_wheels(repo, "0.5.2")
assert [w.name for w in wheels] == ["orisys-0.5.2-cp310-cp310-win_amd64.whl"]
wheels = version_mod.sdk_wheels(repo, "1.2.3")
assert [w.name for w in wheels] == ["orisys-1.2.3-cp310-cp310-win_amd64.whl"]
def test_dynamic_version_attr_read_from_pyproject(tmp_path):
@@ -98,12 +98,12 @@ def test_dynamic_version_attr_none_when_absent(tmp_path):
def test_pypi_released_versions_parses_response(tmp_path, monkeypatch):
fake = _FakeResponse({"releases": {"0.4.2": [{"filename": "a"}], "0.5.2": [{"filename": "b"}], "0.0.0": []}})
fake = _FakeResponse({"releases": {"0.4.2": [{"filename": "a"}], "1.2.3": [{"filename": "b"}], "0.0.0": []}})
monkeypatch.setattr(version_mod.urllib.request, "urlopen", lambda *a, **k: fake)
versions, error = version_mod.pypi_released_versions()
assert error is None
# An empty file list means nothing was ever uploaded for that version.
assert versions == ["0.4.2", "0.5.2"]
assert versions == ["0.4.2", "1.2.3"]
def test_pypi_released_versions_reports_http_404_as_empty(monkeypatch):
@@ -137,7 +137,7 @@ def test_python_for_tag_uses_envs_root(tmp_path):
def test_write_report_round_trips(tmp_path):
payload = {
"version": "0.5.2",
"version": "1.2.3",
"ok": False,
"git": {"commit": "abc", "branch": "dev", "dirty": True},
"interpreter": {"version": "3.10.19", "executable": "python"},
@@ -182,43 +182,43 @@ def test_repo_state_fails_outside_git(tmp_path, capsys):
def test_version_consistency_detects_mismatch(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
step = Preflight(PreflightOptions(version="9.9.9"), repo_root=repo)._version_consistency()
assert not step.ok
assert "0.5.2" in step.detail and "9.9.9" in step.detail
assert "1.2.3" in step.detail and "9.9.9" in step.detail
def test_version_consistency_passes_on_match(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
step = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)._version_consistency()
repo = _make_repo(tmp_path, version="1.2.3")
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._version_consistency()
assert step.ok, step.detail
def test_duplicate_guard_allows_staged_sdk_dir(tmp_path, monkeypatch):
"""A populated SDK/<version>/ is the normal preflight input, not a hazard."""
repo = _make_repo(tmp_path, version="0.5.2")
_stage_wheel(repo, "0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
_stage_wheel(repo, "1.2.3")
monkeypatch.setattr(version_mod, "git_tags", lambda root: [])
step = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)._duplicate_guard()
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._duplicate_guard()
assert step.ok, step.detail
assert "staged wheel(s) to verify" in step.detail
def test_duplicate_guard_blocks_released_tag(tmp_path, monkeypatch):
repo = _make_repo(tmp_path, version="0.5.2")
_stage_wheel(repo, "0.5.2")
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["v0.5.2", "v0.5.1"])
step = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)._duplicate_guard()
repo = _make_repo(tmp_path, version="1.2.3")
_stage_wheel(repo, "1.2.3")
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["v1.2.3", "v0.5.1"])
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._duplicate_guard()
assert not step.ok
assert "already mark 0.5.2 as released" in step.detail
assert "already mark 1.2.3 as released" in step.detail
assert "--force" in step.detail
def test_duplicate_guard_force_accepts_released_tag(tmp_path, monkeypatch):
repo = _make_repo(tmp_path, version="0.5.2")
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["0.5.2"])
repo = _make_repo(tmp_path, version="1.2.3")
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["1.2.3"])
step = Preflight(
PreflightOptions(version="0.5.2", force=True), repo_root=repo
PreflightOptions(version="1.2.3", force=True), repo_root=repo
)._duplicate_guard()
assert step.ok, step.detail
assert "accepted via --force" in step.detail
@@ -226,18 +226,18 @@ def test_duplicate_guard_force_accepts_released_tag(tmp_path, monkeypatch):
def test_duplicate_guard_reports_unreleased_version(tmp_path, monkeypatch):
repo = _make_repo(tmp_path, version="0.5.3")
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["v0.5.2"])
monkeypatch.setattr(version_mod, "git_tags", lambda root: ["v1.2.3"])
step = Preflight(PreflightOptions(version="0.5.3"), repo_root=repo)._duplicate_guard()
assert step.ok, step.detail
assert "no local git tag marks 0.5.3" in step.detail
def test_duplicate_guard_flags_published_version(tmp_path, monkeypatch):
repo = _make_repo(tmp_path, version="0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
monkeypatch.setattr(version_mod, "git_tags", lambda root: [])
monkeypatch.setattr(version_mod, "pypi_released_versions", lambda *a, **k: (["0.5.2"], None))
monkeypatch.setattr(version_mod, "pypi_released_versions", lambda *a, **k: (["1.2.3"], None))
step = Preflight(
PreflightOptions(version="0.5.2", check_pypi=True), repo_root=repo
PreflightOptions(version="1.2.3", check_pypi=True), repo_root=repo
)._duplicate_guard()
assert not step.ok
assert "already published on PyPI" in step.detail
@@ -246,7 +246,7 @@ def test_duplicate_guard_flags_published_version(tmp_path, monkeypatch):
def test_released_tags_for_matches_version_spellings():
tags = ["v0.5.1", "0.5.1", "v0.4.3", "0.50"]
assert version_mod.released_tags_for("0.5.1", tags) == ["0.5.1", "v0.5.1"]
assert version_mod.released_tags_for("0.5.2", tags) == []
assert version_mod.released_tags_for("1.2.3", tags) == []
def test_git_tags_reads_repo_tags(tmp_path):
@@ -261,13 +261,81 @@ def test_git_tags_reads_repo_tags(tmp_path):
(repo / "f.txt").write_text("x", encoding="utf-8")
subprocess.run(["git", "add", "f.txt"], cwd=repo, check=True, capture_output=True)
subprocess.run(["git", "commit", "-qm", "init"], cwd=repo, check=True, capture_output=True)
subprocess.run(["git", "tag", "v0.5.2"], cwd=repo, check=True, capture_output=True)
subprocess.run(["git", "tag", "v1.2.3"], cwd=repo, check=True, capture_output=True)
assert "v0.5.2" in version_mod.git_tags(repo)
assert "v1.2.3" in version_mod.git_tags(repo)
def test_version_string_scan_flags_stale_literal(tmp_path):
repo = tmp_path / "repo"
(repo / "docs").mkdir(parents=True)
(repo / "docs" / "a.md").write_text("版本:0.4.0\n", encoding="utf-8")
(repo / "README.md").write_text("release 1.2.3\n", encoding="utf-8")
hits = version_mod.find_version_literals(repo, "1.2.3")
assert [(path, line) for path, line, _ in hits] == [("docs/a.md", 1)]
def test_version_string_scan_ignores_dependency_constraints(tmp_path):
repo = tmp_path / "repo"
repo.mkdir()
(repo / "docs.md").write_text(
"pip install numpy>=1.24.4 scipy==1.13 opencv-python>=4.10.0.84\n",
encoding="utf-8",
)
assert version_mod.find_version_literals(repo, "1.2.3") == []
def test_version_string_scan_honours_allowlist(tmp_path, monkeypatch):
monkeypatch.setitem(
version_mod.VERSION_STRING_ALLOWLIST, "docs.md", frozenset({"0.4.0"})
)
repo = tmp_path / "repo"
repo.mkdir()
(repo / "docs.md").write_text("历史版本:0.4.0\n", encoding="utf-8")
assert version_mod.find_version_literals(repo, "1.2.3") == []
def test_version_string_scan_skips_tests_directory(tmp_path):
repo = tmp_path / "repo"
(repo / "tests").mkdir(parents=True)
(repo / "tests" / "test_x.py").write_text('v = "0.1.2"\n', encoding="utf-8")
assert version_mod.find_version_literals(repo, "1.2.3") == []
def test_version_string_scan_accepts_current_version_and_v_prefix(tmp_path):
repo = tmp_path / "repo"
repo.mkdir()
(repo / "README.md").write_text("current 1.2.3 and tag v1.2.3\n", encoding="utf-8")
assert version_mod.find_version_literals(repo, "1.2.3") == []
def test_preflight_version_strings_step_fails_on_leftover(tmp_path):
repo = _make_repo(tmp_path, version="1.2.3")
(repo / "docs").mkdir()
(repo / "docs" / "guide.md").write_text("版本:0.4.0\n", encoding="utf-8")
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._version_strings()
assert not step.ok
assert "0.4.0" in step.detail
assert "VERSION_STRING_ALLOWLIST" in step.detail
def test_preflight_version_strings_step_passes_on_clean_tree(tmp_path):
repo = _make_repo(tmp_path, version="1.2.3")
(repo / "README.md").write_text("no version literal here\n", encoding="utf-8")
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._version_strings()
assert step.ok, step.detail
def test_repo_has_no_stale_version_literals():
"""The real repository must satisfy its own version-string gate."""
from common.pyproject_utils import get_version
version = get_version(REPO_ROOT)
assert version_mod.find_version_literals(REPO_ROOT, version) == []
def test_bundle_precheck_fails_when_pattern_resolves_nothing(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
(repo / "pyproject.toml").write_text(
PYPROJECT
+ """
@@ -279,23 +347,23 @@ patterns = ["*.json"]
""",
encoding="utf-8",
)
step = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)._bundle_precheck()
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._bundle_precheck()
assert not step.ok
assert "resolved 0 files" in step.detail
def test_wheel_inspection_fails_without_wheels(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
step = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)._wheel_inspection()
repo = _make_repo(tmp_path, version="1.2.3")
step = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)._wheel_inspection()
assert not step.ok
assert "does not exist" in step.detail
def test_wheel_runtime_fails_without_video(tmp_path, monkeypatch):
repo = _make_repo(tmp_path, version="0.5.2")
_stage_wheel(repo, "0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
_stage_wheel(repo, "1.2.3")
monkeypatch.setattr("release.preflight.default_verification_video", lambda root: None)
preflight = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)
preflight = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)
preflight._discover_wheels()
step = preflight._wheel_runtime()
assert not step.ok
@@ -303,18 +371,18 @@ def test_wheel_runtime_fails_without_video(tmp_path, monkeypatch):
def test_wheel_runtime_skip_is_recorded(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
step = Preflight(
PreflightOptions(version="0.5.2", no_runtime=True), repo_root=repo
PreflightOptions(version="1.2.3", no_runtime=True), repo_root=repo
)._wheel_runtime()
assert step.ok
assert "SKIPPED" in step.detail
def test_tests_skip_is_loud_and_recorded(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
repo = _make_repo(tmp_path, version="1.2.3")
step = Preflight(
PreflightOptions(version="0.5.2", skip_tests=True), repo_root=repo
PreflightOptions(version="1.2.3", skip_tests=True), repo_root=repo
)._tests()
assert step.ok
assert "SKIPPED" in step.detail
@@ -322,8 +390,8 @@ def test_tests_skip_is_loud_and_recorded(tmp_path):
def test_execute_marks_failure_and_aborts(tmp_path, capsys):
repo = _make_repo(tmp_path, version="0.5.2")
preflight = Preflight(PreflightOptions(version="0.5.2"), repo_root=repo)
repo = _make_repo(tmp_path, version="1.2.3")
preflight = Preflight(PreflightOptions(version="1.2.3"), repo_root=repo)
preflight._execute(preflight._repo_state)
assert preflight.aborted
assert preflight.steps[-1].name == "repo-state"
@@ -331,7 +399,7 @@ def test_execute_marks_failure_and_aborts(tmp_path, capsys):
def test_execute_keep_going_does_not_abort(tmp_path):
repo = _make_repo(tmp_path, version="0.5.2")
preflight = Preflight(PreflightOptions(version="0.5.2", keep_going=True), repo_root=repo)
repo = _make_repo(tmp_path, version="1.2.3")
preflight = Preflight(PreflightOptions(version="1.2.3", keep_going=True), repo_root=repo)
preflight._execute(preflight._repo_state)
assert not preflight.aborted
+6 -6
View File
@@ -19,25 +19,25 @@ def wheel_package():
def test_compliant_manylinux_tags(wheel_package):
assert wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-manylinux_2_17_x86_64.whl")
Path("orisys-1.2.3-cp310-cp310-manylinux_2_17_x86_64.whl")
)
assert wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-manylinux2014_x86_64.whl")
Path("orisys-1.2.3-cp310-cp310-manylinux2014_x86_64.whl")
)
assert wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-manylinux_2_28_x86_64.whl")
Path("orisys-1.2.3-cp310-cp310-manylinux_2_28_x86_64.whl")
)
def test_rejects_illegal_manylinux_tags(wheel_package):
assert not wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-manylinux_x86_64.whl")
Path("orisys-1.2.3-cp310-cp310-manylinux_x86_64.whl")
)
assert not wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-linux_x86_64.whl")
Path("orisys-1.2.3-cp310-cp310-linux_x86_64.whl")
)
assert not wheel_package.is_compliant_manylinux_wheel(
Path("orisys-0.5.2-cp310-cp310-win_amd64.whl")
Path("orisys-1.2.3-cp310-cp310-win_amd64.whl")
)
+3 -3
View File
@@ -24,7 +24,7 @@ if str(BUILD_ROOT) not in sys.path:
from common.verify import wheel_contents # noqa: E402
from common.verify.wheel_contents import inspect_wheel # noqa: E402
VERSION = "0.5.2"
VERSION = "1.2.3"
DIST_INFO = f"orisys-{VERSION}.dist-info"
CORE_DEPS = ("numpy>=1.24.4", "opencv-python>=4.10.0.84")
@@ -201,13 +201,13 @@ def test_good_wheel_passes(tmp_path):
def test_missing_wheel_file_fails(tmp_path):
result = inspect_wheel(tmp_path / "nope-0.5.2-cp312-cp312-win_amd64.whl")
result = inspect_wheel(tmp_path / "nope-1.2.3-cp312-cp312-win_amd64.whl")
assert not result.ok
assert "wheel-exists" in _failed_names(result)
def test_unparsable_filename_fails(tmp_path):
wheel, repo = _make_wheel(tmp_path, filename="orisys-0.5.2-cp312-cp312.whl")
wheel, repo = _make_wheel(tmp_path, filename="orisys-1.2.3-cp312-cp312.whl")
result = inspect_wheel(wheel, repo_root=repo)
assert "filename" in _failed_names(result)
+3 -3
View File
@@ -1,7 +1,7 @@
"""Tests for build/wheel/package.py metadata plumbing.
The Nuitka wheel is built from a generated ``setup.py``, which is exactly where
the 0.5.2 release silently lost ``Requires-Python``, classifiers, project URLs
the early 0.5.x releases silently lost ``Requires-Python``, classifiers, project URLs
and the license. These tests run setuptools for real (in a temp dir, on a stub
package tree) and assert on the METADATA it produces.
"""
@@ -57,7 +57,7 @@ def _generate(wheel_dir: Path, tmp_path: Path) -> Path:
_stub_wheel_root(wheel_dir)
write_wheel_setup(
wheel_dir,
version="0.5.2",
version="1.2.3",
package_data={"orisys": ["*.pyd"]},
install_requires=["numpy>=1.24.4"],
extras_require={"gui": ["open3d>=0.17.0"]},
@@ -117,7 +117,7 @@ def test_generated_wheel_metadata_is_complete(tmp_path):
text = metadata_path.read_text(encoding="utf-8")
assert "Name: orisys" in text
assert "Version: 0.5.2" in text
assert "Version: 1.2.3" in text
assert f"Summary: {PROJECT['description']}" in text
assert "Requires-Python: >=3.10" in text
assert "License: Proprietary" in text