@sema-agent/server 7.97.0 → 7.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +11 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/USAGE.md +18 -9
- package/deploy/sema-up/cluster-up.sh +8 -4
- package/deploy/sema-up/kube-up.sh +40 -15
- package/deploy/sema-up/lib/envf.sh +241 -0
- package/deploy/sema-up/sema-up.sh +162 -101
- package/deploy/sema-up/smoke.sh +8 -1
- package/dist/boot/config-center.js +169 -24
- package/dist/boot/org-memory.d.ts +2 -1
- package/dist/boot/org-memory.js +9 -5
- package/dist/boot/resolve-spec.js +9 -4
- package/dist/boot/runtime-caps.js +4 -4
- package/dist/brain.js +28 -18
- package/dist/capabilities/center-prompts.d.ts +3 -0
- package/dist/capabilities/center-prompts.js +1 -1
- package/dist/center-credential.d.ts +112 -0
- package/dist/center-credential.js +131 -0
- package/dist/config-catalog.js +7 -6
- package/dist/config-center/apply-effective.js +7 -1
- package/dist/config-center/center-request.d.ts +60 -0
- package/dist/config-center/center-request.js +71 -0
- package/dist/config-center/facade.d.ts +2 -1
- package/dist/config-center/http-client.d.ts +17 -9
- package/dist/config-center/http-client.js +27 -36
- package/dist/config-center/skills-mcp.d.ts +4 -1
- package/dist/config-center/skills-mcp.js +2 -2
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +9 -9
- package/dist/config-lkg.d.ts +47 -8
- package/dist/config-lkg.js +107 -20
- package/dist/config-provider.d.ts +8 -7
- package/dist/config-provider.js +5 -5
- package/dist/config-types.d.ts +20 -6
- package/dist/config.d.ts +9 -0
- package/dist/config.js +53 -18
- package/dist/fleet-client.d.ts +8 -8
- package/dist/fleet-client.js +7 -7
- package/dist/fleet-lease.d.ts +11 -11
- package/dist/fleet-lease.js +17 -13
- package/dist/hooks/hook-llm.d.ts +1 -1
- package/dist/hooks/hook-llm.js +3 -1
- package/dist/hosted-posture.d.ts +49 -93
- package/dist/hosted-posture.js +1 -25
- package/dist/memory-scope.d.ts +62 -11
- package/dist/memory-scope.js +32 -3
- package/dist/model-provider.d.ts +13 -0
- package/dist/model-provider.js +3 -0
- package/dist/model-route-endpoint.d.ts +82 -0
- package/dist/model-route-endpoint.js +55 -0
- package/dist/observability/secret-env-scrub.d.ts +2 -26
- package/dist/observability/secret-env-scrub.js +2 -2
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -2
- package/dist/project-identity.d.ts +29 -0
- package/dist/project-identity.js +120 -0
- package/dist/project-memory.d.ts +28 -1
- package/dist/project-memory.js +22 -22
- package/dist/run-local.d.ts +14 -4
- package/dist/run-local.js +8 -7
- package/dist/runtime-caps-resolver.d.ts +8 -8
- package/dist/runtime-caps-resolver.js +16 -14
- package/dist/sealed-key.js +3 -7
- package/dist/server-secret-env.d.ts +41 -0
- package/dist/server-secret-env.js +18 -0
- package/dist/tool-approval.d.ts +48 -1
- package/dist/tool-approval.js +10 -3
- package/dist/trace/redact.d.ts +1 -1
- package/package.json +4 -3
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
set -euo pipefail
|
|
16
16
|
|
|
17
17
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
18
|
+
# `.env` 文法(写口 envf_representable + envf_encode / 读口 envf_canonical_line)的唯一定义 —— 写读两半从这一处取判据。
|
|
19
|
+
. "$HERE/lib/envf.sh"
|
|
18
20
|
# 执行车道表的唯一读者(S-599)。定义在**最前面**:`--help` 的词表也从表渲(手抄的词表会在加一条
|
|
19
21
|
# 车道时静默分叉),而 `-h` 在参数循环里就被处理,那时后面的函数还没定义。
|
|
20
22
|
lanes() { python3 "$HERE/lib/lanes.py" "$@"; }
|
|
@@ -120,60 +122,27 @@ if [ -n "$CONF" ]; then
|
|
|
120
122
|
fi
|
|
121
123
|
|
|
122
124
|
WORK="$HOME/.sema-up/$PROJECT"; mkdir -p "$WORK"; chmod 700 "$HOME/.sema-up" "$WORK"
|
|
123
|
-
# 凭据/配置文件的坐标
|
|
124
|
-
# (
|
|
125
|
+
# 凭据/配置文件的坐标(S-599 提到这里:车道决策要在模型面之前读既有 .env)。**读写原语全在 lib/envf.sh**
|
|
126
|
+
# (envf_get / envf_has / envf_load / envf_put / setk / setk_new / resetk,S-618):决策键严格读口、写口序列化、
|
|
127
|
+
# 数据版回读都是那一处的同一套文法;本脚本只调用,不再自带任何一份。物理行假设由 up 之前的
|
|
128
|
+
# `reconcile_render`(`docker compose config` 渲染对账,compose 唯一属主)兜底。
|
|
125
129
|
ENVF="$WORK/.env"
|
|
126
|
-
has_env() { [ -f "$ENVF" ] && grep -q "^$1=..*" "$ENVF"; }
|
|
127
|
-
# ── 决策键的 `.env` 严格读口(7.96.0 合并复审)────────────────────────────────────────────────────
|
|
128
|
-
# 决策(租户形 / 车道)读到的每个键 —— `REQUIRE_PRINCIPAL`、`REMOTE_EXEC`、所选车道的必需键 —— 只经这一只读口。
|
|
129
|
-
# 它**不去猜** compose 会怎么读 `.env`:compose 的 dotenv 方言认 `export ` 前缀、键两侧空格、`K: v`、行内注释、
|
|
130
|
-
# 重复键末次生效、`$VAR` 展开……在 bash 里复刻其中任何一部分都是一份手抄方言,漏一处就是「读口看到
|
|
131
|
-
# false、容器拿到 true」(合并复审实证:`=false` 之后再来一行 `export …=true` ⇒ 本脚本落 host,容器拿到 true)。
|
|
132
|
-
# 所以本读口是一条**闭文法**,fail-closed:
|
|
133
|
-
# ① 凡是在任一方言下**可能**给 <键> 赋值的行(行首空白?+ `export `? + <键> + 空白? + `=` 或 `:`)至多一行;
|
|
134
|
-
# ② 那一行必须是规范形 `<键>=<值>`:值 = 空 | 一个不含空白 / 引号 / `#` / `$` / 反斜杠 / 反引号的词 |
|
|
135
|
-
# 一对同型引号包住的这样一个词(sema-up 自己写出的形全在这里面)。
|
|
136
|
-
# 违反任一条 ⇒ 响亮拒并点名行号(**不回显行内容**:必需键里有凭据)。通过 ⇒ 打印值(剥掉那对引号;空 = 未设)。
|
|
137
|
-
# 调用形恒为 `v="$(envf_get K)" || exit 1`(拒因走 stderr,退出码带回调用方)。
|
|
138
|
-
# ⚠️ **已知未闭环(如实披露,不是本读口的承诺范围)**:本读口按**物理行**判,不认引号跨行 —— 另一个键的跨行
|
|
139
|
-
# 单/双引号值里若有一行恰好长成 `K=<值>`,本读口会把它当成 K 的那一行。生成段的 setk / has_env / resetk
|
|
140
|
-
# 同样按物理行判(早于本读口就是这样),两者对这种输入的误读方向一致:容器拿到的是 compose 的缺省值。
|
|
141
|
-
# 统一形(另立一件):生成 .env 之后用 `docker compose config` 渲染一次,拿容器真正拿到的值与决策对账 ——
|
|
142
|
-
# 那一步以 compose 自己为唯一属主,本读口与 setk 的物理行假设可以一起退役。
|
|
143
|
-
ENVF_WORD='[^[:space:]"'"'"'#$`\\]'
|
|
144
|
-
envf_get() {
|
|
145
|
-
[ -f "$ENVF" ] || return 0
|
|
146
|
-
local k="$1" hits n nums line v
|
|
147
|
-
hits="$(grep -nE "^[[:space:]]*(export[[:space:]]+)?${k}[[:space:]]*[=:]" "$ENVF" || true)"
|
|
148
|
-
[ -n "$hits" ] || return 0
|
|
149
|
-
n="$(printf '%s\n' "$hits" | wc -l | tr -d '[:space:]')"
|
|
150
|
-
nums="$(printf '%s\n' "$hits" | cut -d: -f1 | paste -sd, -)"
|
|
151
|
-
if [ "$n" -gt 1 ]; then
|
|
152
|
-
log "⛔ $ENVF 里可能给 $k 赋值的行有 $n 行(第 $nums 行)—— 决策键只许一行"
|
|
153
|
-
log " ↳ 下一步:只留一行规范形 $k=<值>,其余删掉"
|
|
154
|
-
log " ↳ 为什么不挑一行用:compose 读 .env 的规则(export 前缀、键旁空格、重复键末次生效)与本脚本不同,挑哪一行都可能不是容器真正拿到的那一行"
|
|
155
|
-
return 1
|
|
156
|
-
fi
|
|
157
|
-
line="${hits#*:}"
|
|
158
|
-
if ! printf '%s\n' "$line" | grep -qE "^${k}=(${ENVF_WORD}*|\"${ENVF_WORD}*\"|'${ENVF_WORD}*')\$"; then
|
|
159
|
-
log "⛔ $ENVF 第 $nums 行给 $k 赋值,但不是本脚本认得的规范形"
|
|
160
|
-
log " ↳ 下一步:改成 $k=<值>(行首顶格、不带 export、键旁无空格、值是一个词或一对同型引号包住的一个词、行尾无注释),或删掉这一行"
|
|
161
|
-
log " ↳ 为什么不猜:这一行 compose 怎么读(注释、空白、展开)与本脚本怎么读一旦不一致,容器拿到的就不是决策读到的值"
|
|
162
|
-
return 1
|
|
163
|
-
fi
|
|
164
|
-
v="${line#*=}"
|
|
165
|
-
case "$v" in \"*\") v="${v#\"}"; v="${v%\"}" ;; \'*\') v="${v#\'}"; v="${v%\'}" ;; esac
|
|
166
|
-
printf '%s' "$v"
|
|
167
|
-
}
|
|
168
130
|
# ── 部署事实(租户形与车道两个决策共用)────────────────────────────────────────────────
|
|
169
131
|
# 事实 = `.env`(严格读口)∪ shell / `-f` 环境。compose 插值里 shell **优先于** --env-file(本脚本 teardown 段同一
|
|
170
132
|
# 事实),所以两处都非空而不同 ⇒ 拒并点名两处 —— 不「取第一个非空」:放行的唯一形是两处至多一个值,无论谁赢,
|
|
171
133
|
# 赢的都是它。设 FACT_VAL(可空 = 没有事实)与 FACT_SRC(来源描述)。直接调用,不进命令替换(拒要退整个脚本)。
|
|
172
134
|
deploy_fact() {
|
|
173
|
-
local k="$1" fenv fsh shsrc
|
|
135
|
+
local k="$1" fenv fsh shsrc shset
|
|
174
136
|
fenv="$(envf_get "$k")" || exit 1
|
|
175
|
-
|
|
137
|
+
# C(S-618):shell **导出为空串** ≠ shell 未设。compose 插值里 shell(即便是空串)优先于 --env-file,
|
|
138
|
+
# 所以导出空会**盖掉** .env 的值 —— 必须按「有值 = 空」参与冲突判定,不许当缺席。`${k+x}` 区分两者。
|
|
139
|
+
if eval "[ \"\${$k+x}\" = x ]"; then shset=1; eval "fsh=\"\${$k}\""; else shset=0; fsh=""; fi
|
|
176
140
|
shsrc="${CONF:+-f $CONF / }shell 环境"
|
|
141
|
+
if [ "$shset" = 1 ] && [ -z "$fsh" ] && [ -n "$fenv" ]; then
|
|
142
|
+
log "⛔ 部署事实冲突:$ENVF 里 $k=$fenv,但 $shsrc 里把 $k **导出成了空串** —— compose 插值里 shell 优先于 --env-file,空串会盖掉 .env 的值(容器拿到空 = 走 compose 缺省,不是 $fenv)"
|
|
143
|
+
log " ↳ 下一步:unset $k(用 .env 里的 $fenv),或把 $k 显式设成你要的值"
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
177
146
|
if [ -n "$fenv" ] && [ -n "$fsh" ] && [ "$fenv" != "$fsh" ]; then
|
|
178
147
|
log "⛔ 部署事实冲突:$ENVF 里 $k=$fenv,$shsrc 里 $k=$fsh —— 两处不一致"
|
|
179
148
|
log " ↳ 下一步:两处只留一处(或改成同一个值)"
|
|
@@ -200,11 +169,20 @@ log() { printf '[sema-up] %s\n' "$*" >&2; }
|
|
|
200
169
|
|
|
201
170
|
# compose 入口解析:优先 docker compose(插件),缺了退 docker-compose(独立版)——
|
|
202
171
|
# homebrew 裸 docker CLI 不带插件是真场景;都没有=报错带下一步
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
172
|
+
# dc_argv 设 DC_ARGV(选中的 compose 入口);dc 调它 —— 渲染对账给运维的复现命令也从 DC_ARGV 渲,不另写一份。
|
|
173
|
+
dc_argv() {
|
|
174
|
+
if docker compose version >/dev/null 2>&1; then DC_ARGV=(docker compose)
|
|
175
|
+
elif command -v docker-compose >/dev/null 2>&1; then DC_ARGV=(docker-compose)
|
|
206
176
|
else log "⛔ 没有可用的 compose(docker compose 插件/docker-compose 都缺)。下一步:装 Docker Desktop/OrbStack,或 brew install docker-compose"; exit 1; fi
|
|
207
177
|
}
|
|
178
|
+
dc() { dc_argv; "${DC_ARGV[@]}" "$@"; }
|
|
179
|
+
# compose 的全局参数(-p / --env-file / -f)只在这里拼一次(相对 `$HERE/stack`):up / config 走 compose()、teardown 的
|
|
180
|
+
# down、渲染对账给运维的复现命令,都从同一张 COMPOSE_GLOBALS 取(S-646 ④:复现命令曾是手抄的第二份,漏了
|
|
181
|
+
# -f compose.dataroot.yaml 与 profile;teardown 是第三份)。数据根由调用方先定好 DATA_ROOT。
|
|
182
|
+
compose_globals() {
|
|
183
|
+
COMPOSE_GLOBALS=(--project-name "$PROJECT" --env-file "$ENVF" -f compose.yaml)
|
|
184
|
+
if [ -n "$DATA_ROOT" ]; then COMPOSE_GLOBALS+=(-f compose.dataroot.yaml); fi
|
|
185
|
+
}
|
|
208
186
|
|
|
209
187
|
# ── 跨平台探针(Linux 目标机为主;macOS 开发机跑 docker 档也是真场景——npx 分发)────────────
|
|
210
188
|
ram_mb() { if [ -r /proc/meminfo ]; then awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo
|
|
@@ -266,25 +244,27 @@ emit() { [ "$MODE_INTERACTIVE" = "json" ] && printf '%s\n' "$1" >&3 || log "$2";
|
|
|
266
244
|
# ── teardown ─────────────────────────────────────────────────────────────────────────────────
|
|
267
245
|
if $TEARDOWN; then
|
|
268
246
|
cd "$HERE/stack"
|
|
269
|
-
if [ -f "$
|
|
270
|
-
|
|
247
|
+
if [ -f "$ENVF" ]; then
|
|
248
|
+
# 读 .env 一律经 lib 的严格读口(S-618 续):裸 grep|cut 会连编码形的单引号一起读出来。
|
|
249
|
+
# 数据根以 .env 登记的为准(卸载的是首装那一份,不看本次 --data-root);全局参数与 up 同一张表(compose_globals)。
|
|
250
|
+
DATA_ROOT="$(envf_get DATA_ROOT_DIR)" || exit 1
|
|
271
251
|
# 全 profile down 会对未启用服务也做 config 校验——.env 缺 SEMA_*_IMAGE(该面没开过)时
|
|
272
252
|
# 整个 down 失败(真踩:假「已卸载」)。补占位符只为过校验;shell env 优先级高于 --env-file
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
253
|
+
envf_has SEMA_SERVER_IMAGE || export SEMA_SERVER_IMAGE="teardown-placeholder"
|
|
254
|
+
envf_has SEMA_REGISTRY_IMAGE || export SEMA_REGISTRY_IMAGE="teardown-placeholder"
|
|
255
|
+
compose_globals
|
|
256
|
+
if ! dc "${COMPOSE_GLOBALS[@]}" \
|
|
276
257
|
--profile data --profile pg --profile s3 --profile agent --profile registry --profile git \
|
|
277
258
|
down $($TEARDOWN_VOLUMES && echo -v); then
|
|
278
259
|
log "⛔ 卸载未完全成功。下一步:docker ps -a --filter label=com.docker.compose.project=$PROJECT 手查残留"
|
|
279
260
|
exit 1
|
|
280
261
|
fi
|
|
281
262
|
log "已卸载 project=$PROJECT $($TEARDOWN_VOLUMES && echo '(含数据卷)' || echo '(数据卷保留:pgdata/miniodata/registrydata/gitdata)')"
|
|
282
|
-
if $TEARDOWN_VOLUMES && [ -n "$
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
log "⚠ 数据根在大盘 bind 目录 ${DR}——卷对象已删,盘上文件还在;确认不要了再手动 rm -rf"
|
|
263
|
+
if $TEARDOWN_VOLUMES && [ -n "$DATA_ROOT" ]; then
|
|
264
|
+
# ${DATA_ROOT} 必须带花括号:后面紧跟中文破折号,bash 3.2(macOS 系统壳)会把多字节并进变量名(真踩)
|
|
265
|
+
log "⚠ 数据根在大盘 bind 目录 ${DATA_ROOT}——卷对象已删,盘上文件还在;确认不要了再手动 rm -rf"
|
|
286
266
|
fi
|
|
287
|
-
else log "没有找到 $
|
|
267
|
+
else log "没有找到 $ENVF,无可卸载"; fi
|
|
288
268
|
exit 0
|
|
289
269
|
fi
|
|
290
270
|
|
|
@@ -507,7 +487,10 @@ if [ "$REGION" = "auto" ]; then
|
|
|
507
487
|
REGION=$(awk -v a="$t_cn" -v b="$t_gl" 'BEGIN{print (a<b)?"cn":"global"}')
|
|
508
488
|
fi
|
|
509
489
|
HOSTIP=$(host_ip)
|
|
510
|
-
|
|
490
|
+
# E(S-618):进度日志只打**已校验的决策词**,不回显未过校验的原始输入。REGISTRY_ENABLED 到这里还是运维
|
|
491
|
+
# argv 的原样值(真正的校验在 resetk 写口),曾把带标记的 --registry 值抢先泄进 stderr(test srv796 实测 NEW-5)——
|
|
492
|
+
# 收成 on/off 决策词。region/profile/users/lane/git 到这里都已定死(车道块 / git 词校验都在上面)。
|
|
493
|
+
log "region=$REGION profile=$PROFILE users=$USERS sandbox=${SANDBOX_LANE:-n/a} lane=${LANE_REMOTE_EXEC:-n/a} registry=$([ "$REGISTRY_ENABLED" = "true" ] && echo on || echo off) git=$GIT_MODE"
|
|
511
494
|
|
|
512
495
|
# ── 数据盘选择(多盘机自动选大容量盘;--data-root 显式优先;macOS 不自动——bind 要经 VM 挂载,
|
|
513
496
|
# 显式给了才用)。首装落 .env 后不漂移:换盘=先 --teardown-volumes 迁数据再重装。──────────────
|
|
@@ -539,12 +522,12 @@ case "$PROFILE" in full-single|local-plus) COMPOSE_PROFILES="$COMPOSE_PROFILES,a
|
|
|
539
522
|
[ "$GIT_MODE" = "provision" ] && COMPOSE_PROFILES="$COMPOSE_PROFILES,git"
|
|
540
523
|
COMPOSE_PROFILES="${COMPOSE_PROFILES#,}"; COMPOSE_PROFILES="${COMPOSE_PROFILES%,}"
|
|
541
524
|
case ",$COMPOSE_PROFILES," in *,agent,*)
|
|
542
|
-
if [ -z "${SEMA_SERVER_IMAGE:-}" ] && !
|
|
525
|
+
if [ -z "${SEMA_SERVER_IMAGE:-}" ] && ! envf_has SEMA_SERVER_IMAGE; then
|
|
543
526
|
log "⛔ $PROFILE 档需要 sema-server 镜像。下一步:-f 配置里加 SEMA_SERVER_IMAGE=<registry>/sema-server:<tag>(默认=ghcr.io/sema-agent/sema-server:<tag> 公开仓匿名可拉;CN 兜底=内网 Gitea 仓;SWR 已弃用);镜像分发=D4"
|
|
544
527
|
exit 1
|
|
545
528
|
fi ;;
|
|
546
529
|
esac
|
|
547
|
-
if [ "$REGISTRY_ENABLED" = "true" ] && [ -z "${SEMA_REGISTRY_IMAGE:-}" ] && !
|
|
530
|
+
if [ "$REGISTRY_ENABLED" = "true" ] && [ -z "${SEMA_REGISTRY_IMAGE:-}" ] && ! envf_has SEMA_REGISTRY_IMAGE; then
|
|
548
531
|
log "⛔ registry=true(现在是各档默认)需要 sema-registry 镜像。下一步(三选一):"
|
|
549
532
|
log " ① --registry-image=ghcr.io/clayboby/sema-registry:<tag>(GHCR/Hub 公开仓,匿名可拉;SWR 已弃用)"
|
|
550
533
|
log " ② -f 配置里加 SEMA_REGISTRY_IMAGE=<你的仓>/sema-registry:<tag>"
|
|
@@ -592,25 +575,10 @@ PG_TUNE_FLAGS="-c max_connections=100 -c shared_buffers=${SB}MB -c effective_cac
|
|
|
592
575
|
# ── .env 生成(幂等:已存在则只补缺,凭据绝不轮换;0600) ───────────────────────────────────
|
|
593
576
|
genpw() { openssl rand -hex 24; }
|
|
594
577
|
touch "$ENVF"; chmod 600 "$ENVF"
|
|
595
|
-
#
|
|
596
|
-
#
|
|
597
|
-
#
|
|
598
|
-
#
|
|
599
|
-
# ⇒ **写之前**拒(先判后动:resetk 的删旧行也排在判定之后),拒因不回显值(可能是凭据)。
|
|
600
|
-
envf_check() {
|
|
601
|
-
case "$2" in
|
|
602
|
-
*$'\n'*|*$'\r'*)
|
|
603
|
-
log "⛔ 要写进 $ENVF 的 $1 的值里有换行 / 回车 —— .env 一行一条赋值,换行后的内容会被 compose 当成另一条赋值"
|
|
604
|
-
log " ↳ 下一步:去掉值里的换行(查 $1 对应的命令行参数或 -f 配置项)后重跑"
|
|
605
|
-
exit 1 ;;
|
|
606
|
-
esac
|
|
607
|
-
}
|
|
608
|
-
envf_put() { envf_check "$1" "$2"; printf '%s=%s\n' "$1" "$2" >> "$ENVF"; }
|
|
609
|
-
setk() { grep -q "^$1=" "$ENVF" || envf_put "$1" "$2"; }
|
|
610
|
-
# setk_new:同 setk,但新写入时返回 0(summary 用来判断「密码只显示一次」)
|
|
611
|
-
setk_new() { if grep -q "^$1=" "$ENVF"; then return 1; else envf_put "$1" "$2"; return 0; fi; }
|
|
612
|
-
# resetk:非凭据键每次重写(翻转要落 .env,不然 teardown/汇总口径漂)。先判后删:拒了就一行都不动。
|
|
613
|
-
resetk() { envf_check "$1" "$2"; grep -v "^$1=" "$ENVF" > "$ENVF.tmp" || true; mv "$ENVF.tmp" "$ENVF"; chmod 600 "$ENVF"; envf_put "$1" "$2"; }
|
|
578
|
+
# 写口 = lib/envf.sh 的 envf_put(setk / setk_new / resetk 都落到它):值按同一文法序列化 —— 安全裸词原样,
|
|
579
|
+
# 含空白 / 特殊字符 / 反斜杠的走整值单引号编码形(compose 读成字面量、闭引号后不续解析;被 source 也不执行);换行 /
|
|
580
|
+
# 回车 / 单引号 / 以奇数个反斜杠结尾 ⇒ 写之前响亮拒,点名键、不回显值,「下一步」按拒因给。setk 判「键在不在」也走严格读口:旧文件里该键那一行不是
|
|
581
|
+
# 规范形(比如同物理行闭引号带出的二次赋值)⇒ 响亮拒,不再「见行在就跳过」把毒行原样留着(test srv796 RT1)。
|
|
614
582
|
setk SEMA_PROJECT "$PROJECT"
|
|
615
583
|
setk PG_PASSWORD "$(genpw)"
|
|
616
584
|
setk MINIO_PASSWORD "$(genpw)"
|
|
@@ -621,8 +589,9 @@ setk MINIO_CONSOLE_PORT "$MINIO_CONSOLE_PORT"
|
|
|
621
589
|
setk SERVER_PORT "$SERVER_PORT"
|
|
622
590
|
# data-center 数据面对外服务(agent 在别处连);full-single 数据面仅回环、只有 server 对外
|
|
623
591
|
if [ "$PROFILE" = "data-center" ]; then setk PG_BIND "0.0.0.0"; setk MINIO_BIND "0.0.0.0"; else setk PG_BIND "127.0.0.1"; setk MINIO_BIND "127.0.0.1"; fi
|
|
624
|
-
# PG_TUNE_FLAGS 每次重算(规格可能变;非凭据,轮换无害)
|
|
625
|
-
|
|
592
|
+
# PG_TUNE_FLAGS 每次重算(规格可能变;非凭据,轮换无害)。值带空白 ⇒ 写口按文法自动整值单引号
|
|
593
|
+
# (原来手工加双引号=在写口之外自己拼一套引用,正是本批要消灭的第二套文法);compose 读成同一个字面量。
|
|
594
|
+
resetk PG_TUNE_FLAGS "$PG_TUNE_FLAGS"
|
|
626
595
|
if [ "$PROFILE" = "full-single" ] || [ "$PROFILE" = "local-plus" ]; then
|
|
627
596
|
setk MODEL_GATEWAY_BASEURL "$GATEWAY"
|
|
628
597
|
setk MODEL_ID "$MODEL"
|
|
@@ -703,9 +672,10 @@ elif [ "$GIT_MODE" = "connect" ]; then
|
|
|
703
672
|
resetk GIT_TOKEN "$GIT_TOKEN"
|
|
704
673
|
fi
|
|
705
674
|
|
|
706
|
-
# DATA_ROOT 落定(首装写 .env 后为准——重跑换值会造出一套空卷,数据看似丢失)
|
|
707
|
-
|
|
708
|
-
|
|
675
|
+
# DATA_ROOT 落定(首装写 .env 后为准——重跑换值会造出一套空卷,数据看似丢失)。首装值经严格读口解码回读
|
|
676
|
+
# (S-618 续:路径含空格时写口落整值单引号形,旧的裸 grep|cut 会把引号当成路径的一部分)。
|
|
677
|
+
_stored="$(envf_get DATA_ROOT_DIR)" || exit 1
|
|
678
|
+
if [ -n "$_stored" ]; then
|
|
709
679
|
if [ -n "$DATA_ROOT" ] && [ "$DATA_ROOT" != "$_stored" ]; then
|
|
710
680
|
log "⚠ --data-root=$DATA_ROOT 与首装选定的 $_stored 不同——沿用首装值(换盘流程:--teardown-volumes 后迁数据重装)"
|
|
711
681
|
fi
|
|
@@ -717,8 +687,85 @@ if [ -n "$DATA_ROOT" ]; then
|
|
|
717
687
|
for d in pgdata miniodata registrydata gitdata; do mkdir -p "$DATA_ROOT/$d"; done
|
|
718
688
|
fi
|
|
719
689
|
|
|
690
|
+
# A(S-618):`.env` 生成完毕 —— 把 setk 持久化的**生效端口**回读进 shell 变量(替代旧 source 的这半)。
|
|
691
|
+
# 重跑省略端口旗时,容器绑定的是首装端口(.env),本脚本后续的 API 调用 / 健康检查 / 汇总必须用同一份;
|
|
692
|
+
# 首装(或 .env 无该键)时回读跳过、保留 shell 缺省。凭据在各自用点单独回读(见下 envf_load)。
|
|
693
|
+
envf_load SERVER_PORT PG_PORT MINIO_PORT MINIO_CONSOLE_PORT REGISTRY_PORT GIT_HTTP_PORT GIT_SSH_PORT
|
|
694
|
+
|
|
720
695
|
command -v docker >/dev/null || { log "⛔ 未装 docker。下一步:curl -fsSL https://get.docker.com | sh(cn region 用镜像站,如 https://mirrors.aliyun.com/docker-ce)"; exit 1; }
|
|
721
|
-
compose() { ( cd "$HERE/stack" && dc
|
|
696
|
+
compose() { compose_globals; ( cd "$HERE/stack" && dc "${COMPOSE_GLOBALS[@]}" "$@" ); }
|
|
697
|
+
# shq —— 单引号引用一个词(复现命令要能原样粘贴:路径里可能有空格)。
|
|
698
|
+
shq() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
|
|
699
|
+
|
|
700
|
+
# ── D:渲染对账(S-618 的类闭环;生成 .env 之后、compose up 之前)──────────────────────────────────
|
|
701
|
+
# .env 的写口 / 读口都按物理行判,认不出 compose dotenv 的跨行引号值等边角;唯一能确知「容器真正拿到
|
|
702
|
+
# 什么」的是 compose 自己。所以在 up 之前跑一次 `compose config`——与 up **同一组 -f / profile / 同一 shell
|
|
703
|
+
# 环境**(于是运维导出的空串等也算进来)——从渲染结果取 agent 服务的 REQUIRE_PRINCIPAL / REMOTE_EXEC /
|
|
704
|
+
# REQUIRE_EXECUTION_LANE,与本次决策逐值对比,不一致 ⇒ 拒并点名渲染值与决策值。**以 compose 为唯一属主**。
|
|
705
|
+
# docker 不可达在上面 `command -v docker` 那道已 fail-loud 拦下;config 本身失败(YAML/插值坏)也拒,
|
|
706
|
+
# 不新铸降级臂:未经对账就不 up。只有 agent 面有这三个决策键,别的档跳过。
|
|
707
|
+
#
|
|
708
|
+
# 拒因分两种、各给各的出路(S-656;7.98.0 曾把 stderr 并进 stdout 当 JSON 解析 ⇒ compose 的任何警告都让它 traceback,
|
|
709
|
+
# 拒因还冒充「不一致」):
|
|
710
|
+
# · 渲染结果解析失败 —— compose 退出码非零,或退出 0 但 stdout 不是可解析的 compose JSON:没拿到渲染值,谈不上比对;
|
|
711
|
+
# · 渲染值与决策值不一致 —— 拿到了、逐值比对不上。
|
|
712
|
+
# JSON 只从 stdout 取;stderr 只数行数作诊断,**不转印**(compose 解析 `.env` 失败时报文会原样引用那一行,凭据就在行里;
|
|
713
|
+
# 插值警告里的变量名也可能是凭据的一段)。
|
|
714
|
+
RECON_PY='
|
|
715
|
+
import json, sys
|
|
716
|
+
want = {"REQUIRE_PRINCIPAL": sys.argv[1], "REMOTE_EXEC": sys.argv[2], "REQUIRE_EXECUTION_LANE": sys.argv[3]}
|
|
717
|
+
try:
|
|
718
|
+
d = json.loads(sys.stdin.read())
|
|
719
|
+
env = d["services"]["server"].get("environment") or {}
|
|
720
|
+
if not isinstance(env, dict):
|
|
721
|
+
raise TypeError("environment")
|
|
722
|
+
except Exception:
|
|
723
|
+
raise SystemExit(4) # 渲染结果解析失败:不是 compose JSON / 没有 server 服务
|
|
724
|
+
bad = []
|
|
725
|
+
for k, w in want.items():
|
|
726
|
+
got = env.get(k)
|
|
727
|
+
got = "" if got is None else str(got)
|
|
728
|
+
if got != w:
|
|
729
|
+
bad.append("%s:渲染=%r 决策=%r" % (k, got, w))
|
|
730
|
+
if bad:
|
|
731
|
+
sys.stderr.write("[sema-up] ↳ 明细:" + " | ".join(bad) + "\n")
|
|
732
|
+
raise SystemExit(3) # 渲染值与决策值不一致
|
|
733
|
+
'
|
|
734
|
+
reconcile_render() {
|
|
735
|
+
case ",$COMPOSE_PROFILES," in *,agent,*) : ;; *) return 0 ;; esac
|
|
736
|
+
local exp_rp="$REQUIRE_PRINCIPAL_WANT" exp_re="$LANE_REMOTE_EXEC" exp_rl rendered errf nerr rc=0 prc=0 repro a
|
|
737
|
+
exp_rl="$(printf '%s\n' "$LANE_ENV" | sed -n 's/^REQUIRE_EXECUTION_LANE=//p')"
|
|
738
|
+
# 复现命令:与这次 config 同一个 compose 入口、同一张全局参数表、同一组 profile(从源渲,不手抄)。
|
|
739
|
+
dc_argv; compose_globals
|
|
740
|
+
repro="(cd $(shq "$HERE/stack") && COMPOSE_PROFILES=$(shq "$COMPOSE_PROFILES") ${DC_ARGV[*]}"
|
|
741
|
+
for a in "${COMPOSE_GLOBALS[@]}"; do repro="$repro $(shq "$a")"; done
|
|
742
|
+
repro="$repro 'config' '--format' 'json')"
|
|
743
|
+
errf="$(umask 077 && mktemp "$WORK/.recon-stderr.XXXXXX")" || { log "⛔ 渲染对账:在 $WORK 建不了临时文件,无法对账 —— 拒绝在未对账的情况下 up"; exit 1; }
|
|
744
|
+
rendered="$(COMPOSE_PROFILES="$COMPOSE_PROFILES" compose config --format json 2>"$errf")" || rc=$?
|
|
745
|
+
nerr="$(wc -l < "$errf" | tr -d '[:space:]')"; rm -f "$errf"
|
|
746
|
+
if [ "$rc" -ne 0 ]; then
|
|
747
|
+
log "⛔ 渲染对账:渲染结果解析失败 —— docker compose config 失败(退出码 $rc;它在 stderr 输出了 $nerr 行,本脚本不转印),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
|
|
748
|
+
log " ↳ 下一步:在本机亲跑 $repro 看 compose 的报文(本脚本不转印它:.env 解析失败的报文会原样引用那一行,可能带凭据);常见成因是手改过的 .env 行"
|
|
749
|
+
exit 1
|
|
750
|
+
fi
|
|
751
|
+
printf '%s' "$rendered" | python3 -c "$RECON_PY" "$exp_rp" "$exp_re" "$exp_rl" || prc=$?
|
|
752
|
+
case "$prc" in
|
|
753
|
+
0) : ;;
|
|
754
|
+
3)
|
|
755
|
+
log "⛔ 渲染对账:渲染值与决策值不一致 —— compose 渲染出的容器决策值与本次决策对不上;以 compose 为唯一属主,拒绝 up"
|
|
756
|
+
log " ↳ 下一步:检查 $ENVF 里 REQUIRE_PRINCIPAL / REMOTE_EXEC / REQUIRE_EXECUTION_LANE,以及 shell / -f 里是否**导出**了同名变量(compose 插值 shell 优先于 --env-file,导出为空会盖掉 .env);亲跑 $repro 可看容器 environment"
|
|
757
|
+
exit 1 ;;
|
|
758
|
+
4)
|
|
759
|
+
log "⛔ 渲染对账:渲染结果解析失败 —— docker compose config 退出 0,但 stdout 不是可解析的 compose JSON(或其中没有 server 服务;compose 另在 stderr 输出了 $nerr 行,本脚本不转印),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
|
|
760
|
+
log " ↳ 下一步:亲跑 $repro 看 stdout 是不是一份 JSON;不是 ⇒ 这个 compose 入口不支持 --format json 或往 stdout 多打了东西(${DC_ARGV[*]}),换 Docker Compose v2(docker compose 插件)后重跑"
|
|
761
|
+
exit 1 ;;
|
|
762
|
+
*)
|
|
763
|
+
log "⛔ 渲染对账:对账脚本自身失败(python3 退出码 $prc),无法确认容器会拿到什么 —— 拒绝在未对账的情况下 up"
|
|
764
|
+
log " ↳ 下一步:确认本机 python3 可用(python3 -c 'import json'),然后重跑"
|
|
765
|
+
exit 1 ;;
|
|
766
|
+
esac
|
|
767
|
+
log "渲染对账通过:REQUIRE_PRINCIPAL=$exp_rp REMOTE_EXEC=$exp_re REQUIRE_EXECUTION_LANE=$exp_rl(compose 渲染逐值一致)$([ "$nerr" -gt 0 ] && printf ';compose 另在 stderr 输出了 %s 行(多为插值警告;本脚本不转印,up 时 compose 会自己打出来)' "$nerr")"
|
|
768
|
+
}
|
|
722
769
|
|
|
723
770
|
# ── 外接端点连接检测(装配前 fail-loud;只验兼容可用,不校验证书——内网自签是常态)────────────
|
|
724
771
|
if [ -n "$PG_URL" ]; then
|
|
@@ -740,7 +787,8 @@ GIT_MANUAL_NOTES=()
|
|
|
740
787
|
if [ "$GIT_MODE" = "provision" ]; then
|
|
741
788
|
log "git 面:先起 Gitea(引导管理员/token/OAuth 要在 registry 之前)"
|
|
742
789
|
COMPOSE_PROFILES=git compose up -d --wait git || { log "⛔ Gitea 起不来。下一步:docker compose -p $PROJECT logs git;镜像拉不动时 --gitea-image=<可达镜像仓>/gitea:1.24"; exit 1; }
|
|
743
|
-
.
|
|
790
|
+
# A(S-618):不 source .env —— 回读 sema-up 自己生成、没进 shell 变量的凭据(管理员账号/口令)。
|
|
791
|
+
envf_load GIT_ADMIN_USER GIT_ADMIN_PASSWORD
|
|
744
792
|
GIT_CTR="${PROJECT}-git-1"
|
|
745
793
|
GIT_API="http://127.0.0.1:${GIT_HTTP_PORT}/api/v1"
|
|
746
794
|
# 引导管理员(幂等:已存在即跳过;user 1000=镜像内 git 用户)
|
|
@@ -752,7 +800,7 @@ if [ "$GIT_MODE" = "provision" ]; then
|
|
|
752
800
|
log "Gitea 管理员 $GIT_ADMIN_USER 已建"
|
|
753
801
|
fi
|
|
754
802
|
# 管理员 access token(只生成一次;丢了 .env 就换个名再生成,旧 token 在 Gitea 里可手动清)
|
|
755
|
-
if !
|
|
803
|
+
if ! envf_has GIT_ADMIN_TOKEN; then
|
|
756
804
|
TOK_NAME="sema-up-$(openssl rand -hex 3)"
|
|
757
805
|
TOK=$(curl -m10 -s -u "$GIT_ADMIN_USER:$GIT_ADMIN_PASSWORD" -X POST "$GIT_API/users/$GIT_ADMIN_USER/tokens" \
|
|
758
806
|
-H 'Content-Type: application/json' -d "{\"name\":\"$TOK_NAME\",\"scopes\":[\"all\"]}" \
|
|
@@ -762,9 +810,13 @@ except Exception: print("")')
|
|
|
762
810
|
[ -n "$TOK" ] || { log "⛔ Gitea access token 生成失败。下一步:curl -u <admin> $GIT_API/users/$GIT_ADMIN_USER/tokens 手查;或看 docker compose -p $PROJECT logs git"; exit 1; }
|
|
763
811
|
setk GIT_ADMIN_TOKEN "$TOK"
|
|
764
812
|
fi
|
|
765
|
-
.
|
|
813
|
+
envf_load GIT_ADMIN_TOKEN # A:回读刚生成/已存的 token,不 source .env
|
|
814
|
+
# OAuth 接线只对它被创建时的 git 目标有效(锚 = REGISTRY_GITEA_BASE_URL):目标变了 ⇒ 上一目标的接线作废、
|
|
815
|
+
# 本次重建(建不成 = registry 走表单登录),不把旧身份源静默带进这一次(车MH codex r2 [high],两条路径同一条规则)。
|
|
816
|
+
envf_invalidate_unless REGISTRY_GITEA_BASE_URL "http://$HOSTIP:$GIT_HTTP_PORT" REGISTRY_GITEA_OAUTH_ID REGISTRY_GITEA_OAUTH_SECRET
|
|
817
|
+
if [ "$ENVF_INVALIDATED" = 1 ]; then log "git 面:上一次登记的 OAuth 接线属于另一个 git 目标 —— 已作废,本次重建"; fi
|
|
766
818
|
# registry 登录集成:自动建 OAuth app(真自动化,不是纸面;client secret 只在创建响应里给一次)
|
|
767
|
-
if [ "$REGISTRY_ENABLED" = "true" ] && !
|
|
819
|
+
if [ "$REGISTRY_ENABLED" = "true" ] && ! envf_has REGISTRY_GITEA_OAUTH_ID; then
|
|
768
820
|
REDIRECT="http://$HOSTIP:$REGISTRY_PORT/api/auth/oauth/callback"
|
|
769
821
|
OAUTH=$(curl -m10 -s -H "Authorization: token $GIT_ADMIN_TOKEN" -X POST "$GIT_API/user/applications/oauth2" \
|
|
770
822
|
-H 'Content-Type: application/json' \
|
|
@@ -776,27 +828,33 @@ except Exception: print("")')
|
|
|
776
828
|
try: print(json.load(sys.stdin).get("client_secret",""))
|
|
777
829
|
except Exception: print("")')
|
|
778
830
|
if [ -n "$OA_ID" ] && [ -n "$OA_SECRET" ]; then
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
831
|
+
resetk REGISTRY_GITEA_OAUTH_ID "$OA_ID"
|
|
832
|
+
resetk REGISTRY_GITEA_OAUTH_SECRET "$OA_SECRET"
|
|
833
|
+
resetk REGISTRY_GITEA_BASE_URL "http://$HOSTIP:$GIT_HTTP_PORT"
|
|
782
834
|
log "Gitea→registry OAuth 登录已接(app=sema-registry)"
|
|
783
835
|
else
|
|
784
836
|
GIT_MANUAL_NOTES+=("Gitea OAuth app 自动创建失败——registry 仍可表单登录;手动接法:Gitea 设置→应用→新建 OAuth2(回调 $REDIRECT),把 client id/secret 写进 $ENVF 的 REGISTRY_GITEA_OAUTH_ID/SECRET+REGISTRY_GITEA_BASE_URL 后重跑本命令")
|
|
785
837
|
fi
|
|
786
838
|
fi
|
|
787
839
|
elif [ "$GIT_MODE" = "connect" ]; then
|
|
788
|
-
.
|
|
840
|
+
# A(S-618):不 source .env —— GIT_URL / GIT_TOKEN 就是本次命令行/‑f 传进来的 shell 变量,直接用。
|
|
841
|
+
# (旧代码 source 是历史遗留:那时 source 会拿 .env 旧值盖掉命令行新值,故先 resetk 再 source 找回;
|
|
842
|
+
# 不 source 之后新值本就在 shell 里,连这道找回都不需要了。)
|
|
789
843
|
# 连通验证(fail-loud:接不上就别继续,别部署完才发现 git 面是死的)。
|
|
790
844
|
# 任意三方 git 都支持:探测链 Gitea API → GitHub API → 通用 git(ls-remote/HTTP 可达);
|
|
791
845
|
# 证书一律不校验(-k / http.sslVerify=false)——内网自签是常态,验的是兼容可用性。
|
|
792
846
|
GIT_KIND=""
|
|
847
|
+
# OAuth 接线只对它被创建时的 git 目标有效(锚 = REGISTRY_GITEA_BASE_URL):目标变了 ⇒ 上一目标的接线作废、
|
|
848
|
+
# 本次重建(建不成 = registry 走表单登录),不把旧身份源静默带进这一次(车MH codex r2 [high],两条路径同一条规则)。
|
|
849
|
+
envf_invalidate_unless REGISTRY_GITEA_BASE_URL "$GIT_URL" REGISTRY_GITEA_OAUTH_ID REGISTRY_GITEA_OAUTH_SECRET
|
|
850
|
+
if [ "$ENVF_INVALIDATED" = 1 ]; then log "git=connect:上一次登记的 OAuth 接线属于另一个 git 目标 —— 已作废,本次按新目标重建"; fi
|
|
793
851
|
GT_VER=$(curl -k -m10 -s -H "Authorization: token $GIT_TOKEN" "$GIT_URL/api/v1/version" | python3 -c 'import json,sys
|
|
794
852
|
try: print(json.load(sys.stdin).get("version",""))
|
|
795
853
|
except Exception: print("")')
|
|
796
854
|
if [ -n "$GT_VER" ]; then
|
|
797
855
|
GIT_KIND="gitea"
|
|
798
856
|
log "git=connect:Gitea $GT_VER 验证通过"
|
|
799
|
-
if [ "$REGISTRY_ENABLED" = "true" ] && !
|
|
857
|
+
if [ "$REGISTRY_ENABLED" = "true" ] && ! envf_has REGISTRY_GITEA_OAUTH_ID; then
|
|
800
858
|
REDIRECT="http://$HOSTIP:$REGISTRY_PORT/api/auth/oauth/callback"
|
|
801
859
|
OAUTH=$(curl -k -m10 -s -H "Authorization: token $GIT_TOKEN" -X POST "$GIT_URL/api/v1/user/applications/oauth2" \
|
|
802
860
|
-H 'Content-Type: application/json' \
|
|
@@ -808,9 +866,9 @@ except Exception: print("")')
|
|
|
808
866
|
try: print(json.load(sys.stdin).get("client_secret",""))
|
|
809
867
|
except Exception: print("")')
|
|
810
868
|
if [ -n "$OA_ID" ] && [ -n "$OA_SECRET" ]; then
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
869
|
+
resetk REGISTRY_GITEA_OAUTH_ID "$OA_ID"
|
|
870
|
+
resetk REGISTRY_GITEA_OAUTH_SECRET "$OA_SECRET"
|
|
871
|
+
resetk REGISTRY_GITEA_BASE_URL "$GIT_URL"
|
|
814
872
|
log "已有 Gitea→registry OAuth 登录已接(app=sema-registry)"
|
|
815
873
|
else
|
|
816
874
|
GIT_MANUAL_NOTES+=("OAuth app 自动创建失败(token 可能缺 write:user scope)——手动接法:该 Gitea 设置→应用→新建 OAuth2(回调 $REDIRECT),把 client id/secret 写进 $ENVF 的 REGISTRY_GITEA_OAUTH_ID/SECRET+REGISTRY_GITEA_BASE_URL 后重跑本命令")
|
|
@@ -846,17 +904,20 @@ except Exception: print("")')
|
|
|
846
904
|
fi
|
|
847
905
|
|
|
848
906
|
# ── 装配(Layer 1 compose;profiles 映射形态) ────────────────────────────────────────────────
|
|
907
|
+
# D(S-618):up 之前先 `compose config` 渲染对账 —— 容器真正拿到的决策键与本次决策逐值对齐才 up。
|
|
908
|
+
reconcile_render
|
|
849
909
|
log "装配:docker compose profiles=$COMPOSE_PROFILES project=$PROJECT"
|
|
850
910
|
COMPOSE_PROFILES="$COMPOSE_PROFILES" compose up -d --wait --remove-orphans \
|
|
851
911
|
|| { log "⛔ compose up 失败。下一步:docker compose -p $PROJECT logs(常见:镜像拉取失败→检查镜像引用/docker login;端口占用→--port 换口)"; exit 1; }
|
|
852
912
|
# 建桶(幂等,一次性动作归 Layer 0;只对本地 MinIO——外接 S3 已在连接检测时确保)
|
|
853
|
-
.
|
|
913
|
+
envf_load MINIO_PASSWORD # A:回读生成的 MinIO 口令(建桶用),不 source .env
|
|
854
914
|
case ",$COMPOSE_PROFILES," in *,s3,*)
|
|
855
915
|
docker run --rm --network "${PROJECT}_default" -e MC_HOST_x="http://sema:${MINIO_PASSWORD}@minio:9000" "${MC_IMAGE:-minio/mc:latest}" mb --ignore-existing x/workspaces x/blobs x/backups >/dev/null || { log "⛔ MinIO 建桶失败。下一步:docker compose -p $PROJECT logs minio"; exit 1; } ;;
|
|
856
916
|
esac
|
|
857
917
|
|
|
858
918
|
# ── 验证(装后即验,§3d smoke 的子集;全量随时可跑 smoke.sh --project=$PROJECT)──────────────
|
|
859
|
-
.
|
|
919
|
+
# A:回读验证 / 汇总要用的凭据与登记值(不 source .env);缺的键回读成空,由下游 :- 缺省处置。
|
|
920
|
+
envf_load MINIO_PASSWORD REGISTRY_ADMIN_USER REGISTRY_ADMIN_PASSWORD REGISTRY_GITEA_BASE_URL GIT_ADMIN_USER GIT_ADMIN_PASSWORD
|
|
860
921
|
V_PG="n/a" V_BK="n/a"
|
|
861
922
|
case ",$COMPOSE_PROFILES," in *,pg,*)
|
|
862
923
|
V_PG=$(docker exec "${PROJECT}-pg-1" psql -U sema -d sema -tAc "SHOW shared_buffers" 2>/dev/null || echo FAIL) ;;
|
package/deploy/sema-up/smoke.sh
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
# 远程 URL 模式: smoke.sh --url=http://host:8090 --token=T (引擎面:health/auth/真任务)
|
|
6
6
|
# 每项 OK/FAIL 一行;FAIL 带原因+建议(§3d-2 报错规范);exit 0=全绿。
|
|
7
7
|
set -u
|
|
8
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
# `.env` 的文法与读原语只在 lib/envf.sh 定义(S-618 续):本脚本读 `.env` 只经 envf_load,不 source。
|
|
10
|
+
. "$HERE/lib/envf.sh"
|
|
8
11
|
|
|
9
12
|
PROJECT="" URL="" TOKEN=""
|
|
10
13
|
for a in "$@"; do case "$a" in
|
|
@@ -20,7 +23,11 @@ fail() { printf '[FAIL] %s — %s\n ↳ 建议:%s\n' "$1" "$2" "$3"; FAILS=
|
|
|
20
23
|
if [ -n "$PROJECT" ]; then
|
|
21
24
|
ENVF="$HOME/.sema-up/$PROJECT/.env"
|
|
22
25
|
[ -r "$ENVF" ] || { echo "找不到 $ENVF(先跑 sema-up.sh)"; exit 1; }
|
|
23
|
-
.
|
|
26
|
+
# A(S-618 续):不 source .env(值里的 $() / 反引号 / ; 是数据,不是代码)—— 只经严格读口取本脚本用到的键;
|
|
27
|
+
# 读到非规范行(比如旧写口留下的裸 `$(…)`)⇒ 响亮拒并点名行号,不猜、不执行。
|
|
28
|
+
envf_load SERVICE_AUTH_TOKEN DEPLOY_PROFILE SERVER_PORT MINIO_PASSWORD \
|
|
29
|
+
AGENT_PG_HOST AGENT_PG_USER PG_PASSWORD AGENT_PG_PORT AGENT_PG_DATABASE AGENT_MINIO_ENDPOINT AGENT_MINIO_USER \
|
|
30
|
+
REGISTRY_ENABLED REGISTRY_PORT GIT_MODE GIT_HTTP_PORT GIT_ADMIN_TOKEN GIT_ADMIN_USER
|
|
24
31
|
TOKEN="${TOKEN:-${SERVICE_AUTH_TOKEN:-}}"
|
|
25
32
|
# 引擎面只在带 agent 的档默认探(data-center 没有引擎,探 8090 必假红);--url 显式给了照跑
|
|
26
33
|
case "${DEPLOY_PROFILE:-full-single}" in
|