refactor: 本仓升为唯一源(原 sap-cli 源码仓归档)
方向反转:此前 SKILL.md 是「模板渲染产物」、sap-cli 是源;现 sap-cli 归档, sap-cli-skill 承接开发与分发,SKILL.md 回归手工维护的正本。 迁移(来自 sap-cli,共 104 文件): - tests/ 692 例测试(15 个文件的内联 sys.path 改指 assets/) - openspec/ SDD 规格与归档变更(42 文件) - docs/ 开发文档与 ADT 原理(含 dev/CLAUDE.md、AGENTS.md) - .claude/ rules 副本 + settings.json(供 Claude Code) - .github/ .hermes/ .pre-commit-config.yaml .editorconfig CLAUDE.md - scripts/ 保持仅 setup.py(pack_skill.py 已随旧仓归档,不迁) 修复(迁移暴露的真实缺陷): - assets/pyproject.toml 的 build-backend 写作 `setuptools.backends._legacy:_Backend`, 该模块在 setuptools 中不存在 → `pip install -e` 从来装不上。改为 build_meta。 实测:临时 venv 安装成功,sap-cli --help 正常列出 31 个命令 - pyproject readme 指向不存在的 assets/README.md(editable 安装会失败)→ 改内联文本 - pyproject urls 改指 sap-cli-skill 机制调整: - .github/workflows/ci.yml 适配 assets/ 布局;顶部注明该工作流仅 GitHub 执行, 本仓在 Gitee 不会自动跑 - pre-commit 增本地测试门禁(Gitee 上真正生效的那道) - .gitignore 合并旧仓完整规则(保留 log/ 下 md 知识库入库,只忽略运行日志) - 大文件上限 100KB→1MB(架构图 512KB) 守卫测试 tests/unit/test_repo_guards.py(10 → 18 例): - SKILL.md 须记录 parser 全部 CLI 命令 / 铁律 1-5 须为真实小节标题 / 示例不得违反铁律 5 - references/ 规则齐备;.claude/rules 与 references 必须一致(实测抓到一次真实漂移) - VERSION == sapcli.__version__ == README 版本 - 仓内不得再出现 pack_skill.py / skill-src(防废弃流程回潮) 698 tests OK;editable 安装与 CLI 入口经临时 venv 实测通过。 docs/RELEASING.md 重写为单源开发流程。
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
||||
# sap-cli 功能矩阵分析报告
|
||||
|
||||
> 分析基于源码静态审查(`sapcli/types.py`、`sapcli/cli/parser.py`、`sapcli/commands/*`、`sapcli/client/*`)。
|
||||
> 生成时间:2026-06-18
|
||||
|
||||
---
|
||||
|
||||
## 第一部分:已实现的命令完整列表
|
||||
|
||||
共 **25 个顶层命令**(含子命令则为 **38 个操作**)。命令分发逻辑见 `sapcli/cli/app.py` 的 `command_map`(外加 `config`/`auth` 两个不走 SAP 连接的命令)。
|
||||
|
||||
| # | 命令 | 子命令 | 所在文件 | 功能描述 | 是否需 SAP 连接 |
|
||||
|---|------|--------|----------|----------|----------------|
|
||||
| 1 | `download` | — | `commands/crud.py::cmd_download` | 下载对象源代码到本地文件 | ✅ |
|
||||
| 2 | `sync` | — | `commands/crud.py::cmd_sync` | 同步单对象源码并激活(检查→写入→语法检查→激活) | ✅ |
|
||||
| 2b| `sync --all` | — | `commands/batch.py::cmd_sync_all` | 批量同步清单内所有对象(拓扑排序) | ✅ |
|
||||
| 3 | `upload` | — | `commands/upload.py::cmd_upload` | 仅上传源码(锁定→写入→解锁,不检查不激活) | ✅ |
|
||||
| 4 | `syntax-check` | — | `commands/syntax_check.py::cmd_syntax_check` | 远程对象语法检查(不上传不激活) | ✅ |
|
||||
| 5 | `activate` | — | `commands/activate.py::cmd_activate` | 单独激活对象(单/批量模式) | ✅ |
|
||||
| 6 | `delete` | — | `commands/crud.py::cmd_delete` | 从 SAP 删除对象(交互确认) | ✅ |
|
||||
| 7 | `info` | — | `commands/crud.py::cmd_info` | 查询对象元数据 | ✅ |
|
||||
| 8 | `create` | — | `commands/crud.py::cmd_create` | 创建开发对象(含 DDIC/函数组) | ✅ |
|
||||
| 9 | `diff` | — | `commands/diff_cmd.py::cmd_diff` | 本地 vs SAP 代码差异对比 | ✅ |
|
||||
| 10 | `list` | — | `commands/search.py::cmd_list` | 列出 SAP 对象(类型/包/前缀过滤) | ✅ |
|
||||
| 11 | `whereused` | — | `commands/search.py::cmd_whereused` | Where-Used 引用查询 | ✅ |
|
||||
| 12 | `search` | — | `commands/search.py::cmd_search` | 源代码搜索 | ✅ |
|
||||
| 13 | `check` | — | `commands/quality.py::cmd_check` | ATC 代码检查 | ✅ |
|
||||
| 14 | `format` | — | `commands/quality.py::cmd_format` | 代码格式化(Pretty Printer + 写回 + 激活) | ✅ |
|
||||
| 15 | `transport` | `list` | `commands/transport.py::_transport_list` | 列出可修改的传输请求 | ✅ |
|
||||
| 15b| | `info` | `::_transport_info` | 查看传输请求详情 | ✅ |
|
||||
| 15c| | `release` | `::_transport_release` | 释放传输请求(交互确认) | ✅ |
|
||||
| 15d| | `objects` | `::_transport_objects` | 列出传输请求中对象 | ✅ |
|
||||
| 16 | `package` | `create` | `commands/package_cmd.py::_package_create` | 创建 ABAP 包 | ✅ |
|
||||
| 16b| | `info` | `::_package_info` | 查看包详情 | ✅ |
|
||||
| 16c| | `list` | `::_package_list` | 列出包中对象 | ✅ |
|
||||
| 17 | `cds` | `download` | `commands/cds.py::_cds_download` | 下载 CDS View DDL 源码 | ✅ |
|
||||
| 17b| | `sync` | `::_cds_sync` | 同步本地 DDL 到 SAP(含创建/激活) | ✅ |
|
||||
| 17c| | `create` | `::_cds_create` | 创建新的 CDS View | ✅ |
|
||||
| 18 | `init` | — | `commands/batch.py::cmd_init` | 扫描本地→查询 SAP→生成 manifest.json | ✅ |
|
||||
| 19 | `refresh` | — | `commands/batch.py::cmd_refresh` | 刷新清单中对象状态 | ✅ |
|
||||
| 20 | `analyze` | — | `commands/analyze.py::cmd_analyze` | 本地依赖自动分析(正则匹配) | ✅ |
|
||||
| 21 | `scaffold` | — | `commands/scaffold.py::cmd_scaffold` | 项目模板创建(4 种模板) | ❌(本地) |
|
||||
| 22 | `config` | `show`/`list-profiles`/`set` | `commands/config_cmd.py::cmd_config` | 配置管理 | ❌ |
|
||||
| 23 | `auth` | `login`/`logout`/`status` | `auth.py::cmd_auth_*` | keyring 密钥管理 | ❌ |
|
||||
| 24 | `show-table` | — | `commands/ddl_query.py::cmd_show_table` | 查看 DDIC 表字段结构(SE11) | ✅ |
|
||||
| 25 | `read-table` | — | `commands/ddl_query.py::cmd_read_table` | 查询表数据(freestyle SQL) | ✅ |
|
||||
| 26 | `run-program` | — | `commands/program_run.py::cmd_run_program` | 远程执行 ABAP 程序(SA38) | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 第二部分:已注册的对象类型完整列表
|
||||
|
||||
共 **16 个对象类型**,全部在 `sapcli/types.py` 中通过 `_register()` 注册。
|
||||
|
||||
| # | key | label | 对象端点 (obj_uri) | 源码端点 (src_uri) | has_source | create_content_type |
|
||||
|---|-----|-------|-------------------|-------------------|-----------|---------------------|
|
||||
| 1 | `report` | 程序(REPORT) | `/programs/programs/{name}` | `/programs/programs/{name}/source/main` | ✅ | `vnd.sap.adt.programs.programs.v2+xml` |
|
||||
| 2 | `class` | 类(CLASS) | `/oo/classes/{name}` | `/oo/classes/{name}/source/main` | ✅ | `vnd.sap.adt.oo.classes.v1+xml` |
|
||||
| 3 | `function` | 函数模块 | `/functions/groups/{group}/fmodules/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.functions.fmodules.v1+xml` |
|
||||
| 4 | `functiongroup` | 函数组 | `/functions/groups/{name}` | — (None) | ❌ | `vnd.sap.adt.functions.groups.v2+xml` |
|
||||
| 5 | `interface` | 接口 | `/oo/interfaces/{name}` | `/oo/interfaces/{name}/source/main` | ✅ | `vnd.sap.adt.oo.interfaces.v1+xml` |
|
||||
| 6 | `domain` | 域(DOMAIN) | `/ddic/domains/{name}` | `/ddic/domains/{name}/source/main` | ✅ | `vnd.sap.adt.domains.v2+xml` |
|
||||
| 7 | `dataelement` | 数据元素 | `/ddic/dataelements/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.dataelements.v2+xml` |
|
||||
| 8 | `table` | 透明表 | `/ddic/tables/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.ddic.tables.v1+xml` |
|
||||
| 9 | `structure` | 结构 | `/ddic/structures/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.ddic.structures.v1+xml` |
|
||||
| 10 | `tabletype` | 表类型 | `/vit/.../object_name/{name}` | — (None) | ❌ | `vnd.sap.adt.ddic.tabletypes.v1+xml` |
|
||||
| 11 | `include` | Include程序 | `/programs/includes/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.programs.includes.v2+xml` |
|
||||
| 12 | `cdsview` | CDS视图 | `/dds/ddl/sources/{name}` | `/dds/ddl/sources/{name}/content` | ✅ | `text/plain` |
|
||||
| 13 | `messageclass` | 消息类 | `/oo/t100/messages/classes/{name}` | — (None) | ❌ | `vnd.sap.adt.t100.message.classes.v1+xml` |
|
||||
| 14 | `view` | 数据库视图 | `/ddic/views/{name}` | `.../source/main` | ✅ | `vnd.sap.adt.ddic.views.v1+xml` |
|
||||
| 15 | `searchhelp` | 搜索帮助 | `/ddic/searchhelps/{name}` | — (None) | ❌ | `vnd.sap.adt.ddic.searchhelps.v1+xml` |
|
||||
| 16 | `lockobject` | 锁对象 | `/ddic/lockobjects/{name}` | — (None) | ❌ | `vnd.sap.adt.ddic.lockobjects.v1+xml` |
|
||||
|
||||
**关键判定规则**:`has_source=False` 的 5 种类型(functiongroup / tabletype / messageclass / searchhelp / lockobject)**无法**执行源码类操作(download / sync / upload / syntax-check / format / diff),命令会在 `parse_object_name` 后因 `src_uri is None` 抛 `InvalidNameError`。
|
||||
|
||||
### client.py 中各操作的 HTTP 实现
|
||||
|
||||
| 操作 | client 方法 | HTTP 调用 | 所在 Mixin |
|
||||
|------|------------|----------|-----------|
|
||||
| 读源码 | `get_source` | `GET {src_uri}` (text/plain) | `_source.py` |
|
||||
| 写源码 | `set_source` | `PUT {src_uri}` (含 corrNr 重试) | `_source.py` |
|
||||
| 锁定 | `lock` | `POST {obj_uri}?_action=LOCK` | `_source.py` |
|
||||
| 解锁 | `unlock` | `POST {obj_uri}?_action=UNLOCK` | `_source.py` |
|
||||
| 激活 | `activate` | `POST /adt/activation?method=activate` | `_source.py` |
|
||||
| 语法检查 | `syntax_check` | `POST /adt/activation?method=check` | `_source.py` |
|
||||
| 删除 | `delete_object` | `DELETE {obj_uri}` (先锁) | `_ddic.py` |
|
||||
| 创建(通用) | `create_object` | `POST {collection_uri}` | `_ddic.py` |
|
||||
| 创建(DDIC) | `create_ddic_object` | `POST`/`PUT {src_uri}` (DDL) | `_ddic.py` |
|
||||
| 状态查询 | `get_object_status` | `GET {obj_uri}` (+ lock 探测 corrNr) | `_ddic.py` |
|
||||
| 存在检查 | `object_exists` | `GET {obj_uri}` (200/404) | `_base.py` |
|
||||
| 函数组创建 | `create_function_group` | `POST {collection}` | `_ddic.py` |
|
||||
| CDS 源码 | `get_cds_source`/`create_cds` | `GET/POST /ddic/ddlsources/*` | `_ddic.py` |
|
||||
| 对象列表 | `list_objects` | `GET /repository/informationsystem/search` | `_search.py` |
|
||||
| 引用查询 | `where_used` | `POST /usage/whereusedlist` | `_search.py` |
|
||||
| 代码搜索 | `search_code` | `POST /repository/structuredsearch` | `_search.py` |
|
||||
| ATC 检查 | `atc_check` | `POST /atos/checks` | `_ddic.py` |
|
||||
| 格式化 | `pretty_print` | `POST /prettyprinter` | `_ddic.py` |
|
||||
| 表结构 | `get_table_fields` | `GET /datapreview/ddic/{name}/metadata` | `_ddic.py` |
|
||||
| 表数据 | `query_table_data` | `POST /datapreview/freestyle` | `_ddic.py` |
|
||||
| 运行程序 | `run_program` | `POST /programs/programrun/{name}` | `_ddic.py` |
|
||||
|
||||
---
|
||||
|
||||
## 第三部分:半实现 / 受限功能列表
|
||||
|
||||
### 🔴 关键限制(会导致功能不可用)
|
||||
|
||||
1. **`create` 通用命令不支持 6 种类型** — `client/_ddic.py::_build_create_body` 仅覆盖 9 种类型,对其余直接 `raise ValueError("不支持的对象类型")`:
|
||||
- ❌ `include`、`cdsview`、`messageclass`、`view`、`searchhelp`、`lockobject`
|
||||
- 其中 `cdsview` 有独立的 `cds create` 命令可绕过;其余 5 种**完全无法创建**。
|
||||
|
||||
2. **CDS View 端点不一致(潜在 Bug)** — 两套 URI 并存:
|
||||
- `types.py` 注册的 cdsview:`/sap/bc/adt/dds/ddl/sources/{name}` + `/content`
|
||||
- `cds.py` 命令 & `client._ddic.py` 硬编码:`/sap/bc/adt/ddic/ddlsources/{name}` + `/source/main`
|
||||
- 后果:`download --type cdsview` 走 types.py 的 URI,可能 404;而 `cds download` 走另一套 URI。
|
||||
|
||||
3. **`tabletype` 创建必须传 `--definition`** — `_create_ddic` 对 tabletype 无默认模板,缺 `line_type` 时直接 `raise CreateError`。
|
||||
|
||||
### 🟡 功能降级(有 fallback 但不完整)
|
||||
|
||||
4. **`info` 命令 Accept 头映射不全** — `cmd_info` 的 `INFO_ACCEPT` 字典只覆盖 9 种类型,以下 7 种回退到 `application/xml`(可能触发 406 后再回退):
|
||||
- ⚠️ `structure`、`include`、`cdsview`、`messageclass`、`view`、`searchhelp`、`lockobject`
|
||||
|
||||
5. **`list`/`whereused`/`search` 的 ADT 类型码映射不全** — `search.py::_TYPE_FILTER_MAP` 缺 2 种:
|
||||
- ⚠️ `searchhelp`、`lockobject`(会原样传 key 当类型码,ADT 端可能不识别)
|
||||
|
||||
6. **NW 7.40 激活空响应** — `client/_source.py::activate` 注释标注:NW 7.40 返回空响应时已实现「双重激活」变通,但仍可能提示「对象仍 inactive,请在 SE09 手动激活」(返回 W 级消息)。
|
||||
|
||||
7. **`tabletype` 用 VIT 端点** — `parse_object_name` 对 tabletype 做大写处理,注释说明「VIT 端点需要大写名称以返回完整元数据」,暗示该端点行为特殊。
|
||||
|
||||
### 🟢 设计限制(非 Bug)
|
||||
|
||||
8. **`functiongroup` 是容器对象** — `has_source=False`,download/sync/upload/format 等源码操作明确拒绝(提示「不包含可编辑的源代码文件」)。
|
||||
|
||||
9. **scaffold 模板中的 `TODO`** — `scaffold.py` 模板字符串内的 `\" TODO: 填充数据` / `\" TODO: 调用 BAPI 函数` 是**生成的代码占位符**,非 CLI 自身 TODO。
|
||||
|
||||
10. **DDIC 源码下载语义** — domain/dataelement/table/structure 虽有 `src_uri`,但其 `source/main` 返回的是 DDL/XML 定义而非可编辑 ABAP 文本,download 技术可用但语义需注意。
|
||||
|
||||
### 未发现的问题
|
||||
- ✅ 未发现 `NotImplementedError`、`pass` 占位方法、或裸 `...` 存根。
|
||||
- ✅ 所有 parser 注册的命令在 `app.py` 均有对应 handler(无悬空命令)。
|
||||
|
||||
---
|
||||
|
||||
## 第四部分:命令 × 类型 完整交叉矩阵
|
||||
|
||||
**图例**:✅ 完全支持 | ⚠️ 部分支持/有降级 | ❌ 不支持 | ➖ 不适用(命令与对象类型无关)
|
||||
|
||||
> 仅列出「带 `--type` 参数、面向单个对象类型」的 14 个命令。`init`/`refresh`/`sync --all`/`analyze`/`config`/`auth`/`transport`/`package`/`scaffold`/`show-table`/`read-table`/`run-program`/`cds` 为项目级或领域专属命令,对所有类型行为一致,不纳入交叉矩阵。
|
||||
|
||||
| 命令 \ 类型 | report | class | function | functiongroup | interface | domain | dataelement | table | structure | tabletype | include | cdsview | messageclass | view | searchhelp | lockobject |
|
||||
|:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| **download** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **sync** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **upload** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **syntax-check** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **format** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **diff** | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅¹ | ❌ | ✅ | ❌ | ❌ |
|
||||
| **info** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️² | ⚠️² | ⚠️² | ⚠️² | ⚠️² | ⚠️² | ⚠️² | ⚠️² |
|
||||
| **delete** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **activate** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **create** | ✅ | ✅ | ✅³ | ✅³ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️⁴ | ❌⁵ | ⚠️⁶ | ❌⁵ | ❌⁵ | ❌⁵ | ❌⁵ |
|
||||
| **check (ATC)** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
| **whereused** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️⁷ | ⚠️⁷ |
|
||||
| **list** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️⁷ | ⚠️⁷ |
|
||||
| **search** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⚠️⁷ | ⚠️⁷ |
|
||||
|
||||
**脚注**:
|
||||
- ¹ **cdsview 经通用源码命令**:走 `types.py` 的 `/dds/ddl/sources/{name}/content` 端点,与专用 `cds` 命令的 `/ddic/ddlsources/{name}/source/main` 不一致,可能失败(见第三部分#2)。
|
||||
- ² **info 降级**:无显式 Accept 头,回退 `application/xml`(见第三部分#4)。
|
||||
- ³ **function/functiongroup 特殊创建**:function 自动创建函数组;functiongroup 走 `create_function_group` 专用路径。
|
||||
- ⁴ **tabletype 创建受限**:必须 `--definition` 指定 line_type,无默认模板(见第三部分#3)。
|
||||
- ⁵ **create 不支持**:`_build_create_body` 直接 `ValueError`(见第三部分#1)。`messageclass`/`view`/`searchhelp`/`lockobject`/`include` 无任何创建路径。
|
||||
- ⁶ **cdsview 经通用 create 失败**:但可用专用 `cds create` 命令。
|
||||
- ⁷ **ADT 类型码缺失**:`_TYPE_FILTER_MAP` 无 searchhelp/lockobject 映射,过滤参数原样透传(见第三部分#5)。
|
||||
|
||||
### 支持度统计
|
||||
|
||||
| 命令 | ✅ 完全 | ⚠️ 部分 | ❌ 不支持 |
|
||||
|:---|:---:|:---:|:---:|
|
||||
| download/sync/upload/syntax-check/format/diff | 11 | 0 | 5 |
|
||||
| info | 9 | 7 | 0 |
|
||||
| delete | 16 | 0 | 0 |
|
||||
| activate / check | 16 | 0 | 0 |
|
||||
| create | 9 | 2 | 5 |
|
||||
| whereused / list / search | 14 | 2 | 0 |
|
||||
|
||||
**结论**:`delete`/`activate`/`check` 三命令对全部 16 种类型通用;源码类 6 命令对 11 种 has_source 类型通用;**`create` 是缺口最大的命令**(6 种类型无法创建)。
|
||||
|
||||
---
|
||||
|
||||
*报告结束。如需复查某条判定,可按文中引用的文件路径与行号定位源码。*
|
||||
Reference in New Issue
Block a user