7.9 KiB
发布流程(构建 → 校验 → 发布)
本文档说明如何构建 orisys 的 Nuitka 二进制 wheel、如何在发布前用一条命令做完整门禁,以及当前仍需人工执行的发布步骤。
历史说明:
README.md与build/wheel/docker/run.ps1早就引用了本文件,但它此前并不存在。本文件补齐该引用。
0. 环境准备
推荐用 py310 conda 环境执行构建与校验(它同时具备 pytest / twine / numba / Nuitka / open3d / PyQt5):
conda activate py310
python -m pytest -m "not slow and not hardware" # 开发期快速回归
py311、py312 仅作为 Nuitka 各 ABI 的编译环境按需调用(build/wheel/win.bat 会依次使用 py310/py311/py312)。
不要在同一个环境里既装旧版 orisys 又从源码目录跑测试:site-packages 里的旧包会静默遮蔽 src/orisys。
tests/test_harness_guards.py 会显式报错提示执行 pip uninstall -y orisys。
1. 构建 wheel
# Windows:py310 / py311 / py312 三个 ABI
python build/run.py wheel-win
# 单个解释器(当前环境)
python build/run.py wheel
# Linux manylinux(Docker Desktop,Linux 容器模式)
python build/run.py manylinux
python build/run.py wheel-manylinux -PythonVersions 3.10
产物落点:
SDK/<version>/— 交付用 wheel(git 忽略)dist/repaired/— 上传候选(git 忽略)
2. 发布前门禁:release preflight
python build/run.py release preflight --version 0.5.2
按顺序执行 7 个步骤,任一步失败即整体失败(--keep-going 可改为收集全部失败):
| 步骤 | 检查内容 |
|---|---|
repo-state |
是 git 仓库、工作树干净、记录 HEAD(--allow-dirty 可豁免) |
version-consistency |
src/orisys/__init__.py::__version__ == pyproject 的 dynamic attr == SDK/<version>/ 中 wheel 文件名里的版本 |
duplicate-guard |
SDK/<version>/ 已有产物则拒绝(--force 豁免);--check-pypi 时只读查询 PyPI,版本已存在则拒绝 |
tests |
pytest -m "not slow and not hardware"(--longrun 时包含 slow) |
bundle-precheck |
每个 [[tool.orisys.bundle.items]] 至少解析出 1 个文件;finger 必需资产存在 |
wheel-inspection |
逐个 wheel 做静态断言:文件名/版本/METADATA 字段/RECORD 哈希/压缩包路径/体积上限/无 pyc 与缓存/无 orisys/**/*.py 源码泄漏/与源码树逐文件字节一致 |
wheel-runtime |
在一次性 venv 中安装该 wheel,断言内置 configs/sensors 资产可加载,并对真实视频跑 40 帧公开流水线(无需相机、无 GUI) |
产物报告:
build/release/reports/<version>/preflight.json— 机器可读,含每个 wheel 的 sha256,供后续发布步骤复用build/release/reports/<version>/preflight.txt— 人工阅读build/release/reports/<version>/junit.xml— pytest 结果
(build/release/reports/ 已加入 .gitignore。)
常用参数
| 参数 | 作用 |
|---|---|
--wheel-dir DIR |
校验指定目录下的 wheel(默认 SDK/<version>) |
--all-wheel-pythons |
cp310/cp311/cp312 各自在对应 conda 环境里独立校验 |
--video PATH / --frames N / --config NAME |
覆盖运行时校验输入(默认 data/sample/s1_480p.mp4 / 40 帧 / box) |
--offline |
复用父环境的 numpy/opencv/numba,不去 PyPI 装依赖 |
--check-pypi |
唯一一处网络调用:只读 PyPI,防止重复发布 |
--skip-tests / --no-runtime / --no-assets |
显式跳过并在报告中留痕;跳过的运行不能作为发布依据 |
--report-dir DIR |
覆盖报告根目录 |
wheel-runtime 不会静默跳过:找不到视频时直接失败,提示 --video / ORISYS_VERIFY_VIDEO。
单体体检:release audit
对单个 wheel 做静态断言(+ 可选运行时校验),不需要版本上下文。适合体检历史产物:
python build/run.py release audit SDK\0.5.2\orisys-0.5.2-cp312-cp312-win_amd64.whl
python build/run.py release audit dist\repaired\orisys-0.5.2-cp310-cp310-win_amd64.whl --strict-bundle
# 追加运行时校验
python build/run.py release audit <wheel> --assets --runtime --video data/sample/s1_480p.mp4
默认对「内置文件与当前源码树不一致」只告警(历史 wheel 与当前分支不一致是正常的);加 --strict-bundle 改成失败(刚构建完的 wheel 应当一致)。
旧的导入冒烟测试仍然可用,行为不变:
python build/run.py verify <wheel>
python build/run.py verify <wheel> --contents # 静态断言
python build/run.py verify <wheel> --assets --runtime # 资产 + 真实帧
python build/run.py verify --gui-dist --product ory_finger
3. 发布(目前仍需人工)
阶段一不包含自动发布。当前人工步骤:
- 用
release preflight拿到 PASS(并保留preflight.json)。 - 把
SDK/<version>/同步到orisys-sdk-release仓库的release/<version>/。 - GitHub:打 tag
v<version>并创建 Release,上传 6 个 wheel 作为附件。- 注意历史 tag 里有一个 typo:
0.50(与v0.5.2指向同一 commit)。
- 注意历史 tag 里有一个 typo:
- PyPI:先 TestPyPI 再正式仓库。
python -m twine upload --repository testpypi SDK/0.5.2/*.whl
python -m twine upload SDK/0.5.2/*.whl
不要把 API token 写进仓库。release preflight --check-pypi 只做只读查询,不需要 token。
已知的历史发布空洞
| 版本 | 本地 SDK/ |
PyPI | GitHub Release |
|---|---|---|---|
| 0.4.2 | — | 有 | 有 |
| 0.4.3 | — | 有 | 有 |
| 0.4.4 | 有 | 无 | 无 |
| 0.5.0 | 有 | 无 | 无 |
| 0.5.2 | 有 | 有 | 有(tag v0.5.2 + typo tag 0.50) |
4. 环境变量
| 变量 | 用途 |
|---|---|
ORISYS_TEST_VIDEO |
pytest 用的视频;默认自动取 data/sample/s1_480p.mp4 |
ORISYS_TEST_CONFIG |
pytest 用的配置名;默认自动挑一个含 stitcher.box_size 的配置 |
ORISYS_VERIFY_VIDEO |
运行时校验用的视频 |
ORISYS_VERIFY_CONFIG |
运行时校验用的配置名(默认 box) |
ORISYS_LONGRUN_BACKEND / ORISYS_LONGRUN_FRAMES |
slow 长稳测试的参数 |
CONDA_ENVS_ROOT |
conda envs 根目录(--all-wheel-pythons 用它解析 py310/py311/py312) |
5. 两个必须记住的坑
5.1 Nuitka 编译包的 __file__ 是假的
Nuitka 把整个 orisys 编译成单个扩展模块,其 __init__ 会安装 nuitka_module_loader。于是:
>>> import orisys
>>> orisys.__file__ # 合成的、磁盘上并不存在的路径
'...\\site-packages\\orisys\\orisys\\__init__.py'
>>> orisys.__path__ # 第二项是 Nuitka 的哨兵值
['...\\site-packages\\orisys\\orisys', '\\not_existing']
>>> orisys.__loader__.__class__.__name__
'nuitka_module_loader'
后果:在 Nuitka wheel 里不能靠 orisys.__file__ 或 importlib.resources.files("orisys") 定位内置数据——它们会指向不存在的目录,静默返回「文件不存在」。请改用发行包定位:
from importlib.metadata import distribution
root = distribution("orisys").locate_file("orisys") # site-packages/orisys
build/common/verify/wheel.py 的内置资产校验就是这么做的,并且会显式断言该目录位于校验用 venv 内,避免被父环境里的旧 orisys 顶替。
5.2 --offline 会被父环境的脏安装污染
--offline 通过 venv 的 --system-site-packages 复用父环境的 numpy/numba 等依赖,这也顺带暴露了父环境里的 orisys。若父环境残留了半卸载的 editable 安装(典型症状是 pip 提示 Ignoring invalid distribution -risys,且 site-packages/orisys/__init__.py 是一个指向死路径的 shim),校验就会读到它。
处置:在构建环境里执行 pip uninstall -y orisys,或改用不带 --offline 的完全隔离 venv(会联网安装依赖)。