Files
knowledge-governance/scripts/deploy-api-web.sh
T
姚章浩 4d43a9386a
build / build (push) Successful in 1m5s
fix(version-history): tag 之前的提交不再被编号 + 部署脚本注入构建身份
真实数据发现(本机 p1 实例第一次同步后核对):
- **缺陷**:tag `v0.1.0` 之前的 20 个提交,提交内 CHANGELOG 顶部**也已经**是 0.1.0(旧分支遗留条目),
  而它们的祖先集合被那个**更晚才打**的 tag 全覆盖 ⇒ 按提交数算得 0 ⇒ 被错标成「0.1.0 开发版」。
  它们的正确判定是「历史提交」:那个 tag 是后来才打的,当时还没有发布基线。
- **修复**:基线 tag 必须是该提交的**祖先(或就是该提交)**,否则不给号;新增原因码
  `release_tag_created_later`(页面文案:'该版本是在更晚的提交上发布的 —— 这个提交当时还没有 tag')。
  回归用例两条:`TestDeriveVersionsDoesNotNumberCommitsThatPredateTheTag`、
  `TestDeriveVersionsSideBranchWithoutTagStaysHistorical`(不含 tag 的侧分支同样不给号)。
- **部署脚本注入构建身份**:`scripts/deploy-api-web.sh` 原先不传 `-ldflags -X`,产物 `CommitSHA=unknown`
  ⇒ 页面只能显示"当前运行版本未知"(契约上诚实,但没用)。现在按 `scripts/version.py --identity`
  (唯一来源,与 Makefile/CI 同规则)注入 VERSION/RELEASE_VERSION/BUILD_KIND/COMMITS_SINCE_RELEASE/
  COMMIT_SHA/BUILD_TIME;取不到身份**拒绝继续**(宁可不上线,也不上线一个认不出自己的产物),
  并在收尾核对注入的 SHA 真的进了产物。
- compose.yaml:把 VERSION_HISTORY_* 五项接进应用环境(api/worker 共享锚点),留空即未接线。

验证:真实仓库复验(114 条对账一致)⇒ 正式 1 / 开发 4 / 历史 109(原来错的 19 条现在都是
`release_tag_created_later`);p1 实例重新同步后当前快照分布一致,带号的记录只剩 1 正式 + 4 开发
(含真实存在的「同号不同 SHA」:950c1b4 与 3a1d32d 都是 0.1.1,各占一行);
make verify 15/15 success;前端 18 用例 + typecheck 通过。
2026-10-14 10:00:27 +08:00

83 lines
5.4 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 重建并上线 governance api + web(本机 docker build 需要宿主代理 + --network host)
set -u
cd /home/zheromiku/knowledge-governance || exit 1
TS=$(date +%Y%m%d-%H%M%S)
PROXY_ARGS=(--build-arg HTTP_PROXY=http://127.0.0.1:7890 --build-arg HTTPS_PROXY=http://127.0.0.1:7890 --build-arg NO_PROXY=localhost,127.0.0.1 --build-arg GOPROXY=https://goproxy.cn,direct)
echo "=== 1. 打回滚标签 ($TS)"
docker tag knowledge-governance-p1-api:latest "knowledge-governance-p1-api:rollback-$TS" || exit 1
docker tag knowledge-governance-p1-web:latest "knowledge-governance-p1-web:rollback-$TS" || exit 1
echo "=== 1b. 构建身份(构建期注入:页面「当前运行」只认产物里的完整 SHA 精确匹配)"
# 唯一来源是 scripts/version.py --identity(与 Makefile、CI 同一份规则):不注入 ⇒ 页面只能显示"未知"。
IDENTITY_ARGS=()
while IFS= read -r line; do IDENTITY_ARGS+=("$line"); done < <(python3 scripts/version.py --identity 2>/dev/null | python3 -c '
import json, sys
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
release = d.get("release_version") or ""
count = d.get("commits_since_release")
print("--build-arg=VERSION=" + (release or "dev"))
print("--build-arg=RELEASE_VERSION=" + release)
print("--build-arg=BUILD_KIND=" + (d.get("build_kind") or "development"))
print("--build-arg=COMMITS_SINCE_RELEASE=" + ("" if count is None else str(count)))
print("--build-arg=COMMIT_SHA=" + (d.get("commit_sha") or "unknown"))
print("--build-arg=BUILD_TIME=" + (d.get("built_at") or "unknown"))
')
if [ "${#IDENTITY_ARGS[@]}" -lt 6 ]; then
echo "IDENTITY_UNAVAILABLE"
echo " 取不到构建身份(scripts/version.py --identity 失败)⇒ 拒绝继续:"
echo " 否则构建出来的产物无法被「版本记录」页识别成「当前运行」,只会显示未知。"
exit 1
fi
printf ' %s\n' "${IDENTITY_ARGS[@]}"
echo "=== 2. 构建 api"
docker build --network host "${PROXY_ARGS[@]}" "${IDENTITY_ARGS[@]}" -f Dockerfile -t knowledge-governance-p1-api:latest . || { echo "API_BUILD_FAILED"; exit 1; }
echo "=== 3. 构建 web"
docker build --network host "${PROXY_ARGS[@]}" -f Dockerfile.web -t knowledge-governance-p1-web:latest . || { echo "WEB_BUILD_FAILED"; exit 1; }
echo "=== 4. 上线 api + web(不动依赖)"
# COMPOSE_FILES 让调用方叠加发布接线 / 门禁 override,默认只有基础 compose.yaml。
# 例:COMPOSE_FILES="-f compose.yaml -f compose.release-sim.yaml" bash scripts/deploy-api-web.sh
#
# 注意(2026-10-10 实测踩到):重建 api 时若不叠加 compose.release-gates.yaml,两道生产门禁会被
# **静默关掉**(PRODUCTION_RELEASE_REQUESTS_ENABLED/BACKUP_RESTORE_VERIFIED=false),页面只在点击
# 「授权发布」时回一句 "production release requests are disabled" —— 很容易误判成功能坏了。
# 因此下面做前置保护:正在运行的 api 若已开闸、而本次没叠加门禁文件,直接拒绝(确要关闸用 ALLOW_GATE_OFF=1)。
COMPOSE_FILES_EFFECTIVE="${COMPOSE_FILES:--f compose.yaml}"
GATES_PREV=$(docker exec knowledge-governance-p1-api-1 sh -lc 'printf %s "${PRODUCTION_RELEASE_REQUESTS_ENABLED:-}"' 2>/dev/null || true)
if [ "${GATES_PREV}" = "true" ] && [[ "${COMPOSE_FILES_EFFECTIVE}" != *compose.release-gates.yaml* ]] && [ "${ALLOW_GATE_OFF:-0}" != "1" ]; then
echo "REFUSED_WOULD_CLOSE_PRODUCTION_GATES"
echo " 当前 api 已开闸(PRODUCTION_RELEASE_REQUESTS_ENABLED=true),而本次 COMPOSE_FILES 不含 compose.release-gates.yaml,"
echo " 继续会用基础 compose 重建 api → 生产授权接口被静默关闭。"
echo " 保留门禁:COMPOSE_FILES=\"-f compose.yaml -f compose.release-sim.yaml -f compose.release-gates.yaml\" bash scripts/deploy-api-web.sh"
echo " 确实要关闸:ALLOW_GATE_OFF=1 bash scripts/deploy-api-web.sh"
exit 1
fi
docker compose ${COMPOSE_FILES_EFFECTIVE} up -d --no-build --no-deps api web || { echo "UP_FAILED"; exit 1; }
echo "=== 5. 状态"
docker ps --filter name=knowledge-governance-p1 --format '{{.Names}} {{.Status}} {{.Image}}'
echo "api image=$(docker inspect -f '{{.Image}}' knowledge-governance-p1-api-1)"
echo "web image=$(docker inspect -f '{{.Image}}' knowledge-governance-p1-web-1)"
echo "=== 6. 健康"
echo "health=$(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8088/health)"
echo "=== 7. 生产端点环境变量(只报名字与非空)"
docker exec knowledge-governance-p1-api-1 sh -lc 'for v in PRODUCTION_WEKNORA_BASE_URL PRODUCTION_WEKNORA_API_KEY_FILE PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP; do eval "val=\$$v"; if [ -n "$val" ]; then echo "$v=<set>"; else echo "$v=<empty>"; fi; done'
echo "=== 7b. 生产门禁状态(重建后必须与预期一致)"
docker exec knowledge-governance-p1-api-1 sh -lc 'for v in PRODUCTION_RELEASE_REQUESTS_ENABLED PRODUCTION_BACKUP_RESTORE_VERIFIED; do eval "val=\$$v"; echo "$v=${val:-<empty>}"; done'
echo "=== 8. 新包"
docker exec knowledge-governance-p1-web-1 sh -lc 'grep -o "index-[A-Za-z0-9_-]*\.js" /usr/share/nginx/html/index.html | head -1'
echo "=== 9. 构建身份是否真的进了产物(页面据此判「当前运行」)"
INJECTED=$(printf '%s\n' "${IDENTITY_ARGS[@]}" | sed -n 's/^--build-arg=COMMIT_SHA=//p')
if docker exec knowledge-governance-p1-api-1 sh -lc "strings /app/api | grep -q '$INJECTED'"; then
echo "INJECTED_COMMIT_SHA=$INJECTED(已进 api 产物)"
else
echo "INJECTION_MISSING(产物里没有 $INJECTED —— 页面会显示当前运行未知)"
fi
echo "DONE"