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.
@@ -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,5 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ __pycache__/
4
+ dist/
5
+ *.pyc
@@ -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
+ ]