--- title: ADT 代码同步工具 — 使用指南 created: 2026-05-19 tags: - SAP - ADT - REST - python - sync - pull - push - activate parent: "[[sap-cli/README|SAP ADT 学习笔记总览]]" prev: "[[sap-cli/06.ADT测试代码|06.ADT 测试代码]]" --- # ADT 代码同步工具 — 使用指南 > [!abstract] 概述 > 通过 `test_adt_client.py` 脚本实现本地与 SAP 系统之间的 ABAP 代码双向同步。支持拉取代码到本地编辑、修改后推送回 SAP 并自动激活。 > > 完整工作流遵循 [[sap-cli/03.修改代码-原理]] 中描述的 **锁定 → 编辑 → 保存 → 解锁 → 激活** 流程。 ## 1. 快速开始 ### 前置条件 ```bash pip install requests ``` ### 基本用法 ```bash # 拉取代码(从 SAP 下载到本地) python sap-cli/test_adt_client.py pull # 推送代码(从本地上传到 SAP 并激活) python sap-cli/test_adt_client.py push ``` ### 配置修改 脚本顶部的配置区域: ```python ADT_BASE_URL = "http://support.learningleader.com.cn:55955" # SAP 服务器地址 SAP_CLIENT = "100" # SAP 客户端 USER = "admin2" # 用户名 PASSWORD = "654321" # 密码 PROGRAM_NAME = "ZIDTR_IMPORT_ACCOUNTING_ORDER" # 程序名 ``` ## 2. 拉取代码 (Pull) ### 流程 ``` 登录 → GET 源代码 → 保存到本地 .abap 文件 ``` ### REST API 调用序列 | 步骤 | HTTP 方法 | URL | 说明 | |------|----------|-----|------| | 登录 | `GET` | `/sap/bc/adt/compatibility/graph` | 获取 CSRF Token | | 读取源码 | `GET` | `/sap/bc/adt/programs/programs/{name}/source/main` | Accept: text/plain | ### 关键请求头 ```http GET /sap/bc/adt/programs/programs/zidtr_import_accounting_order/source/main Accept: text/plain x-csrf-token: {token} X-sap-adt-sessiontype: stateless sap-client: 100 ``` ### 输出 源代码保存到脚本同目录下的 `{程序名小写}.abap` 文件,例如: ``` sap-cli/zidtr_import_accounting_order.abap ``` > [!tip] 格式说明 > SAP ADT API 返回的源代码每行之间可能插入多余空行。脚本已内置格式优化逻辑,自动清理连续空行。 ## 3. 推送代码 (Push) ### 完整流程 ``` 登录 → 查询传输请求 → 锁定对象 → 写入源代码 → 解锁对象 → 激活对象 ``` 对应 [[sap-cli/03.修改代码-原理]] 中的完整工作流: ``` ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ 1.锁定 │───▶│ 2.写入 │───▶│ 3.解锁 │───▶│ 4.激活 │ │ Lock │ │ Write │ │ Unlock │ │ Activate│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ ``` ### REST API 调用序列 #### Step 0: 登录获取 CSRF Token ```http GET /sap/bc/adt/compatibility/graph x-csrf-token: fetch X-sap-adt-sessiontype: stateless ``` 响应头中返回 CSRF Token,后续所有写操作必须携带。 #### Step 1: 查询传输请求 ```http GET /sap/bc/adt/cts/transportrequests Accept: application/vnd.sap.adt.transportorganizer.v1+xml ``` 响应 XML 中解析第一个状态为 `D`(可修改)的传输请求号。 响应示例: ```xml ``` > [!important] 传输请求号 (corrNr) > 传输请求号是代码写入 SAP 的必要参数。脚本自动查询并使用第一个可修改的请求。 > > XML 属性名带有命名空间前缀:`{http://www.sap.com/cts/adt/tm}number` #### Step 2: 锁定对象 ```http POST /sap/bc/adt/programs/programs/{name} ?_action=LOCK &accessMode=MODIFY X-sap-adt-sessiontype: stateful x-csrf-token: {token} ``` 响应: ```xml ``` > [!warning] 会话状态 > 锁定操作**必须在 stateful 会话**中执行(`X-sap-adt-sessiontype: stateful`)。 > 登录端点必须使用 `/sap/bc/adt/compatibility/graph`(不是 `/discovery`),否则锁定会失败。 #### Step 3: 写入源代码 ```http PUT /sap/bc/adt/programs/programs/{name}/source/main ?lockHandle={lockHandle} &corrNr={传输请求号} Content-Type: text/plain; charset=utf-8 X-sap-adt-sessiontype: stateful x-csrf-token: {token} {ABAP 源代码内容} ``` > [!important] 关键参数 > - **lockHandle**:来自锁定步骤的返回值,作为**查询参数**传递 > - **corrNr**:传输请求号,也是查询参数 > - 请求体是**纯文本**(不是 XML),UTF-8 编码 #### Step 4: 解锁对象 ```http POST /sap/bc/adt/programs/programs/{name} ?_action=UNLOCK &lockHandle={lockHandle} X-sap-adt-sessiontype: stateful x-csrf-token: {token} ``` #### Step 5: 激活对象 ```http POST /sap/bc/adt/activation ?method=activate &preauditRequested=true Content-Type: application/xml x-csrf-token: {token} ``` 激活响应可能包含错误消息: ```xml Type "XXX" is unknown. ``` | type | 含义 | |------|------| | `E` | 错误 — 阻止激活 | | `W` | 警告 — 不阻止 | | `I` | 信息 — 不阻止 | | `S` | 成功 | > [!note] 空响应 > 激活成功时可能返回空响应体(HTTP 200),这表示激活无错误。 ## 4. 传输请求详解 ### 查询可用的传输请求 ```http GET /sap/bc/adt/cts/transportrequests Accept: application/vnd.sap.adt.transportorganizer.v1+xml ``` ### 响应结构 ```xml ``` ### 属性说明 | 属性 | 值 | 说明 | |------|-----|------| | `status` | `D` | 可修改 (Modifiable) | | `status` | `R` | 已发布 (Released) | | `number` | `DEVK901360` | 传输请求号 | | `desc` | 文本描述 | 请求描述 | ### XML 解析注意事项 属性名带有命名空间前缀,解析时需要使用完整名称: ```python tm_ns = "http://www.sap.com/cts/adt/tm" for req in root.findall(f".//{{{tm_ns}}}request"): number = req.attrib.get(f"{{{tm_ns}}}number", "") # 不是 "number" status = req.attrib.get(f"{{{tm_ns}}}status", "") # 不是 "status" ``` ## 5. 日志系统 每次执行 push/pull 操作时,详细的 API 请求/响应日志会写入: ``` sap-cli/adt_client.log ``` ### 日志内容 ``` 2026-05-19 10:57:56 | INFO | REQUEST: POST http://.../activation 2026-05-19 10:57:56 | INFO | PARAMS: {'method': 'activate', 'preauditRequested': 'true'} 2026-05-19 10:57:56 | INFO | HEADER: x-csrf-token = xxx... 2026-05-19 10:57:56 | INFO | BODY: 2026-05-19 10:57:56 | INFO | RESPONSE [ACTIVATE]: HTTP 200 OK 2026-05-19 10:57:56 | INFO | BODY (xml): ... 2026-05-19 10:57:56 | INFO | Activation msg [E] line=1: Type "XXX" is unknown. 2026-05-19 10:57:56 | INFO | Activation summary: 1 errors, 0 warnings, 0 info 2026-05-19 10:57:56 | WARNING | Activation has ERRORS - object may NOT be active! ``` ### 排查激活失败 1. 查看 `adt_client.log` 中 `[ACTIVATE]` 部分 2. 搜索 `Activation msg [E]` 找到错误消息 3. 错误消息中的 `line` 是语句序号(不是源文件行号) 4. `href` 中的 `start=XXX` 是字符偏移量,可用于定位源码位置 ## 6. 完整 API 端点速查 ### 通用端点 | 操作 | 方法 | 端点 | 关键参数 | |------|------|------|---------| | 登录/CSRF | `GET` | `/sap/bc/adt/compatibility/graph` | `x-csrf-token: fetch` | | 服务发现 | `GET` | `/sap/bc/adt/discovery` | — | | 激活 | `POST` | `/sap/bc/adt/activation` | `method=activate` | ### 程序 (PROG) 端点 | 操作 | 方法 | 端点 | 关键参数 | |------|------|------|---------| | 读取源码 | `GET` | `/sap/bc/adt/programs/programs/{name}/source/main` | `Accept: text/plain` | | 锁定 | `POST` | `/sap/bc/adt/programs/programs/{name}` | `_action=LOCK` | | 写入源码 | `PUT` | `/sap/bc/adt/programs/programs/{name}/source/main` | `lockHandle`, `corrNr` | | 解锁 | `POST` | `/sap/bc/adt/programs/programs/{name}` | `_action=UNLOCK` | ### 类 (CLASS) 端点 | 操作 | 方法 | 端点 | |------|------|------| | 读取源码 | `GET` | `/sap/bc/adt/oo/classes/{name}/source/main` | | 锁定 | `POST` | `/sap/bc/adt/oo/classes/{name}` | | 写入源码 | `PUT` | `/sap/bc/adt/oo/classes/{name}/source/main` | | 解锁 | `POST` | `/sap/bc/adt/oo/classes/{name}` | ### CTS 传输管理端点 | 操作 | 方法 | 端点 | |------|------|------| | 查询传输请求 | `GET` | `/sap/bc/adt/cts/transportrequests` | | 创建传输请求 | `POST` | `/sap/bc/adt/cts/transportrequests` | | 发布传输请求 | `POST` | `/sap/bc/adt/cts/transportrequests/{number}/newreleasejobs` | ## 7. ABAP 开发对象激活步骤 > [!important] 标准激活流程 > 1. **修改本地源代码** — 在 `.abap` 文件中编辑 > 2. **同步到 SAP** — `python test_adt_client.py push` > - 脚本自动执行:锁定 → 写入 → 解锁 → 激活 > 3. **查看日志确认** — 检查 `adt_client.log` 确认无激活错误 ## 🔗 相关笔记 - [[sap-cli/03.修改代码-原理|03.修改代码 - 原理]] — 锁定/编辑/激活原理 - [[sap-cli/04.检查代码-原理|04.检查代码 - 原理]] — 语法检查与 ATC - [[sap-cli/05.提交与传输-原理|05.提交与传输 - 原理]] — CTS 传输管理 - [[sap-cli/06.ADT测试代码|06.ADT 测试代码]] — 初始测试脚本 ## 📚 参考来源 - [abap-adt-py (GitHub)](https://github.com/timkoehne/abap-adt-py) — Python ADT 客户端库 - [SAP Help Portal - ADT User Guide](https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/about-abap-development-tools-user-guide) - [End-to-End SAP Automation with ADT REST Services](https://medium.com/@onuryz.itu/end-to-end-sap-automation-with-adt-rest-services-and-ai-a-modern-alternative-to-gui-scripting-343ea86064ea)