From 143cd76c6a41c568a9921ac0aa445c5b6cb9da69 Mon Sep 17 00:00:00 2001 From: dark Date: Wed, 20 May 2026 16:58:01 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=96=B0=E5=A2=9Eaction=E7=BB=99agen?= =?UTF-8?q?t=E4=BD=BF=E7=94=A8=202=E3=80=81=E6=96=B0=E5=A2=9ESKILL?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc_scripts/PAM_AUTO_DEPLY_SKILL.md | 462 ++++++++++++++++++ .../PAM智能部署 Agent Skill 文档.md.md | 7 +- .../PAM智能部署 Shell & Bat 脚本实现.md.md | 17 + doc_scripts/deploy.ps1 | 222 ++++++++- doc_scripts/deploy.sh | 237 ++++++++- doc_scripts/当前脚本情况总结.md | 14 + 6 files changed, 953 insertions(+), 6 deletions(-) create mode 100644 doc_scripts/PAM_AUTO_DEPLY_SKILL.md diff --git a/doc_scripts/PAM_AUTO_DEPLY_SKILL.md b/doc_scripts/PAM_AUTO_DEPLY_SKILL.md new file mode 100644 index 0000000..9cc842b --- /dev/null +++ b/doc_scripts/PAM_AUTO_DEPLY_SKILL.md @@ -0,0 +1,462 @@ +--- +name: pam-auto-deply +description: 面向 PAM HOME/NODE 的智能部署 Skill。由 Skill 负责理解用户需求、收集并确认参数、选择执行模式、编排主流程、控制回滚确认与最终汇总;由现有 deploy.sh / deploy.ps1 提供 action 能力执行建版、上传、发布、节点发现、云下载、升级、启停、校验、日志下载和手动回滚。禁止自动生成或修改脚本,禁止使用脚本主流程做部署。 +--- + +# PAM_AUTO_DEPLY Skill + +## 1. Skill 定位 + +本 Skill 面向 PAM 软件发布、下载、升级、校验、日志采集和回滚场景。 + +职责划分如下: + +- `Skill` 负责主流程编排。 +- `Agent` 负责理解用户意图、补齐参数、按步骤调用脚本 action、控制高风险交互。 +- `deploy.sh` / `deploy.ps1` 负责执行具体动作。 + +默认原则: + +- 只允许由 Skill 编排,逐步调用脚本 `action` 入口。 +- 禁止使用脚本主流程做部署,包括 `bash ./deploy.sh --config ...` 和 `powershell -File .\deploy.ps1 -ConfigPath ...` 这类整条执行方式。 +- 禁止自动生成、重建、覆盖或修改 `deploy.sh`、`deploy.ps1`、`deploy.bat`、`test_deploy.sh`、`test_deploy.ps1`、`test_deploy.bat`。 +- 在任何真实调用前,必须先向用户展示归一化后的参数并得到确认。 +- 回滚不得自动执行。脚本只能输出 `PENDING_AGENT_CONFIRMATION(...)`,必须由 Agent 先向用户确认。 + +## 2. 执行模式选择 + +### 2.1 模式判定 + +先识别用户期望的入口: + +- 用户明确说“用 MCP”“直接在线执行”“不要生成脚本”:使用 `MCP`。 +- 用户明确说“用脚本”“离线执行”:使用 `API脚本`。 +- 用户只说“帮我部署”,且当前环境存在可用 MCP:优先 `MCP`。 +- 用户只说“给我脚本”“生成脚本”“输出 sh / ps1 / bat / config”:直接说明本 Skill 禁止自动生成或修改脚本文件;如需仅提供现有脚本使用方式,可切到 `API脚本` 说明模式,但不执行真实部署。 +- 用户只说“不要直接动环境”:优先 `API脚本`,但仅允许说明现有脚本调用方式,不自动产出脚本。 + +### 2.2 脚本模式优先级 + +在 `API脚本` 模式下,优先使用真实脚本文件: + +- Linux / macOS / Git Bash:`deploy.sh` +- Windows:`deploy.ps1` +- `deploy.bat` 仅作为兼容入口,不作为默认主入口 + +硬约束: + +- 只允许调用当前目录中已经存在的真实脚本文件。 +- 禁止根据参考文档自动生成脚本。 +- 禁止为适配当前任务而自动修改脚本实现。 +- 如果目标脚本文件缺失、损坏或能力不足,必须停止并向用户说明,不得自行补写脚本。 + +## 3. 输入参数规范 + +### 3.1 必填业务参数 + +| 规范字段 | 脚本字段 | 必填 | 说明 | +| --- | --- | --- | --- | +| `HOME_BASE_URL` | `HOME_BASE_URL` | 是 | PAM HOME 基础地址 | +| `client_id` | `CLIENT_ID` | 是 | OAuth 客户端 ID | +| `client_secret` | `CLIENT_SECRET` | 是 | OAuth 客户端密钥 | +| `airportCode` | `AIRPORT_CODE` | 是 | 机场三字码 | +| `applicationName` | `APP_NAME` | 是 | 应用名 | +| `moduleName` | `MODULE_NAME` | 是 | 模块名 | +| `versionNumber` | `VERSION_NUMBER` | 是 | 目标版本号 | +| `zipFilePath` | `ZIP_FILE_PATH` | 是 | 本地软件包路径 | +| `actionType` | `ACTION_TYPE` | 否 | 升级类型,默认 `FULL` | +| `timeOut` | `TIMEOUT` | 否 | 接口级超时参数,默认 `120` | +| `logName` | `LOG_NAME` | 否 | 日志文件名,默认 `app.log` | + +### 3.2 运行控制参数 + +以下参数不一定写入 `config.txt`,但 Skill 需要掌握: + +- `mode`: `MCP` 或 `API脚本` +- `generateOnly`: 是否只生成文件不执行 +- `userSpecifiedIps`: 用户指定的目标 IP 子集 +- `allOrNothing`: 是否要求全有或全无 +- `rollbackApproved`: 用户是否已确认回滚 +- `osTarget`: 目标脚本入口环境 + +### 3.3 参数确认要求 + +在执行任何真实动作前,Agent 必须先输出一份“归一化参数确认单”,至少包含: + +- 模式:`MCP` 或 `API脚本` +- 脚本入口:`deploy.sh` 或 `deploy.ps1` +- `HOME_BASE_URL` +- `airportCode` +- `applicationName` +- `moduleName` +- `versionNumber` +- `zipFilePath` +- `actionType` +- `timeOut` +- `logName` +- 用户指定 IP 子集(如有) + +确认规则: + +- 未确认,不执行任何真实 `action` +- 参数有歧义,先追问,不猜测 +- 敏感字段如 `client_secret` 不明文回显,可显示为已提供/未提供 + +### 3.4 参数落地规则 + +参数确认完成后,Agent 应先将业务参数写入 `config.txt`,再调用脚本 `action`。 + +规则如下: + +- `config.txt` 承载稳定业务参数: + - `HOME_BASE_URL` + - `CLIENT_ID` + - `CLIENT_SECRET` + - `AIRPORT_CODE` + - `APP_NAME` + - `MODULE_NAME` + - `VERSION_NUMBER` + - `ZIP_FILE_PATH` + - `ACTION_TYPE` + - `TIMEOUT` + - `LOG_NAME` +- 命令行只传 action 级控制参数: + - `--action` / `-Action` + - `--ip` / `-Ip` + - `--hash-code` / `-HashCode` + - `--stop-first` / `-RollbackStopFirst` +- 不要把整套业务参数直接拼接到命令行。 +- `client_secret` 等敏感字段不得通过命令行透传。 +- 如果用户明确要求“不落地配置文件”,则本 Skill 不执行真实部署,只说明限制和原因。 + +## 4. 主流程(硬约束) + +### 4.1 正式部署主流程 + +在 `API脚本` 模式下,真实部署必须严格按以下顺序执行: + +1. 读取用户输入并识别本次意图是否为真实部署。 +2. 归一化业务参数与控制参数。 +3. 输出参数确认单,并等待用户确认。 +4. 检查现有脚本文件是否存在且可用: + - `deploy.sh` + - `deploy.ps1` +5. 选择脚本入口: + - Linux / macOS / Git Bash 用 `deploy.sh` + - Windows 用 `deploy.ps1` +6. 将确认后的业务参数写入 `config.txt`。 +7. 调用 `get-token`。 +8. 调用 `create-version`。 +9. 调用 `upload-package`。 +10. 调用 `publish-version`。 +11. 调用 `get-node-url`。 +12. 调用 `get-online-ips`。 +13. 若用户指定了目标 IP,则基于在线 IP 列表做过滤。 +14. 调用 `create-download-task`。 +15. 调用 `poll-download-progress`,直到下载完成、失败或超时。 +16. 按在线 IP 或过滤后的目标 IP 列表逐台执行: + - `upgrade-ip` + - `start-ip` + - `verify-ip` + - `download-log` +17. 汇总每台 IP 的结果。 +18. 若出现 `PENDING_AGENT_CONFIRMATION(...)`,立即中止自动后续动作,转入回滚确认分支。 +19. 输出最终报告。 + +### 4.2 主流程中的强制确认点 + +以下节点必须等待用户确认,不能自动越过: + +1. 参数确认单确认前。 +2. 出现回滚条件时。 +3. 用户指定 IP 与在线 IP 过滤结果不一致,且会影响部署范围时。 + +### 4.3 主流程逐步说明 + +| 步骤 | 目标 | 调用或动作 | 成功判定 | 失败处理 | +| --- | --- | --- | --- | --- | +| 1 | 识别意图 | 判断是否为真实部署 | 意图明确为真实部署 | 若不是,转入分支流程,不执行真实部署 | +| 2 | 归一化参数 | 整理用户输入、补齐默认值 | 参数结构完整 | 参数不清时先追问,不猜测 | +| 3 | 参数确认 | 输出参数确认单 | 用户明确确认 | 未确认前停止 | +| 4 | 检查脚本 | 检查 `deploy.sh` / `deploy.ps1` 是否存在且可用 | 至少存在一个与当前 OS 对应的脚本入口 | 缺失或不可用时停止并说明 | +| 5 | 选择入口 | 根据 OS 选择 `deploy.sh` 或 `deploy.ps1` | 入口唯一且明确 | 入口不明确时停止 | +| 6 | 落地配置 | 将稳定业务参数写入 `config.txt` | `config.txt` 已生成且内容与确认单一致 | 写入失败则停止 | +| 7 | 获取 Token | `get-token` | 返回 `TOKEN=...` 且非空 | 停止并报告 `TOKEN` 阶段失败 | +| 8 | 创建版本 | `create-version` | 返回 `RESULT=OK` | 停止并报告 `CREATE_VERSION` 失败 | +| 9 | 上传软件包 | `upload-package` | 返回 `HASH_CODE=...` 且非空 | 停止并报告 `UPLOAD_PACKAGE` 失败 | +| 10 | 发布版本 | `publish-version --hash-code ...` | 返回 `RESULT=OK` | 停止并报告 `PUBLISH_VERSION` 失败 | +| 11 | 获取 Node | `get-node-url` | 返回 `NODE_URL=...` 且非空 | 停止并报告 `GET_NODE_URL` 失败 | +| 12 | 获取在线 IP | `get-online-ips` | 返回 `COUNT>0` 且有 `IP=...` 行 | 停止并报告 `GET_ONLINE_IPS` 失败 | +| 13 | 过滤目标 IP | 按用户指定 IP 与在线 IP 交集过滤 | 过滤结果明确 | 过滤后为空时停止;范围变化需确认 | +| 14 | 创建云下载任务 | `create-download-task` | 返回 `RESULT=TASK_CREATED` | 停止并报告 `CREATE_DOWNLOAD_TASK` 失败 | +| 15 | 轮询下载进度 | `poll-download-progress` | `STEP=DONE` 或 `MSG=success` 且 `RATE_OF_PROGRESS=100` | 停止并报告 `POLL_DOWNLOAD_PROGRESS` 失败或超时 | +| 16.1 | 升级单 IP | `upgrade-ip --ip ...` | 返回 `SUCCESS=true` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=false)` | +| 16.2 | 启动单 IP | `start-ip --ip ...` | action 成功返回 | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` | +| 16.3 | 校验单 IP | `verify-ip --ip ...` | 返回 `SUCCESS=true` | 记录失败,标记 `PENDING_AGENT_CONFIRMATION(stopFirst=true)` | +| 16.4 | 下载日志 | `download-log --ip ...` | 返回 `LOG_FILE=...` | 记录日志下载失败,但不覆盖原主失败原因 | +| 17 | 汇总结果 | 汇总每台 IP 的阶段、失败原因、回滚状态、日志路径 | 报告内容完整 | 若汇总失败,至少保留原始 action 输出 | +| 18 | 回滚确认分支 | 发现 `PENDING_AGENT_CONFIRMATION(...)` 时进入回滚确认 | 用户明确是否回滚 | 未确认时停止,不自动回滚 | +| 19 | 最终报告 | 输出最终报告 | 报告包含模式、入口、阶段结果、日志、回滚状态 | 不省略失败细节 | + +## 5. 通用执行原则 + +1. 始终通过接口动态获取在线工作站 IP,不要求用户手填 `TARGET_IPS`。 +2. 用户若指定部分 IP,必须先查在线 IP,再做过滤。 +3. 所有关键步骤都要保留原始响应、错误摘要、阶段名。 +4. 单机成功或失败都要下载对应日志。 +5. 执行前必须先完成参数确认。 +6. 脚本模式下统一输出流程日志: + - `[FLOW][START]` + - `[FLOW][DONE]` + - `[FLOW][FAIL]` +7. 只允许调用脚本 `action` 入口,禁止调用脚本主流程。 +8. 脚本 action 输出以 `key=value` 为主,Agent 应优先读取这些结果行。 +9. 遇到需要回滚的场景,脚本只返回 `PENDING_AGENT_CONFIRMATION(stopFirst=...)`,Agent 必须先确认。 + +## 6. 接口约定 + +### 6.1 基础约定 + +- Node 侧路径统一使用 `node-proxy` +- Node 侧接口始终携带: + - `Authorization: Bearer {TOKEN}` + - `Target-Node: {NODE_URL}` +- `airport-code: {airportCode}` 仅在下载到 Node 等需要时携带 + +### 6.2 云下载接口 + +- 创建任务接口: + - `GET /api/mcp/version/upgrade/download-cloud` + - 固定传 `timeOut=0` + - 含义是“任务创建成功立即返回”,不要等待长超时 +- 进度接口: + - `GET /api/mcp/version/upgrade/download-cloud/progress?...&versionNumber={versionNumber}` + - 完成判定优先看: + - `msg` + - `step` + - `rateOfProgress` + - 当 `msg=success`、`step=DONE`、`rateOfProgress=100` 时,判定下载完成 + +### 6.3 升级与启停接口 + +- `POST /api/mcp/version/upgrade` + - 参数直接拼到 URL query + - 不使用 body 表单 +- `POST /api/mcp/version/upgrade/start-stop` + - 参数直接拼到 URL query + - 不使用 body 表单 + - 参数名统一使用 `runStart` + +## 7. 脚本 action 能力 + +本节仅定义允许调用的入口。除 `action` 入口外,其他脚本运行方式一律不允许用于真实部署。 + +### 7.1 Shell 入口 + +```bash +bash ./deploy.sh --config ./config.txt --action [--ip 192.168.1.10] [--hash-code xxx] [--stop-first] +``` + +### 7.2 PowerShell 入口 + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action [-Ip 192.168.1.10] [-HashCode xxx] [-RollbackStopFirst] +``` + +### 7.3 可用 action + +| action | 用途 | 额外参数 | +| --- | --- | --- | +| `get-token` | 获取访问令牌 | 无 | +| `create-version` | 新建版本记录 | 无 | +| `upload-package` | 上传软件包 | 无 | +| `publish-version` | 发布版本 | `--hash-code` / `-HashCode` | +| `get-node-url` | 获取目标 Node 地址 | 无 | +| `get-online-ips` | 获取在线工作站 IP 列表 | 无 | +| `create-download-task` | 创建云下载任务 | 无 | +| `poll-download-progress` | 轮询下载进度 | 无 | +| `download-cloud-to-node` | 创建下载任务并轮询至完成 | 无 | +| `upgrade-ip` | 升级指定 IP | `--ip` / `-Ip` | +| `start-ip` | 启动指定 IP 应用 | `--ip` / `-Ip` | +| `stop-ip` | 停止指定 IP 应用 | `--ip` / `-Ip` | +| `verify-ip` | 校验指定 IP | `--ip` / `-Ip` | +| `download-log` | 下载指定 IP 日志 | `--ip` / `-Ip` | +| `rollback-ip` | 执行指定 IP 回滚 | `--ip` / `-Ip`,可带 `--stop-first` / `-RollbackStopFirst` | + +### 7.4 action 输出约定 + +典型返回为: + +```text +ACTION=get-online-ips +COUNT=2 +IP=192.168.1.10 +IP=192.168.1.11 +``` + +Agent 读取时: + +- 优先解析 `key=value` +- 将 `[INFO]`、`[WARN]`、`[FLOW]` 视为辅助日志 +- 若 action 失败,以退出码和错误日志为准 + +## 8. 分支流程与禁止事项 + +### 8.1 仅说明现有脚本用法分支 + +当用户意图不是“真实部署”,而是“查看现有脚本如何使用”时: + +1. 只说明现有 `deploy.sh` / `deploy.ps1` / `deploy.bat` 的用途与调用方式。 +2. 不执行任何真实 `action`。 +3. 不生成、不修改任何脚本文件。 + +### 8.2 参数确认后不执行分支 + +当用户只想确认参数、检查部署计划,但不执行真实部署时: + +1. 读取并归一化参数。 +2. 输出参数确认单。 +3. 说明预计会调用的 action 顺序。 +4. 不执行任何真实 `action`。 +5. 不生成、不修改任何脚本文件。 + +### 8.3 仅查看 Node 与在线 IP 分支 + +当用户只需要确认目标 Node 和在线工作站,而不是正式部署时: + +1. 读取并归一化参数。 +2. 输出参数确认单并等待确认。 +3. 将参数写入 `config.txt`。 +4. 调用: + - `get-token` + - `get-node-url` + - `get-online-ips` +5. 输出 Node 地址、在线 IP 数量和 IP 列表。 +6. 不执行: + - `create-version` + - `upload-package` + - `publish-version` + - `create-download-task` + - `upgrade-ip` + +### 8.4 手动回滚分支 + +当部署结果出现 `PENDING_AGENT_CONFIRMATION(...)` 且用户明确同意回滚时: + +1. 再次向用户确认目标 IP 和 `stopFirst` 值。 +2. 调用 `rollback-ip` action。 +3. 如有需要,再调用: + - `verify-ip` + - `download-log` +4. 将回滚结果写入最终报告。 + +### 8.5 明确禁止的做法 + +以下做法在本 Skill 中一律禁止: + +- 自动生成或修改部署脚本 +- 自动生成或修改测试脚本 +- 为了方便执行而切换到脚本主流程 +- 未确认参数就直接执行真实 action +- 在出现回滚条件时自动执行回滚 + +## 9. 失败处理与回滚 + +### 9.1 全局失败 + +以下步骤失败时,终止整次部署并报告失败阶段: + +- 获取 Token +- 建版 +- 上传 +- 发布 +- 获取 Node +- 获取在线 IP +- 创建云下载任务 +- 云下载进度轮询失败或超时 + +### 9.2 单 IP 失败 + +单 IP 失败时: + +- 必须记录失败阶段和失败原因 +- 必须下载该 IP 日志 +- 不得自动执行回滚 + +### 9.3 回滚规则 + +回滚只允许在 Agent 与用户确认后执行。 + +回滚状态有三类: + +- `ROLLBACK_NOT_RUN` +- `PENDING_AGENT_CONFIRMATION(stopFirst=true|false)` +- 真正执行后的结果: + - `ROLLBACK_SUCCESS` + - `ROLLBACK_FAILED` + - `ROLLBACK_REQUEST_FAILED` + - `ROLLBACK_VERIFY_FAILED` + +推荐确认逻辑: + +- 升级失败:建议回滚,`stopFirst=false` +- 启动失败:建议回滚,`stopFirst=true` +- 校验失败:建议回滚,`stopFirst=true` + +手动回滚命令示例: + +```bash +bash ./deploy.sh --config ./config.txt --action rollback-ip --ip 192.168.1.10 --stop-first +``` + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action rollback-ip -Ip 192.168.1.10 -RollbackStopFirst +``` + +## 10. 输出要求 + +最终报告至少包含: + +- 本次模式:`MCP` 或 `API脚本` +- 实际入口:`MCP`、`deploy.sh --action ...` 或 `deploy.ps1 -Action ...` +- 机场、应用、模块、版本 +- 在线工作站总数、成功数、失败数 +- 每个 IP 的状态、失败阶段、失败原因 +- 每个 IP 的回滚状态 +- 日志路径 +- 若有 trace,则给出 trace 路径 + +建议结构: + +```markdown +## PAM 智能部署报告 + +- 模式: API脚本 +- 入口: deploy.sh --action +- 机场: HET +- 应用: PAM +- 模块: Node +- 版本: 2.0.5 +- 总工作站数: 3 +- 成功: 2 +- 失败: 1 + +| IP | 状态 | 失败阶段 | 回滚状态 | 日志 | +| --- | --- | --- | --- | --- | +| 192.168.1.10 | SUCCESS | - | - | logs/deploy_192.168.1.10.log | +| 192.168.1.11 | SUCCESS | - | - | logs/deploy_192.168.1.11.log | +| 192.168.1.12 | FAILED | VERIFY | PENDING_AGENT_CONFIRMATION(stopFirst=true) | logs/deploy_192.168.1.12.log | +``` + +## 11. Agent 执行建议 + +1. 只能调用 `action`,不要调用脚本主流程。 +2. 不要自动生成、补写、覆盖或修改脚本文件。 +3. 在高风险动作前显式说明: + - 会创建版本 + - 会上传包 + - 会触发升级 + - 回滚需要确认 +4. 参数未确认前,不触发任何真实部署 action。 +5. 用户只要求“生成脚本不执行”时,由于本 Skill 禁止自动生成或修改脚本,应直接说明限制,而不是自动产出脚本文件。 +6. 如果 action 输出中出现 `PENDING_AGENT_CONFIRMATION(...)`,立即中止自动后续动作并请求确认。 diff --git a/doc_scripts/PAM智能部署 Agent Skill 文档.md.md b/doc_scripts/PAM智能部署 Agent Skill 文档.md.md index 12f793f..f304686 100644 --- a/doc_scripts/PAM智能部署 Agent Skill 文档.md.md +++ b/doc_scripts/PAM智能部署 Agent Skill 文档.md.md @@ -109,12 +109,13 @@ description: 基于 PAM HOME/NODE 流程执行软件发布、下载、升级、 - Linux / Mac:使用 `deploy.sh`。 - Windows:优先使用 `deploy.ps1`。 - `deploy.bat` 只在用户明确要求 Batch,或必须兼容旧入口,且确认特殊字符风险可接受时才使用。 -3. 若当前目录只有文档而没有真实脚本文件,先从参考实现中落地实际脚本文件,再执行。 -4. 若用户要求“只生成脚本不执行”,完成以下产物后即可结束: +3. Agent 优先通过脚本的 `action` 入口逐步调用能力方法,由 Skill 负责主流程编排;只有在用户明确要求“一键整条执行”时,才直接运行脚本主流程。 +4. 若当前目录只有文档而没有真实脚本文件,先从参考实现中落地实际脚本文件,再执行。 +5. 若用户要求“只生成脚本不执行”,完成以下产物后即可结束: - `config.txt` - `deploy.sh` 或 `deploy.ps1` - 如用户明确要求,再额外提供 `deploy.bat` -5. 执行脚本后,读取脚本输出和 `./logs/` 目录内容,整理成最终报告。 +6. 执行脚本后,读取脚本输出和 `./logs/` 目录内容,整理成最终报告。 ## 失败处理与回滚 diff --git a/doc_scripts/PAM智能部署 Shell & Bat 脚本实现.md.md b/doc_scripts/PAM智能部署 Shell & Bat 脚本实现.md.md index 310e7ac..0f7e077 100644 --- a/doc_scripts/PAM智能部署 Shell & Bat 脚本实现.md.md +++ b/doc_scripts/PAM智能部署 Shell & Bat 脚本实现.md.md @@ -32,6 +32,7 @@ 8. `download-cloud/progress` 的完成判定优先读取 `msg`、`step`、`rateOfProgress`;当 `msg=success`、`step=DONE`、`rateOfProgress=100` 时代表下载完成,其中 `rateOfProgress` 即下载进度值。 9. 正式部署脚本不会自动执行回滚;发现需要回滚时,只输出 `PENDING_AGENT_CONFIRMATION(stopFirst=...)`,由 Agent 先和用户确认,再调用手动回滚入口。 10. `POST /api/mcp/version/upgrade` 和 `POST /api/mcp/version/upgrade/start-stop` 的业务参数都直接放在 URL query 中,不再使用 body 表单;启停接口参数名使用 `runStart`;`download-cloud` 固定传 `timeOut=0` 创建任务。 +11. 脚本同时提供主流程入口与 `action` 入口;建议 Agent 优先调用 `action` 入口,由 Skill 负责主流程编排。 ## 0.1 当前实现边界 @@ -739,6 +740,22 @@ goto :eof ## 6. 使用说明 +### Agent action 调用示例 + +```bash +bash ./deploy.sh --config ./config.txt --action get-online-ips +bash ./deploy.sh --config ./config.txt --action create-download-task +bash ./deploy.sh --config ./config.txt --action poll-download-progress +bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10 +``` + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action GetOnlineIps +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action CreateDownloadTask +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action PollDownloadProgress +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action UpgradeIp -Ip 192.168.1.10 +``` + ### 前置依赖 1. **Linux/Mac**: diff --git a/doc_scripts/deploy.ps1 b/doc_scripts/deploy.ps1 index ad5e920..d44432f 100644 --- a/doc_scripts/deploy.ps1 +++ b/doc_scripts/deploy.ps1 @@ -1,5 +1,8 @@ param( [string]$ConfigPath = (Join-Path $PSScriptRoot 'config.txt'), + [string]$Action = '', + [string]$Ip = '', + [string]$HashCode = '', [string]$RollbackIp = '', [switch]$RollbackStopFirst, [switch]$Help @@ -13,6 +16,7 @@ function Show-DeployUsage { @' Usage: powershell -File .\deploy.ps1 [-ConfigPath .\config.txt] + powershell -File .\deploy.ps1 -Action [-ConfigPath .\config.txt] [-Ip 192.168.1.10] [-HashCode xxxxx] [-RollbackStopFirst] powershell -File .\deploy.ps1 [-ConfigPath .\config.txt] -RollbackIp 192.168.1.10 [-RollbackStopFirst] Notes: @@ -26,6 +30,53 @@ function Write-Info([string]$Message) { Write-Host "[INFO] $Message" } function Write-WarnLog([string]$Message) { Write-Host "[WARN] $Message" } function Write-ErrLog([string]$Message) { Write-Host "[ERROR] $Message" } $script:ActiveConfigPath = $ConfigPath +$script:DownloadProgressState = [ordered]@{ + Status = '' + Success = '' + Step = '' + Msg = '' + Message = '' + RateOfProgress = '' + RawResponse = '' +} + +function Write-ResultLine([string]$Key, [AllowEmptyString()][string]$Value) { + if ($null -eq $Value) { + $Value = '' + } + $normalized = $Value.Replace("`r", ' ').Replace("`n", ' ') + Write-Host ("{0}={1}" -f $Key, $normalized) +} + +function Require-IpArgument([string]$TargetIp) { + if (-not $TargetIp) { + throw 'This action requires -Ip.' + } +} + +function Require-HashCodeArgument([string]$PublishHashCode) { + if (-not $PublishHashCode) { + throw 'This action requires -HashCode.' + } +} + +function Write-OnlineIpsResult([string[]]$Ips) { + Write-ResultLine -Key 'ACTION' -Value 'get-online-ips' + Write-ResultLine -Key 'COUNT' -Value ([string]$Ips.Count) + foreach ($entry in $Ips) { + Write-ResultLine -Key 'IP' -Value $entry + } +} + +function Write-DownloadProgressResult([string]$ActionName = 'poll-download-progress') { + Write-ResultLine -Key 'ACTION' -Value $ActionName + Write-ResultLine -Key 'STEP' -Value ([string]$script:DownloadProgressState.Step) + Write-ResultLine -Key 'MSG' -Value ([string]$script:DownloadProgressState.Msg) + Write-ResultLine -Key 'RATE_OF_PROGRESS' -Value ([string]$script:DownloadProgressState.RateOfProgress) + Write-ResultLine -Key 'STATUS' -Value ([string]$script:DownloadProgressState.Status) + Write-ResultLine -Key 'SUCCESS' -Value ([string]$script:DownloadProgressState.Success) + Write-ResultLine -Key 'MESSAGE' -Value ([string]$script:DownloadProgressState.Message) +} function Write-FlowStart([string]$Name, [string]$Detail = '') { if ($Detail) { @@ -489,6 +540,15 @@ function Wait-DownloadProgress { versionNumber = $Config.VERSION_NUMBER }) $progressUrl = "$($Config.HOME_BASE_URL)/node-proxy/$($Config.AIRPORT_CODE)/api/mcp/version/upgrade/download-cloud/progress?$query" + $script:DownloadProgressState = [ordered]@{ + Status = '' + Success = '' + Step = '' + Msg = '' + Message = '' + RateOfProgress = '' + RawResponse = '' + } for ($attempt = 0; $attempt -lt 60; $attempt++) { $response = Invoke-PamWebRequest -Method GET -Url $progressUrl -Token $Token -Headers @{ @@ -502,6 +562,15 @@ function Wait-DownloadProgress { $progressValue = Get-ResponseValue -Response $response -Candidates @('rateOfProgress', 'progress', 'percent', 'data.rateOfProgress', 'data.progress', 'data.percent') $message = Get-ResponseValue -Response $response -Candidates @('message') if (-not $message) { $message = $msg } + $script:DownloadProgressState = [ordered]@{ + Status = [string]$status + Success = [string]$successFlag + Step = [string]$step + Msg = [string]$msg + Message = [string]$message + RateOfProgress = [string]$progressValue + RawResponse = [string]$response + } $progressParts = [System.Collections.Generic.List[string]]::new() if ($msg) { $progressParts.Add("msg=$msg") } @@ -533,7 +602,7 @@ function Wait-DownloadProgress { throw 'Node download timed out.' } -function Download-CloudToNode { +function Create-DownloadTask { param($Config, [string]$Token, [string]$NodeUrl) Write-Info 'Step 3.3: download package to node' @@ -548,7 +617,12 @@ function Download-CloudToNode { 'Target-Node' = $NodeUrl 'airport-code' = $Config.AIRPORT_CODE }) +} +function Download-CloudToNode { + param($Config, [string]$Token, [string]$NodeUrl) + + Create-DownloadTask -Config $Config -Token $Token -NodeUrl $NodeUrl Wait-DownloadProgress -Config $Config -Token $Token -NodeUrl $NodeUrl } @@ -909,6 +983,148 @@ function Invoke-PamManualRollback { Write-Host "ROLLBACK RESULT: $result" } +function Invoke-PamAction { + param( + [string]$ConfigPath, + [string]$Action, + [string]$Ip, + [string]$HashCode, + [bool]$StopFirst + ) + + $script:ActiveConfigPath = $ConfigPath + $config = Invoke-FlowStep -Name 'Get-PamConfig' -Detail "path=$ConfigPath" -Action { + Get-PamConfig -Path $ConfigPath + } + + switch ($Action.ToLowerInvariant()) { + 'get-token' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + Write-ResultLine -Key 'ACTION' -Value 'get-token' + Write-ResultLine -Key 'TOKEN' -Value $token + } + 'create-version' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + Invoke-FlowStep -Name 'New-VersionRecord' -Action { New-VersionRecord -Config $config -Token $token } | Out-Null + Write-ResultLine -Key 'ACTION' -Value 'create-version' + Write-ResultLine -Key 'VERSION_NUMBER' -Value $config.VERSION_NUMBER + Write-ResultLine -Key 'RESULT' -Value 'OK' + } + 'upload-package' { + Invoke-FlowStep -Name 'Test-ZipFile' -Action { Test-ZipFile -Config $config } | Out-Null + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $hash = Invoke-FlowStep -Name 'Upload-Package' -Action { Upload-Package -Config $config -Token $token } + Write-ResultLine -Key 'ACTION' -Value 'upload-package' + Write-ResultLine -Key 'HASH_CODE' -Value $hash + } + 'publish-version' { + Require-HashCodeArgument -PublishHashCode $HashCode + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + Invoke-FlowStep -Name 'Publish-Version' -Action { Publish-Version -Config $config -Token $token -HashCode $HashCode } | Out-Null + Write-ResultLine -Key 'ACTION' -Value 'publish-version' + Write-ResultLine -Key 'HASH_CODE' -Value $HashCode + Write-ResultLine -Key 'RESULT' -Value 'OK' + } + 'get-node-url' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Write-ResultLine -Key 'ACTION' -Value 'get-node-url' + Write-ResultLine -Key 'NODE_URL' -Value $nodeUrl + } + 'get-online-ips' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + $ips = Invoke-FlowStep -Name 'Get-OnlineIps' -Action { Get-OnlineIps -Config $config -Token $token -NodeUrl $nodeUrl } + Write-OnlineIpsResult -Ips @($ips) + } + 'create-download-task' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Invoke-FlowStep -Name 'Create-DownloadTask' -Action { Create-DownloadTask -Config $config -Token $token -NodeUrl $nodeUrl } | Out-Null + Write-ResultLine -Key 'ACTION' -Value 'create-download-task' + Write-ResultLine -Key 'TIME_OUT' -Value '0' + Write-ResultLine -Key 'RESULT' -Value 'TASK_CREATED' + } + 'poll-download-progress' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Invoke-FlowStep -Name 'Wait-DownloadProgress' -Action { Wait-DownloadProgress -Config $config -Token $token -NodeUrl $nodeUrl } | Out-Null + Write-DownloadProgressResult + } + 'download-cloud-to-node' { + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Invoke-FlowStep -Name 'Download-CloudToNode' -Action { Download-CloudToNode -Config $config -Token $token -NodeUrl $nodeUrl } | Out-Null + Write-DownloadProgressResult -ActionName 'download-cloud-to-node' + Write-ResultLine -Key 'RESULT' -Value 'DONE' + } + 'upgrade-ip' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + $response = Invoke-FlowStep -Name "Invoke-UpgradeRequest[$Ip]" -Action { Invoke-UpgradeRequest -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } + Write-ResultLine -Key 'ACTION' -Value 'upgrade-ip' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'SUCCESS' -Value (Get-ResponseValue -Response $response -Candidates @('success')) + Write-ResultLine -Key 'MESSAGE' -Value (Get-ResponseValue -Response $response -Candidates @('message')) + Write-ResultLine -Key 'RAW_RESPONSE' -Value ([string]$response) + } + 'start-ip' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Invoke-FlowStep -Name "Start-Application[$Ip]" -Action { Start-Application -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } | Out-Null + Write-ResultLine -Key 'ACTION' -Value 'start-ip' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'RUN_START' -Value 'true' + Write-ResultLine -Key 'RESULT' -Value 'OK' + } + 'stop-ip' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + Invoke-FlowStep -Name "Stop-Application[$Ip]" -Action { Stop-Application -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } | Out-Null + Write-ResultLine -Key 'ACTION' -Value 'stop-ip' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'RUN_START' -Value 'false' + Write-ResultLine -Key 'RESULT' -Value 'OK' + } + 'verify-ip' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + $response = Invoke-FlowStep -Name "Verify-Ip[$Ip]" -Action { Verify-Ip -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } + Write-ResultLine -Key 'ACTION' -Value 'verify-ip' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'SUCCESS' -Value (Get-ResponseValue -Response $response -Candidates @('success')) + Write-ResultLine -Key 'MESSAGE' -Value (Get-ResponseValue -Response $response -Candidates @('message')) + Write-ResultLine -Key 'RAW_RESPONSE' -Value ([string]$response) + } + 'download-log' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + $logFile = Invoke-FlowStep -Name "Download-DeployLog[$Ip]" -Action { Download-DeployLog -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip } + Write-ResultLine -Key 'ACTION' -Value 'download-log' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'LOG_FILE' -Value $logFile + } + 'rollback-ip' { + Require-IpArgument -TargetIp $Ip + $token = Invoke-FlowStep -Name 'Get-Token' -Action { Get-Token -Config $config } + $nodeUrl = Invoke-FlowStep -Name 'Get-NodeUrl' -Action { Get-NodeUrl -Config $config -Token $token } + $rollbackResult = Invoke-FlowStep -Name "Invoke-Rollback[$Ip]" -Action { Invoke-Rollback -Config $config -Token $token -NodeUrl $nodeUrl -Ip $Ip -StopFirst:$StopFirst } + Write-ResultLine -Key 'ACTION' -Value 'rollback-ip' + Write-ResultLine -Key 'IP' -Value $Ip + Write-ResultLine -Key 'STOP_FIRST' -Value ($StopFirst.ToString().ToLowerInvariant()) + Write-ResultLine -Key 'ROLLBACK_RESULT' -Value $rollbackResult + } + default { + throw "Unknown action: $Action" + } + } +} + if ($Help) { Show-DeployUsage exit 0 @@ -916,7 +1132,9 @@ if ($Help) { if ($MyInvocation.InvocationName -ne '.') { try { - if ($RollbackIp) { + if ($Action) { + Invoke-PamAction -ConfigPath $ConfigPath -Action $Action -Ip $Ip -HashCode $HashCode -StopFirst:$RollbackStopFirst.IsPresent + } elseif ($RollbackIp) { Invoke-PamManualRollback -ConfigPath $ConfigPath -Ip $RollbackIp -StopFirst:$RollbackStopFirst.IsPresent } else { Invoke-PamDeploy -ConfigPath $ConfigPath diff --git a/doc_scripts/deploy.sh b/doc_scripts/deploy.sh index 0cb496e..cacbeda 100644 --- a/doc_scripts/deploy.sh +++ b/doc_scripts/deploy.sh @@ -21,12 +21,20 @@ API_TRACE_FILE="" API_TRACE_SEQ=0 TRACE_ANNOUNCED=0 CURRENT_TRACE_ID="" +DOWNLOAD_PROGRESS_STATUS="" +DOWNLOAD_PROGRESS_SUCCESS="" +DOWNLOAD_PROGRESS_STEP="" +DOWNLOAD_PROGRESS_MSG="" +DOWNLOAD_PROGRESS_MESSAGE="" +DOWNLOAD_PROGRESS_RATE="" +DOWNLOAD_PROGRESS_RESPONSE="" usage() { cat <<'EOF' 用法: ./deploy.sh [--config /path/to/config.txt] ./deploy.sh [--config /path/to/config.txt] --rollback-ip 192.168.1.10 [--rollback-stop-first] + ./deploy.sh [--config /path/to/config.txt] --action [--ip ] [--hash-code ] [--stop-first] 配置项: HOME_BASE_URL @@ -46,6 +54,7 @@ EOF log_info() { printf '[INFO] %s\n' "$*"; } log_warn() { printf '[WARN] %s\n' "$*"; } log_error() { printf '[ERROR] %s\n' "$*" >&2; } +result_line() { printf '%s=%s\n' "$1" "$2"; } log_flow_start() { local name="$1" @@ -376,6 +385,39 @@ sanitize_field() { printf '%s' "$value" } +require_ip_arg() { + local ip="$1" + [[ -n "$ip" ]] && return 0 + log_error "该 action 需要 --ip" + return 1 +} + +require_hash_code_arg() { + local hash_code="$1" + [[ -n "$hash_code" ]] && return 0 + log_error "该 action 需要 --hash-code" + return 1 +} + +print_online_ips_result() { + result_line "ACTION" "get-online-ips" + result_line "COUNT" "${#ONLINE_IPS[@]}" + for ip in "${ONLINE_IPS[@]}"; do + result_line "IP" "$ip" + done +} + +print_progress_result() { + local action_name="${1:-poll-download-progress}" + result_line "ACTION" "$action_name" + result_line "STEP" "$DOWNLOAD_PROGRESS_STEP" + result_line "MSG" "$DOWNLOAD_PROGRESS_MSG" + result_line "RATE_OF_PROGRESS" "$DOWNLOAD_PROGRESS_RATE" + result_line "STATUS" "$DOWNLOAD_PROGRESS_STATUS" + result_line "SUCCESS" "$DOWNLOAD_PROGRESS_SUCCESS" + result_line "MESSAGE" "$DOWNLOAD_PROGRESS_MESSAGE" +} + json_value() { local input="$1" local query="$2" @@ -762,6 +804,14 @@ poll_download_progress() { local max_attempts=60 local error_regex='[Ff]ail|[Ee]rror' + DOWNLOAD_PROGRESS_STATUS="" + DOWNLOAD_PROGRESS_SUCCESS="" + DOWNLOAD_PROGRESS_STEP="" + DOWNLOAD_PROGRESS_MSG="" + DOWNLOAD_PROGRESS_MESSAGE="" + DOWNLOAD_PROGRESS_RATE="" + DOWNLOAD_PROGRESS_RESPONSE="" + while (( attempt < max_attempts )); do local response response=$(http_request "GET" "$progress_url" "" "" "Target-Node: ${NODE_URL}") || return 1 @@ -783,6 +833,13 @@ poll_download_progress() { [[ -z "$progress_value" ]] && progress_value="$(json_value "$response" '.data.progress')" [[ -z "$progress_value" ]] && progress_value="$(json_value "$response" '.data.percent')" [[ -z "$message" ]] && message="$msg_value" + DOWNLOAD_PROGRESS_STATUS="$status" + DOWNLOAD_PROGRESS_SUCCESS="$success_flag" + DOWNLOAD_PROGRESS_STEP="$step_value" + DOWNLOAD_PROGRESS_MSG="$msg_value" + DOWNLOAD_PROGRESS_MESSAGE="$message" + DOWNLOAD_PROGRESS_RATE="$progress_value" + DOWNLOAD_PROGRESS_RESPONSE="$response" if [[ -n "$msg_value" || -n "$step_value" || -n "$progress_value" || -n "$status" || -n "$success_flag" || -n "$message" ]]; then local -a progress_parts=() @@ -820,7 +877,7 @@ poll_download_progress() { return 1 } -download_cloud_to_node() { +create_download_task() { log_info "Step 3.3: 下载软件包到 Node..." http_request "GET" \ "${HOME_BASE_URL}/node-proxy/${AIRPORT_CODE}/api/mcp/version/upgrade/download-cloud?versionNumber=${VERSION_NUMBER}&applicationName=${APP_NAME}&moduleName=${MODULE_NAME}&timeOut=0" \ @@ -828,7 +885,10 @@ download_cloud_to_node() { "" \ "Target-Node: ${NODE_URL}" \ "airport-code: ${AIRPORT_CODE}" >/dev/null +} +download_cloud_to_node() { + create_download_task || return 1 poll_download_progress } @@ -980,6 +1040,151 @@ run_manual_rollback() { printf 'ROLLBACK RESULT: %s\n' "$rollback_result" } +run_action() { + local config_path="$1" + local action="$2" + local ip="$3" + local hash_code="$4" + local stop_first="$5" + local response="" + local log_file="" + local rollback_result="" + + ACTIVE_CONFIG_PATH="$config_path" + init_runtime + load_config "$config_path" + ensure_dependencies + + case "$action" in + get-token) + run_flow_step "get_token" get_token || return 1 + result_line "ACTION" "get-token" + result_line "TOKEN" "$TOKEN" + ;; + create-version) + run_flow_step "get_token" get_token || return 1 + run_flow_step "create_version" create_version || return 1 + result_line "ACTION" "create-version" + result_line "VERSION_NUMBER" "$VERSION_NUMBER" + result_line "RESULT" "OK" + ;; + upload-package) + ensure_zip_file || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "upload_package" upload_package || return 1 + result_line "ACTION" "upload-package" + result_line "HASH_CODE" "$HASH_CODE" + ;; + publish-version) + require_hash_code_arg "$hash_code" || return 1 + HASH_CODE="$hash_code" + run_flow_step "get_token" get_token || return 1 + run_flow_step "publish_version" publish_version || return 1 + result_line "ACTION" "publish-version" + result_line "HASH_CODE" "$HASH_CODE" + result_line "RESULT" "OK" + ;; + get-node-url) + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + result_line "ACTION" "get-node-url" + result_line "NODE_URL" "$NODE_URL" + ;; + get-online-ips) + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "get_online_ips" get_online_ips || return 1 + print_online_ips_result + ;; + create-download-task) + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "create_download_task" create_download_task || return 1 + result_line "ACTION" "create-download-task" + result_line "TIME_OUT" "0" + result_line "RESULT" "TASK_CREATED" + ;; + poll-download-progress) + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "poll_download_progress" poll_download_progress || return 1 + print_progress_result + ;; + download-cloud-to-node) + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "download_cloud_to_node" download_cloud_to_node || return 1 + print_progress_result "download-cloud-to-node" + result_line "RESULT" "DONE" + ;; + upgrade-ip) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_capture response "upgrade_ip[${ip}]" upgrade_ip "$ip" || return 1 + result_line "ACTION" "upgrade-ip" + result_line "IP" "$ip" + result_line "SUCCESS" "$(json_value "$response" '.success')" + result_line "MESSAGE" "$(json_value "$response" '.message')" + result_line "RAW_RESPONSE" "$(sanitize_field "$response")" + ;; + start-ip) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "start_application[${ip}]" start_application "$ip" || return 1 + result_line "ACTION" "start-ip" + result_line "IP" "$ip" + result_line "RUN_START" "true" + result_line "RESULT" "OK" + ;; + stop-ip) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_step "stop_application[${ip}]" stop_application "$ip" || return 1 + result_line "ACTION" "stop-ip" + result_line "IP" "$ip" + result_line "RUN_START" "false" + result_line "RESULT" "OK" + ;; + verify-ip) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_capture response "verify_ip[${ip}]" verify_ip "$ip" || return 1 + result_line "ACTION" "verify-ip" + result_line "IP" "$ip" + result_line "SUCCESS" "$(json_value "$response" '.success')" + result_line "MESSAGE" "$(json_value "$response" '.message')" + result_line "RAW_RESPONSE" "$(sanitize_field "$response")" + ;; + download-log) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_capture log_file "download_log[${ip}]" download_log "$ip" || true + result_line "ACTION" "download-log" + result_line "IP" "$ip" + result_line "LOG_FILE" "$log_file" + ;; + rollback-ip) + require_ip_arg "$ip" || return 1 + run_flow_step "get_token" get_token || return 1 + run_flow_step "get_node_url" get_node_url || return 1 + run_flow_capture rollback_result "rollback_ip[${ip}]" rollback_ip "$ip" "$stop_first" || return 1 + result_line "ACTION" "rollback-ip" + result_line "IP" "$ip" + result_line "STOP_FIRST" "$stop_first" + result_line "ROLLBACK_RESULT" "$rollback_result" + ;; + *) + log_error "未知 action: $action" + return 1 + ;; + esac +} + add_result() { local ip="$1" local status="$2" @@ -1094,6 +1299,10 @@ init_runtime() { main() { local config_path="$DEFAULT_CONFIG_PATH" + local action="" + local action_ip="" + local action_hash_code="" + local action_stop_first="false" local manual_rollback_ip="" local manual_rollback_stop_first="false" @@ -1109,8 +1318,29 @@ main() { manual_rollback_ip="$2" shift 2 ;; + --action) + [[ $# -lt 2 ]] && { log_error "--action 缺少名称"; exit 1; } + action="$2" + shift 2 + ;; + --ip) + [[ $# -lt 2 ]] && { log_error "--ip 缺少目标IP"; exit 1; } + action_ip="$2" + shift 2 + ;; + --hash-code) + [[ $# -lt 2 ]] && { log_error "--hash-code 缺少值"; exit 1; } + action_hash_code="$2" + shift 2 + ;; + --stop-first) + action_stop_first="true" + manual_rollback_stop_first="true" + shift + ;; --rollback-stop-first) manual_rollback_stop_first="true" + action_stop_first="true" shift ;; -h|--help) @@ -1126,6 +1356,11 @@ main() { done ACTIVE_CONFIG_PATH="$config_path" + if [[ -n "$action" ]]; then + run_action "$config_path" "$action" "$action_ip" "$action_hash_code" "$action_stop_first" + return + fi + if [[ -n "$manual_rollback_ip" ]]; then run_manual_rollback "$config_path" "$manual_rollback_ip" "$manual_rollback_stop_first" return diff --git a/doc_scripts/当前脚本情况总结.md b/doc_scripts/当前脚本情况总结.md index 5fa7493..caaf2b2 100644 --- a/doc_scripts/当前脚本情况总结.md +++ b/doc_scripts/当前脚本情况总结.md @@ -45,6 +45,8 @@ 12. 失败时标记回滚待确认,由 Agent 与用户确认后再执行手动回滚 13. 输出最终部署报告 +同时,两个主脚本都已提供 `action` 入口,适合由 Agent 按步骤调用,而不是只能整条主流程一把执行。 + 当前接口约定补充: - `/api/mcp/version/upgrade` 使用 query 参数,不再使用 body 表单。 @@ -86,6 +88,18 @@ bash ./deploy.sh --config ./config.txt --rollback-ip 192.168.1.10 --rollback-sto powershell -File .\deploy.ps1 -ConfigPath .\config.txt -RollbackIp 192.168.1.10 -RollbackStopFirst ``` +常见的 Agent 调用方式示例: + +```bash +bash ./deploy.sh --config ./config.txt --action get-online-ips +bash ./deploy.sh --config ./config.txt --action upgrade-ip --ip 192.168.1.10 +``` + +```powershell +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action GetOnlineIps +powershell -File .\deploy.ps1 -ConfigPath .\config.txt -Action UpgradeIp -Ip 192.168.1.10 +``` + ## 3. 当前运行方式 ### 3.1 Windows