tg-bot-plugin-contract-core 0.1.1__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.
- tg_bot_plugin_contract_core-0.1.1/.github/workflows/ci.yml +50 -0
- tg_bot_plugin_contract_core-0.1.1/.github/workflows/release.yml +54 -0
- tg_bot_plugin_contract_core-0.1.1/.gitignore +5 -0
- tg_bot_plugin_contract_core-0.1.1/PKG-INFO +114 -0
- tg_bot_plugin_contract_core-0.1.1/README.md +102 -0
- tg_bot_plugin_contract_core-0.1.1/pyproject.toml +28 -0
- tg_bot_plugin_contract_core-0.1.1/scripts/check_release_tag.py +55 -0
- tg_bot_plugin_contract_core-0.1.1/src/tg_bot_plugin_contract_core/__init__.py +39 -0
- tg_bot_plugin_contract_core-0.1.1/src/tg_bot_plugin_contract_core/core.py +760 -0
- tg_bot_plugin_contract_core-0.1.1/src/tg_bot_plugin_contract_core/errors.py +11 -0
- tg_bot_plugin_contract_core-0.1.1/src/tg_bot_plugin_contract_core/models.py +102 -0
- tg_bot_plugin_contract_core-0.1.1/tests/test_contract_core.py +243 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version:
|
|
16
|
+
- "3.11"
|
|
17
|
+
- "3.13"
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
python -m pip install ".[dev]" build
|
|
31
|
+
|
|
32
|
+
- name: Run unit tests
|
|
33
|
+
run: |
|
|
34
|
+
python -m pytest -q
|
|
35
|
+
|
|
36
|
+
- name: Build package
|
|
37
|
+
run: |
|
|
38
|
+
python -m build
|
|
39
|
+
|
|
40
|
+
- name: Wheel install smoke test
|
|
41
|
+
run: |
|
|
42
|
+
python -m venv .venv-smoke
|
|
43
|
+
. .venv-smoke/bin/activate
|
|
44
|
+
python -m pip install --upgrade pip
|
|
45
|
+
python -m pip install --no-deps dist/*.whl
|
|
46
|
+
python - <<'PY'
|
|
47
|
+
from tg_bot_plugin_contract_core import __all__
|
|
48
|
+
|
|
49
|
+
print("imported symbols:", ", ".join(__all__))
|
|
50
|
+
PY
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment: pypi
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.13"
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: |
|
|
27
|
+
python -m pip install --upgrade pip
|
|
28
|
+
python -m pip install ".[dev]" build
|
|
29
|
+
|
|
30
|
+
- name: Validate tag and version
|
|
31
|
+
run: |
|
|
32
|
+
python scripts/check_release_tag.py --tag "${{ github.ref_name }}"
|
|
33
|
+
|
|
34
|
+
- name: Run unit tests
|
|
35
|
+
run: |
|
|
36
|
+
python -m pytest -q
|
|
37
|
+
|
|
38
|
+
- name: Build package
|
|
39
|
+
run: |
|
|
40
|
+
python -m build
|
|
41
|
+
|
|
42
|
+
- name: Upload build artifact
|
|
43
|
+
uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: tg-bot-plugin-contract-core-dist
|
|
46
|
+
path: dist/*
|
|
47
|
+
|
|
48
|
+
- name: Publish GitHub Release
|
|
49
|
+
uses: softprops/action-gh-release@v2
|
|
50
|
+
with:
|
|
51
|
+
files: dist/*
|
|
52
|
+
|
|
53
|
+
- name: Publish to PyPI
|
|
54
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tg-bot-plugin-contract-core
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: TG-BOT 插件的共享 manifest、bundle、digest 与 signer 校验合同层。
|
|
5
|
+
Author: Fire Dragons
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: sigstore>=4.2.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# tg-bot-plugin-contract-core
|
|
14
|
+
|
|
15
|
+
`tg-bot-plugin-contract-core` 是 TG-BOT 插件 `manifest` 与 `.tgpkg` bundle 的共享正式合同层。
|
|
16
|
+
|
|
17
|
+
它提供:
|
|
18
|
+
|
|
19
|
+
- manifest 规范化与合同校验
|
|
20
|
+
- `artifact_digest` 计算
|
|
21
|
+
- 通过 `package_sha256` 计算 canonical `.tgpkg` 字节身份
|
|
22
|
+
- 可复现 bundle 写入
|
|
23
|
+
- bundle 结构检查
|
|
24
|
+
- Sigstore bundle 验签
|
|
25
|
+
- signer identity 提取与 trusted signer 规则匹配
|
|
26
|
+
|
|
27
|
+
它有意不提供:
|
|
28
|
+
|
|
29
|
+
- TG-BOT runtime 加载或插件类实例化
|
|
30
|
+
- trusted signer 文件发现或配置加载
|
|
31
|
+
- 超出 bundle 与 signer 合同范围的平台策略决策
|
|
32
|
+
|
|
33
|
+
## 安装
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install tg-bot-plugin-contract-core
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 发布与验证
|
|
40
|
+
|
|
41
|
+
- `pull_request` / `main` 分支会执行:
|
|
42
|
+
- `pytest`
|
|
43
|
+
- wheel / sdist 构建
|
|
44
|
+
- wheel 安装 smoke test
|
|
45
|
+
- `tag push v*` 会额外执行:
|
|
46
|
+
- `tag == project.version` 校验
|
|
47
|
+
- PyPI 发布
|
|
48
|
+
- GitHub Release 附件上传
|
|
49
|
+
|
|
50
|
+
## 公开 API
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from tg_bot_plugin_contract_core import (
|
|
54
|
+
compute_artifact_digest,
|
|
55
|
+
compute_package_sha256,
|
|
56
|
+
inspect_bundle,
|
|
57
|
+
match_signer_identity,
|
|
58
|
+
validate_bundle_same_profile,
|
|
59
|
+
validate_bundle_target_profile,
|
|
60
|
+
verify_bundle,
|
|
61
|
+
write_reproducible_bundle,
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 使用示例
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
|
|
70
|
+
from tg_bot_plugin_contract_core import (
|
|
71
|
+
compute_artifact_digest,
|
|
72
|
+
inspect_bundle,
|
|
73
|
+
match_signer_identity,
|
|
74
|
+
verify_bundle,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
plugin_dir = Path("plugin-staging")
|
|
78
|
+
manifest_payload = {
|
|
79
|
+
"plugin_id": "demo",
|
|
80
|
+
"plugin_version": "1.0.0",
|
|
81
|
+
"name": "demo",
|
|
82
|
+
"description": "demo plugin",
|
|
83
|
+
"category": "utility",
|
|
84
|
+
"runtime_profile_id": "cpython-3.13-linux-x86_64-gnu",
|
|
85
|
+
"artifact_digest": "",
|
|
86
|
+
"entrypoint": {"module_path": "__init__", "symbol": "DemoPlugin"},
|
|
87
|
+
"config_schema": {},
|
|
88
|
+
"interaction_schema": {},
|
|
89
|
+
"declared_scopes": [],
|
|
90
|
+
"declared_capabilities": [],
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
artifact_digest = compute_artifact_digest(plugin_dir, manifest_payload)
|
|
94
|
+
inspection = inspect_bundle("dist/plugins/demo/demo-1.0.0-cpython-3.13-linux-x86_64-gnu.tgpkg")
|
|
95
|
+
verification = verify_bundle(inspection, allow_unsigned_dev=False)
|
|
96
|
+
match_result = match_signer_identity(verification, trusted_signer_rules=[
|
|
97
|
+
{
|
|
98
|
+
"rule_id": "github-release-main",
|
|
99
|
+
"issuer": "https://token.actions.githubusercontent.com",
|
|
100
|
+
"repository_owner": "Fire-Dragons",
|
|
101
|
+
"repository_name": "demo",
|
|
102
|
+
"workflow_ref": "Fire-Dragons/demo/.github/workflows/release.yml@refs/heads/main",
|
|
103
|
+
"reusable_workflow_ref": "Fire-Dragons/tg-bot-plugin-buildkit/.github/workflows/release-plugin.yml@refs/tags/v1",
|
|
104
|
+
"ref_pattern": "refs/tags/v*",
|
|
105
|
+
"status": "active",
|
|
106
|
+
}
|
|
107
|
+
])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 说明
|
|
111
|
+
|
|
112
|
+
- `verify_bundle()` 会校验 Sigstore 签名材料,并从签名证书中提取 signer identity。
|
|
113
|
+
- `match_signer_identity()` 会基于已解析的 trusted signer 规则执行平台无关的匹配。
|
|
114
|
+
- `allow_unsigned_dev=True` 仅用于受控的本地开发链路。
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# tg-bot-plugin-contract-core
|
|
2
|
+
|
|
3
|
+
`tg-bot-plugin-contract-core` 是 TG-BOT 插件 `manifest` 与 `.tgpkg` bundle 的共享正式合同层。
|
|
4
|
+
|
|
5
|
+
它提供:
|
|
6
|
+
|
|
7
|
+
- manifest 规范化与合同校验
|
|
8
|
+
- `artifact_digest` 计算
|
|
9
|
+
- 通过 `package_sha256` 计算 canonical `.tgpkg` 字节身份
|
|
10
|
+
- 可复现 bundle 写入
|
|
11
|
+
- bundle 结构检查
|
|
12
|
+
- Sigstore bundle 验签
|
|
13
|
+
- signer identity 提取与 trusted signer 规则匹配
|
|
14
|
+
|
|
15
|
+
它有意不提供:
|
|
16
|
+
|
|
17
|
+
- TG-BOT runtime 加载或插件类实例化
|
|
18
|
+
- trusted signer 文件发现或配置加载
|
|
19
|
+
- 超出 bundle 与 signer 合同范围的平台策略决策
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install tg-bot-plugin-contract-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 发布与验证
|
|
28
|
+
|
|
29
|
+
- `pull_request` / `main` 分支会执行:
|
|
30
|
+
- `pytest`
|
|
31
|
+
- wheel / sdist 构建
|
|
32
|
+
- wheel 安装 smoke test
|
|
33
|
+
- `tag push v*` 会额外执行:
|
|
34
|
+
- `tag == project.version` 校验
|
|
35
|
+
- PyPI 发布
|
|
36
|
+
- GitHub Release 附件上传
|
|
37
|
+
|
|
38
|
+
## 公开 API
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from tg_bot_plugin_contract_core import (
|
|
42
|
+
compute_artifact_digest,
|
|
43
|
+
compute_package_sha256,
|
|
44
|
+
inspect_bundle,
|
|
45
|
+
match_signer_identity,
|
|
46
|
+
validate_bundle_same_profile,
|
|
47
|
+
validate_bundle_target_profile,
|
|
48
|
+
verify_bundle,
|
|
49
|
+
write_reproducible_bundle,
|
|
50
|
+
)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 使用示例
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from pathlib import Path
|
|
57
|
+
|
|
58
|
+
from tg_bot_plugin_contract_core import (
|
|
59
|
+
compute_artifact_digest,
|
|
60
|
+
inspect_bundle,
|
|
61
|
+
match_signer_identity,
|
|
62
|
+
verify_bundle,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
plugin_dir = Path("plugin-staging")
|
|
66
|
+
manifest_payload = {
|
|
67
|
+
"plugin_id": "demo",
|
|
68
|
+
"plugin_version": "1.0.0",
|
|
69
|
+
"name": "demo",
|
|
70
|
+
"description": "demo plugin",
|
|
71
|
+
"category": "utility",
|
|
72
|
+
"runtime_profile_id": "cpython-3.13-linux-x86_64-gnu",
|
|
73
|
+
"artifact_digest": "",
|
|
74
|
+
"entrypoint": {"module_path": "__init__", "symbol": "DemoPlugin"},
|
|
75
|
+
"config_schema": {},
|
|
76
|
+
"interaction_schema": {},
|
|
77
|
+
"declared_scopes": [],
|
|
78
|
+
"declared_capabilities": [],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
artifact_digest = compute_artifact_digest(plugin_dir, manifest_payload)
|
|
82
|
+
inspection = inspect_bundle("dist/plugins/demo/demo-1.0.0-cpython-3.13-linux-x86_64-gnu.tgpkg")
|
|
83
|
+
verification = verify_bundle(inspection, allow_unsigned_dev=False)
|
|
84
|
+
match_result = match_signer_identity(verification, trusted_signer_rules=[
|
|
85
|
+
{
|
|
86
|
+
"rule_id": "github-release-main",
|
|
87
|
+
"issuer": "https://token.actions.githubusercontent.com",
|
|
88
|
+
"repository_owner": "Fire-Dragons",
|
|
89
|
+
"repository_name": "demo",
|
|
90
|
+
"workflow_ref": "Fire-Dragons/demo/.github/workflows/release.yml@refs/heads/main",
|
|
91
|
+
"reusable_workflow_ref": "Fire-Dragons/tg-bot-plugin-buildkit/.github/workflows/release-plugin.yml@refs/tags/v1",
|
|
92
|
+
"ref_pattern": "refs/tags/v*",
|
|
93
|
+
"status": "active",
|
|
94
|
+
}
|
|
95
|
+
])
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 说明
|
|
99
|
+
|
|
100
|
+
- `verify_bundle()` 会校验 Sigstore 签名材料,并从签名证书中提取 signer identity。
|
|
101
|
+
- `match_signer_identity()` 会基于已解析的 trusted signer 规则执行平台无关的匹配。
|
|
102
|
+
- `allow_unsigned_dev=True` 仅用于受控的本地开发链路。
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.27.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tg-bot-plugin-contract-core"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "TG-BOT 插件的共享 manifest、bundle、digest 与 signer 校验合同层。"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Fire Dragons" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"sigstore>=4.2.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=8.3.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/tg_bot_plugin_contract_core"]
|
|
26
|
+
|
|
27
|
+
[tool.pytest.ini_options]
|
|
28
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""校验 Git tag 与 pyproject 中的项目版本一致。"""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import sys
|
|
9
|
+
import tomllib
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _parse_args() -> argparse.Namespace:
|
|
13
|
+
parser = argparse.ArgumentParser(description="校验 release tag 与项目版本是否一致。")
|
|
14
|
+
parser.add_argument("--tag", required=True, help="例如 v0.1.0")
|
|
15
|
+
parser.add_argument(
|
|
16
|
+
"--project-root",
|
|
17
|
+
default=".",
|
|
18
|
+
help="包含 pyproject.toml 的项目根目录。",
|
|
19
|
+
)
|
|
20
|
+
return parser.parse_args()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def main() -> int:
|
|
24
|
+
args = _parse_args()
|
|
25
|
+
project_root = Path(args.project_root).expanduser().resolve()
|
|
26
|
+
pyproject_path = project_root / "pyproject.toml"
|
|
27
|
+
tag = str(args.tag or "").strip()
|
|
28
|
+
expected_prefix = "v"
|
|
29
|
+
|
|
30
|
+
if not pyproject_path.is_file():
|
|
31
|
+
print(f"missing pyproject.toml: {pyproject_path}", file=sys.stderr)
|
|
32
|
+
return 1
|
|
33
|
+
if not tag.startswith(expected_prefix):
|
|
34
|
+
print(f"release tag must start with '{expected_prefix}': {tag}", file=sys.stderr)
|
|
35
|
+
return 1
|
|
36
|
+
|
|
37
|
+
payload = tomllib.loads(pyproject_path.read_text(encoding="utf-8"))
|
|
38
|
+
project_version = str(payload.get("project", {}).get("version", "")).strip()
|
|
39
|
+
tag_version = tag[len(expected_prefix):]
|
|
40
|
+
if project_version == "":
|
|
41
|
+
print("project.version is missing in pyproject.toml", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
if project_version != tag_version:
|
|
44
|
+
print(
|
|
45
|
+
f"release tag mismatch: tag={tag_version}, project.version={project_version}",
|
|
46
|
+
file=sys.stderr,
|
|
47
|
+
)
|
|
48
|
+
return 1
|
|
49
|
+
|
|
50
|
+
print(f"release tag matches project version: {project_version}")
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""TG-BOT 插件 bundle 的共享正式合同辅助接口。"""
|
|
2
|
+
|
|
3
|
+
from .core import (
|
|
4
|
+
compute_artifact_digest,
|
|
5
|
+
compute_package_sha256,
|
|
6
|
+
inspect_bundle,
|
|
7
|
+
match_signer_identity,
|
|
8
|
+
validate_bundle_same_profile,
|
|
9
|
+
validate_bundle_target_profile,
|
|
10
|
+
verify_bundle,
|
|
11
|
+
write_reproducible_bundle,
|
|
12
|
+
)
|
|
13
|
+
from .errors import ContractCoreError
|
|
14
|
+
from .models import (
|
|
15
|
+
ArtifactDescriptor,
|
|
16
|
+
BundleInspectionResult,
|
|
17
|
+
BundleVerificationResult,
|
|
18
|
+
PluginManifest,
|
|
19
|
+
SignerIdentity,
|
|
20
|
+
SignerIdentityMatchResult,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"ArtifactDescriptor",
|
|
25
|
+
"BundleInspectionResult",
|
|
26
|
+
"BundleVerificationResult",
|
|
27
|
+
"ContractCoreError",
|
|
28
|
+
"PluginManifest",
|
|
29
|
+
"SignerIdentity",
|
|
30
|
+
"SignerIdentityMatchResult",
|
|
31
|
+
"compute_artifact_digest",
|
|
32
|
+
"compute_package_sha256",
|
|
33
|
+
"inspect_bundle",
|
|
34
|
+
"match_signer_identity",
|
|
35
|
+
"validate_bundle_same_profile",
|
|
36
|
+
"validate_bundle_target_profile",
|
|
37
|
+
"verify_bundle",
|
|
38
|
+
"write_reproducible_bundle",
|
|
39
|
+
]
|