1panel-toolkit 0.9.2__tar.gz → 0.9.3__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.
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/CHANGELOG.md +30 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/PKG-INFO +12 -1
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/README.md +11 -0
- 1panel_toolkit-0.9.3/docs/architecture.md +123 -0
- 1panel_toolkit-0.9.3/docs/status.md +134 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/pyproject.toml +1 -1
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/__init__.py +1 -1
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/i18n.py +0 -1
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/depth.py +0 -4
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/fix.py +0 -9
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/.gitignore +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/LICENSE +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/docs/generation-depth.md +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/docs/patterns.md +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/examples/demo-db-spec.json +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/examples/demo-spec.json +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/skill/SKILL.md +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/__init__.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/compose.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/forms.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/hygiene.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/lessons.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/structure.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/checks/substance.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/cli.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/config.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/dependencies.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/deploy.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/errors.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/evidence.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/form.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/gen.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/lifecycle.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/loader.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/logo.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/model.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/net.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/panel.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/patterns/__init__.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/patterns/catalog.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/patterns/data/patterns.json +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/patterns/extract.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/recipes.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/remote.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/report.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/resolve/__init__.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/resolve/analyze.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/resolve/sources.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/__init__.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/decisions.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/dependencies.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/envkeys.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/form-design.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/form-review.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/i18n.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/lessons.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/recipes.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/rules/data/store.yaml +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/src/p1toolkit/selftest.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/conftest.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_checks.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_config.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_corpus.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_dependencies.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_deploy.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_depth.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_evidence.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_fix.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_form.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_forms.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_gen.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_github_source.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_lifecycle.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_net.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_panel.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_patterns.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_recipes.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_regressions.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_remote.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_remote_cli.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_resolve.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_rules_data.py +0 -0
- {1panel_toolkit-0.9.2 → 1panel_toolkit-0.9.3}/tests/test_selftest.py +0 -0
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.9.3 — 2026-09-20
|
|
4
|
+
|
|
5
|
+
收尾版本:全量自检、清掉死代码、补齐交接文档、整理目录。
|
|
6
|
+
|
|
7
|
+
### 死代码
|
|
8
|
+
|
|
9
|
+
`_recon/audit_source.py`(新增的静态审计脚本)扫出三处"定义了但从没被调用"的东西,
|
|
10
|
+
删掉:`checks/i18n.py` 的 `_LABEL_LINE` 正则、`depth.py` 的 `_distinct`、
|
|
11
|
+
`fix.py` 的 `_write`(真正的写盘在各处直接做)。
|
|
12
|
+
|
|
13
|
+
### 文档(交接用)
|
|
14
|
+
|
|
15
|
+
- 新增 `docs/architecture.md`:模块地图、三段式(证据 → 决策 → 产物)、
|
|
16
|
+
证据链为什么这么设计、扩展点(加规则 / 加依赖 family / 加配方 / 加教训分别改哪里)、
|
|
17
|
+
测试策略、已知限制。
|
|
18
|
+
- 新增 `docs/status.md`:命令清单(标注哪些需要面板/SSH)、规则与数据规模、
|
|
19
|
+
写代码前量到的实测数据表、最近一次全量质量门、真实环境验证记录、
|
|
20
|
+
现有包的收敛结果、维护手册(含发版与 uv 缓存坑)。
|
|
21
|
+
- README 增加「文档」索引;开头明确核心路径不需要面板/SSH/密钥。
|
|
22
|
+
|
|
23
|
+
### 目录整理
|
|
24
|
+
|
|
25
|
+
- 一次性排查脚本 `_debug_resolve.*` 移入 `1panel-toolkit/out/_archive/`
|
|
26
|
+
(本会话的 `Remove-Item` 被策略拦下,改用它处无副作用的移动;`_debug_*` 也已在构建排除列表里)。
|
|
27
|
+
- `1panel-toolkit/out/` 与 `1panel-toolkit/dist/` 里的历史产物收进各自的 `_archive/`,
|
|
28
|
+
两个目录现在只留当前版本与归档。
|
|
29
|
+
|
|
30
|
+
`pytest` 303 passed、`p1 selftest` 10/10、`_recon/verify_all.py` 12 节全绿、
|
|
31
|
+
whl 51 项 / sdist 81 项且密钥与真实 IP 扫描干净。
|
|
32
|
+
|
|
3
33
|
## 0.9.2 — 2026-09-20
|
|
4
34
|
|
|
5
35
|
面板侧做到"够用"就停:**失败时把根因说清楚**,并把"可选"这条边界写进文档。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: 1panel-toolkit
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Generate, check, fix and runtime-verify 1Panel app packages from a single spec.
|
|
5
5
|
Author: idkan
|
|
6
6
|
License: MIT License
|
|
@@ -460,6 +460,17 @@ p1 deploy ./out/app --param ... --uninstall # 5. 走面板 API 正式安装并
|
|
|
460
460
|
命令一览:`resolve` / `gen` / `check` / `fix` / `depth` / `patterns` / `ports` /
|
|
461
461
|
`remote` / `deploy` / `uninstall` / `config` / `panel` / `rules` / `selftest`。
|
|
462
462
|
|
|
463
|
+
## 文档
|
|
464
|
+
|
|
465
|
+
| 文档 | 内容 |
|
|
466
|
+
| --- | --- |
|
|
467
|
+
| 本文件 | 用户视角:装什么、怎么用、为什么这么设计 |
|
|
468
|
+
| `docs/architecture.md` | **接手先读**:模块地图、证据链、扩展点、测试策略 |
|
|
469
|
+
| `docs/status.md` | 工具状态:命令清单、规则与数据规模、实测数据、验证记录、已知限制、维护手册 |
|
|
470
|
+
| `docs/generation-depth.md` | 为什么模板化生成会做出重复的包(实测分布 + 判定口径) |
|
|
471
|
+
| `docs/patterns.md` | 22 条官方真实写法的教程(`p1 patterns export` 生成) |
|
|
472
|
+
| `CHANGELOG.md` | 每个版本改了什么、为什么改 |
|
|
473
|
+
|
|
463
474
|
## 设计约束
|
|
464
475
|
|
|
465
476
|
没有官方 Docker 证据就不猜镜像、端口、卷、UID/GID。高风险运行时权限先保留并
|
|
@@ -426,6 +426,17 @@ p1 deploy ./out/app --param ... --uninstall # 5. 走面板 API 正式安装并
|
|
|
426
426
|
命令一览:`resolve` / `gen` / `check` / `fix` / `depth` / `patterns` / `ports` /
|
|
427
427
|
`remote` / `deploy` / `uninstall` / `config` / `panel` / `rules` / `selftest`。
|
|
428
428
|
|
|
429
|
+
## 文档
|
|
430
|
+
|
|
431
|
+
| 文档 | 内容 |
|
|
432
|
+
| --- | --- |
|
|
433
|
+
| 本文件 | 用户视角:装什么、怎么用、为什么这么设计 |
|
|
434
|
+
| `docs/architecture.md` | **接手先读**:模块地图、证据链、扩展点、测试策略 |
|
|
435
|
+
| `docs/status.md` | 工具状态:命令清单、规则与数据规模、实测数据、验证记录、已知限制、维护手册 |
|
|
436
|
+
| `docs/generation-depth.md` | 为什么模板化生成会做出重复的包(实测分布 + 判定口径) |
|
|
437
|
+
| `docs/patterns.md` | 22 条官方真实写法的教程(`p1 patterns export` 生成) |
|
|
438
|
+
| `CHANGELOG.md` | 每个版本改了什么、为什么改 |
|
|
439
|
+
|
|
429
440
|
## 设计约束
|
|
430
441
|
|
|
431
442
|
没有官方 Docker 证据就不猜镜像、端口、卷、UID/GID。高风险运行时权限先保留并
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# 架构与开发思路
|
|
2
|
+
|
|
3
|
+
写给接手的人。看完这份再读代码,顺序会顺很多。
|
|
4
|
+
|
|
5
|
+
## 一句话
|
|
6
|
+
|
|
7
|
+
`p1` 把"一个 Docker 应用 → 一个 1Panel 应用包"里**重复的部分变成数据与代码**,
|
|
8
|
+
把**需要判断的部分列成清单交给人**,并且在缺证据时拒绝生成。
|
|
9
|
+
|
|
10
|
+
入口是一个仓库(GitHub 地址或本地检出),出口是一个可安装的应用包;
|
|
11
|
+
面板 API / SSH 只是可选的附加能力(检查部署、解释失败),核心不依赖它们。
|
|
12
|
+
|
|
13
|
+
## 三段式
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
① 证据 ② 决策 ③ 产物
|
|
17
|
+
resolve → D01–D10 + draft-spec.json → gen → 包
|
|
18
|
+
↓ ↓ ↓
|
|
19
|
+
上游文件:line 每条都带出处 证据审计 / 校验 / 修复 / 深度判定
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**① 证据**(`resolve/`):从 Dockerfile / compose / README / `.env.example` 里抽端口、
|
|
23
|
+
挂载、环境变量、镜像、运行用户、依赖,每条都带 `文件:行号`。
|
|
24
|
+
证据分三态:`evidenced`(明写)、`inferred`(弱证据,例如路径来自 `HALO_WORK_DIR=/root/.halo2`)、
|
|
25
|
+
`missing`。
|
|
26
|
+
|
|
27
|
+
**② 决策**(`rules/data/decisions.yaml` + `resolve/__init__.py`):
|
|
28
|
+
写包必须回答的 10 个问题(D01–D10),每个都带"实测多样性、未知时怎么办、猜错的后果"。
|
|
29
|
+
其中 D03(挂载点)/ D05(容器端口)/ D06(镜像)标了 `blocking: true`——
|
|
30
|
+
没有证据时 `resolve` 拒答、`gen` 拒生成。
|
|
31
|
+
|
|
32
|
+
**③ 产物**(`gen.py` → `checks/` → `fix.py` → `depth.py`):生成包、7 组规则校验、
|
|
33
|
+
幂等修复、判定它是"适配过的"还是"套模板"。
|
|
34
|
+
|
|
35
|
+
## 模块地图
|
|
36
|
+
|
|
37
|
+
| 模块 | 职责 | 关键约束 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `cli.py` | 命令入口(16 个子命令) | 只做参数与输出,逻辑都在模块里 |
|
|
40
|
+
| `config.py` | 本地配置(面板 / SSH) | **只有附加命令读它**,核心路径不碰 |
|
|
41
|
+
| `net.py` | 端口三态判定(open / closed / filtered) | 用来区分"安全组拦了"和"服务没起" |
|
|
42
|
+
| `model.py` `loader.py` | 包的内存模型与读取 | 检查与生成都只对模型说话,不直接读 YAML |
|
|
43
|
+
| `report.py` | `Issue` / `Report`(fail / warn / info) | 所有规则共用同一种报告结构 |
|
|
44
|
+
| `checks/*` | 7 组规则:结构 P / i18n I / compose C / 卫生 H / 表单设计 F / 经验 L / 实质 S | 规则能数据化的一律进 `rules/data/*.yaml` |
|
|
45
|
+
| `rules/data/*.yaml` | **9 份规则数据**(见下表) | 改规则 = 改数据,不是改代码 |
|
|
46
|
+
| `resolve/sources.py` | 上游来源(GitHub API / 本地检出) | 抓取有总预算,raw 不可达时明确报出来 |
|
|
47
|
+
| `resolve/analyze.py` | 从文件里抽证据 | `service_facts()` 按**服务**归属:镜像/端口/挂载/变量不跨服务串味 |
|
|
48
|
+
| `resolve/__init__.py` | 10 个决策 + draft spec + 报告 | draft 是给人改的,不是最终产物 |
|
|
49
|
+
| `evidence.py` | `gen` 前置的证据审计 | 阻塞项 / 漂移 / D07、D09、D10 的纪律检查 |
|
|
50
|
+
| `dependencies.py` | 面板托管依赖的接线(D10) | 只为 env 模板引用到的面板变量生成表单字段 |
|
|
51
|
+
| `lifecycle.py` | 脚本与首次启动配置(D09) | `scripts` / `data_owner` / `config_files` |
|
|
52
|
+
| `recipes.py` | 配方:一类应用的成套写法 | 只填结构;应用变量名只能来自 hints 或 `--bind` |
|
|
53
|
+
| `gen.py` | 生成器 | 表单是决策清单不是配置转储;依赖"拥有"它引用的变量 |
|
|
54
|
+
| `form.py` | 表单审计(哪些框真必须填) | 判定规则在 `form-review.yaml` |
|
|
55
|
+
| `fix.py` | 幂等修复 | 机械的自动做,需要判断的列入清单(可配 `--plan` 执行) |
|
|
56
|
+
| `depth.py` | 深度判定 + 语料统计 | 三个维度缺两个才算 `thin` |
|
|
57
|
+
| `patterns/` | 22 条官方真实写法(带采用数) | 从语料抽取,不是手写示例 |
|
|
58
|
+
| `remote.py` | SSH 冒烟(**可选**) | 用包自己的默认端口;失败时匹配 lesson 解释根因 |
|
|
59
|
+
| `deploy.py` `panel.py` | 面板 API 安装与验证(**可选**) | 失败时读容器日志给根因;`--probe` 检查端口是否真的有应答 |
|
|
60
|
+
| `selftest.py` | 装完之后的自检(10 项) | 规则数据、写法库、生成→校验、审计、闸门、配方、端口、签名 |
|
|
61
|
+
|
|
62
|
+
规则数据一览:
|
|
63
|
+
|
|
64
|
+
| 文件 | 管什么 |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `decisions.yaml` | D01–D10:问题、证据来源、未知处理、`blocking` 标记 |
|
|
67
|
+
| `store.yaml` | 商店格式:data.yml 允许的键、必备元数据、tag 白名单 |
|
|
68
|
+
| `i18n.yaml` | 10 种语言、必备语言、标签字典(33 条) |
|
|
69
|
+
| `envkeys.yaml` | 面板隐式提供的变量(`CONTAINER_NAME` 等) |
|
|
70
|
+
| `form-design.yaml` | 表单预算、密钥识别 |
|
|
71
|
+
| `dependencies.yaml` | 面板依赖:选择器形状、5 个 family(db / redis / mongo …)与变量表 |
|
|
72
|
+
| `recipes.yaml` | 4 条配方(依赖接线成套写法) |
|
|
73
|
+
| `form-review.yaml` | 表单审计的判定模式(哪些名字是官方约定) |
|
|
74
|
+
| `lessons.yaml` | 8 条实战教训(L001–L008),可被检查器或日志匹配使用 |
|
|
75
|
+
|
|
76
|
+
## 证据链:为什么这么设计
|
|
77
|
+
|
|
78
|
+
一次真实的翻车:包静态校验全绿,装上去容器一起来就退出——因为应用启动时要读
|
|
79
|
+
`data/.config.yaml`,而包只创建了空目录。这类失败(`lessons.yaml` 的 L001)
|
|
80
|
+
是这套设计要拦的东西,所以有了三条纪律:
|
|
81
|
+
|
|
82
|
+
1. **没有证据就停下来。** D03/D05/D06 缺证据 → `p1 gen` 退出 2、一个文件都不写。
|
|
83
|
+
2. **替人做过的决定要留痕。** `"acknowledged": {"D03": "理由"}` 或 `--allow-unresolved`
|
|
84
|
+
都能放行,但会写进包内 `decision-audit.json`。
|
|
85
|
+
3. **失败要能解释。** 冒烟/安装失败时把日志映射到 lesson,直接给根因与改法,
|
|
86
|
+
而不是丢一段原始日志。
|
|
87
|
+
|
|
88
|
+
## 扩展点(怎么加东西)
|
|
89
|
+
|
|
90
|
+
| 想做什么 | 改哪里 | 要写代码吗 |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| 加一条校验规则 | `checks/*` + `rules/data/store.yaml` | 要(规则逻辑在代码,阈值在数据) |
|
|
93
|
+
| 调整表单预算 / 密钥识别 | `rules/data/form-design.yaml` | 不用 |
|
|
94
|
+
| 新增一种面板依赖(如 rabbitmq) | `rules/data/dependencies.yaml`(family + kind) | 不用;`i18n.yaml` 补标签 |
|
|
95
|
+
| 新增一条配方 | `rules/data/recipes.yaml` | 不用 |
|
|
96
|
+
| 新增一条教训 | `rules/data/lessons.yaml`(+ `remote.LOG_SIGNATURES` 若要日志匹配) | 视情况 |
|
|
97
|
+
| 调整表单审计的判断 | `rules/data/form-review.yaml` | 不用 |
|
|
98
|
+
| 新增一个决策 | `rules/data/decisions.yaml` + `resolve/__init__.py` 的 `decide()` | 要 |
|
|
99
|
+
| 更新官方语料统计 | `p1 patterns build` / `p1 depth --corpus`,再更新 `docs/generation-depth.md` | 不用 |
|
|
100
|
+
|
|
101
|
+
## 测试与验收
|
|
102
|
+
|
|
103
|
+
三层,越往下越接近真实:
|
|
104
|
+
|
|
105
|
+
1. `pytest`(303 条):单元与集成,包含对抗性审查留下的回归(危险行为、
|
|
106
|
+
同名变量覆盖、跨服务串味、静默丢弃)。
|
|
107
|
+
2. `p1 selftest`(10 项):**装完之后**这套安装能不能用——规则数据能不能加载、
|
|
108
|
+
生成→校验链路通不通、证据闸门拦不拦得住、配方能不能接线。
|
|
109
|
+
3. `_recon/verify_all.py`(12 节,工作区里):一键复算所有对外声明过的数字,
|
|
110
|
+
包括产物扫描与"修复前/后"对照。
|
|
111
|
+
|
|
112
|
+
真实环境验证(可选):云服务器上的 `p1 deploy` / `p1 uninstall` / `remote smoke` /
|
|
113
|
+
`p1 ports`,每次验证后按基线核对并清理。
|
|
114
|
+
|
|
115
|
+
## 已知限制
|
|
116
|
+
|
|
117
|
+
- `p1 patterns` 依赖语料构建,语料更新后要重跑 `patterns build`。
|
|
118
|
+
- `remote smoke` 与 `deploy --probe` 的判定是 `HTTP < 500 即通过`:
|
|
119
|
+
403/404 也算"有应答",它回答的是"端口有没有服务",不是"功能对不对"。
|
|
120
|
+
- `form audit` 按名字与引用判断,语义层面的取舍仍要人做。
|
|
121
|
+
- `dependencies.yaml` 只覆盖实测过的 family(db / redis / mongo)。
|
|
122
|
+
- 面板侧功能(deploy / remote / panel)按"够用即停"维护,不再扩展;
|
|
123
|
+
它们不是核心路径,也不该成为依赖。
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# 工具状态(截至 2026-09-20,版本 0.9.3)
|
|
2
|
+
|
|
3
|
+
交接时先看这份:现在有什么、验过什么、还差什么。
|
|
4
|
+
|
|
5
|
+
## 发布与版本
|
|
6
|
+
|
|
7
|
+
- PyPI:`1panel-toolkit`,latest = **0.9.3**(安装 `uv tool install 1panel-toolkit`)。
|
|
8
|
+
- 同一天从 0.3.0 走到 0.9.3,共 10 次发布;每次都是"先量 → 改 → 全量验 → 发"。
|
|
9
|
+
- 版本号在两处,必须一起改:`pyproject.toml` 与 `src/p1toolkit/__init__.py`。
|
|
10
|
+
- 发布命令:`uv publish dist/1panel_toolkit-<version>*`,token 走 `UV_PUBLISH_TOKEN`。
|
|
11
|
+
- 上传后 `uv tool install ==X` 常常先报 unsatisfiable(索引缓存):`uv cache clean 1panel-toolkit` 后等 20 秒重试,通常第 2 次成功。
|
|
12
|
+
|
|
13
|
+
## 能力清单(16 个命令)
|
|
14
|
+
|
|
15
|
+
| 命令 | 用途 | 需要面板/SSH |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `resolve` | 抽上游证据,回答 D01–D10,产出 draft-spec + 报告 | 否(GitHub 需网络;本地检出全离线) |
|
|
18
|
+
| `recipe` | `list` / `show` / `apply`:把一类应用的接线成套写进 spec | 否 |
|
|
19
|
+
| `gen` | 从 spec 生成包(生成前跑证据审计) | 否 |
|
|
20
|
+
| `check` | 7 组规则校验 | 否 |
|
|
21
|
+
| `form` | `audit`:表单里哪些框用户真必须填,输出计划骨架 | 否 |
|
|
22
|
+
| `fix` | 幂等修复;`--plan` 执行人工决定 | 否 |
|
|
23
|
+
| `depth` | 语料多样性 + 单包"是不是模板复制品" | 否(语料可选) |
|
|
24
|
+
| `patterns` | 22 条官方真实写法(list / show / find / build / export) | 否 |
|
|
25
|
+
| `ports` | 主机端口三态(open / closed / filtered) | 网络 |
|
|
26
|
+
| `remote` | `check` / `smoke` / `logs`:SSH 起容器冒烟 | SSH(可选) |
|
|
27
|
+
| `deploy` | 走面板 API 安装 → 验证 → 可选卸载;`--probe` 敲端口 | 面板 + SSH(可选) |
|
|
28
|
+
| `uninstall` | 卸载已安装应用(无参数会拒绝) | 面板(可选) |
|
|
29
|
+
| `panel` | `ping`:面板鉴权与连通性 | 面板(可选) |
|
|
30
|
+
| `config` | 读写本地配置(面板 URL / key / SSH) | 否 |
|
|
31
|
+
| `rules` | 打印随包分发的规则数据 | 否 |
|
|
32
|
+
| `selftest` | 装完之后的自检(10 项) | 否 |
|
|
33
|
+
|
|
34
|
+
**核心路径(前 8 个)完全离线可用**,有测试盯着:把配置指向不存在的文件,`gen --check` / `check` / `form audit` / `depth` 必须照常通过。
|
|
35
|
+
|
|
36
|
+
## 规则与数据规模
|
|
37
|
+
|
|
38
|
+
- 规则数据 **9 份**:decisions / store / i18n / envkeys / form-design / dependencies / recipes / form-review / lessons。
|
|
39
|
+
- 校验规则组:结构 `P0xx`、i18n `I0xx`、compose `C0xx`(含 C005 healthcheck 必须是映射)、卫生 `H0xx`、表单设计 `F0xx`(含 F007 字段没人引用)、经验 `L0xx`、实质 `S0xx`。
|
|
40
|
+
- 写法库 **22 条**(带官方采用数);配方 **4 条**;教训 **8 条**;生成决策 **10 条**;依赖 family **5 个 kind**(postgresql / mysql / mariadb / redis / mongodb);标签字典 **33 条**。
|
|
41
|
+
|
|
42
|
+
## 实测数据(写代码前量的,避免拍脑袋)
|
|
43
|
+
|
|
44
|
+
语料:882 个官方应用 / 1617 个版本(`apps库/`)。
|
|
45
|
+
|
|
46
|
+
| 事实 | 数值 | 用在哪 |
|
|
47
|
+
| --- | --- | --- |
|
|
48
|
+
| 容器内挂载点取值 | 642 种 | D03 必须逐个决定 |
|
|
49
|
+
| 容器内端口取值 | 359 种 | D05 必须逐个决定 |
|
|
50
|
+
| 应用环境变量名 | 3197 个 | D01 必须逐个决定 |
|
|
51
|
+
| 表单字段中位数 / 必填比例 | 5 个 / 88% | form-design.yaml 的预算 |
|
|
52
|
+
| 表单字段总数 / 必填带默认值 | 10640 / 7733(73%) | form-review.yaml 的判定 |
|
|
53
|
+
| 面板依赖选择器 | 106 个版本 / 58 个应用 | dependencies.yaml |
|
|
54
|
+
| 两步式 apps vs 一步式 service | 72 : 53 | 默认走两步式 |
|
|
55
|
+
| 依赖应用自己声明的字段 | PANEL_DB_USER 92 / USER_PASSWORD 92 / NAME 90 / PORT 49 | 只为被引用的变量生成字段 |
|
|
56
|
+
| 商店简介写法 | title = description = shortDescZh,739/771 | gen 的元数据 |
|
|
57
|
+
| Kasm 批量包互相重合 | 75% | depth 的 possibly-reskinned 阈值依据 |
|
|
58
|
+
| 自产包在官方语料里的相似对象 | 无 | 说明是真适配 |
|
|
59
|
+
|
|
60
|
+
测量脚本在 `_recon/`:measure_fix.py(fix 前后)、measure_deps.py(依赖选择器)、measure_forms.py(表单与 mongo)、audit_source.py(死代码与文档引用)。
|
|
61
|
+
|
|
62
|
+
## 质量门(最近一次全量结果)
|
|
63
|
+
|
|
64
|
+
| 检查 | 结果 |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `pytest` | 303 passed(无本地语料时同样通过) |
|
|
67
|
+
| `p1 selftest` | 10/10 |
|
|
68
|
+
| `_recon/verify_all.py` | 12 节全绿(含 7 项历史修复、产物扫描、无本地语料重跑) |
|
|
69
|
+
| 产物 | whl 51 项 / sdist 81 项,无 .venv/out/dist/__pycache__ 垃圾 |
|
|
70
|
+
| 密钥扫描 | 无真实密钥、无真实 IP(三处已知假向量除外) |
|
|
71
|
+
|
|
72
|
+
## 真实环境验证记录
|
|
73
|
+
|
|
74
|
+
云服务器(Ubuntu 24.04 / Docker 29.5.1 / Compose v5.1.3),验证后每次都已还原基线:
|
|
75
|
+
|
|
76
|
+
| 验证 | 结果 |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `p1 panel ping` | ok(v2 签名鉴权) |
|
|
79
|
+
| `p1 deploy … --uninstall` | PASS,约 50s,容器 Running |
|
|
80
|
+
| 部署后独立 `p1 uninstall <名>` | PASS,回到基线 |
|
|
81
|
+
| `p1 deploy … --probe` | PASS,`探针: http://127.0.0.1:10000/ -> HTTP 403` |
|
|
82
|
+
| `p1 deploy … --probe <无人监听端口>` | FAIL deploy/probe,exit 1,仍按参数清理 |
|
|
83
|
+
| `p1 remote smoke <包> --prune-image` | PASS,端口用包自己的默认值,测试后镜像已清 |
|
|
84
|
+
| `p1 ports <host> …` | 正确区分 filtered(安全组拦)与 closed(可用) |
|
|
85
|
+
|
|
86
|
+
面板/SSH 侧按"够用即停"维护:失败时给根因(lesson 匹配、容器日志),不再扩展。
|
|
87
|
+
|
|
88
|
+
## 用户现有包的收敛结果
|
|
89
|
+
|
|
90
|
+
2026-09-20 用 `p1 fix` 就地修复;修复前副本在 `out/fix-backup-20260920/`,可用 `_recon/measure_fix.py --baseline out/fix-backup-20260920` 复算 before 列。
|
|
91
|
+
|
|
92
|
+
| 包 | 修复前 | 修复后 |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| hindsight | 2 fail + 8 warn | 0 fail + 2 warn(表单 30 → 12 → 按 plan 11) |
|
|
95
|
+
| openviking | 1 fail + 6 warn | 0 fail + 1 warn |
|
|
96
|
+
| xiaozhi-esp32-server | 1 fail + 13 warn | 0 fail + 1 warn |
|
|
97
|
+
| xiaozhi-esp32-server-full | 1 fail + 15 warn | OK,无问题 |
|
|
98
|
+
|
|
99
|
+
四个包的 `p1 depth` 判定均为 adapted(xiaozhi 附一条"没有应用自有变量"的说明——它的配置在挂载的 `data/.config.yaml` 里)。
|
|
100
|
+
|
|
101
|
+
## 未做 / 已知限制
|
|
102
|
+
|
|
103
|
+
- 面板侧不再扩展(用户明确要求"够用即停")。
|
|
104
|
+
- 不做服务器专属优化:端口用包自己的默认值,冲突由 1Panel 提示(lesson L008)。
|
|
105
|
+
- patterns 与 recipes 尚未互相引用(配方里点不开对应的官方写法示例)。
|
|
106
|
+
- 多版本包(一个 spec 生成 latest + 固定版本目录)未实现。
|
|
107
|
+
- 探测语义是"HTTP < 500 即通过",不区分 2xx/4xx。
|
|
108
|
+
- 语料更新后需要手动重跑 `patterns build` 与 `depth --corpus`。
|
|
109
|
+
|
|
110
|
+
## 维护手册
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# 改完代码
|
|
114
|
+
.venv\Scripts\python.exe -m pytest # 303 条
|
|
115
|
+
.venv\Scripts\p1.exe selftest # 10 项
|
|
116
|
+
python _recon\verify_all.py # 12 节(工作区根目录)
|
|
117
|
+
|
|
118
|
+
# 发版(记得同时改两个版本号)
|
|
119
|
+
uv build --out-dir dist
|
|
120
|
+
uv publish dist/1panel_toolkit-<version>*
|
|
121
|
+
uv cache clean 1panel-toolkit
|
|
122
|
+
uv tool install 1panel-toolkit==<version> --refresh --index-url https://pypi.org/simple
|
|
123
|
+
|
|
124
|
+
# 语料更新
|
|
125
|
+
p1 patterns build --corpus <apps目录> --markdown docs/patterns.md
|
|
126
|
+
p1 depth --corpus <apps目录> # 复核 docs/generation-depth.md 的数字
|
|
127
|
+
|
|
128
|
+
# 真实环境(可选,动手前先问,之后按基线核对并清理)
|
|
129
|
+
p1 panel ping
|
|
130
|
+
p1 deploy <包> --param PANEL_APP_PORT_HTTP=10000 --probe --uninstall --cleanup-local
|
|
131
|
+
p1 remote smoke <包> --prune-image
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
发布前自检:产物扫描(`_recon/verify_all.py` 第 8 节)会抓真实 IP 与密钥——写文档时把服务器地址写成 `<host>` 就不会踩。
|
|
@@ -10,7 +10,6 @@ from ..model import Package
|
|
|
10
10
|
from ..report import Report
|
|
11
11
|
|
|
12
12
|
_BOOL_LABEL = re.compile(r"^\s*(?:-\s*)?label:\s*(true|false)\s*$", re.IGNORECASE)
|
|
13
|
-
_LABEL_LINE = re.compile(r"^\s*(?:-\s*)?label:\s*(\S+)")
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def _missing(data: Any, languages: list[str]) -> list[str]:
|
|
@@ -273,10 +273,6 @@ def scan(corpus_dirs: list[Path], limit: int | None = None) -> list[Profile]:
|
|
|
273
273
|
# --------------------------------------------------------------------------
|
|
274
274
|
|
|
275
275
|
|
|
276
|
-
def _distinct(values: Iterable[str]) -> int:
|
|
277
|
-
return len({v for v in values if v})
|
|
278
|
-
|
|
279
|
-
|
|
280
276
|
def dimension_report(
|
|
281
277
|
profiles: list[Profile], top: int = 6, include_values: bool = False
|
|
282
278
|
) -> dict[str, Any]:
|
|
@@ -75,15 +75,6 @@ def _dump(value) -> str:
|
|
|
75
75
|
return yaml.safe_dump(value, allow_unicode=True, sort_keys=False, width=100)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def _write(path: Path, text: str, result: FixResult, rule: str, detail: str, dry_run: bool) -> None:
|
|
79
|
-
current = path.read_text(encoding="utf-8-sig", errors="replace") if path.exists() else None
|
|
80
|
-
if current == text:
|
|
81
|
-
return
|
|
82
|
-
result.add(rule, path.name, detail)
|
|
83
|
-
if not dry_run:
|
|
84
|
-
path.write_text(text, encoding="utf-8", newline="\n")
|
|
85
|
-
|
|
86
|
-
|
|
87
78
|
# --------------------------------------------------------------------------
|
|
88
79
|
# mechanical
|
|
89
80
|
# --------------------------------------------------------------------------
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|