matelab-python-sdk 0.2.0a9__tar.gz → 0.3.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matelab_python_sdk-0.3.0a1/.agents/skills/matelab-sdk-release/SKILL.md +89 -0
- matelab_python_sdk-0.3.0a1/.agents/skills/matelab-sdk-release/agents/openai.yaml +4 -0
- matelab_python_sdk-0.3.0a1/.agents/skills/matelab-sdk-release/scripts/verify_pypi.py +165 -0
- matelab_python_sdk-0.3.0a1/.python-version +1 -0
- matelab_python_sdk-0.3.0a1/.workflow/release.yml +89 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/PKG-INFO +8 -8
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/README.md +4 -4
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.lock.json +3 -3
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.openapi.yaml +26 -26
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/docs/agent-workflows.md +9 -9
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/docs/api.md +16 -14
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/docs/development.md +9 -2
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/docs/operation-coverage.yaml +1 -1
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/pyproject.toml +8 -8
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/scripts/generate_models.py +1 -1
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/models.py +5 -5
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/_transport.py +6 -8
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/_wire_validation.py +4 -5
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/records.py +6 -7
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/uploads.py +2 -2
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_release.py +2 -2
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_streaming.py +1 -1
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_transport.py +1 -2
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_uploads_and_literature.py +1 -1
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/uv.lock +157 -243
- matelab_python_sdk-0.2.0a9/.github/workflows/release.yml +0 -119
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/.env.example +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/.gitignore +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/AGENTS.md +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/CONTEXT.md +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/LICENSE +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/NOTICE +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/docs/agent-architecture.md +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/scripts/check_installed_package.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/scripts/check_release.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/_generated/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/_hashes.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/client.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/__init__.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/_attachment_content.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/groups.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/literature.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/templates.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/domains/users.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/errors.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/py.typed +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/session.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/src/matelab/streaming.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/conftest.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/provider/test_provider_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_async_attachment_upload.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_client.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_cloud_drive.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_comments_and_downloads.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_consumer_smoke.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_errors.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_extended_records.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_generation.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_groups.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_notebook_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_notebooks.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_operation_coverage.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_record_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_record_mutations.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_records.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_template_lifecycle.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_templates.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/test_users.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/typing/consumer.py +0 -0
- {matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/tests/typing/pyproject.toml +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: matelab-sdk-release
|
|
3
|
+
description: 完成 matelab-python-sdk 的 spec 增量同步、必要适配、完整验证、审查和 PyPI 发布。用于该项目的完整交付;仅同步、验证或提交推送时遵守用户限定的终点。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Matelab SDK 完整交付
|
|
7
|
+
|
|
8
|
+
在包含本 skill 的 `matelab-python-sdk` 仓库根目录执行。
|
|
9
|
+
以下项目路径均相对此仓库。先读适用 `AGENTS.md`;具体规范以仓库当前文件为准。
|
|
10
|
+
|
|
11
|
+
## 调用范围
|
|
12
|
+
|
|
13
|
+
- 用户要求执行 `$matelab-sdk-release` 且未限制阶段时,完成同步、适配、测试、审查、提交、推送、tag 和
|
|
14
|
+
PyPI 发布;可附目标版本。不重复索取已覆盖目标和副作用的授权。
|
|
15
|
+
- “只同步”“只验证”“仅提交推送”等限制优先,停在相应阶段;解释、制作或修改本技能不执行发布。
|
|
16
|
+
- 待发布内容包括相对上次已发布 tag 的提交及工作区改动,不能仅凭工作区干净就认定无变更。
|
|
17
|
+
无待发布内容且当前版本已发布时,核对状态后结束;目标 tag 已推送时,优先继续其 CI/PyPI 核验。
|
|
18
|
+
- 依赖更新仅在用户要求时进行。真实业务 mutation、生产访问及新增发布目标不属于默认范围。
|
|
19
|
+
|
|
20
|
+
## 1. 定界与 spec 增量
|
|
21
|
+
|
|
22
|
+
先检查 status、当前版本、Contract lock、最近发布 tag 和实际 remotes,保留用户已有改动。
|
|
23
|
+
按 `docs/agent-workflows.md#contract-adoption` 选择新的远端 annotated spec tag,固定 tag object、peeled commit、
|
|
24
|
+
snapshot 和 SHA-256。
|
|
25
|
+
|
|
26
|
+
比较相对当前 pin 的 request、response、security、examples、成功码及 PVD/PCG,只适配本次增量。
|
|
27
|
+
即使仅 description/examples 改变,也检查现有 public 输入能否表达新能力。专用 schema 与顶层请求可能服务不同
|
|
28
|
+
流程,先核对适用范围,再判断是否确有契约矛盾;把 SDK 尚未实现的能力如实归为 SDK 缺项。
|
|
29
|
+
|
|
30
|
+
从 release commit 复制快照、更新 lock、运行生成脚本,审查生成差异;同步受影响的 adapter、public 类型、测试、
|
|
31
|
+
coverage 和当前态文档。公开接口和生命周期沿 public intent → wire operation → Provider evidence 核对。
|
|
32
|
+
按需读 `docs/agent-architecture.md`,邻接仓库仅作只读证据,不运行 spec 测试或修改其配置。
|
|
33
|
+
|
|
34
|
+
## 2. 验证与审查
|
|
35
|
+
|
|
36
|
+
执行 `docs/agent-workflows.md#delivery-gate`,具体命令取 `docs/development.md#development`。
|
|
37
|
+
代码、契约、依赖或构建行为变化先聚焦再完整测试;聚焦用例不能代替完整 `pytest`。修复需补充的用例来自契约、
|
|
38
|
+
独立示例或可验证属性,不复刻实现,不增加 Provider 逐路由 conformance。
|
|
39
|
+
|
|
40
|
+
真实 smoke 按 `docs/agent-workflows.md#provider-validation` 确认目标、ignore 和授权。授权覆盖隔离服务的登录、
|
|
41
|
+
Session 恢复、刷新和查询时,完整测试显式加载 SDK 自己的配置:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv run --frozen --env-file .env.test pytest -ra
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
否则运行不加载 Provider 连接变量的完整测试,明确报告 smoke 的 skip 及原因;用户要求的实时验证缺失时不能声称
|
|
48
|
+
已经完成。区分 MockTransport 的 SDK 映射证据与真实服务证据,禁止将凭证或敏感 payload 打印或提交。
|
|
49
|
+
|
|
50
|
+
对实质代码差异使用系统 `review-agent` skill,委派一个全新、只读子代理(`fork_turns: "none"`)。根代理处理
|
|
51
|
+
可证实问题并验证受影响部分。复用仍覆盖当前差异的完整测试、静态检查和审查结果,记录命令、结果及其覆盖的
|
|
52
|
+
commit/diff;只改版本后补版本一致性检查,不重跑未受影响的全套。
|
|
53
|
+
|
|
54
|
+
## 3. 收敛并一次提交
|
|
55
|
+
|
|
56
|
+
按 `docs/development.md#versioning` 选择兼容的下个 SDK 版本;使用用户指定版本时先检查 tag 和 PyPI 是否占用。
|
|
57
|
+
SDK 版本与 spec 日历版本独立,不跳过未占用的预发布序号。`pyproject.toml` 是唯一版本来源,更新 `uv.lock` 的
|
|
58
|
+
本项目版本。仅改版本时保持其余 lock 内容,使用发布 workflow 固定的 uv 检查兼容性。
|
|
59
|
+
|
|
60
|
+
按需读配置/依赖章节;不因发布顺手升级依赖、Node、uv 或流水线插件。文档在提交前完成,仅维护受影响的当前态,不新建
|
|
61
|
+
CHANGELOG。正常发布由 CI 完成可复现构建、制品审计和安装后消费;只有打包故障或打包配置变化需要时才本地构建,
|
|
62
|
+
使用临时目录,不积累 `dist/`。
|
|
63
|
+
|
|
64
|
+
复核最终 diff、版本、Contract pin 和有效验证证据,将本次尚未提交的实现、测试、文档及版本号放入同一个
|
|
65
|
+
release commit。已有提交的历史仅在另有授权时改写。
|
|
66
|
+
|
|
67
|
+
## 4. 推送、发布并核验
|
|
68
|
+
|
|
69
|
+
执行 `docs/agent-workflows.md#release`:创建指向 release commit 的 annotated `v{version}` tag,推送
|
|
70
|
+
`origin` 的 main/tag,并验证远端 refs;tag 推送自动触发发布 CI。跟踪该 commit/tag 的 CI,确认测试、
|
|
71
|
+
可复现构建、制品审计、安装后消费和 PyPI 上传成功;tag 推送成功不等于发布完成。
|
|
72
|
+
|
|
73
|
+
从本次 CI 构建审计输出或下载的制品取得 wheel/sdist 的 SHA-256,不能仅用 PyPI 自报哈希证明 CI/PyPI 一致。
|
|
74
|
+
在 SDK 锁定开发环境中运行本技能的只读核验器:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uv run --frozen --no-sync python -B .agents/skills/matelab-sdk-release/scripts/verify_pypi.py \
|
|
78
|
+
--repo "$PWD" --ref RELEASE_COMMIT \
|
|
79
|
+
--sha256 'WHEEL_FILENAME=CI_SHA256' --sha256 'SDIST_FILENAME=CI_SHA256'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
参数替换为这次发布的 commit、文件名和 CI 哈希。
|
|
83
|
+
纯核验使用现有环境;`--no-sync` 禁止自动同步虚拟环境,`-B` 禁止写入字节码,环境缺失时如实报告。
|
|
84
|
+
[核验器](scripts/verify_pypi.py) 在内存中下载制品,比对发布提交的源码、配置、版本、依赖和 README,不构建或保留
|
|
85
|
+
历史制品。PyPI 暂未可读时允许短暂重试,等待期间保持进度更新。
|
|
86
|
+
|
|
87
|
+
失败时定位具体阶段,优先重试失败环节;不通过删测试、跳检查或自动另选版本制造成功。改变已推送历史或处理失败
|
|
88
|
+
候选前核对现有授权,已发布版本不可覆盖。最后报告版本/链接、单次提交、`origin` 的 main/tag refs、CI/PyPI 核验和工作区状态;
|
|
89
|
+
明确剩余失败或未验证项。
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Read-only comparison of a Matelab SDK release, CI hashes and PyPI artifacts."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import hashlib
|
|
5
|
+
import io
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
import subprocess
|
|
9
|
+
import tarfile
|
|
10
|
+
import tomllib
|
|
11
|
+
import zipfile
|
|
12
|
+
from email.message import Message
|
|
13
|
+
from email.parser import BytesParser
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from urllib.parse import quote, urlsplit
|
|
16
|
+
from urllib.request import urlopen
|
|
17
|
+
|
|
18
|
+
from packaging.requirements import Requirement
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def require(condition: bool, message: str) -> None:
|
|
22
|
+
if not condition:
|
|
23
|
+
raise ValueError(message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def git_bytes(repo: Path, commit: str, path: str) -> bytes:
|
|
27
|
+
return subprocess.check_output(["git", "-C", str(repo), "show", f"{commit}:{path}"])
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def ci_hash(value: str) -> tuple[str, str]:
|
|
31
|
+
name, separator, digest = value.partition("=")
|
|
32
|
+
if not separator or not name or re.fullmatch(r"[a-fA-F0-9]{64}", digest) is None:
|
|
33
|
+
raise argparse.ArgumentTypeError("Expected FILENAME=64_HEX_SHA256 from this release's CI artifacts")
|
|
34
|
+
return name, digest.lower()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def metadata_matches(metadata: Message, project: dict, readme: str) -> None:
|
|
38
|
+
require(metadata["Version"] == project["version"], "Artifact version mismatch")
|
|
39
|
+
require(metadata["Requires-Python"] == project["requires-python"], "Artifact Python requirement mismatch")
|
|
40
|
+
expected = {Requirement(value) for value in project["dependencies"]}
|
|
41
|
+
actual = {Requirement(value) for value in metadata.get_all("Requires-Dist", [])}
|
|
42
|
+
require(actual == expected, "Artifact dependency mismatch")
|
|
43
|
+
require(metadata["License-Expression"] == project["license"], "Artifact license mismatch")
|
|
44
|
+
author = ", ".join(entry["name"] for entry in project["authors"])
|
|
45
|
+
require(metadata["Author"] == author, "Artifact author mismatch")
|
|
46
|
+
description = metadata.get_payload()
|
|
47
|
+
require(isinstance(description, str) and description.strip() == readme.strip(), "Artifact README mismatch")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def verify_wheel(payload: bytes, repo: Path, commit: str, project: dict, readme: str, tracked: set[str]) -> int:
|
|
51
|
+
package = {path.removeprefix("src/"): path for path in tracked if path.startswith("src/matelab/")}
|
|
52
|
+
metadata_dir = f"matelab_python_sdk-{project['version']}.dist-info"
|
|
53
|
+
with zipfile.ZipFile(io.BytesIO(payload)) as archive:
|
|
54
|
+
names = archive.namelist()
|
|
55
|
+
require(len(names) == len(set(names)), "Duplicate wheel members")
|
|
56
|
+
require({name for name in names if name.startswith("matelab/")} == set(package), "Wheel package file mismatch")
|
|
57
|
+
for name, source in package.items():
|
|
58
|
+
require(archive.read(name) == git_bytes(repo, commit, source), f"Wheel source mismatch: {source}")
|
|
59
|
+
metadata_matches(BytesParser().parsebytes(archive.read(f"{metadata_dir}/METADATA")), project, readme)
|
|
60
|
+
for license_file in project["license-files"]:
|
|
61
|
+
require(
|
|
62
|
+
archive.read(f"{metadata_dir}/licenses/{license_file}") == git_bytes(repo, commit, license_file),
|
|
63
|
+
f"Wheel license file mismatch: {license_file}",
|
|
64
|
+
)
|
|
65
|
+
return len(package)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def verify_sdist(payload: bytes, repo: Path, commit: str, project: dict, readme: str, tracked: set[str]) -> int:
|
|
69
|
+
prefix = f"matelab_python_sdk-{project['version']}/"
|
|
70
|
+
seen: set[str] = set()
|
|
71
|
+
with tarfile.open(fileobj=io.BytesIO(payload), mode="r:gz") as archive:
|
|
72
|
+
for member in archive.getmembers():
|
|
73
|
+
require(member.name.startswith(prefix) or member.name == prefix.rstrip("/"), "Unexpected sdist root")
|
|
74
|
+
if member.isdir():
|
|
75
|
+
continue
|
|
76
|
+
require(member.isfile(), "Unexpected sdist link or special member")
|
|
77
|
+
path = member.name.removeprefix(prefix)
|
|
78
|
+
require(path not in seen, f"Duplicate sdist member: {path}")
|
|
79
|
+
seen.add(path)
|
|
80
|
+
stream = archive.extractfile(member)
|
|
81
|
+
require(stream is not None, f"Unreadable sdist member: {path}")
|
|
82
|
+
contents = stream.read()
|
|
83
|
+
if path == "PKG-INFO":
|
|
84
|
+
metadata_matches(BytesParser().parsebytes(contents), project, readme)
|
|
85
|
+
else:
|
|
86
|
+
require(path in tracked, f"Untracked sdist member: {path}")
|
|
87
|
+
require(contents == git_bytes(repo, commit, path), f"Sdist source mismatch: {path}")
|
|
88
|
+
required = {path for path in tracked if path.startswith("src/matelab/")}
|
|
89
|
+
required.update({"PKG-INFO", "pyproject.toml", "uv.lock", project["readme"], *project["license-files"]})
|
|
90
|
+
required.update(path for path in tracked if path.startswith("contracts/"))
|
|
91
|
+
require(required <= seen, f"Missing sdist files: {sorted(required - seen)}")
|
|
92
|
+
return len(seen - {"PKG-INFO"})
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main() -> None:
|
|
96
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
97
|
+
parser.add_argument("--repo", type=Path, default=Path.cwd())
|
|
98
|
+
parser.add_argument("--ref", required=True, help="Release commit or immutable release tag")
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--sha256", action="append", type=ci_hash, required=True, help="FILENAME=CI_SHA256; repeat per file"
|
|
101
|
+
)
|
|
102
|
+
args = parser.parse_args()
|
|
103
|
+
repo = args.repo.resolve()
|
|
104
|
+
commit = subprocess.check_output(
|
|
105
|
+
["git", "-C", str(repo), "rev-parse", "--verify", "--end-of-options", f"{args.ref}^{{commit}}"], text=True
|
|
106
|
+
).strip()
|
|
107
|
+
project = tomllib.loads(git_bytes(repo, commit, "pyproject.toml").decode())["project"]
|
|
108
|
+
require(project["name"] == "matelab-python-sdk", "This verifier is for matelab-python-sdk")
|
|
109
|
+
expected_hashes = dict(args.sha256)
|
|
110
|
+
require(len(expected_hashes) == len(args.sha256), "Duplicate expected artifact names")
|
|
111
|
+
readme = git_bytes(repo, commit, project["readme"]).decode()
|
|
112
|
+
tracked = set(
|
|
113
|
+
subprocess.check_output(
|
|
114
|
+
["git", "-C", str(repo), "ls-tree", "-r", "--name-only", commit], text=True
|
|
115
|
+
).splitlines()
|
|
116
|
+
)
|
|
117
|
+
url = f"https://pypi.org/pypi/{quote(project['name'], safe='')}/{quote(project['version'], safe='')}/json"
|
|
118
|
+
with urlopen(url, timeout=30) as response:
|
|
119
|
+
published = json.load(response)
|
|
120
|
+
info = published["info"]
|
|
121
|
+
require(info["version"] == project["version"], "PyPI version mismatch")
|
|
122
|
+
require(info["requires_python"] == project["requires-python"], "PyPI Python requirement mismatch")
|
|
123
|
+
require(info["description"].strip() == readme.strip(), "PyPI README mismatch")
|
|
124
|
+
require(
|
|
125
|
+
{Requirement(value) for value in info["requires_dist"] or []}
|
|
126
|
+
== {Requirement(value) for value in project["dependencies"]},
|
|
127
|
+
"PyPI dependency mismatch",
|
|
128
|
+
)
|
|
129
|
+
entries = published["urls"]
|
|
130
|
+
require(len(entries) == len(expected_hashes), "PyPI/CI artifact count mismatch")
|
|
131
|
+
require({entry["filename"] for entry in entries} == set(expected_hashes), "PyPI/CI artifact names mismatch")
|
|
132
|
+
counts = {}
|
|
133
|
+
for entry in entries:
|
|
134
|
+
name = entry["filename"]
|
|
135
|
+
digest = expected_hashes[name]
|
|
136
|
+
require(entry["digests"]["sha256"] == digest, f"CI/PyPI hash mismatch: {name}")
|
|
137
|
+
location = urlsplit(entry["url"])
|
|
138
|
+
require(
|
|
139
|
+
location.scheme == "https" and location.hostname == "files.pythonhosted.org", "Unexpected artifact host"
|
|
140
|
+
)
|
|
141
|
+
with urlopen(entry["url"], timeout=30) as response:
|
|
142
|
+
payload = response.read()
|
|
143
|
+
require(hashlib.sha256(payload).hexdigest() == digest, f"Downloaded hash mismatch: {name}")
|
|
144
|
+
if entry["packagetype"] == "bdist_wheel":
|
|
145
|
+
counts["wheel_package_files"] = verify_wheel(payload, repo, commit, project, readme, tracked)
|
|
146
|
+
elif entry["packagetype"] == "sdist":
|
|
147
|
+
counts["sdist_source_files"] = verify_sdist(payload, repo, commit, project, readme, tracked)
|
|
148
|
+
else:
|
|
149
|
+
raise ValueError(f"Unexpected artifact type: {entry['packagetype']}")
|
|
150
|
+
require("wheel_package_files" in counts and "sdist_source_files" in counts, "Wheel and sdist are both required")
|
|
151
|
+
print(json.dumps({"version": project["version"], "commit": commit, "files": expected_hashes, **counts}, indent=2))
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if __name__ == "__main__":
|
|
155
|
+
try:
|
|
156
|
+
main()
|
|
157
|
+
except (
|
|
158
|
+
OSError,
|
|
159
|
+
ValueError,
|
|
160
|
+
KeyError,
|
|
161
|
+
subprocess.CalledProcessError,
|
|
162
|
+
tarfile.TarError,
|
|
163
|
+
zipfile.BadZipFile,
|
|
164
|
+
) as error:
|
|
165
|
+
raise SystemExit(f"Release verification failed: {error}") from None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.15
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
version: "1.0"
|
|
2
|
+
name: sdk-release
|
|
3
|
+
displayName: Publish SDK to PyPI
|
|
4
|
+
ref_variable:
|
|
5
|
+
- UV_PUBLISH_TOKEN
|
|
6
|
+
triggers:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
prefix:
|
|
10
|
+
- v
|
|
11
|
+
stages:
|
|
12
|
+
- name: release
|
|
13
|
+
displayName: Test, build and publish
|
|
14
|
+
strategy: naturally
|
|
15
|
+
trigger: auto
|
|
16
|
+
steps:
|
|
17
|
+
- step: build@python
|
|
18
|
+
name: build-and-publish
|
|
19
|
+
displayName: Validate and publish release
|
|
20
|
+
# Gitee's Python bootstraps uv; the SDK runs on uv-managed Python 3.15.
|
|
21
|
+
pythonVersion: "3.14"
|
|
22
|
+
commands: |
|
|
23
|
+
set +x
|
|
24
|
+
set -eu
|
|
25
|
+
# Gitee injects its pytest tooling into Python's import path.
|
|
26
|
+
unset PYTHONPATH PYTHONHOME
|
|
27
|
+
python -m pip install --user --index-url https://pypi.org/simple "uv==0.13.7"
|
|
28
|
+
export PATH="$(python -m site --user-base)/bin:$PATH"
|
|
29
|
+
uv --version
|
|
30
|
+
release_tag="v$(uv version --short)"
|
|
31
|
+
build_ref="${GITEE_BRANCH:?Gitee build ref is required}"
|
|
32
|
+
if [ "$build_ref" != main ] && [ "$build_ref" != "$release_tag" ]; then
|
|
33
|
+
echo "Release ref must be $release_tag; received $build_ref" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
if [ "$(git rev-parse HEAD)" != "${GITEE_COMMIT:?Gitee trigger commit is required}" ]; then
|
|
37
|
+
echo "Checkout does not match the Gitee build commit" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
export SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
|
|
41
|
+
unset MATELAB_PROVIDER_BASE_URL MATELAB_PROVIDER_USERNAME MATELAB_PROVIDER_PASSWORD
|
|
42
|
+
|
|
43
|
+
test_status=0
|
|
44
|
+
for python_version in 3.12 3.13 3.14 3.15; do
|
|
45
|
+
uv run --frozen --exact --python "$python_version" pytest -rs || test_status=1
|
|
46
|
+
done
|
|
47
|
+
test "$test_status" -eq 0
|
|
48
|
+
|
|
49
|
+
export UV_PYTHON="$(cat .python-version)"
|
|
50
|
+
uv run --frozen python scripts/generate_models.py --check
|
|
51
|
+
uv run --frozen ruff check .
|
|
52
|
+
uv run --frozen ruff format --check .
|
|
53
|
+
uv run --frozen basedpyright
|
|
54
|
+
|
|
55
|
+
mkdir -p dist/release dist/reproducible
|
|
56
|
+
uv build --no-build-isolation --out-dir dist/release
|
|
57
|
+
uv build --no-build-isolation --out-dir dist/reproducible
|
|
58
|
+
cmp dist/release/*.whl dist/reproducible/*.whl
|
|
59
|
+
cmp dist/release/*.tar.gz dist/reproducible/*.tar.gz
|
|
60
|
+
uv run --frozen python scripts/check_release.py dist/release/*.whl dist/release/*.tar.gz
|
|
61
|
+
(cd dist/release && sha256sum *.whl *.tar.gz > SHA256SUMS)
|
|
62
|
+
cat dist/release/SHA256SUMS
|
|
63
|
+
|
|
64
|
+
sdk_consumer_dir="$(mktemp -d)"
|
|
65
|
+
trap 'rm -rf -- "$sdk_consumer_dir"' EXIT
|
|
66
|
+
uv venv "$sdk_consumer_dir/venv"
|
|
67
|
+
uv pip install \
|
|
68
|
+
--python "$sdk_consumer_dir/venv/bin/python" \
|
|
69
|
+
dist/release/*.whl \
|
|
70
|
+
"basedpyright>=1.40.2" \
|
|
71
|
+
"nodejs-wheel-binaries==22.20.0"
|
|
72
|
+
"$sdk_consumer_dir/venv/bin/python" scripts/check_installed_package.py
|
|
73
|
+
"$sdk_consumer_dir/venv/bin/basedpyright" \
|
|
74
|
+
--pythonpath "$sdk_consumer_dir/venv/bin/python" \
|
|
75
|
+
--project tests/typing/pyproject.toml
|
|
76
|
+
|
|
77
|
+
if [ "$build_ref" = main ]; then
|
|
78
|
+
echo "Validation passed on main; publishing requires tag $release_tag"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
: "${UV_PUBLISH_TOKEN:?Configure UV_PUBLISH_TOKEN as a protected pipeline secret}"
|
|
82
|
+
uv publish --trusted-publishing never \
|
|
83
|
+
--publish-url https://upload.pypi.org/legacy/ \
|
|
84
|
+
dist/release/*.whl dist/release/*.tar.gz
|
|
85
|
+
artifacts:
|
|
86
|
+
- name: RELEASE_ARTIFACT
|
|
87
|
+
path:
|
|
88
|
+
- ./dist/release
|
|
89
|
+
type: .tar.gz
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: matelab-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0a1
|
|
4
4
|
Summary: Reusable async Python client for the Matelab Integration Contract
|
|
5
5
|
Author: CMPDC, Institute of Physics, Chinese Academy of Sciences
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,24 +12,24 @@ Classifier: Framework :: AsyncIO
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
19
|
Classifier: Topic :: Scientific/Engineering
|
|
20
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
21
|
Classifier: Typing :: Typed
|
|
22
|
-
Requires-Python: >=3.
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
23
|
Requires-Dist: httpx2>=2.16.0
|
|
24
24
|
Requires-Dist: jsonschema>=4.26.0
|
|
25
|
-
Requires-Dist: pydantic>=2.14.
|
|
25
|
+
Requires-Dist: pydantic>=2.14.3
|
|
26
26
|
Requires-Dist: typing-extensions>=4.16.0
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# matelab-python-sdk
|
|
30
30
|
|
|
31
31
|
Async Python client for the Matelab Integration Contract, with typed interfaces for authentication,
|
|
32
|
-
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.
|
|
32
|
+
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.12 or newer.
|
|
33
33
|
|
|
34
34
|
## Installation
|
|
35
35
|
|
|
@@ -64,6 +64,6 @@ asyncio.run(main())
|
|
|
64
64
|
|
|
65
65
|
## Documentation
|
|
66
66
|
|
|
67
|
-
- [API reference](https://
|
|
68
|
-
- [Operation coverage](https://
|
|
69
|
-
- [Development and releases](https://
|
|
67
|
+
- [API reference](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
|
|
68
|
+
- [Operation coverage](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
|
|
69
|
+
- [Development and releases](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# matelab-python-sdk
|
|
2
2
|
|
|
3
3
|
Async Python client for the Matelab Integration Contract, with typed interfaces for authentication,
|
|
4
|
-
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.
|
|
4
|
+
notebooks, records, attachments, cloud files, templates, and literature. Requires Python 3.12 or newer.
|
|
5
5
|
|
|
6
6
|
## Installation
|
|
7
7
|
|
|
@@ -36,6 +36,6 @@ asyncio.run(main())
|
|
|
36
36
|
|
|
37
37
|
## Documentation
|
|
38
38
|
|
|
39
|
-
- [API reference](https://
|
|
40
|
-
- [Operation coverage](https://
|
|
41
|
-
- [Development and releases](https://
|
|
39
|
+
- [API reference](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/api.md): Session persistence, errors, examples, and Provider limits.
|
|
40
|
+
- [Operation coverage](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/operation-coverage.yaml): supported operations.
|
|
41
|
+
- [Development and releases](https://gitee.com/mat-code/matelab-python-sdk/blob/main/docs/development.md): checks, Provider smoke, versioning, and builds.
|
{matelab_python_sdk-0.2.0a9 → matelab_python_sdk-0.3.0a1}/contracts/matelab-integration-v1.lock.json
RENAMED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"contract_line": "integration-v1",
|
|
4
4
|
"release": {
|
|
5
|
-
"tag": "v2026.11.
|
|
6
|
-
"commit": "
|
|
5
|
+
"tag": "v2026.11.2",
|
|
6
|
+
"commit": "a986f73b0edc6bb67f2993a9ee1291aba31df5e5"
|
|
7
7
|
},
|
|
8
8
|
"openapi": {
|
|
9
9
|
"source_path": "openapi/matelab-integration-v1.openapi.yaml",
|
|
10
10
|
"snapshot": "matelab-integration-v1.openapi.yaml",
|
|
11
|
-
"sha256": "
|
|
11
|
+
"sha256": "4a485f0ab2b589136cdcccad77407d8b5768e6e8cbb8e3a7636d8b9a07649991"
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: Matelab Integration Contract
|
|
4
|
-
version: "2026.11.
|
|
4
|
+
version: "2026.11.2"
|
|
5
5
|
description: |
|
|
6
6
|
Current PHP wire contract for generating SDKs against the selected
|
|
7
7
|
`matelab-server` Provider version.
|
|
@@ -18,12 +18,12 @@ info:
|
|
|
18
18
|
x-contract-line: integration-v1
|
|
19
19
|
x-provider-evidence:
|
|
20
20
|
repository: /home/tnzhu/projects/matelab-server
|
|
21
|
-
commit:
|
|
21
|
+
commit: f5a714e17cd76076efe7579db279936ac29b50a6
|
|
22
22
|
verificationRuntime:
|
|
23
23
|
baseUrl: https://matelab.iphy.ac.cn/eln-test/api
|
|
24
24
|
container: matelab-test-server-1
|
|
25
25
|
coreSourceComparison: sha256-exact
|
|
26
|
-
coreSourceFileCount:
|
|
26
|
+
coreSourceFileCount: 153
|
|
27
27
|
dataServerScope: localhost
|
|
28
28
|
files:
|
|
29
29
|
- controller/Actions.php
|
|
@@ -53,6 +53,7 @@ info:
|
|
|
53
53
|
- lib/Route.php
|
|
54
54
|
- lib/HttpServer.php
|
|
55
55
|
- lib/WsServer.php
|
|
56
|
+
- start.php
|
|
56
57
|
- lib/MultiException.php
|
|
57
58
|
- config/Db.php
|
|
58
59
|
- config/Channel.php
|
|
@@ -61,9 +62,9 @@ info:
|
|
|
61
62
|
- sql/matelab.sql
|
|
62
63
|
- sql/recommend.sql
|
|
63
64
|
x-provider-audit:
|
|
64
|
-
currentCommit:
|
|
65
|
-
previousReleaseEvidenceCommit:
|
|
66
|
-
lastProviderVerifiedCommit:
|
|
65
|
+
currentCommit: f5a714e17cd76076efe7579db279936ac29b50a6
|
|
66
|
+
previousReleaseEvidenceCommit: 00f17b6ce0906bb78b0a3e9bb8c50abee90ca3d0
|
|
67
|
+
lastProviderVerifiedCommit: f5a714e17cd76076efe7579db279936ac29b50a6
|
|
67
68
|
contractStatus: current-provider-aligned
|
|
68
69
|
# Summary of changes adopted by current Supported Routes since the last
|
|
69
70
|
# released contract, including contract corrections. The Provider diff is
|
|
@@ -71,8 +72,7 @@ info:
|
|
|
71
72
|
# updates against that fixed release baseline; use [] if no relevant changes.
|
|
72
73
|
# Entries are not per-change verification results or a cumulative history.
|
|
73
74
|
incorporatedChanges:
|
|
74
|
-
- {operationId:
|
|
75
|
-
- {operationId: updateRecordData, change: authorized-cloud-drive-hash-references-pass-permission-query}
|
|
75
|
+
- {operationId: stageTemporaryFile, change: filename-independent-continuation-with-final-fragment-name-and-extensionless-abort}
|
|
76
76
|
x-consumer-evidence:
|
|
77
77
|
- repository: /home/tnzhu/projects/matelab-mcp
|
|
78
78
|
commit: 94a129b2df0ef0fbcf179c10550fb7c9a5dd2e6f
|
|
@@ -93,7 +93,7 @@ info:
|
|
|
93
93
|
identity must come from token issuance. ELN-group and notebook-list
|
|
94
94
|
calls still provide Consumer evidence for their canonical GET forms.
|
|
95
95
|
- repository: /home/tnzhu/projects/matelab-front
|
|
96
|
-
commit:
|
|
96
|
+
commit: f7a7b952d7981f169d5c2585aea5c94b418086a3
|
|
97
97
|
files:
|
|
98
98
|
- src/App.vue
|
|
99
99
|
- src/fn/index.js
|
|
@@ -2020,11 +2020,11 @@ paths:
|
|
|
2020
2020
|
workflow therefore preserves the exact upload `eln`/`user` selector and
|
|
2021
2021
|
permits one occurrence in one target record. The operation has no
|
|
2022
2022
|
contracted abort, status, expiration, or idempotent retry. A hash
|
|
2023
|
-
mismatch in this one-fragment flow
|
|
2024
|
-
new bytes. Errors after import/update writing starts still
|
|
2025
|
-
whether the stage was consumed.
|
|
2026
|
-
filename's extension after removing
|
|
2027
|
-
the response still reports the original source filename.
|
|
2023
|
+
mismatch in this one-fragment flow occurs before row registration and
|
|
2024
|
+
removes the new bytes. Errors after import/update writing starts still
|
|
2025
|
+
do not prove whether the stage was consumed. On completion, bytes retain
|
|
2026
|
+
the multipart filename's extension after removing characters outside
|
|
2027
|
+
`[A-Za-z0-9]`; the response still reports the original source filename.
|
|
2028
2028
|
Raw identifiers identify the staging row;
|
|
2029
2029
|
their filesystem components are sanitized and can alias (PVD-051).
|
|
2030
2030
|
x-known-provider-issues: [PVD-014, PVD-051]
|
|
@@ -3735,18 +3735,18 @@ paths:
|
|
|
3735
3735
|
successful response's `offset`. Omitting it defaults to zero and
|
|
3736
3736
|
restarts incomplete bytes rather than appending. The final fragment
|
|
3737
3737
|
returns the combined SHA-256, size, source filename, temporary row id,
|
|
3738
|
-
and legacy preview query.
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
filename
|
|
3738
|
+
and legacy preview query. Incomplete bytes are selected by the same
|
|
3739
|
+
`uid` and `tempid`, independently of the multipart filename and extension.
|
|
3740
|
+
Both may change between fragments. Only after final hash validation does
|
|
3741
|
+
PHP append the final fragment's extension, stripped of characters outside
|
|
3742
|
+
`[A-Za-z0-9]` with case preserved, and register the completed row. The
|
|
3743
|
+
response `filename` is the final fragment's original source filename.
|
|
3744
3744
|
|
|
3745
3745
|
`abort=1` is the compensating cleanup operation. It requires the same
|
|
3746
3746
|
`uid` and `tempid` and omits `file`. If no completed row exists for
|
|
3747
|
-
those identifiers, it removes
|
|
3748
|
-
|
|
3749
|
-
it preserves the row and bytes without cleaning any
|
|
3747
|
+
those identifiers, it removes the extensionless incomplete file.
|
|
3748
|
+
It does not scan extension-suffixed orphan files. If a completed row
|
|
3749
|
+
exists, it preserves the row and bytes without cleaning any files. Both
|
|
3750
3750
|
states return the standard HTTP 200 code-2 error envelope ("upload
|
|
3751
3751
|
cancelled"); that response does not prove completed staging was deleted.
|
|
3752
3752
|
Complete uploads must be consumed by their intended finalizer; this
|
|
@@ -7490,9 +7490,9 @@ components:
|
|
|
7490
7490
|
type: string
|
|
7491
7491
|
format: binary
|
|
7492
7492
|
description: |
|
|
7493
|
-
Required for upload branches and omitted for abort.
|
|
7494
|
-
|
|
7495
|
-
|
|
7493
|
+
Required for upload branches and omitted for abort. Filename and
|
|
7494
|
+
extension may change between fragments in the same uid/tempid
|
|
7495
|
+
session. Completion uses the final fragment's source filename.
|
|
7496
7496
|
allOf:
|
|
7497
7497
|
- if:
|
|
7498
7498
|
required: [abort]
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
- `[tool.basedpyright]` 字段依次为 `pythonVersion`、`typeCheckingMode`、`include`、`exclude`、
|
|
32
32
|
`reportUnannotatedClassAttribute`、`reportIncompatibleUnannotatedOverride`,其他设置放在其后。沿用 SDK 的目标
|
|
33
33
|
Python 版本与检查路径,不照搬其他项目的值。
|
|
34
|
-
- runtime、build
|
|
35
|
-
|
|
34
|
+
- 升级 runtime、build 或 dev 依赖时,必须将验证通过的新版本同步为 `pyproject.toml` 中对应依赖的 `>=` 下限,
|
|
35
|
+
同时更新对应 CI 约束(包括安装后消费检查)。`uv.lock` 必须通过 `uv lock` 等 uv 命令自动更新,不手工编辑。
|
|
36
|
+
例外:`nodejs-wheel-binaries` 保留系统所需的精确版本,并同步 lock 和对应 CI 约束。
|
|
36
37
|
- 构建后端依赖和 wheel package 显式配置;metadata 与制品检查从这些声明推导预期值。
|
|
37
38
|
|
|
38
39
|
完成标准:配置可解析、声明与 lock 一致、受影响 CI 使用同一约束;字段重排不改变配置含义。
|
|
@@ -85,16 +86,15 @@ Ruff/Basedpyright 诊断通过修复代码或精确 ignore 解决,不缩小检
|
|
|
85
86
|
## Release
|
|
86
87
|
|
|
87
88
|
普通 commit/push 只处理已授权的 diff,不自动改版本、建 tag 或发布。发布准备完成但缺少外部操作授权时,
|
|
88
|
-
|
|
89
|
+
先交付可审查结果,再请求执行授权。发布与普通推送只同步 `origin`。以下步骤适用于实际发布:
|
|
89
90
|
|
|
90
91
|
1. 按 [版本规则](development.md#versioning) 选择 SDK 版本,读取 `[project].version`,确认待发布版本未发布
|
|
91
92
|
且 Contract pin 完整。公开接口删除、重命名、类型收紧或不兼容行为变化按 breaking change 处理。
|
|
92
93
|
完成标准:版本、当前文档和 tag 名 `v{version}` 一致。
|
|
93
94
|
2. 提交前同步受影响的当前文档,完成 [delivery gate](#delivery-gate)。构建元数据或发布流程变化时,核对
|
|
94
|
-
metadata
|
|
95
|
+
metadata 与流水线固定版本的 uv 是否兼容;需要本地制品证据时使用
|
|
95
96
|
[手工构建命令](development.md#reproducible-release-build)。创建已审查、已授权的 release commit。
|
|
96
|
-
3. 创建指向 release commit 的 annotated `v{version}` tag
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
完成标准:两个远端 main/tag 对应 release commit,CI 全部通过,PyPI 对应版本可读取。
|
|
97
|
+
3. 创建指向 release commit 的 annotated `v{version}` tag,推送 `origin` 的 main/tag;tag 推送自动触发
|
|
98
|
+
[发布 CI](../.workflow/release.yml)。
|
|
99
|
+
4. 跟踪发布 CI 的测试、可复现构建、制品审计、安装后消费和 PyPI publish。
|
|
100
|
+
完成标准:`origin` 的 main/tag 对应 release commit,CI 全部通过,PyPI 对应版本与 CI 制品哈希一致。
|