Add service config-file switching with schema validation to svc.sh
Why: beyond database binding, services needed a way to switch the JSON config file they run with and have the new config take effect promptly; hand-editing exec= risks restarting a service into an invalid config. Evidence: in the task worktree with a real agent binary -- a config with an unknown key is rejected by the binary's strict schema check before any state change; a valid file binds via --config in exec= + conffile= (survives record_state rewrites), restarts the running service, and the config's dsn verifiably drives the process (wrong-password variant failed PG auth in the log; correct variant connected and created 21 tables); editing the file and re-running 'config <same file>' re-validates and restarts (pid changes); '--clear' removes both the flag and conffile; db and config coexist in exec=; view mode shows masked JSON content plus 'config explain' effective values with masked dsn; list DB column also resolves dsns that live only inside the config file. Full log in the task card. Impact: new service-level action 'svc.sh <name> config [file | --clear] [--no-restart]' for agent services; config-file content snapshots (masked, --reveal to show) in info; a config-file line in status; conffile persisted in svc.env. No agent binary changes. Refs: task card .task/feature-svc-db-bind.md (same doc as PR #182); docs/svc.md sections 2/4.5/4.6/6/10.1
This commit is contained in:
@@ -6,25 +6,33 @@
|
|||||||
|
|
||||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3836ea88e3954878f257b7708ae5ccef947ccd79
|
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3836ea88e3954878f257b7708ae5ccef947ccd79
|
||||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-db-bind(worktree);分支:feature/svc-db-bind
|
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-db-bind(worktree);分支:feature/svc-db-bind
|
||||||
- 授权与交付终点:实现"服务↔数据库绑定"(svc.sh 服务级 db 子命令 + info/list 显示绑定)并修复
|
- 授权与交付终点:实现"服务↔数据库绑定"(svc.sh 服务级 db 子命令 + info/list 显示绑定)与
|
||||||
adjust_args_for_bin 单横线误判;终点 = PR 合入 main(附 precheck / task-sync / 测试证据)
|
"服务配置文件更新/即时生效"(config 子命令),并修复 adjust_args_for_bin 单横线误判;
|
||||||
|
终点 = PR 合入 main(用户指示完成后一并合入;附 precheck / task-sync / 测试证据)
|
||||||
|
|
||||||
## 目标与非目标
|
## 目标与非目标
|
||||||
|
|
||||||
目标:
|
目标:
|
||||||
- agent 服务可绑定/切换/解除数据库:`svc.sh <name> db [pg名 [--db X] | --clear] [--no-restart]`;
|
- **数据库绑定**:agent 服务可绑定/切换/解除数据库:`svc.sh <name> db [pg名 [--db X] | --clear]
|
||||||
缺省查看当前绑定与可用实例。绑定 = DSN 写入持久化 exec= + dbref=<pg>/<库> 登记,实例运行中
|
[--no-restart]`;缺省查看当前绑定与可用实例。绑定 = DSN 写入持久化 exec= + dbref=<pg>/<库>
|
||||||
缺库自动建,服务运行中自动重启生效;--clear 回内嵌 SQLite 档。
|
登记,实例运行中缺库自动建,服务运行中自动重启生效;--clear 回内嵌 SQLite 档。
|
||||||
- 显示层全链路:list 表新增 DB 列;status 详情新增"数据库"行;info 数据库节新增"绑定服务"
|
- **配置文件更新**:`svc.sh <name> config [file.json |--clear] [--no-restart]`;缺省查看当前
|
||||||
反向映射;无 dbref 的旧服务按 DSN host:port 反查匹配实例(外部 DSN 显示 外部@host:port)。
|
配置文件(掩码内容 + `agent-system config explain -config` 视角的生效值)。切换前经二进制
|
||||||
- 修复既有缺陷:adjust_args_for_bin 以 grep '--dsn' 检测旗标支持,Go flag 帮助打印单横线
|
schema 强校验(未知键/非法值拒绝,不把服务重启进坏配置),通过后把 --config <绝对路径>
|
||||||
|
写入 exec= + conffile= 登记,运行中自动重启即时生效(agent 无 SIGHUP 热重载,重启秒级);
|
||||||
|
--clear 移除回命令行参数形态。绑定后改文件内容 + restart(或重跑 config 同文件)即生效。
|
||||||
|
- **显示层全链路**:list 表新增 DB 列(含配置文件 dsn 反查);status 详情新增"数据库""配置
|
||||||
|
文件"行;info 数据库节新增"绑定服务"反向映射、配置节附配置文件内容快照(敏感值掩码,
|
||||||
|
--reveal 明文);无 dbref 的旧服务按 exec= --dsn 或配置文件 dsn 的 host:port 反查匹配实例
|
||||||
|
(外部 DSN 显示 外部@host:port)。
|
||||||
|
- **修复既有缺陷**:adjust_args_for_bin 以 grep '--dsn' 检测旗标支持,Go flag 帮助打印单横线
|
||||||
`-dsn`,导致现役二进制的 --dsn/--auth 在 deploy 重建参数时被误删——改为"去横线旗标名 +
|
`-dsn`,导致现役二进制的 --dsn/--auth 在 deploy 重建参数时被误删——改为"去横线旗标名 +
|
||||||
1-2 横线"匹配。
|
1-2 横线"匹配。
|
||||||
- record_state 跨重启保留 dbref;svc_del_key 辅助;db create 尾部提示绑定命令;
|
- record_state 跨重启保留 dbref/conffile;svc_del_key 辅助;db create 尾部提示绑定命令;
|
||||||
usage/usage_short/help(lifecycle/db)/docs/svc.md 同步。
|
usage/usage_short/help(lifecycle/config/db)/docs/svc.md(§2/§4.5/§4.6/§6/§10.1)同步。
|
||||||
|
|
||||||
非目标:不改 agent 二进制与部署/生命周期语义;不做 static/postgres 的绑定;不做运行中热切换
|
非目标:不改 agent 二进制与部署/生命周期语义;不做 static/postgres 的绑定;不做运行中热切换
|
||||||
(切换经重启生效)。
|
(切换经重启生效);不做配置文件的编辑器(内容仍由用户维护,svc.sh 负责校验/切换/生效)。
|
||||||
|
|
||||||
## 同步记录
|
## 同步记录
|
||||||
|
|
||||||
@@ -34,19 +42,26 @@
|
|||||||
|
|
||||||
## 结果与验证(完成后填写)
|
## 结果与验证(完成后填写)
|
||||||
|
|
||||||
- 结果:已实现(上列目标全部落地;`<name> db` 服务级动作与顶层 `db` 命令并存无冲突)。
|
- 结果:已实现(数据库绑定与配置文件更新两块目标全部落地;`<name> db`/`<name> config`
|
||||||
|
服务级动作与顶层 `db` 命令、`--config` 旗标并存无冲突)。
|
||||||
- 验证(worktree runtime 实测,真实 agent 二进制 + PostgreSQL 18,2026-10-04):
|
- 验证(worktree runtime 实测,真实 agent 二进制 + PostgreSQL 18,2026-10-04):
|
||||||
- `bash -n` 通过;
|
- `bash -n` 通过;
|
||||||
- 绑定:`t-app db bpg` → exec= 写入 --dsn、dbref=bpg/agent、自动重启生效;**record_state
|
- 数据库绑定:`t-app db bpg` → exec= 写入 --dsn、dbref=bpg/agent、自动重启生效;
|
||||||
重写 svc.env 后 dbref 保留**;PG 的 agent 库出现 **21 张表**(服务真实落库,非仅配置);
|
**record_state 重写 svc.env 后 dbref 保留**;PG 的 agent 库出现 **21 张表**(服务真实
|
||||||
- 切换:`t-app db bpg --db t2` → 自动建库 t2、重启、t2 库 21 张表;
|
落库,非仅配置);`--db t2` 切换自动建库(t2 库 21 张表);`--clear` 干净回内嵌档;
|
||||||
- 解绑:`db --clear` → dbref/--dsn 移除、重启回内嵌档;
|
`--no-restart` 只改登记;错误路径(未登记实例/多余参数/postgres 服务)均 exit 2;
|
||||||
- `--no-restart` 只改登记;错误路径(未登记实例/多余参数/postgres 服务)均 exit 2 且提示正确;
|
- 配置文件更新:坏文件(未知键 bogus)被 schema 校验拒绝且不动 exec、不重启;合法文件
|
||||||
- 显示层:list DB 列(t-app→bpg/agent)、`t-app db` 查看(掩码 DSN+实例列表)、info 数据库节
|
绑定后 cmdline 含 `--config <abs>`、conffile= 跨 record_state 保留;**配置文件内的 dsn
|
||||||
"绑定服务 t-app(agent)";手删 dbref 模拟旧部署 → list 按 DSN host:port 反查仍显示 bpg/agent;
|
真实生效**(错误密码版本在日志中报 PG 认证失败=配置确已应用;正确密码版连上 PG、
|
||||||
|
agent 库 21 张表);内容修改后重跑 `config <同一文件>` 再校验并重启(pid 更新);
|
||||||
|
`--clear` 移除 --config/conffile 并重启;错误路径(文件不存在/双文件)exit 2;
|
||||||
|
- db + config 共存:exec= 同时带 --dsn 与 --config,重启健康;
|
||||||
|
- 显示层:list DB 列(含"仅配置文件带 dsn"场景的反查 bpg/agent)、`config` 查看视图
|
||||||
|
(JSON 掩码 `"dsn": "***"` + explain 生效值 dsn 掩码)、status"数据库/配置文件"行、
|
||||||
|
info 配置节文件内容快照、info 数据库节"绑定服务";手删 dbref 模拟旧部署 → 反查仍显示;
|
||||||
- adjust 修复:正例(帮助含 `-dsn` 判支持)与负例(无 dsn 判不支持)均正确;
|
- adjust 修复:正例(帮助含 `-dsn` 判支持)与负例(无 dsn 判不支持)均正确;
|
||||||
- 回归:db dsn(掩码)/ db ls / info / history(新增 `set ok db bind → bpg/agent` 事件)/
|
- 回归:db dsn(掩码)/ db ls / info / history(新增 `set ok db bind →` / `config →` /
|
||||||
--help 正常;测试现场(t-app/bpg)已 rm --purge 清除;
|
`config unbind` 事件)/ --help 正常;测试现场(t-app/bpg、临时 JSON)已清除。
|
||||||
- 正式验收:`make test` exit 0(test-boundaries/pins/libs/core/backend/frontend/ui/agentcmd 全过)。
|
- 正式验收:`make test` exit 0(test-boundaries/pins/libs/core/backend/frontend/ui/agentcmd 全过)。
|
||||||
- 影响与剩余项:list 表加宽一列;老脚本无兼容性问题(dbref 缺省视为未绑定);
|
- 影响与剩余项:list 表加宽一列;老脚本无兼容性问题(dbref 缺省视为未绑定);
|
||||||
发现并修复的 adjust_args_for_bin 缺陷影响既有 `deploy --dsn/--auth` 路径(此前被静默丢弃)。
|
发现并修复的 adjust_args_for_bin 缺陷影响既有 `deploy --dsn/--auth` 路径(此前被静默丢弃)。
|
||||||
|
|||||||
+22
@@ -65,6 +65,7 @@ scripts/svc.sh pg1 start # pg_ctl 启动 + 自动建库
|
|||||||
scripts/svc.sh prod deploy --from dev@<sha> \
|
scripts/svc.sh prod deploy --from dev@<sha> \
|
||||||
--dsn "$(scripts/svc.sh db dsn pg1 --reveal)" # DSN 直接组合进部署
|
--dsn "$(scripts/svc.sh db dsn pg1 --reveal)" # DSN 直接组合进部署
|
||||||
scripts/svc.sh prod db pg1 --db prod # 或事后绑定/切换(缺库自动建,重启生效)
|
scripts/svc.sh prod db pg1 --db prod # 或事后绑定/切换(缺库自动建,重启生效)
|
||||||
|
scripts/svc.sh prod config .svc/prod.json # 指定/更换配置文件(校验+重启即时生效)
|
||||||
|
|
||||||
# 日常操作
|
# 日常操作
|
||||||
scripts/svc.sh list # 全服务总览(含 DB 绑定列)
|
scripts/svc.sh list # 全服务总览(含 DB 绑定列)
|
||||||
@@ -240,6 +241,26 @@ scripts/svc.sh <agent名> db --clear # 解除绑定(回内嵌 SQL
|
|||||||
登记实例(显示 `<pg名>/<库>`;匹配不上显示 `外部@host:port`);
|
登记实例(显示 `<pg名>/<库>`;匹配不上显示 `外部@host:port`);
|
||||||
- `--clear` 后服务回到内嵌 SQLite 档(数据在原 `--data` 目录,切回即见旧数据)。
|
- `--clear` 后服务回到内嵌 SQLite 档(数据在原 `--data` 目录,切回即见旧数据)。
|
||||||
|
|
||||||
|
### 4.6 config(agent 服务的配置文件更新/生效)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scripts/svc.sh <agent名> config # 查看当前配置文件(掩码内容 + agent 视角生效值)
|
||||||
|
scripts/svc.sh <agent名> config <file.json> # 更换使用的配置文件(校验通过才切换)
|
||||||
|
scripts/svc.sh <agent名> config <file> --no-restart # 只改登记,下次 start 生效
|
||||||
|
scripts/svc.sh <agent名> config --clear # 移除(回命令行参数形态)
|
||||||
|
```
|
||||||
|
|
||||||
|
配置文件是 agent 二进制的 `--config`(JSON,schema 严格校验;可用
|
||||||
|
`agent-system config explain -config <file>` 预览每个键的来源与生效值)。
|
||||||
|
|
||||||
|
- **切换前强校验**:文件必须存在且通过二进制的 schema 校验(未知键/非法值直接拒绝,
|
||||||
|
不会把服务重启进坏配置);旧产物不识别 `--config` 时警告;
|
||||||
|
- **即时生效**:运行中的服务自动重启应用新配置(agent 无 SIGHUP 热重载,重启秒级);
|
||||||
|
绑定后日常改配置 = 编辑文件 + `restart`(或重跑 `config <同一文件>` 再校验一遍);
|
||||||
|
- 持久化:`exec=` 的 `--config <绝对路径>` + `conffile=` 键(跨重启保留,600);
|
||||||
|
- 展示:`status`/`info <name>` 的"配置文件"行、`info` 配置节的文件内容快照
|
||||||
|
(敏感值掩码,`info --reveal` 显示明文);文件丢失时明确告警并提示恢复或 `--clear`。
|
||||||
|
|
||||||
## 5. 命令参考:部署来源
|
## 5. 命令参考:部署来源
|
||||||
|
|
||||||
`deploy --from` 支持三种来源(**缺省 = `dev` 远端最新**),agent 与 static 共用:
|
`deploy --from` 支持三种来源(**缺省 = `dev` 远端最新**),agent 与 static 共用:
|
||||||
@@ -578,6 +599,7 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
|
|||||||
| `pidcheck` | 进程判定标记(见 §6.1) |
|
| `pidcheck` | 进程判定标记(见 §6.1) |
|
||||||
| `exec` | agent 上次完整启动参数(含 `--dsn`,密码在其中) |
|
| `exec` | agent 上次完整启动参数(含 `--dsn`,密码在其中) |
|
||||||
| `dbref` | agent 数据库绑定 `<pg名>/<库名>`(`<name> db` 命令写入,跨重启保留;见 §4.5) |
|
| `dbref` | agent 数据库绑定 `<pg名>/<库名>`(`<name> db` 命令写入,跨重启保留;见 §4.5) |
|
||||||
|
| `conffile` | agent 配置文件绝对路径(`<name> config` 命令写入,跨重启保留;见 §4.6) |
|
||||||
| `user` / `password` / `dbname` / `authmode` / `pgdata` | postgres 专属 |
|
| `user` / `password` / `dbname` / `authmode` / `pgdata` | postgres 专属 |
|
||||||
| `htdocs` | static 专属 |
|
| `htdocs` | static 专属 |
|
||||||
|
|
||||||
|
|||||||
+183
-13
@@ -6,6 +6,7 @@
|
|||||||
# ════════════════════ 命令一览 ════════════════════
|
# ════════════════════ 命令一览 ════════════════════
|
||||||
# scripts/svc.sh <name> [action] [参数...] 服务级:deploy(默认)|start|stop|restart|status|logs [N]
|
# scripts/svc.sh <name> [action] [参数...] 服务级:deploy(默认)|start|stop|restart|status|logs [N]
|
||||||
# |db [pg名|--clear](agent 数据库绑定/切换)
|
# |db [pg名|--clear](agent 数据库绑定/切换)
|
||||||
|
# |config [file|--clear](agent 配置文件更新/生效)
|
||||||
# scripts/svc.sh list [--tag T] 服务总览(status 等价)
|
# scripts/svc.sh list [--tag T] 服务总览(status 等价)
|
||||||
# scripts/svc.sh info [name] [--tag T] [--reveal] 综合报告(只读):服务/数据库/配置快照/最近事件
|
# scripts/svc.sh info [name] [--tag T] [--reveal] 综合报告(只读):服务/数据库/配置快照/最近事件
|
||||||
# scripts/svc.sh set <name> [--port N --apply] [--tag T] 变更登记(换端口/标签)
|
# scripts/svc.sh set <name> [--port N --apply] [--tag T] 变更登记(换端口/标签)
|
||||||
@@ -42,6 +43,8 @@
|
|||||||
# --plugins/--dsn/--auth/--mock/--data agent 运行参数
|
# --plugins/--dsn/--auth/--mock/--data agent 运行参数
|
||||||
# <name> db <pg名> [--db X] [--no-restart] agent 绑定/切换数据库(实例运行中缺库自动建;
|
# <name> db <pg名> [--db X] [--no-restart] agent 绑定/切换数据库(实例运行中缺库自动建;
|
||||||
# --clear 解绑回内嵌 SQLite;无参查看当前绑定与可用实例)
|
# --clear 解绑回内嵌 SQLite;无参查看当前绑定与可用实例)
|
||||||
|
# <name> config <file.json> [--no-restart] agent 更换使用的配置文件(schema 校验通过才切换,
|
||||||
|
# 运行中自动重启即时生效;--clear 移除;无参查看当前配置与生效值)
|
||||||
# --model-base/--model-key/--model-name 与 --light-*/--tavily-keys 启动后播种设置
|
# --model-base/--model-key/--model-name 与 --light-*/--tavily-keys 启动后播种设置
|
||||||
# -- <剩余参数> 原样透传给 agent-system serve(escape hatch)
|
# -- <剩余参数> 原样透传给 agent-system serve(escape hatch)
|
||||||
# db create:--user U(默认 agent)--password P(缺省自动生成)--db NAME --auth-mode scram-sha-256|md5
|
# db create:--user U(默认 agent)--password P(缺省自动生成)--db NAME --auth-mode scram-sha-256|md5
|
||||||
@@ -75,6 +78,8 @@
|
|||||||
# scripts/svc.sh dev db pg1 --db dev # 把 dev 服务绑定到 pg1/dev 库(自动重启)
|
# scripts/svc.sh dev db pg1 --db dev # 把 dev 服务绑定到 pg1/dev 库(自动重启)
|
||||||
# scripts/svc.sh dev db pg2 --no-restart # 切换但不重启(下次 start 生效)
|
# scripts/svc.sh dev db pg2 --no-restart # 切换但不重启(下次 start 生效)
|
||||||
# scripts/svc.sh dev db --clear # 解绑回内嵌 SQLite 档
|
# scripts/svc.sh dev db --clear # 解绑回内嵌 SQLite 档
|
||||||
|
# scripts/svc.sh dev config .svc/dev.json # 更换配置文件(校验+重启即时生效)
|
||||||
|
# scripts/svc.sh dev config # 查看当前配置文件与生效值
|
||||||
# scripts/svc.sh db copy pg1 pg2 --db app # 把 pg1 的 app 库搬到 pg2
|
# scripts/svc.sh db copy pg1 pg2 --db app # 把 pg1 的 app 库搬到 pg2
|
||||||
# scripts/svc.sh db restore pg2 --file <dump> --db app --force
|
# scripts/svc.sh db restore pg2 --file <dump> --db app --force
|
||||||
# scripts/svc.sh list --tag core && scripts/svc.sh history
|
# scripts/svc.sh list --tag core && scripts/svc.sh history
|
||||||
@@ -130,6 +135,7 @@ svc.sh —— 本机服务管理:后端(agent) / 前端(static) / 数据库(po
|
|||||||
scripts/svc.sh <name> start|stop|restart|logs [N] 生命周期与日志
|
scripts/svc.sh <name> start|stop|restart|logs [N] 生命周期与日志
|
||||||
scripts/svc.sh <name> status 服务详情(版本/来源/DSN 等)
|
scripts/svc.sh <name> status 服务详情(版本/来源/DSN 等)
|
||||||
scripts/svc.sh <name> db [pg名|--clear] agent 数据库绑定/切换(缺省查看)
|
scripts/svc.sh <name> db [pg名|--clear] agent 数据库绑定/切换(缺省查看)
|
||||||
|
scripts/svc.sh <name> config [file|--clear] agent 配置文件更新/即时生效(缺省查看)
|
||||||
scripts/svc.sh db create pg1 --port 8890 建数据库
|
scripts/svc.sh db create pg1 --port 8890 建数据库
|
||||||
scripts/svc.sh db dsn|backup|backups|restore|clone|copy|ls|set pg1 ... 数据库运维
|
scripts/svc.sh db dsn|backup|backups|restore|clone|copy|ls|set pg1 ... 数据库运维
|
||||||
scripts/svc.sh set pg1 --port 8895 --apply 换端口(--tag 改标签)
|
scripts/svc.sh set pg1 --port 8895 --apply 换端口(--tag 改标签)
|
||||||
@@ -264,6 +270,10 @@ EOF
|
|||||||
绑定与可用实例;给 pg名=绑定/切换(DSN 写入 exec=,dbref= 登记,
|
绑定与可用实例;给 pg名=绑定/切换(DSN 写入 exec=,dbref= 登记,
|
||||||
实例运行中缺库自动建,服务运行中自动重启生效);--clear 解绑回内嵌
|
实例运行中缺库自动建,服务运行中自动重启生效);--clear 解绑回内嵌
|
||||||
SQLite 档;--no-restart 只改登记
|
SQLite 档;--no-restart 只改登记
|
||||||
|
scripts/svc.sh <name> config [file|--clear] [--no-restart] agent 配置文件:缺省查看当前
|
||||||
|
文件与生效值;给 file=更换(agent 二进制 --config,JSON schema 校验
|
||||||
|
通过才切换,运行中自动重启即时生效);--clear 移除回命令行参数形态。
|
||||||
|
绑定后改文件内容 + restart 即生效(agent 无热重载,重启秒级)
|
||||||
scripts/svc.sh <name> logs [N] 跟随日志(默认 50 行起;如 svc.sh pg1 logs 200)
|
scripts/svc.sh <name> logs [N] 跟随日志(默认 50 行起;如 svc.sh pg1 logs 200)
|
||||||
scripts/svc.sh <name> status 单服务详情(kind/状态/地址/来源/版本/标签/DSN/目录)
|
scripts/svc.sh <name> status 单服务详情(kind/状态/地址/来源/版本/标签/DSN/目录)
|
||||||
scripts/svc.sh list [--tag T] 全服务表格:NAME KIND STATE PID ADDR SOURCE TAGS
|
scripts/svc.sh list [--tag T] 全服务表格:NAME KIND STATE PID ADDR SOURCE TAGS
|
||||||
@@ -394,6 +404,11 @@ dotenv 格式:KEY=VALUE,支持 # 注释、成对引号、export 前缀;只
|
|||||||
- 裸 start/restart 复用 exec=(上次完整参数);任何显式参数(或 --config 文件、
|
- 裸 start/restart 复用 exec=(上次完整参数);任何显式参数(或 --config 文件、
|
||||||
SVC_* 变量)出现即整体重建参数
|
SVC_* 变量)出现即整体重建参数
|
||||||
- tags 随每次登记合并去重,跨重启保留
|
- tags 随每次登记合并去重,跨重启保留
|
||||||
|
|
||||||
|
服务级配置文件(与上述 svc.sh dotenv 层不同——这是 agent 二进制自己的 JSON 配置):
|
||||||
|
scripts/svc.sh <name> config [file|--clear] 更换/移除/查看(见 help lifecycle);
|
||||||
|
切换前 schema 校验(agent-system config explain -config <file>),运行中自动重启
|
||||||
|
即时生效;登记于 svc.env conffile=,info/status 可见。
|
||||||
EOF
|
EOF
|
||||||
return 0 ;;
|
return 0 ;;
|
||||||
examples)
|
examples)
|
||||||
@@ -644,15 +659,17 @@ svc_merge_tags() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# record_state EXTRA_KEY=VALUE...:把当前态+持久化配置写入 svc.env(600)。
|
# record_state EXTRA_KEY=VALUE...:把当前态+持久化配置写入 svc.env(600)。
|
||||||
# 基本键:kind/env/pid/addr/tags/source/version/started/pidcheck/exec + dbref(跨重启
|
# 基本键:kind/env/pid/addr/tags/source/version/started/pidcheck/exec + dbref/
|
||||||
# 保留的数据库绑定);kind 特有键(user/password/dbname/authmode/pgdata/htdocs...)经参数追加。
|
# conffile(跨重启保留的数据库绑定与配置文件);kind 特有键(user/password/dbname/
|
||||||
|
# authmode/pgdata/htdocs...)经参数追加。
|
||||||
record_state() {
|
record_state() {
|
||||||
# 先读完旧文件再开重定向(块内 {} > svc.env 会先截断文件,读旧值必须在其之前)。
|
# 先读完旧文件再开重定向(块内 {} > svc.env 会先截断文件,读旧值必须在其之前)。
|
||||||
local pid prev_src prev_dbref merged_tags
|
local pid prev_src prev_dbref prev_conffile merged_tags
|
||||||
pid="$(read_pid)"
|
pid="$(read_pid)"
|
||||||
prev_src=""
|
prev_src=""
|
||||||
[ -f "$ENV_DIR/svc.env" ] && prev_src="$(svc_get source)"
|
[ -f "$ENV_DIR/svc.env" ] && prev_src="$(svc_get source)"
|
||||||
prev_dbref="$(svc_get dbref)"
|
prev_dbref="$(svc_get dbref)"
|
||||||
|
prev_conffile="$(svc_get conffile)"
|
||||||
SVC_SOURCE="${SRC_LABEL:-${prev_src:-}}"
|
SVC_SOURCE="${SRC_LABEL:-${prev_src:-}}"
|
||||||
merged_tags="$(svc_merge_tags)"
|
merged_tags="$(svc_merge_tags)"
|
||||||
mkdir -p "$ENV_DIR"
|
mkdir -p "$ENV_DIR"
|
||||||
@@ -668,6 +685,7 @@ record_state() {
|
|||||||
printf 'pidcheck=%s\n' "${SVC_PIDCHECK:-}"
|
printf 'pidcheck=%s\n' "${SVC_PIDCHECK:-}"
|
||||||
printf 'exec=%s\n' "${SVC_EXEC:-}"
|
printf 'exec=%s\n' "${SVC_EXEC:-}"
|
||||||
[ -n "$prev_dbref" ] && printf 'dbref=%s\n' "$prev_dbref"
|
[ -n "$prev_dbref" ] && printf 'dbref=%s\n' "$prev_dbref"
|
||||||
|
[ -n "$prev_conffile" ] && printf 'conffile=%s\n' "$prev_conffile"
|
||||||
[ $# -gt 0 ] && printf '%s\n' "$@"
|
[ $# -gt 0 ] && printf '%s\n' "$@"
|
||||||
} > "$ENV_DIR/svc.env"
|
} > "$ENV_DIR/svc.env"
|
||||||
chmod 600 "$ENV_DIR/svc.env"
|
chmod 600 "$ENV_DIR/svc.env"
|
||||||
@@ -1895,6 +1913,127 @@ db_ls() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# mask_json TEXT:JSON 文本里键名含 key/pass/secret/token/dsn(不区分大小写)的
|
||||||
|
# 字符串值掩为 ***(配置文件内容快照用)。输出带尾换行。
|
||||||
|
mask_json() {
|
||||||
|
printf '%s\n' "$1" | sed -E 's/("[^"]*(key|pass|secret|token|dsn)[^"]*"[[:space:]]*:[[:space:]]*")[^"]*"/\1***"/Ig'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- agent 服务的配置文件(<name> config ...)----
|
||||||
|
|
||||||
|
# svc_config [FILE|--clear] [--no-restart]:查看 / 更新 / 移除 agent 服务使用的
|
||||||
|
# 配置文件(agent 二进制的 --config,JSON,schema 严格校验)。更新 = 校验通过后
|
||||||
|
# 把 --config <绝对路径> 写入持久化 exec= 并登记 conffile=,运行中自动重启生效
|
||||||
|
# (agent 无 SIGHUP 热重载,重启即"及时生效",秒级);--clear 移除回命令行参数形态。
|
||||||
|
svc_config() {
|
||||||
|
[ "$SVC_KIND" = agent ] || { c_err "[$ENV_NAME] kind=$SVC_KIND 无配置文件绑定(仅 agent 服务)"; return 2; }
|
||||||
|
[ -f "$ENV_DIR/svc.env" ] || { c_err "[$ENV_NAME] 无登记(先 deploy)"; return 2; }
|
||||||
|
local target="" clear=0 restart=1
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--clear) clear=1; shift ;;
|
||||||
|
--no-restart) restart=0; shift ;;
|
||||||
|
-h|--help)
|
||||||
|
cat >&2 <<EOF
|
||||||
|
[$ENV_NAME] 配置文件:
|
||||||
|
scripts/svc.sh $ENV_NAME config 查看当前配置文件与生效值
|
||||||
|
scripts/svc.sh $ENV_NAME config <file.json> [--no-restart] 更新使用的配置文件
|
||||||
|
(schema 校验通过才切换;运行中自动重启生效)
|
||||||
|
scripts/svc.sh $ENV_NAME config --clear [--no-restart] 移除(回命令行参数形态)
|
||||||
|
提示:配置文件绑定后,改文件内容 + restart 即生效;config <同一文件> 也会校验并重启。
|
||||||
|
EOF
|
||||||
|
return 0 ;;
|
||||||
|
-*) c_err "[$ENV_NAME] config 未知参数:$1(可用:<文件> | --clear,--no-restart)"; return 2 ;;
|
||||||
|
*) [ -z "$target" ] || { c_err "[$ENV_NAME] config 只接受一个文件路径"; return 2; }
|
||||||
|
target="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# 当前配置文件(conffile= 优先;旧服务回退 exec= 的 --config)。
|
||||||
|
local cur=""
|
||||||
|
cur="$(svc_get conffile)"
|
||||||
|
[ -z "$cur" ] && cur="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||||
|
|
||||||
|
# 无参:查看(文件状态 + 掩码内容 + 二进制视角的生效值)。
|
||||||
|
if [ -z "$target" ] && [ "$clear" = 0 ]; then
|
||||||
|
if [ -n "$cur" ]; then
|
||||||
|
printf ' [%s] 配置文件:%s\n' "$ENV_NAME" "$cur"
|
||||||
|
if [ -f "$cur" ]; then
|
||||||
|
printf ' 权限 %s;内容(敏感值掩码):\n' "$(info_file_mode "$cur")"
|
||||||
|
mask_json "$(cat "$cur")" | sed 's/^/ /'
|
||||||
|
if [ -x "$ENV_BIN" ] && "$ENV_BIN" config explain -config "$cur" >/dev/null 2>&1; then
|
||||||
|
printf ' 生效值(agent 视角,敏感值掩码):\n'
|
||||||
|
"$ENV_BIN" config explain -config "$cur" 2>/dev/null \
|
||||||
|
| sed -E 's#(postgres(ql)?://[^:/@[:space:]]+:)[^@[:space:]]+@#\1***@#g; s/^/ /'
|
||||||
|
else
|
||||||
|
printf ' (当前产物无法校验该文件:config explain 不可用或文件不合法)\n'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf ' 文件不存在!重启将失败——请恢复文件或 config --clear\n'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf ' [%s] 配置文件:无(命令行参数形态;绑定:config <file.json>)\n' "$ENV_NAME"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 移除。
|
||||||
|
if [ "$clear" = 1 ]; then
|
||||||
|
[ -n "$target" ] && { c_err "--clear 不接受文件参数"; return 2; }
|
||||||
|
local exec_old new_exec
|
||||||
|
exec_old="$(svc_get exec)"
|
||||||
|
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||||
|
new_exec="$(printf '%s' "$exec_old" | sed -E 's/(^| )--config [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||||
|
svc_set_key exec "$new_exec"
|
||||||
|
fi
|
||||||
|
svc_del_key conffile
|
||||||
|
hist_event set ok "$(read_pid)" "config unbind"
|
||||||
|
c_ok "[$ENV_NAME] 已移除配置文件(回命令行参数形态)"
|
||||||
|
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||||
|
do_stop; do_start || { c_err "[$ENV_NAME] 重启失败——移除已登记,排查后 start"; return 1; }
|
||||||
|
else
|
||||||
|
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 更新。
|
||||||
|
[ -f "$target" ] || { c_err "配置文件不存在:$target"; return 2; }
|
||||||
|
local abs
|
||||||
|
abs="$(cd "$(dirname "$target")" && pwd)/$(basename "$target")"
|
||||||
|
if [ -x "$ENV_BIN" ]; then
|
||||||
|
if ! "$ENV_BIN" config explain -config "$abs" >/dev/null 2>&1; then
|
||||||
|
c_err "[$ENV_NAME] 配置文件未通过 schema 校验:$abs"
|
||||||
|
"$ENV_BIN" config explain -config "$abs" >&2 || true
|
||||||
|
c_err "(键与取值以 config explain 为准;先修正文件再切换)"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
if ! "$ENV_BIN" serve -h 2>&1 | grep -Eq -- "(^|[[:space:]])-{1,2}config([[:space:]]|=|$)"; then
|
||||||
|
c_warn "[$ENV_NAME] 当前产物不识别 --config(旧版本二进制)——绑定已登记,但重启将忽略该参数"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
local exec_old new_exec
|
||||||
|
exec_old="$(svc_get exec)"
|
||||||
|
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||||
|
new_exec="$(printf '%s' "$exec_old" | sed -E "s|--config [^ ]+|--config $abs|")"
|
||||||
|
else
|
||||||
|
new_exec="${exec_old:+$exec_old }--config $abs"
|
||||||
|
fi
|
||||||
|
svc_set_key exec "$new_exec"
|
||||||
|
svc_set_key conffile "$abs"
|
||||||
|
hist_event set ok "$(read_pid)" "config → $abs"
|
||||||
|
c_ok "[$ENV_NAME] 配置文件已更新:$abs(schema 校验通过;svc.env 600 保存)"
|
||||||
|
if [ "$restart" = 1 ] && [ -n "$(read_pid)" ] && pid_alive "$(read_pid)"; then
|
||||||
|
c_info "[$ENV_NAME] 重启以应用新配置……"
|
||||||
|
do_stop
|
||||||
|
do_start || { c_err "[$ENV_NAME] 重启失败——绑定已登记,排查日志后 scripts/svc.sh $ENV_NAME start"; return 1; }
|
||||||
|
c_ok "[$ENV_NAME] 新配置已生效:$abs"
|
||||||
|
else
|
||||||
|
c_info "[$ENV_NAME] 未运行或 --no-restart——下次 start 生效"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# ---- agent 服务的数据库绑定(<name> db ...)----
|
# ---- agent 服务的数据库绑定(<name> db ...)----
|
||||||
|
|
||||||
# svc_db_bind [pg名] [--db X] [--clear] [--no-restart]:查看 / 绑定切换 / 解除
|
# svc_db_bind [pg名] [--db X] [--clear] [--no-restart]:查看 / 绑定切换 / 解除
|
||||||
@@ -2183,13 +2322,21 @@ registered_names() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# db_ref_of NAME:agent 服务的数据库绑定标识(list/status/info 共用)。
|
# db_ref_of NAME:agent 服务的数据库绑定标识(list/status/info 共用)。
|
||||||
# dbref= 优先;否则从 exec= 的 --dsn 提取 host:port 匹配已登记 postgres 实例
|
# dbref= 优先;否则从 exec= 的 --dsn 或配置文件(conffile=)的 dsn 键提取 host:port
|
||||||
# (兼容直接 --dsn 部署、尚未登记 dbref 的服务);都匹配不上则回 外部@host:port。
|
# 匹配已登记 postgres 实例(兼容直接 --dsn/--config 部署、尚未登记 dbref 的服务);
|
||||||
|
# 都匹配不上则回 外部@host:port。
|
||||||
db_ref_of() {
|
db_ref_of() {
|
||||||
local name="$1" ref dsn hostport pg addr
|
local name="$1" ref dsn hostport pg addr cf
|
||||||
ref="$(env_kv_of "$name" dbref)"
|
ref="$(env_kv_of "$name" dbref)"
|
||||||
if [ -n "$ref" ]; then printf '%s' "$ref"; return 0; fi
|
if [ -n "$ref" ]; then printf '%s' "$ref"; return 0; fi
|
||||||
dsn="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--dsn \([^ ]*\).*/\1/p')"
|
dsn="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--dsn \([^ ]*\).*/\1/p')"
|
||||||
|
if [ -z "$dsn" ]; then
|
||||||
|
cf="$(env_kv_of "$name" conffile)"
|
||||||
|
[ -z "$cf" ] && cf="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||||
|
if [ -n "$cf" ] && [ -f "$cf" ]; then
|
||||||
|
dsn="$(grep -oE '"dsn"[[:space:]]*:[[:space:]]*"[^"]*"' "$cf" 2>/dev/null | head -1 | sed -E 's/.*"([^"]*)"$/\1/')"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
[ -n "$dsn" ] || return 0
|
[ -n "$dsn" ] || return 0
|
||||||
hostport="$(printf '%s' "$dsn" | sed -n 's#.*@\([^/?]*\).*#\1#p')"
|
hostport="$(printf '%s' "$dsn" | sed -n 's#.*@\([^/?]*\).*#\1#p')"
|
||||||
while IFS= read -r pg; do
|
while IFS= read -r pg; do
|
||||||
@@ -2250,8 +2397,12 @@ env_status_detail() {
|
|||||||
[ -n "$tags" ] && printf ' 标签 %s\n' "$tags"
|
[ -n "$tags" ] && printf ' 标签 %s\n' "$tags"
|
||||||
case "$kind" in
|
case "$kind" in
|
||||||
agent)
|
agent)
|
||||||
local dbref; dbref="$(db_ref_of "$ENV_NAME")"
|
local dbref cfile
|
||||||
printf ' 数据库 %s\n' "${dbref:-无(内嵌 SQLite 档;绑定:<name> db <pg名>)}" ;;
|
dbref="$(db_ref_of "$ENV_NAME")"
|
||||||
|
printf ' 数据库 %s\n' "${dbref:-无(内嵌 SQLite 档;绑定:<name> db <pg名>)}"
|
||||||
|
cfile="$(svc_get conffile)"
|
||||||
|
[ -z "$cfile" ] && cfile="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||||
|
printf ' 配置文件 %s\n' "${cfile:-无(命令行参数形态;<name> config <file> 绑定)}" ;;
|
||||||
postgres)
|
postgres)
|
||||||
printf ' 库 %s 用户 %s\n' "$(svc_get dbname)" "$(svc_get user)"
|
printf ' 库 %s 用户 %s\n' "$(svc_get dbname)" "$(svc_get user)"
|
||||||
printf ' DSN postgresql://%s:***@%s:%s/%s(db dsn %s --reveal 看密码)\n' \
|
printf ' DSN postgresql://%s:***@%s:%s/%s(db dsn %s --reveal 看密码)\n' \
|
||||||
@@ -2419,11 +2570,27 @@ info_pg_block() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# info_config_block NAME:单服务 svc.env 内容快照(文件路径+权限+逐行内容)。
|
# info_config_block NAME:单服务 svc.env 内容快照(文件路径+权限+逐行内容),
|
||||||
|
# agent 服务另附其配置文件(conffile)的掩码内容快照。
|
||||||
info_config_block() {
|
info_config_block() {
|
||||||
local name="$1" f="$RUNTIME/$1/svc.env"
|
local name="$1" f="$RUNTIME/$1/svc.env"
|
||||||
printf ' [%s] %s(权限 %s,kind=%s)\n' "$name" "$f" "$(info_file_mode "$f")" "$(svc_kind "$name")"
|
printf ' [%s] %s(权限 %s,kind=%s)\n' "$name" "$f" "$(info_file_mode "$f")" "$(svc_kind "$name")"
|
||||||
info_snapshot_lines "$f"
|
info_snapshot_lines "$f"
|
||||||
|
local cf
|
||||||
|
cf="$(env_kv_of "$name" conffile)"
|
||||||
|
[ -z "$cf" ] && cf="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||||
|
if [ -n "$cf" ]; then
|
||||||
|
if [ -f "$cf" ]; then
|
||||||
|
printf ' ── 配置文件 %s(权限 %s)──\n' "$cf" "$(info_file_mode "$cf")"
|
||||||
|
if [ "$CLI_REVEAL" = 1 ]; then
|
||||||
|
cat "$cf" | sed 's/^/ /'
|
||||||
|
else
|
||||||
|
mask_json "$(cat "$cf")" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf ' ── 配置文件 %s:文件不存在(重启将失败,恢复或 %s config --clear)──\n' "$cf" "$name"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2769,15 +2936,18 @@ main() {
|
|||||||
# 动作词可省略(省略 = deploy);flags 可直接跟随服务名。
|
# 动作词可省略(省略 = deploy);flags 可直接跟随服务名。
|
||||||
local action="deploy"
|
local action="deploy"
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
deploy|stop|start|restart|status|logs|db) action="$1"; shift ;;
|
deploy|stop|start|restart|status|logs|db|config) action="$1"; shift ;;
|
||||||
esac
|
esac
|
||||||
if [ "$action" = logs ] && [ "${1:-}" != "" ] && [[ "$1" =~ ^[0-9]+$ ]]; then
|
if [ "$action" = logs ] && [ "${1:-}" != "" ] && [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||||
CLI_LOGS_N="$1"; shift
|
CLI_LOGS_N="$1"; shift
|
||||||
fi
|
fi
|
||||||
# db 绑定子命令:自带参数解析(位置参数 <pg名> 不走 parse_args;未知 flags 由其自行拒绝)。
|
# db / config 子命令:自带参数解析(位置参数不走 parse_args;未知 flags 由其自行拒绝)。
|
||||||
if [ "$action" = db ]; then
|
if [ "$action" = db ] || [ "$action" = config ]; then
|
||||||
env_config "$ENV_NAME"
|
env_config "$ENV_NAME"
|
||||||
svc_db_bind "$@" || exit $?
|
case "$action" in
|
||||||
|
db) svc_db_bind "$@" || exit $? ;;
|
||||||
|
config) svc_config "$@" || exit $? ;;
|
||||||
|
esac
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user