Files
jiu/deploy/render-env.sh
T
wangjia 145bf06ff8 devops: production.env 同步通道修复——render-env 切 ali + 发版漂移告警
2026-07-04 二维码 IP 事故复盘:env 不随发版同步(by design,密钥走
render-env 手动通道),但 render-env 还停在 EC2 时代且割接后没跑过,
线上 env 的 STORAGE_*/CORS 一直是割接临时 IP。三处修复:

- deploy/render-env.sh:目标改 ssh 别名 ali(可 JIU_DEPLOY_SSH 覆盖)、
  SECRET_KEYS 补 PAY_SECRET(旧模板缺此行,直接 deploy 会删掉线上支付
  密钥)、健康检查改 :8081、覆盖前先备份线上 env
- deploy/production.env.template:SERVER_PORT 8080→8081(对齐 ali nginx
  反代口径)、补 PAY_SECRET 占位
- scripts/ci/deploy-server.sh(ali 分支):部署后比对模板与线上 env 的
  非密钥项,漂移只告警不覆盖——手改线上配置不再长期无人发现

线上 env 已手工修正(STORAGE_BASE_URL/STORAGE_PUBLIC_URL/SERVER_CORS_ORIGIN
→ https://jiu.51yanmei.com,退役 LICENSE_* 三行清除,改前有备份)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:20:53 +08:00

117 lines
3.6 KiB
Bash
Executable File
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
# render-env.sh — 从 Bitwarden 渲染 jiu 的 production.env(方案一)
#
# 配置基底 = production.env.template(非密钥项的权威来源,随仓库维护)。
# 密钥 = 从 Bitwarden 条目 DB_PASSWORD 的同名字段按需取,Mac 上不常驻明文。
#
# 用法:
# sh deploy/render-env.sh --check # 只校验:4 个密钥都能从金库取到、长度非空
# sh deploy/render-env.sh --print # 渲染到 stdout 预览(密钥打码,可安全粘贴)
# sh deploy/render-env.sh --out FILE # 渲染到指定文件(含明文,慎用,自负保管)
# sh deploy/render-env.sh --deploy # 渲染→scp 到目标机(默认 ali)→重启 jiu→健康检查→删本地临时文件
#
# 前提:rbw 已 unlock。
set -euo pipefail
# ---- 可配置项 ----
RBW="${RBW_BIN:-/opt/homebrew/bin/rbw}"
BW_ITEM="${JIU_BW_ITEM:-jiu db password}" # 存放 jiu 密钥字段的 Bitwarden 条目
SECRET_KEYS="DATABASE_DSN JWT_SECRET DB_PASSWORD PAY_SECRET"
# 目标机:~/.ssh/config 别名(2026-07-02 割接后主站 = aliEC2 已退役)
TARGET="${JIU_DEPLOY_SSH:-ali}"
REMOTE_ENV="/opt/jiu/config/production.env"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEMPLATE="$SCRIPT_DIR/production.env.template"
# ---- 工具函数 ----
is_secret() {
case " $SECRET_KEYS " in *" $1 "*) return 0;; *) return 1;; esac
}
require_unlocked() {
if ! "$RBW" unlocked >/dev/null 2>&1; then
echo "render-env: 金库未解锁,请先运行: rbw unlock" >&2
exit 1
fi
}
# 渲染:逐行读模板,密钥行用金库值替换。$1=mask 时密钥打码。
render() {
local mask="${1:-}"
local line key val
while IFS= read -r line || [ -n "$line" ]; do
case "$line" in
''|\#*) printf '%s\n' "$line"; continue ;;
esac
key="${line%%=*}"
if is_secret "$key"; then
val="$("$RBW" get --field "$key" "$BW_ITEM")"
if [ -z "$val" ]; then
echo "render-env: 金库条目 '$BW_ITEM' 的字段 '$key' 为空" >&2
exit 1
fi
if [ "$mask" = "mask" ]; then
printf '%s=<%d 字符·已打码>\n' "$key" "${#val}"
else
printf '%s=%s\n' "$key" "$val"
fi
else
printf '%s\n' "$line"
fi
done < "$TEMPLATE"
}
# ---- 主流程 ----
[ -f "$TEMPLATE" ] || { echo "render-env: 找不到模板 $TEMPLATE" >&2; exit 1; }
MODE="${1:---print}"
require_unlocked
case "$MODE" in
--check)
for k in $SECRET_KEYS; do
v="$("$RBW" get --field "$k" "$BW_ITEM")"
printf ' %-22s %s\n' "$k" "$([ -n "$v" ] && echo "OK (${#v} 字符)" || echo "缺失 ❌")"
done
;;
--print)
render mask
;;
--out)
OUT="${2:?用法: render-env.sh --out <文件路径>}"
umask 077
render > "$OUT"
echo "render-env: 已写入 $OUT(含明文,注意保管)"
;;
--deploy)
TMP="$(mktemp -t jiu-prod-env)"
trap 'rm -f "$TMP"' EXIT
umask 077
render > "$TMP"
echo "render-env: 已本地渲染(临时文件,退出即删)"
echo "render-env: 上传到 $TARGET:$REMOTE_ENV"
scp "$TMP" "$TARGET:/tmp/production.env.new"
ssh "$TARGET" bash <<REMOTE
set -e
mkdir -p /opt/jiu/config
cp "$REMOTE_ENV" "$REMOTE_ENV.bak-\$(date +%Y%m%d%H%M%S)" 2>/dev/null || true
mv /tmp/production.env.new "$REMOTE_ENV"
chmod 600 "$REMOTE_ENV"
systemctl restart jiu
sleep 3
curl -sf http://localhost:8081/health >/dev/null && echo " 健康检查通过 ✅" || { echo " 健康检查失败 ❌"; exit 1; }
REMOTE
echo "render-env: 部署完成,$TARGET 上的 production.env 已更新并重启"
;;
*)
echo "未知参数: $MODE" >&2
sed -n '2,16p' "$0" >&2
exit 1
;;
esac