164 lines
6.2 KiB
Python
164 lines
6.2 KiB
Python
"""Command line for ``python build/run.py release``."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
_BUILD_ROOT = Path(__file__).resolve().parents[1]
|
|
if str(_BUILD_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_BUILD_ROOT))
|
|
|
|
from common.paths import REPO_ROOT # noqa: E402
|
|
from common.verify.wheel import ( # noqa: E402
|
|
DEFAULT_RUNTIME_CONFIG,
|
|
default_verification_video,
|
|
verify_wheel_assets,
|
|
verify_wheel_runtime,
|
|
)
|
|
from common.verify.wheel_contents import inspect_wheel # noqa: E402
|
|
from release.preflight import PreflightOptions, run_preflight # noqa: E402
|
|
|
|
|
|
def _cmd_preflight(argv: list[str]) -> int:
|
|
parser = argparse.ArgumentParser(
|
|
prog="build/run.py release preflight",
|
|
description="Gate a release locally: version, tests, wheel content, runtime.",
|
|
)
|
|
parser.add_argument("--version", help="release version (default: pyproject's dynamic version)")
|
|
parser.add_argument("--wheel-dir", type=Path, help="directory to inspect (default: SDK/<version>)")
|
|
parser.add_argument("--video", type=Path, help="clip for the end-to-end runtime check")
|
|
parser.add_argument("--config", default=DEFAULT_RUNTIME_CONFIG, help="config name for that check")
|
|
parser.add_argument("--frames", type=int, default=40, help="frames to process (default: 40)")
|
|
parser.add_argument("--skip-tests", action="store_true", help="skip pytest (recorded in the report)")
|
|
parser.add_argument("--longrun", action="store_true", help="include the slow long-run stability test")
|
|
parser.add_argument("--allow-dirty", action="store_true", help="allow a dirty working tree")
|
|
parser.add_argument("--force", action="store_true", help="allow re-staging an existing SDK/<version>")
|
|
parser.add_argument("--check-pypi", action="store_true", help="read PyPI and fail if the version exists")
|
|
parser.add_argument(
|
|
"--all-wheel-pythons",
|
|
action="store_true",
|
|
help="verify cp310/cp311/cp312 wheels, each in its own conda env",
|
|
)
|
|
parser.add_argument("--offline", action="store_true", help="reuse parent-env deps instead of PyPI")
|
|
parser.add_argument("--no-runtime", action="store_true", help="skip the isolated runtime check")
|
|
parser.add_argument("--no-assets", action="store_true", help="skip the bundled-asset check")
|
|
parser.add_argument("--keep-going", action="store_true", help="collect every failure instead of stopping")
|
|
parser.add_argument("--report-dir", type=Path, help="override build/release/reports")
|
|
args = parser.parse_args(argv)
|
|
|
|
options = PreflightOptions(
|
|
version=args.version,
|
|
wheel_dir=args.wheel_dir,
|
|
video=args.video,
|
|
config_name=args.config,
|
|
frames=args.frames,
|
|
skip_tests=args.skip_tests,
|
|
longrun=args.longrun,
|
|
allow_dirty=args.allow_dirty,
|
|
force=args.force,
|
|
check_pypi=args.check_pypi,
|
|
all_wheel_pythons=args.all_wheel_pythons,
|
|
offline=args.offline,
|
|
keep_going=args.keep_going,
|
|
report_dir=args.report_dir,
|
|
no_runtime=args.no_runtime,
|
|
no_assets=args.no_assets,
|
|
)
|
|
return run_preflight(options, repo_root=REPO_ROOT)
|
|
|
|
|
|
def _cmd_audit(argv: list[str]) -> int:
|
|
parser = argparse.ArgumentParser(
|
|
prog="build/run.py release audit",
|
|
description="Inspect one built wheel without a release context.",
|
|
)
|
|
parser.add_argument("wheel", type=Path, help="path to an .whl")
|
|
parser.add_argument("--expected-version", help="assert the wheel reports this version")
|
|
parser.add_argument(
|
|
"--strict-bundle",
|
|
action="store_true",
|
|
help="treat bundled-file drift from the current source tree as a failure",
|
|
)
|
|
parser.add_argument("--runtime", action="store_true", help="also install and run the wheel")
|
|
parser.add_argument("--assets", action="store_true", help="also assert bundled assets load")
|
|
parser.add_argument("--video", type=Path, help="clip for --runtime")
|
|
parser.add_argument("--config", default=DEFAULT_RUNTIME_CONFIG)
|
|
parser.add_argument("--frames", type=int, default=40)
|
|
parser.add_argument("--offline", action="store_true")
|
|
parser.add_argument(
|
|
"--python",
|
|
type=Path,
|
|
default=None,
|
|
help="interpreter to build the verification venv from (default: the running one)",
|
|
)
|
|
args = parser.parse_args(argv)
|
|
|
|
wheel = args.wheel
|
|
if not wheel.is_file():
|
|
print(f"[ERROR] wheel not found: {wheel}", file=sys.stderr)
|
|
return 2
|
|
|
|
exit_code = 0
|
|
|
|
result = inspect_wheel(
|
|
wheel,
|
|
expected_version=args.expected_version,
|
|
repo_root=REPO_ROOT,
|
|
strict_bundle=args.strict_bundle,
|
|
)
|
|
print(result.render())
|
|
if not result.ok:
|
|
exit_code = 1
|
|
|
|
if args.assets or args.runtime:
|
|
try:
|
|
print(verify_wheel_assets(wheel, python_executable=args.python))
|
|
except Exception as exc:
|
|
print(f"[FAIL] bundled assets: {type(exc).__name__}: {exc}")
|
|
exit_code = 1
|
|
|
|
if args.runtime:
|
|
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:
|
|
info = verify_wheel_runtime(
|
|
wheel,
|
|
video=video,
|
|
config_name=args.config,
|
|
frames=args.frames,
|
|
python_executable=args.python,
|
|
offline=args.offline,
|
|
)
|
|
print(f"[ok ] runtime: {info}")
|
|
except Exception as exc:
|
|
print(f"[FAIL] runtime: {type(exc).__name__}: {exc}")
|
|
exit_code = 1
|
|
|
|
return exit_code
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
argv = list(sys.argv[1:] if argv is None else argv)
|
|
if not argv or argv[0] in ("-h", "--help"):
|
|
print(__doc__)
|
|
print("Commands: preflight | audit")
|
|
return 0
|
|
|
|
command, rest = argv[0], argv[1:]
|
|
if command == "preflight":
|
|
return _cmd_preflight(rest)
|
|
if command == "audit":
|
|
return _cmd_audit(rest)
|
|
|
|
print(f"Unknown release command: {command!r}", file=sys.stderr)
|
|
print("Choose from: preflight, audit", file=sys.stderr)
|
|
return 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|