deploy: 本地预构建发布(push-build),对齐 1Panel 与 backend_v2 目录
- 新增 Dockerfile.prebuilt / scripts/push-build.sh:本机构建、只上传产物,服务器零编译 - deploy.sh 增加 --prebuilt(自动 --no-pull + 产物校验),并保留 --no-cache - compose 支持 BACKEND_DOCKERFILE / WEB_DOCKERFILE 切换;预构建时用 prebuilt 变体 - init-db.sh / scripts/backup-db.sh 改为 docker exec 1Panel 的 Postgres 容器 - WEB_DIR 默认改为 ../frontend_v2;nginx 切换文件改到 .deploy/ 避免弄脏工作区 - 新增 .gitattributes 固定脚本与配置为 LF
This commit is contained in:
30
scripts/backup-db.sh
Normal file
30
scripts/backup-db.sh
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# 备份 1Panel 管理的 Postgres(直接 docker exec 进它的容器,不依赖宿主端口)
|
||||
#
|
||||
# ./scripts/backup-db.sh # 输出 backups/db-YYYY-MM-DD-HHMMSS.sql.gz
|
||||
# KEEP_DAYS=7 ./scripts/backup-db.sh # 顺手删掉 7 天前的备份
|
||||
#
|
||||
# cron(每天 4 点):
|
||||
# 0 4 * * * cd /opt/fashion/backend && KEEP_DAYS=7 ./scripts/backup-db.sh >> .deploy/backup.log 2>&1
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
[ -f .env ] || { echo "!! 缺少 .env"; exit 1; }
|
||||
set -a; . ./.env; set +a
|
||||
|
||||
PG_CONTAINER="${PG_CONTAINER:?PG_CONTAINER 未设置:填 .env 里那个 postgres 容器名}"
|
||||
KEEP_DAYS="${KEEP_DAYS:-0}"
|
||||
mkdir -p backups
|
||||
|
||||
OUT="backups/db-$(date +%F-%H%M%S).sql.gz"
|
||||
echo "==> 备份到 $OUT"
|
||||
docker exec -e PGPASSWORD="${DB_PASSWORD:?DB_PASSWORD 未设置}" "$PG_CONTAINER" \
|
||||
pg_dump -U "${DB_USER:-fashion}" "${DB_NAME:-fashion}" | gzip > "$OUT"
|
||||
|
||||
echo " $(du -h "$OUT" | cut -f1)"
|
||||
|
||||
if [ "$KEEP_DAYS" -gt 0 ]; then
|
||||
find backups -name 'db-*.sql.gz' -mtime +"$KEEP_DAYS" -print -delete
|
||||
fi
|
||||
|
||||
echo "==> 完成"
|
||||
105
scripts/push-build.sh
Normal file
105
scripts/push-build.sh
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# 本地构建 + 上传 + 远程发布(前后端都走预构建,服务器不编译)
|
||||
#
|
||||
# SERVER=user@server ./scripts/push-build.sh
|
||||
# SERVER=user@server REMOTE_DIR=/opt/fashion WEB_DIR=../test ./scripts/push-build.sh
|
||||
#
|
||||
# 流程:
|
||||
# 1. 建 SSH 隧道(本地 18091 → 服务器回环 18091):前端构建期靠它读线上 SSG 数据
|
||||
# 2. 本机 npm run build → 静态页内容与线上数据一致,且不占服务器资源
|
||||
# 3. 本机交叉编译 linux/amd64 → 产出 server-linux-amd64
|
||||
# 4. 上传 dist/ 与二进制
|
||||
# 5. 远程 ./deploy.sh all --prebuilt → 服务器只打镜像 + 蓝绿切换
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.." # 后端仓根
|
||||
|
||||
SERVER="${SERVER:-}"
|
||||
if [ -z "$SERVER" ]; then
|
||||
echo "用法: SERVER=user@host ./scripts/push-build.sh"
|
||||
echo ""
|
||||
echo "可选环境变量:"
|
||||
echo " REMOTE_DIR 服务器上的项目根(默认 /opt/fashion)"
|
||||
echo " WEB_DIR 本地前端仓(默认 ../test)"
|
||||
echo " SSH_OPTS 额外 ssh 参数(默认 -o ConnectTimeout=10)"
|
||||
exit 1
|
||||
fi
|
||||
REMOTE_DIR="${REMOTE_DIR:-/opt/fashion}"
|
||||
REMOTE_BACKEND="${REMOTE_BACKEND:-${REMOTE_DIR}/backend_v2}"
|
||||
REMOTE_FRONTEND="${REMOTE_FRONTEND:-${REMOTE_DIR}/frontend_v2}"
|
||||
WEB_DIR="${WEB_DIR:-../frontend_v2}"
|
||||
SSH_OPTS="${SSH_OPTS:--o ConnectTimeout=10}"
|
||||
TUNNEL_PORT="${TUNNEL_PORT:-18091}"
|
||||
|
||||
# 本机也放一份 .env(复制服务器那份即可),用于取 SSG_TOKEN 等构建期密钥
|
||||
SSG_TOKEN="${SSG_TOKEN:-}"
|
||||
if [ -f .env ]; then
|
||||
# shellcheck disable=SC1091
|
||||
set -a; . ./.env; set +a
|
||||
fi
|
||||
|
||||
TUNNEL_PID=""
|
||||
cleanup() { [ -n "$TUNNEL_PID" ] && kill "$TUNNEL_PID" 2>/dev/null || true; }
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "==> 0/5 检查 SSH 免密登录"
|
||||
# 隧道是后台起的(ssh -N ... &),一旦需要交互输密码就会静默卡住,所以必须免密。
|
||||
if ! ssh $SSH_OPTS -o BatchMode=yes "$SERVER" true 2>/dev/null; then
|
||||
echo "!! 无法免密登录 $SERVER"
|
||||
echo ""
|
||||
echo " Windows(PowerShell):"
|
||||
echo " ssh-keygen -t ed25519 -C deploy@local"
|
||||
echo " type \$env:USERPROFILE\\.ssh\\id_ed25519.pub | ssh $SERVER \"mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys\""
|
||||
echo ""
|
||||
echo " Linux / macOS / WSL:"
|
||||
echo " ssh-keygen -t ed25519 -C deploy@local && ssh-copy-id $SERVER"
|
||||
echo ""
|
||||
echo " 配好后用这条确认:ssh $SERVER true && echo ok"
|
||||
exit 1
|
||||
fi
|
||||
echo " 通过"
|
||||
|
||||
# 服务器若开了 SSG_TOKEN,构建期必须带同一个值。自动从服务器 .env 取,避免本机再维护一份密钥。
|
||||
if [ -z "${SSG_TOKEN:-}" ]; then
|
||||
SSG_TOKEN="$(ssh $SSH_OPTS -o BatchMode=yes "$SERVER" \
|
||||
"grep -m1 '^SSG_TOKEN=' '${REMOTE_BACKEND}/.env' 2>/dev/null | cut -d= -f2-" 2>/dev/null || true)"
|
||||
if [ -n "$SSG_TOKEN" ]; then
|
||||
echo " 已从服务器读取 SSG_TOKEN(${#SSG_TOKEN} 字符)"
|
||||
else
|
||||
echo " 服务器未设置 SSG_TOKEN,构建期不带 token"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "==> 1/5 建立 SSH 隧道(本地 ${TUNNEL_PORT} → ${SERVER}:127.0.0.1:18091)"
|
||||
ssh -N $SSH_OPTS -o ExitOnForwardFailure=yes \
|
||||
-L "${TUNNEL_PORT}:127.0.0.1:18091" "$SERVER" &
|
||||
TUNNEL_PID=$!
|
||||
|
||||
for i in $(seq 1 15); do
|
||||
if (echo > "/dev/tcp/127.0.0.1/${TUNNEL_PORT}") >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
if [ "$i" -eq 15 ]; then
|
||||
echo "!! 隧道 15 秒内没建起来(检查 SSH 连通性、服务器上 proxy 是否在跑)"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "==> 2/5 构建前端(构建期数据来自线上)"
|
||||
(cd "$WEB_DIR" && BASE_API_SSG="http://127.0.0.1:${TUNNEL_PORT}" SSG_TOKEN="$SSG_TOKEN" npm run build)
|
||||
[ -f "$WEB_DIR/dist/server/entry.mjs" ] || { echo "!! 前端产物缺失:$WEB_DIR/dist/server/entry.mjs"; exit 1; }
|
||||
|
||||
echo "==> 3/5 交叉编译后端(linux/amd64)"
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath -ldflags "-s -w" -o server-linux-amd64 ./cmd/server
|
||||
|
||||
echo "==> 4/5 上传产物到 ${SERVER}:${REMOTE_DIR}"
|
||||
ssh $SSH_OPTS "$SERVER" "mkdir -p '${REMOTE_BACKEND}' '${REMOTE_FRONTEND}'"
|
||||
tar -C "$WEB_DIR" -czf - dist | ssh $SSH_OPTS "$SERVER" "tar -xzf - -C '${REMOTE_FRONTEND}'"
|
||||
scp $SSH_OPTS server-linux-amd64 "${SERVER}:${REMOTE_BACKEND}/server-linux-amd64"
|
||||
|
||||
echo "==> 5/5 远程发布(预构建 + 蓝绿切换)"
|
||||
RELEASE="$(date +%Y%m%d-%H%M%S)-$(git rev-parse --short HEAD 2>/dev/null || echo nogit)"
|
||||
ssh $SSH_OPTS "$SERVER" "cd '${REMOTE_BACKEND}' && RELEASE='${RELEASE}' ./deploy.sh all --prebuilt"
|
||||
|
||||
echo "完成:$RELEASE"
|
||||
Reference in New Issue
Block a user