Add svc.sh update command: auto-detect repo advances and refresh services #65

Merged
weichen merged 3 commits from feature/svc-auto-update into main 2026-09-30 15:22:53 +00:00
5 changed files with 329 additions and 8 deletions
@@ -42,3 +42,4 @@
| 同步前验收 | d54bfdd8e99d5d3d9a7f5b1db65ab90c841dd7e0 | 69871136e591b2b8416660651679554824efc823 | 2026-09-30T14:11:30Z |
| 同步前验收 | 22d14db80c31e4bcfa24411f847018f527b16a01 | 6cd8cf1d0d8c7b82cabe4ecbdf82b5cfd83e39fe | 2026-09-30T14:19:33Z |
| 同步前验收 | 7db242792a1561b2aad7877278d42f249369c2b9 | 314de91df3755cae006f5ced777eb71d0baa92b1 | 2026-09-30T14:29:40Z |
| 同步前验收 | ffda6d33eabb23c9be5bac221529367d771c5f7b | ac8db54ecb8ddc1e077bfec772a1665d3c2d753a | 2026-09-30T15:22:40Z |
+58
View File
@@ -0,0 +1,58 @@
# feature/svc-auto-update 任务卡
任务主记录:<PR 链接,task-finish 时填写>;状态:进行中;owner:weichen
## 基线与授权
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= ffda6d33eabb23c9be5bac221529367d771c5f7b
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-auto-update(worktree);分支:feature/svc-auto-update
- 授权与交付终点:用户确认方案(按需 update 命令、可点名、local 源允许自动 pull)后授权实施;
终点 = 实现合入 main 并回收工作区。仅改 Dev 仓 scripts/svc.sh / Makefile / docs/svc.md。
## 目标与非目标
目标:新增 `svc.sh update [name...] [--check] [--all] [--no-fetch]`——
fetch 远端 → 逐服务比较登记 source 与最新(Dev 仓 origin/main / Releases 最新版本)→
--check 只报告;否则更新过时且运行中的服务(点名/--all 含已停止)。local 源自动 pull
三重防护(分支==main + 树干净 + --ff-only);dev@ 源部署远端 main 最新(隔离 worktree);
release@ 源部署 Releases 最新(sha256 校验);postgres/static --dir 不适用。复用 deploy
全链路与持久化配置;update 事件入 history。Makefile 加 svc-update;帮助与文档同步。
非目标:不做常驻轮询/守护(--watch 不在本轮);不自动 pull Releases 仓以外的主检出分支;
不处理 local 源工作树的未提交变更内容(脏树=跳过)。
## 同步记录
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|---|---|---|---|
| 启动 | ffda6d33eabb23c9be5bac221529367d771c5f7b | ffda6d33eabb23c9be5bac221529367d771c5f7b | 2026-09-30T15:02:15Z |
## 关键实现与修复记录
- 修复 main 潜伏 bug(v2 重构引入):materialize_dev_commit 改为 $() 捕获 worktree 路径后,
git submodule update 与 make 的 stdout 混入捕获值 → 产物路径损坏(cp: File name too
long)。dev@sha 源的真实部署自 v2 起实际不可用(此前仅 dry-run 覆盖)。修复:构建期
stdout 全部改走 stderr,stdout 只留 worktree 路径。已实测修复后 dev@73b2802 部署成功。
- 修复本轮新代码:cmd_update 执行循环中 `local name=... rest=... from="${rest%%|*}..."`
同语句后项引用前项,set -u 下 unbound variable。拆分两条 local 语句。
## 结果与验证(完成后填写)
- 结果:update 命令落地(检测表 + 分源更新 + 三重 pull 防护 + 汇总退出码);Makefile
svc-update;--help(命令表/短帮助/lifecycle 主题)与 docs/svc.md(6.3 节/上手/配方/
事件表/排查表)同步;RESERVED_ENVS 加 update。
- 验证(任务工作区 runtime 内,5 个测试服务全链路,已全部清理):
- dev@ 源完整更新链路:deploy --from dev@73b2802(真实隔离构建)→ update --check 报告
可更新 → update devold → 隔离构建 ffda6d3 组合 → source=dev@ffda6d3、HTTP 200、
标签 updtest 与端口 8794 保留(复用持久化 exec=)→ 构建 worktree 回收。
- local 源防护:source 置为旧祖先提交(测试 fixture)→ 状态"可更新"→ 执行时"当前分支
非 main——跳过自动 pull",汇总 跳过=1、exit 0、登记未动。"领先远端 main(不动作)"
路径由 local@<feature-head> 天然覆盖(feature 分支领先 origin/main)。
- 已停止 + 可更新:不加 --all 仅警告;--all 后更新并启动(release@0.0.9 → 0.1.0,
sha256 校验通过)。
- postgres / static --dir:报告"不适用"。release@0.1.0:报告"✓ 最新"。
- --check 只读性:前后 svc.env md5 一致。点名未登记服务 exit 2。--no-fetch 跳过网络
并在报告注明。history 记录 update begin/ok 事件。
- 影响与剩余项:main 分支上"落后→自动 pull→重建"路径受三重防护保护、逻辑简单,留待
真实使用首次触发验证;--watch 常驻轮询未做(用户选择按需命令);dirty-tree 跳过路径
代码就位(同防护块),工作区(feature 分支)无法真实触发,与 pull 路径一并留待验证。
+5 -1
View File
@@ -13,7 +13,7 @@ PLUGINS := plugins
COMBO := core=$(shell git -C $(CORE) rev-parse --short HEAD 2>/dev/null)+backend=$(shell git -C $(BACKEND) rev-parse --short HEAD 2>/dev/null)+frontend=$(shell git -C $(FRONTEND) rev-parse --short HEAD 2>/dev/null)+ui=$(shell git -C $(UI) rev-parse --short HEAD 2>/dev/null)+agentcmd=$(shell git -C deps/agent-command rev-parse --short HEAD 2>/dev/null)+documents=$(shell git -C modules/documents rev-parse --short HEAD 2>/dev/null)
VERSION := $(shell git describe --tags --always --dirty 2>/dev/null)+$(COMBO)
.PHONY: help sync status build frontend svc svc-list svc-commits svc-history test-backend test-frontend test-ui test-core test-agentcmd dev pin clean setup precheck task-start task-sync task-close task-finish version
.PHONY: help sync status build frontend svc svc-list svc-commits svc-history svc-update test-backend test-frontend test-ui test-core test-agentcmd dev pin clean setup precheck task-start task-sync task-close task-finish version
version:
@echo "agent-system $(VERSION)"
@@ -49,6 +49,7 @@ help:
@echo "make svc-list 服务总览:make svc-list [ARGS='--tag core'];数据库运维:scripts/svc.sh db create|dsn|backup|backups|restore|clone|copy|ls|set"
@echo "make svc-commits 列出可部署提交:make svc-commits SRC=dev|release [N=20]"
@echo "make svc-history 事件历史:make svc-history [ENV] [N=20](完整文档:docs/svc.md)"
@echo "make svc-update 自动更新过时服务:make svc-update [NAMES='dev pg1'] [ARGS='--check --all']"
@echo "make dev 开发模式说明(前后端分离热更新)"
@echo "make pin 推进 submodule 指针到各仓远端 main(提交前须 make test 通过)"
@echo "make status submodule 状态"
@@ -110,6 +111,9 @@ svc-commits:
svc-history:
scripts/svc.sh history $(ENV) $(N)
svc-update:
scripts/svc.sh update $(NAMES) $(ARGS)
demo: build
$(BIN) serve --addr 127.0.0.1:8787 --data ./data --plugins $(PLUGINS) --mock
+50 -4
View File
@@ -73,6 +73,10 @@ scripts/svc.sh history # 事件追溯
scripts/svc.sh db backup pg1 # pg_dump -Fc,自动自检
scripts/svc.sh db restore pg1 --file <dump> --force # 覆盖恢复需 --force
scripts/svc.sh db clone pg1 pg1test --port 8896 # 克隆一套到新端口
# 仓库更新后,一条命令把服务刷到最新
scripts/svc.sh update --check # 先看哪些服务落后
scripts/svc.sh update # 自动更新过时且运行中的服务
```
## 3. 概念与目录布局
@@ -80,8 +84,8 @@ scripts/svc.sh db clone pg1 pg1test --port 8896 # 克隆一套到新端口
### 3.1 服务名
`^[a-z][a-z0-9-]{0,15}$`(小写字母开头,可含数字与连字符,≤16 位)。保留字不可用作
服务名:`status list add set db rm create dsn commits history deploy stop start restart
logs help version`。
服务名:`status list add set db rm create dsn commits history update deploy stop start
restart logs help version`。
三个名字有默认端口:`dev` = `127.0.0.1:8788`,`prod` = `127.0.0.1:8787`;其余名字按
名称哈希在 **8800–9099** 确定性派生端口,与已登记端口或正在监听的端口冲突时自动向后
@@ -293,7 +297,42 @@ scripts/svc.sh history [name] [N] # 默认全部服务、最近 20 条;首
合并各服务 `history.jsonl`(按时间排序),字段:时间 / 服务 / kind / 事件 / 结果 /
来源 / pid / 详情。事件类型见 §10.2。
### 6.3 help
### 6.3 update(自动检索并更新)
```bash
scripts/svc.sh update [name...] [--check] [--all] [--no-fetch]
```
仓库更新后把服务刷到最新:fetch 远端 → 逐服务比较登记来源与最新 → 报告并(可选)更新。
| 用法 | 行为 |
|---|---|
| `update --check` | 只输出检测表,不改任何状态 |
| `update` | 自动更新所有"过时且**运行中**"的服务 |
| `update dev pg1` | 点名更新(已停止的过时服务也会更新并启动) |
| `update --all` | 范围扩大到已停止服务 |
| `update --no-fetch` | 跳过网络 fetch,用本地已有引用比较(报告注明) |
检测表输出:服务 / 当前来源 / 最新 / 状态。状态含义:
- `✓ 最新`——登记来源与最新一致,不动作;
- `可更新`——本地来源是远端最新的祖先,将被更新;
- `领先远端 main(不动作)` / `与远端 main 分叉(不自动更新)`——不盲目降级或强更;
- `不适用`——postgres(无代码版本)与 static `--dir`(外部目录)不参与。
各来源的更新动作(复用 deploy 全链路:停旧 → 构建/校验 → 启动 → 健康检查;持久化配置
与标签保留;写 `update` 事件入 history):
| 来源 | 更新方式 |
|---|---|
| `local@<sha>` | 当前分支为 main 且工作树干净时:自动 `git pull --ff-only origin main` + `make sync` + 重建;**树脏或非 main 分支则跳过并警告**(绝不碰未提交现场) |
| `dev@<sha>` | `deploy --from dev@<远端 main 最新>`(隔离 worktree 构建,不碰主检出) |
| `release@<版本>` | `deploy --from release@<Releases 最新版本>`(sha256 校验) |
任一服务失败不中断其余服务;最终汇总 `成功/失败/跳过`。退出码:0 全部成功或最新;
1 有失败;2 用法错误。
### 6.4 help
```bash
scripts/svc.sh # 一屏速览(短帮助)
@@ -487,6 +526,7 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
| `restore` | ok / fail | 恢复(库、文件、表数) |
| `clone` | ok / fail | 克隆(物理/在线) |
| `copy` | ok / fail | 搬库 |
| `update` | begin / ok / fail | 自动更新(from → to) |
| `rm` | ok | 注销(是否 purge) |
## 11. 安全与边界
@@ -526,6 +566,7 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
| `worktree 回收失败` | 按提示手动 `git worktree remove --force runtime/.worktrees/<sha>` |
| `子模块物化失败` | 子模块对象缺失:先 fetch;或网络问题重试 |
| `目标库已有 N 张表——拒绝覆盖` | 确认后加 `--force`(会 drop 重建) |
| `update 报"当前分支非 main"或"工作树不干净" | local 源服务的自动 pull 被防护跳过:先自行收尾/pull,再重跑 update;或改用 `deploy --from dev@<sha>` |
| `list 里服务显示未运行但进程在` | 手工起的进程不满足 pidcheck 标记:脚本不会动它;纳入管理请走 svc 部署 |
## 14. 配方索引
@@ -567,7 +608,12 @@ scripts/svc.sh db copy pgdev pgprod --db app --force
# ⑨ 换端口
scripts/svc.sh set pg1 --port 8895 --apply
# ⑩ 总览与追溯
# ⑩ 仓库更新后刷新服务
scripts/svc.sh update --check # 看哪些落后
scripts/svc.sh update # 更新过时且运行中的(数据与配置保留)
scripts/svc.sh update dev # 只刷新指定服务
# ⑪ 总览与追溯
scripts/svc.sh list --tag test
scripts/svc.sh history pg1 50
```
+215 -3
View File
@@ -19,6 +19,7 @@
# scripts/svc.sh db set <name> [...] 等价于 set
# scripts/svc.sh commits [dev|release] [N] 可部署提交列表(默认 dev,N=20)
# scripts/svc.sh history [name] [N] 事件历史(默认全部,最近 20 条)
# scripts/svc.sh update [name] [--check] [--all] 检测远端最新并更新过时服务
# scripts/svc.sh help [主题] 分主题详细帮助
#
# ════════════════════ 部署来源(agent/static 的 deploy)════════════════════
@@ -118,6 +119,7 @@ svc.sh —— 本机服务管理:后端(agent) / 前端(static) / 数据库(po
scripts/svc.sh rm pg1 [--purge] 注销(--purge 连数据删除)
scripts/svc.sh commits [dev|release] [N] 可部署提交
scripts/svc.sh history [name] [N] 事件历史
scripts/svc.sh update [name] [--check] 检测远端最新并更新过时服务
帮助:--help 完整帮助 | help [主题] 分主题(model deploy lifecycle db dbops config examples safety)
文档:docs/svc.md
@@ -252,6 +254,17 @@ EOF
注销:
scripts/svc.sh rm <name> 停止并移除登记(目录/数据/历史保留)
scripts/svc.sh rm <name> --purge 连 runtime/<name>/ 整个目录一起删除
自动更新(仓库更新后一条命令刷新服务):
scripts/svc.sh update --check 只报告:每服务 当前来源 vs 最新(fetch 后比较)
scripts/svc.sh update 更新所有"过时且运行中"的服务
scripts/svc.sh update dev prod 点名更新(已停止的过时服务也会更新并启动)
scripts/svc.sh update --all 含已停止服务;--no-fetch 跳过网络
- local 源:自动 git pull --ff-only origin main + make sync 后重建
(仅当前分支==main 且工作树干净;否则跳过并警告,不碰未提交现场)
- dev@sha 源:直接部署远端 main 最新提交(隔离 worktree 构建,不碰主检出)
- release@源:部署 Releases 仓最新版本(sha256 校验)
- postgres / static --dir 不参与更新;每次更新写 history(update 事件)
EOF
return 0 ;;
db)
@@ -444,6 +457,7 @@ CLI_LBASE=""; CLI_LKEY=""; CLI_LNAME=""; CLI_TAVILY=""
CLI_FROM=""; CLI_PICK=""; CLI_DRYRUN=0
CLI_KIND=""; CLI_DIR=""; CLI_PURGE=0; CLI_REVEAL=0
CLI_APPLY=0; CLI_FORCE=0; CLI_ONLINE=0; CLI_FILE=""; CLI_OUT=""
CLI_CHECK=0; CLI_ALL=0; CLI_NOFETCH=0
CLI_DB_USER=""; CLI_DB_PASSWORD=""; CLI_DB_NAME=""; CLI_DB_AUTH=""
CLI_TAGS=()
@@ -503,7 +517,7 @@ addr_compose() {
}
# ---- 名称与端口 ----
RESERVED_ENVS=" status list add set db rm create dsn commits history deploy stop start restart logs help version "
RESERVED_ENVS=" status list add set db rm create dsn commits history update deploy stop start restart logs help version "
# valid_env NAME:服务名合法性(小写字母开头、a-z0-9-、≤16 位;保留字不可用)。
valid_env() {
@@ -932,12 +946,13 @@ materialize_dev_commit() {
git -C "$ROOT" worktree add --detach "$wt" "$full" >/dev/null || return 1
fi
c_info "[$ENV_NAME] 物化子模块组合(git submodule update --init --recursive;缺对象时需网络)"
git -C "$wt" submodule update --init --recursive || {
# stdout 只留给最后的 worktree 路径(调用方 $() 捕获)——子模块/构建输出全部走 stderr。
git -C "$wt" submodule update --init --recursive >&2 || {
c_err "[$ENV_NAME] 子模块物化失败(对象缺失时先 git -C $ROOT fetch origin)"
return 1
}
c_info "[$ENV_NAME] 构建(make $target:npm ci + 构建,需网络)"
make -C "$wt" "$target" || return 1
make -C "$wt" "$target" >&2 || return 1
echo "$wt"
}
@@ -2029,6 +2044,202 @@ cmd_history() {
done
}
# ---- 自动检索与更新 ----
# cmd_update [NAME...] [--check] [--all] [--no-fetch]:
# fetch 远端 → 逐服务比较登记 source 与最新(Dev 仓 origin/main / Releases 最新版本)→
# --check 只报告;否则更新"过时且运行中"的服务(点名或 --all 时含已停止)。
# local 源的自动 pull 受三重保护:当前分支==main + 工作树干净 + --ff-only。
cmd_update() {
local names=() a
while [ $# -gt 0 ]; do
case "$1" in
--check) CLI_CHECK=1; shift ;;
--all) CLI_ALL=1; shift ;;
--no-fetch) CLI_NOFETCH=1; shift ;;
--) shift; break ;;
-h|--help) usage; exit 0 ;;
-*) c_err "update 未知参数:$1(可用:--check --all --no-fetch)"; return 2 ;;
*) names+=("$1"); shift ;;
esac
done
local targets=() d e
if [ "${#names[@]}" -gt 0 ]; then
for a in ${names[@]+"${names[@]}"}; do
valid_env "$a" || { c_err "非法服务名:$a"; return 2; }
[ -f "$RUNTIME/$a/svc.env" ] || { c_err "[$a] 未登记(scripts/svc.sh list 查看)"; return 2; }
targets+=("$a")
done
else
for d in "$RUNTIME"/*/; do
[ -f "${d}svc.env" ] || continue
e="$(basename "$d")"
[ "$e" = ".worktrees" ] && continue
valid_env "$e" || continue
targets+=("$e")
done
fi
[ "${#targets[@]}" -gt 0 ] || { c_info "暂无已登记服务(db create / deploy 会登记)"; return 0; }
# fetch 远端(一次性;失败不致命,退化为本地缓存引用比较)。
local fetch_note=""
if [ "$CLI_NOFETCH" = 1 ]; then
fetch_note="基于本地缓存引用"
else
c_info "git fetch origin(检索远端更新)"
if git -C "$ROOT" fetch origin --quiet 2>/dev/null; then
:
else
fetch_note="fetch 失败,基于本地缓存引用"
c_warn "git fetch 失败——$fetch_note"
fi
fi
local latest_sha
latest_sha="$(git -C "$ROOT" rev-parse --verify --quiet origin/main || true)"
# Releases 最新版本(仅当存在 release 源服务时才访问 Releases 仓)。
local latest_rel="" need_rel=0 e_src
for e in ${targets[@]+"${targets[@]}"}; do
e_src="$(sed -n 's/^source=//p' "$RUNTIME/$e/svc.env" | head -1)"
case "$e_src" in release@*) need_rel=1 ;; esac
done
if [ "$need_rel" = 1 ]; then
releases_root
if [ "$CLI_NOFETCH" != 1 ]; then
git -C "$REL_ROOT" fetch --quiet 2>/dev/null || c_warn "Releases 仓 fetch 失败——基于本地缓存"
fi
local rel_head
rel_head="$(git -C "$REL_ROOT" rev-parse --verify --quiet HEAD || true)"
if [ -n "$rel_head" ]; then
latest_rel="$(git -C "$REL_ROOT" ls-tree --name-only "$rel_head:versions" 2>/dev/null | sort -V | tail -1 || true)"
fi
fi
# 逐服务分析:状态判定 + 收集可更新动作。
c_info "更新检测($fetch_note;root: $ROOT)"
printf ' %-12s %-20s %-12s %s\n' 服务 当前来源 最新 状态
local acts=() state to latest_disp="" updated=0 failed=0 skipped=0
for e in ${targets[@]+"${targets[@]}"}; do
local kind src cur st_running
kind="$(svc_kind "$e")"
src="$(sed -n 's/^source=//p' "$RUNTIME/$e/svc.env" | head -1)"
to=""
latest_disp=""
case "$kind" in
postgres)
state="不适用(数据库)" ;;
*)
case "$src" in
dir@*)
state="不适用(外部目录)" ;;
"")
state="来源未知(重新 deploy 后可参与更新)" ;;
local@*|dev@*)
cur="${src##*@}"
if [ -z "$latest_sha" ]; then
state="远端 main 未知"; latest_disp="-"
elif [ "${latest_sha:0:${#cur}}" = "$cur" ]; then
state="✓ 最新"; latest_disp="$(git -C "$ROOT" rev-parse --short "$latest_sha" 2>/dev/null || echo "-")"
elif git -C "$ROOT" merge-base --is-ancestor "$cur" "$latest_sha" 2>/dev/null; then
to="$(git -C "$ROOT" rev-parse --short "$latest_sha" 2>/dev/null || true)"
latest_disp="$to"
state="可更新"
elif git -C "$ROOT" merge-base --is-ancestor "$latest_sha" "$cur" 2>/dev/null; then
state="领先远端 main(不动作)"; latest_disp="$(git -C "$ROOT" rev-parse --short "$latest_sha" 2>/dev/null || echo "-")"
else
state="与远端 main 分叉(不自动更新)"; latest_disp="-"
fi
;;
release@*)
cur="${src#release@}"
latest_disp="${latest_rel:--}"
if [ -z "$latest_rel" ]; then
state="Releases 仓不可用"
elif [ "$cur" = "$latest_rel" ]; then
state="✓ 最新"
else
local ordered
ordered="$(printf '%s\n%s\n' "$cur" "$latest_rel" | sort -V | tail -1)"
if [ "$ordered" = "$latest_rel" ]; then
state="可更新"; to="$latest_rel"
else
state="领先 Releases(不动作)"
fi
fi
;;
*)
state="来源未知(重新 deploy 后可参与更新)" ;;
esac
;;
esac
printf ' %-12s %-20s %-12s %s\n' "$e" "${src:--}" "$latest_disp" "$state"
if [ "$state" = "可更新" ]; then
st_running="$(svc_pid_state "$e")"
if [ "${st_running%% *}" = alive ] || [ "$CLI_ALL" = 1 ] || [ "${#names[@]}" -gt 0 ]; then
acts+=("$e|$src|$to")
else
c_warn "[$e] 已停止且可更新——加 --all 或点名以更新(更新会启动服务)"
fi
fi
done
if [ "$CLI_CHECK" = 1 ]; then
c_info "--check:仅报告,未做任何变更"
return 0
fi
[ ${#acts[@]} -gt 0 ] || { c_ok "无需更新(全部最新或不适用)"; return 0; }
# 执行更新:逐服务走 deploy 全链路(复用持久化配置与标签)。
for a in ${acts[@]+"${acts[@]}"}; do
local name="${a%%|*}" rest="${a#*|}"
local from="${rest%%|*}" tov="${rest#*|}"
local deploy_from=""
env_config "$name"
case "$from" in
local@*)
local branch dirty
branch="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"
if [ "$branch" != "main" ]; then
c_warn "[$name] 当前分支 $branch 非 main——跳过自动 pull(手动更新后重跑 update)"
skipped=$((skipped + 1)); continue
fi
dirty="$(git -C "$ROOT" status --porcelain | grep -v '^.. \.zcode/' || true)"
if [ -n "$dirty" ]; then
c_warn "[$name] 主检出工作树不干净——跳过(不碰未提交现场)"
skipped=$((skipped + 1)); continue
fi
c_info "[$name] git pull --ff-only origin main + make sync"
if ! git -C "$ROOT" pull --ff-only origin main >/dev/null 2>&1; then
c_err "[$name] 自动 pull 失败(可能本地 main 与远端分叉)——跳过,请手动处理"
hist_event update fail "$(read_pid)" "pull 失败(from=$from)"
failed=$((failed + 1)); continue
fi
if ! make -C "$ROOT" sync >/dev/null 2>&1; then
c_warn "[$name] make sync 失败——继续构建(若子模块缺失构建会失败)"
fi
deploy_from="local"
;;
dev@*) deploy_from="dev@$tov" ;;
release@*) deploy_from="release@$tov" ;;
esac
c_info "[$name] 更新:$from → $tov($deploy_from)"
hist_event update begin "$(read_pid)" "from=$from to=$tov"
CLI_FROM="$deploy_from"
if agent_do_deploy; then
hist_event update ok "$(read_pid)" "from=$from to=$tov"
c_ok "[$name] 已更新:$from → $tov"
updated=$((updated + 1))
else
hist_event update fail "$(read_pid)" "from=$from to=$tov(部署失败)"
c_err "[$name] 更新失败($from → $tov),其余服务继续"
failed=$((failed + 1))
fi
CLI_FROM=""
done
c_info "更新汇总:成功 $updated / 失败 $failed / 跳过 $skipped / 其余最新或不适用"
[ "$failed" -eq 0 ] && return 0 || return 1
}
# ---- main ----
main() {
@@ -2069,6 +2280,7 @@ main() {
rm) shift; cmd_rm "$@"; exit 0 ;;
commits) shift; cmd_commits "$@"; exit 0 ;;
history) shift; cmd_history "$@"; exit 0 ;;
update) shift; cmd_update "$@" || exit $?; exit 0 ;;
esac
ENV_NAME="$1"; shift
# 动作词可省略(省略 = deploy);flags 可直接跟随服务名。