#!/usr/bin/env bash # scripts/ci/deploy.sh — CI 发布:交叉编译 linux/amd64 → 传 ali → 备份 → # 切换 → 重启 pay → 探活(失败自动回滚)。本地 ./deploy.sh 的 CI 版:不用 # ~/.ssh 里的 `ali` 别名,而是从 secret 注入部署私钥、从 secret 取主机。 # # 由 .gitea/workflows/deploy.yml 在打 v* tag 时触发(runs-on: mac,复用 jiu # 的自建 mac runner)。业务密钥不在此处理——运行时由 ali 上 /etc/pay/pay.env # 提供(Bitwarden 灌)。 # # 需要的 secrets(pay 仓 Gitea → Settings → Actions → Secrets): # ALI_SSH_KEY 已授权 ali root 的部署私钥(整段,OpenSSH 或 PEM) # ALI_HOST ali 可达地址 # ALI_USER 部署用户(默认 root) set -euo pipefail # shellcheck source=scripts/ci/_env.sh . "$(dirname "$0")/_env.sh" TAG="${1:-manual}" TARGET_HOST="${DEPLOY_HOST:?ALI_HOST secret 未设}" TARGET_USER="${DEPLOY_USER:-root}" DIR=/opt/pay BIN=dist/payd KEY="${HOME}/.ssh/pay_deploy.key" echo "==> deploy: tag=${TAG} host=${TARGET_HOST}" echo "[1/4] 交叉编译 linux/amd64 …" mkdir -p dist GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o "$BIN" . echo "[2/4] 写部署 key + 上传到 ${TARGET_HOST}:${DIR}/payd-new …" mkdir -p "${HOME}/.ssh" # printf '%s\n' 末尾补换行:Gitea 存 secret 会去掉结尾换行,而 OpenSSH 格式 # 私钥缺结尾换行会被判 "invalid format" 拒绝加载 → 退化成无密钥 Permission denied。 printf '%s\n' "${DEPLOY_SSH_KEY:?ALI_SSH_KEY secret 未设}" > "$KEY" chmod 600 "$KEY" ssh-keyscan -H "$TARGET_HOST" >> "${HOME}/.ssh/known_hosts" 2>/dev/null || true SSH="ssh -i $KEY -o StrictHostKeyChecking=no" SCP="scp -O -i $KEY -o StrictHostKeyChecking=no" cleanup() { rm -f "$KEY"; } trap cleanup EXIT $SCP "$BIN" "${TARGET_USER}@${TARGET_HOST}:${DIR}/payd-new" echo "[3/4] 备份旧版 + 切换 + 重启 …" $SSH "${TARGET_USER}@${TARGET_HOST}" \ "cp ${DIR}/payd ${DIR}/payd.bak && mv ${DIR}/payd-new ${DIR}/payd && chmod +x ${DIR}/payd && systemctl restart pay" echo "[4/4] 探活 …" if $SSH "${TARGET_USER}@${TARGET_HOST}" \ "curl -fsS --retry 8 --retry-delay 1 --retry-connrefused -o /dev/null -w 'health %{http_code}\n' http://127.0.0.1:8080/health"; then echo "✅ 发布完成(上一版已备份在 ${DIR}/payd.bak)" else echo "❌ 探活失败,自动回滚到上一版 …" $SSH "${TARGET_USER}@${TARGET_HOST}" "cp ${DIR}/payd.bak ${DIR}/payd && systemctl restart pay" echo "↩️ 已回滚。请检查本次改动后重试。" exit 1 fi