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
+26 -1
View File
@@ -14,6 +14,7 @@ from common.paths import REPO_ROOT # noqa: E402
from common.verify.wheel import ( # noqa: E402
DEFAULT_RUNTIME_CONFIG,
default_verification_video,
verify_wheel,
verify_wheel_assets,
verify_wheel_runtime,
)
@@ -116,7 +117,31 @@ def _cmd_audit(argv: list[str]) -> int:
if not result.ok:
exit_code = 1
if args.assets or args.runtime:
if args.assets and args.runtime:
# One venv for both checks: building it (and downloading dependencies in
# online mode) is the expensive part.
video = args.video or default_verification_video(REPO_ROOT)
if video is None:
print("[FAIL] runtime: no verification clip; pass --video or set ORISYS_VERIFY_VIDEO")
return 1
try:
record = verify_wheel(
wheel,
video=video,
config_name=args.config,
frames=args.frames,
python_executable=args.python,
offline=args.offline,
)
print(f"[ok ] {record['imports']}")
print(f"[ok ] {record['assets']}")
print(f"[ok ] runtime: frames={record['frames']} flow={record['flow_shape']}")
except Exception as exc:
print(f"[FAIL] isolated verification: {type(exc).__name__}: {exc}")
exit_code = 1
return exit_code
if args.assets:
try:
print(verify_wheel_assets(wheel, python_executable=args.python))
except Exception as exc:
+49 -16
View File
@@ -8,12 +8,13 @@ Steps, in order:
1. ``repo-state`` - git repository, clean working tree, recorded commit
2. ``version-consistency`` - __version__ == pyproject dynamic attr == wheel names
3. ``duplicate-guard`` - SDK/<version>/ already populated; optionally PyPI
4. ``tests`` - pytest (slow/hardware markers excluded)
5. ``bundle-precheck`` - every bundle item resolves; required assets present
6. ``wheel-inspection`` - static metadata/content assertions per wheel
7. ``wheel-runtime`` - isolated venv: bundled assets load and real frames
flow through the public pipeline
3. ``version-strings`` - no stale version literal left in tracked prose
4. ``duplicate-guard`` - already-released version (git tag, optionally PyPI)
5. ``tests`` - pytest (slow/hardware markers excluded)
6. ``bundle-precheck`` - every bundle item resolves; required assets present
7. ``wheel-inspection`` - static metadata/content assertions per wheel
8. ``wheel-runtime`` - one isolated venv per wheel: bundled assets load and
real frames flow through the public pipeline
Output lands in ``build/release/reports/<version>/preflight.{json,txt}``.
"""
@@ -39,8 +40,7 @@ from common.pyproject_utils import bundle_items, get_version # noqa: E402
from common.verify.wheel import ( # noqa: E402
DEFAULT_RUNTIME_CONFIG,
default_verification_video,
verify_wheel_assets,
verify_wheel_runtime,
verify_wheel,
)
from common.verify.wheel_contents import inspect_wheel # noqa: E402
from release import report as report_mod # noqa: E402
@@ -108,7 +108,9 @@ class Preflight:
self.version = get_version(self.repo_root)
self.version_source = "pyproject.toml dynamic attr"
else:
self.version = "0.0.0"
# Not version-shaped on purpose: this is a sentinel for "unresolved",
# and it must not look like a release version to the version-string scan.
self.version = "unknown"
self.version_source = "unresolved"
self.report_dir = report_mod.reports_root(self.repo_root, options.report_dir) / self.version
@@ -270,6 +272,34 @@ class Preflight:
detail += "\n" + "\n".join(problems)
return Step("duplicate-guard", not problems, detail)
def _version_strings(self) -> Step:
"""Keep the version out of prose: one source, everything else derived.
Scans tracked text for version-looking literals that disagree with the
release version. Dependency constraints are ignored by design; deliberate
exceptions live in ``version.VERSION_STRING_ALLOWLIST``.
"""
findings = version_mod.find_version_literals(self.repo_root, self.version)
if not findings:
scanned = len(version_mod.tracked_text_files(self.repo_root))
return Step(
"version-strings",
True,
f"no stale version literal in {scanned} scanned file(s); "
f"version {self.version} lives only where it is derived from",
)
by_file: dict[str, list[str]] = {}
for path, lineno, literal in findings:
by_file.setdefault(path, []).append(f"{path}:{lineno} -> {literal}")
detail = (
f"{len(findings)} version literal(s) disagree with {self.version}. "
"Update them, or make the file version-agnostic, or add a documented "
"entry to build/release/version.py::VERSION_STRING_ALLOWLIST:\n"
+ "\n".join(line for lines in by_file.values() for line in lines)
)
return Step("version-strings", False, detail, data={"count": len(findings)})
def _tests(self) -> Step:
if self.options.skip_tests:
return Step(
@@ -439,17 +469,16 @@ class Preflight:
for tag, wheel, executable in targets:
label = f"{tag} {wheel.name}"
try:
if self.options.no_assets:
assets_out = "asset check skipped (--no-assets)"
else:
assets_out = verify_wheel_assets(wheel, python_executable=executable)
runtime_out = verify_wheel_runtime(
# One venv per wheel: it runs the import smoke test, the bundled
# asset check and the frame pipeline together.
record = verify_wheel(
wheel,
video=video,
config_name=self.options.config_name,
frames=self.options.frames,
python_executable=executable,
offline=self.options.offline,
check_assets=not self.options.no_assets,
)
except subprocess.CalledProcessError as exc:
problems.append(f"{label}: command failed ({exc})")
@@ -458,8 +487,11 @@ class Preflight:
problems.append(f"{label}: {type(exc).__name__}: {exc}")
continue
lines.append(f"{label}: {assets_out} | frames={runtime_out['frames']} flow={runtime_out['flow_shape']}")
self.runtime_records.append({"tag": tag, "wheel": wheel.name, **runtime_out})
lines.append(
f"{label}: {record['assets']} | frames={record['frames']} "
f"flow={record['flow_shape']}"
)
self.runtime_records.append({"tag": tag, **record})
detail = "\n".join(lines)
if problems:
@@ -480,6 +512,7 @@ class Preflight:
for step_fn in (
self._repo_state,
self._version_consistency,
self._version_strings,
self._duplicate_guard,
self._tests,
self._bundle_precheck,
+91
View File
@@ -143,6 +143,97 @@ def released_tags_for(version: str, tags: list[str]) -> list[str]:
return sorted({version, f"v{version}"} & set(tags))
# Version literals that are deliberately not the package version. Keyed by
# repo-relative path; each entry documents *why* the literal may stay.
VERSION_STRING_ALLOWLIST: dict[str, frozenset[str]] = {
# Historical release records: rewriting these would destroy the audit trail.
"docs/publishing.md": frozenset({"0.4.2", "0.4.3", "0.4.4", "0.5.0", "0.5.1", "0.5.2"}),
# Unrelated third-party/CUDA versions quoted in prose.
"docs/get_started.md": frozenset({"12.9.0"}),
}
# Paths never scanned: tests deliberately use synthetic versions, reports are
# generated output, and this module's own allowlist/sentinel literals would
# otherwise flag themselves.
VERSION_SCAN_EXCLUDED_PREFIXES = (
"tests/",
"build/release/reports/",
"build/nuitka/",
"build/lib/",
"dist/",
"SDK/",
"data/",
)
VERSION_SCAN_EXCLUDED_FILES = ("build/release/version.py",)
_VERSION_LITERAL_RE = re.compile(
r"(?<![<>=!~\w.])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.]+)?)(?![0-9A-Za-z.])"
)
_SCANNED_SUFFIXES = (".md", ".py", ".txt", ".toml", ".cfg", ".ini", ".rst")
def tracked_text_files(repo_root: Path) -> list[Path]:
"""Tracked files worth scanning for version literals."""
repo_root = Path(repo_root)
try:
completed = subprocess.run(
["git", "-C", str(repo_root), "ls-files", "-z"],
capture_output=True,
text=True,
check=False,
)
names = [n for n in (completed.stdout or "").split("\0") if n]
except OSError:
names = []
if not names:
names = [
str(path.relative_to(repo_root)).replace("\\", "/")
for path in repo_root.rglob("*")
if path.is_file()
]
selected = []
for name in names:
if name.startswith(VERSION_SCAN_EXCLUDED_PREFIXES):
continue
if name in VERSION_SCAN_EXCLUDED_FILES:
continue
if not name.endswith(_SCANNED_SUFFIXES):
continue
selected.append(Path(name))
return selected
def find_version_literals(
repo_root: Path, version: str
) -> list[tuple[str, int, str]]:
"""``(path, lineno, literal)`` for every version-looking string != ``version``.
Dependency constraints are skipped by the regex lookbehind (``numpy>=1.24.4``
is not a version reference), and historical/third-party literals require an
explicit entry in :data:`VERSION_STRING_ALLOWLIST`.
"""
repo_root = Path(repo_root)
findings: list[tuple[str, int, str]] = []
for relative in tracked_text_files(repo_root):
absolute = repo_root / relative
try:
text = absolute.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
if "\0" in text:
continue
allowed = VERSION_STRING_ALLOWLIST.get(relative.as_posix(), frozenset())
for lineno, line in enumerate(text.splitlines(), start=1):
for match in _VERSION_LITERAL_RE.finditer(line):
literal = match.group(1)
if literal == version or literal in allowed:
continue
findings.append((relative.as_posix(), lineno, literal))
return findings
def conda_envs_root() -> Path | None:
"""Locate the conda envs directory the same way build/wheel/win.bat does."""
override = os.getenv("CONDA_ENVS_ROOT")