Files
orisys-sdk/docs/publishing.md
T
2026-09-12 15:49:28 +08:00

184 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 发布流程(构建 → 校验 → 发布)
本文档说明如何构建 orisys 的 Nuitka 二进制 wheel、如何在发布前用一条命令做完整门禁,以及当前仍需人工执行的发布步骤。
> 历史说明:`README.md` 与 `build/wheel/docker/run.ps1` 早就引用了本文件,但它此前并不存在。本文件补齐该引用。
---
## 0. 环境准备
推荐用 `py310` conda 环境执行构建与校验(它同时具备 pytest / twine / numba / Nuitka / open3d / PyQt5):
```powershell
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
```powershell
# 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`
```powershell
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 做静态断言(+ 可选运行时校验),不需要版本上下文。适合体检历史产物:
```powershell
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 应当一致)。
旧的导入冒烟测试仍然可用,行为不变:
```powershell
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. 发布(目前仍需人工)
阶段一不包含自动发布。当前人工步骤:
1. 用 `release preflight` 拿到 PASS(并保留 `preflight.json`)。
2. 把 `SDK/<version>/` 同步到 `orisys-sdk-release` 仓库的 `release/<version>/`。
3. GitHub:打 tag **`v<version>`** 并创建 Release,上传 6 个 wheel 作为附件。
- 注意历史 tag 里有一个 typo:`0.50`(与 `v0.5.2` 指向同一 commit)。
4. PyPI:先 TestPyPI 再正式仓库。
```powershell
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`。于是:
```python
>>> 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")` 定位内置数据**——它们会指向不存在的目录,静默返回「文件不存在」。请改用发行包定位:
```python
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(会联网安装依赖)。