feat: sap-cli skill v2.1.0 — self-contained distributable package

- assets/: sap-cli source (v2.1.0, 26 commands, 16 object types)
- references/: tool constraints + error handling (self-contained)
- scripts/setup.py: one-click install/config/verify
- SKILL.md: full command reference + dual-platform install guide
- VERSION: 2.1.0

Built from D:/Codespace/sap-cli via scripts/pack_skill.py
This commit is contained in:
2026-06-13 20:52:24 +08:00
commit 1e39b6da88
49 changed files with 7172 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
"""CDS View 命令:cds (download / sync / create)。"""
from __future__ import annotations
import argparse
import logging
import os
from sapcli.client import ADTClient
logger = logging.getLogger("sapcli.commands.cds")
def cmd_cds(args: argparse.Namespace, client: ADTClient) -> None:
"""CDS View 操作。"""
action = getattr(args, "cds_action", None)
if action == "download":
_cds_download(args, client)
elif action == "sync":
_cds_sync(args, client)
elif action == "create":
_cds_create(args, client)
else:
print(" 用法: sap-cli cds [download|sync|create]")
print(" download — 下载 CDS View DDL 源码")
print(" sync — 同步本地 DDL 到 SAP")
print(" create — 创建新的 CDS View")
def _cds_download(args: argparse.Namespace, client: ADTClient) -> None:
"""下载 CDS View DDL 源码。"""
name: str = args.name
save_path: str = getattr(args, "path", ".")
print("=" * 60)
print(" SAP CDS View DDL 源码下载")
print("=" * 60)
print(f" CDS 名称: {name}")
print(f" 保存路径: {save_path}")
print(f"\n → 正在下载 DDL 源码...")
try:
source = client.get_cds_source(name)
except Exception as e:
print(f" ✗ 下载失败: {e}")
return
source = source.replace("\r\n", "\n").replace("\r", "\n")
line_count = len(source.splitlines())
print(f" ✓ DDL 源码下载成功! {len(source)} 字符, {line_count}")
if not os.path.isdir(save_path):
os.makedirs(save_path, exist_ok=True)
filename = f"{name.lower()}.ddl"
filepath = os.path.join(save_path, filename)
with open(filepath, "w", encoding="utf-8") as f:
f.write(source)
print(f"\n ✓ 文件已保存: {filepath}")
def _cds_sync(args: argparse.Namespace, client: ADTClient) -> None:
"""同步本地 DDL 到 SAP。"""
name: str = args.name
ddl_path: str = args.path
print("=" * 60)
print(" SAP CDS View DDL 同步")
print("=" * 60)
print(f" CDS 名称: {name}")
print(f" DDL 文件: {ddl_path}")
if not os.path.isfile(ddl_path):
print(f"\n ✗ DDL 文件不存在: {ddl_path}")
return
with open(ddl_path, "r", encoding="utf-8") as f:
ddl_source = f.read()
obj_uri = f"/sap/bc/adt/ddic/ddlsources/{name.lower()}"
src_uri = f"/sap/bc/adt/ddic/ddlsources/{name.lower()}/source/main"
# 检查对象是否存在
print(f"\n → 检查 CDS View 是否存在...")
exists = client.object_exists(obj_uri)
if not exists:
print(f" ℹ CDS View 不存在,需要先创建")
try:
client.create_cds(name, name, ddl_source)
print(f" ✓ CDS View 创建并同步成功!")
except Exception as e:
print(f" ✗ 创建失败: {e}")
return
print(f" ✓ CDS View 存在")
# 锁定 → 写入 → 解锁 → 激活
print(f"\n → 正在同步 DDL 源码...")
try:
lock_handle, corr_nr = client.lock(obj_uri)
try:
client.set_source(src_uri, ddl_source, lock_handle, corr_nr)
print(f" ✓ DDL 源码写入成功")
finally:
client.unlock(obj_uri, lock_handle)
except Exception as e:
print(f" ✗ 写入失败: {e}")
return
# 激活
print(f"\n → 正在激活...")
try:
success, messages = client.activate(name.upper(), obj_uri)
if success:
print(f" ✓ 激活成功")
else:
errors = [m for m in messages if m["type"] == "E"]
print(f" ✗ 激活失败: {len(errors)} 个错误")
for e in errors:
print(f" [错误] 行 {e['line']}: {e['text']}")
except Exception as e:
print(f" ⚠ 激活失败: {e}")
print(f"\n ✓ CDS View 同步完成!")
def _cds_create(args: argparse.Namespace, client: ADTClient) -> None:
"""创建新的 CDS View。"""
name: str = args.name
description: str = getattr(args, "description", None) or name
ddl_path: str | None = getattr(args, "ddl_path", None)
print("=" * 60)
print(" SAP 创建 CDS View")
print("=" * 60)
print(f" CDS 名称: {name}")
print(f" 描述: {description}")
# 读取 DDL 源码
ddl_source = ""
if ddl_path and os.path.isfile(ddl_path):
with open(ddl_path, "r", encoding="utf-8") as f:
ddl_source = f.read()
print(f" DDL 文件: {ddl_path}")
else:
# 生成默认模板
ddl_source = _default_cds_template(name, description)
print(f" DDL: 使用默认模板")
print(f"\n → 正在创建 CDS View...")
try:
obj_uri, src_uri = client.create_cds(name, description, ddl_source)
except Exception as e:
print(f" ✗ 创建失败: {e}")
return
print(f" ✓ CDS View 创建成功!")
print(f" ✓ URI: {obj_uri}")
# 保存本地文件
save_dir = getattr(args, "path", ".")
if save_dir:
if not os.path.isdir(save_dir):
os.makedirs(save_dir, exist_ok=True)
filepath = os.path.join(save_dir, f"{name.lower()}.ddl")
with open(filepath, "w", encoding="utf-8") as f:
f.write(ddl_source)
print(f" ✓ DDL 已保存: {filepath}")
def _default_cds_template(name: str, description: str) -> str:
"""生成默认 CDS View DDL 模板。"""
view_name = name[:16].upper()
return (
f"@AbapCatalog.sqlViewName: \'{view_name}\'\n"
f"@EndUserText.label: \'{description}\'\n"
f"define view {name.lower()}\n"
f" as select from sflight\n"
f" {{\n"
f" carrid,\n"
f" connid,\n"
f" fldate\n"
f" }}\n"
)