Files
guzhujushiBlog/deploy/update_server.sh
T

128 lines
5.0 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.
#!/bin/bash
# ============================================================
# MyBlog 服务器端更新脚本(由本地 deploy/update.ps1 上传并执行)
# 职责:备份 -> 解压新代码 -> 数据库迁移 -> 重启服务 -> 验证
# 用法:bash /tmp/update_server.sh [域名]
# 适用:Ubuntu + systemdmyblog 服务);需要 root 或已配置免密 sudo
# 安全说明:
# - 更新前自动备份到 /root/blog-backups(含数据库与上传文件),可随时回滚
# - 只增量覆盖代码文件,不删除服务器上的任何文件(不会加 --delete)
# - 数据库迁移是幂等的:重复执行只会补齐缺失的列,不会损坏数据
# ============================================================
set -eu
# bash 支持 pipefaildash/sh 下自动跳过),保证失败即退出
if [ -n "${BASH_VERSION:-}" ]; then set -o pipefail; fi
BLOG_DIR="/var/www/blog"
PKG="/tmp/myblog.tar.gz"
BACKUP_DIR="/root/blog-backups"
DOMAIN="${1:-}"
# 非 root 用户运行时自动加 sudo(前提:已配置免密 sudo)
SUDO=""
if [ "$(id -u)" -ne 0 ]; then
SUDO="sudo"
fi
echo "== [1/5] 更新前备份 =="
$SUDO mkdir -p "$BACKUP_DIR"
TS=$(date +%Y%m%d_%H%M%S)
$SUDO tar -czf "$BACKUP_DIR/blog_backup_$TS.tar.gz" --exclude=venv --exclude='*/__pycache__' -C "$BLOG_DIR" .
echo "备份完成:$BACKUP_DIR/blog_backup_$TS.tar.gz"
echo "== [2/5] 解压新代码 =="
if [ ! -f "$PKG" ]; then
echo "错误:找不到更新包 $PKG,请先通过本地脚本上传" >&2
exit 1
fi
$SUDO tar -xzf "$PKG" -C "$BLOG_DIR"
$SUDO chown -R blog:blog "$BLOG_DIR"
# 前端缓存指纹:每次部署自动生成新版本号,浏览器强制拉取最新 JS/CSS,无需手动 Ctrl+F5
VERSION=$(date +%Y%m%d%H%M%S)
$SUDO sed -i "s/__VERSION__/${VERSION}/g" "$BLOG_DIR/frontend/index.html"
echo "前端版本号:${VERSION}"
# 把 Windows 上传的脚本统一转成 LF 行尾,防止 bash 解析 CR 报错
find "$BLOG_DIR/deploy" -maxdepth 2 -name '*.sh' -type f -exec sed -i 's/\r$//' {} +
echo "解压完成,文件属主已修正为 blog:blog"
echo "== [3/5] 数据库迁移(幂等) =="
python3 - "$BLOG_DIR" <<'PY'
import sqlite3, sys
from pathlib import Path
base = Path(sys.argv[1])
db = base / "backend" / "blog.db"
# 读取 .env 的 DATABASE_URL(与后端 database.py 的解析方式一致,留空用默认)
try:
for line in (base / ".env").read_text(encoding="utf-8").splitlines():
line = line.strip()
if line.startswith("DATABASE_URL="):
val = line.split("=", 1)[1].strip().strip('"').strip("'")
if val.startswith("sqlite:///"):
p = val[len("sqlite:///"):]
db = Path(p) if p.startswith("/") else base / p
break
except OSError:
pass
print("目标数据库:", db)
con = sqlite3.connect(db)
cur = con.cursor()
def ensure_table(table, ddl):
rows = cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table,)).fetchall()
if not rows:
cur.execute(ddl)
print(f"建表:{table}")
else:
print(f"已存在:{table}")
def ensure_column(table, col, ddl):
cols = [r[1] for r in cur.execute(f"PRAGMA table_info({table})").fetchall()]
if col not in cols:
cur.execute(f"ALTER TABLE {table} ADD COLUMN {ddl}")
print(f"迁移:{table}.{col} 已新增")
else:
print(f"已存在:{table}.{col}")
ensure_column("articles", "category", "category VARCHAR(30) NOT NULL DEFAULT 'life'")
ensure_column("users", "token_version", "token_version INTEGER NOT NULL DEFAULT 0")
ensure_column("users", "avatar_updated_time", "avatar_updated_time DATETIME")
ensure_column("friends", "created_time", "created_time DATETIME")
ensure_table("projects", """
CREATE TABLE IF NOT EXISTS projects (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
tech VARCHAR(500),
project_type VARCHAR(20) NOT NULL DEFAULT 'static',
demo_url VARCHAR(500),
download_url VARCHAR(500),
github_url VARCHAR(500),
created_time DATETIME NOT NULL,
updated_time DATETIME NOT NULL
)""")
ensure_column("projects", "visibility", "visibility VARCHAR(20) NOT NULL DEFAULT 'public'")
con.commit()
con.close()
print("数据库迁移完成")
PY
echo "== [4/5] 重启后端服务 =="
$SUDO systemctl restart myblog
sleep 3
if ! $SUDO systemctl is-active --quiet myblog; then
echo "错误:myblog 服务未正常运行,请查看日志:journalctl -u myblog -n 50" >&2
exit 1
fi
echo "myblog 服务运行正常"
echo "== [5/5] 验证 =="
echo "--- 后端接口(应返回 success:true ---"
curl -s http://127.0.0.1:8080/api/article/list | head -c 200
echo
echo "--- 新前端是否生效(发布管理弹窗标记 pub-confirm 数量) ---"
grep -c "pub-confirm" "$BLOG_DIR/frontend/manage.js" || true
if [ -n "$DOMAIN" ]; then
echo "--- 线上 HTTPS$DOMAIN ---"
curl -sk -o /dev/null -w "%{http_code}\n" "https://$DOMAIN/" || echo "HTTPS 检查失败(可能是证书或网络问题)"
fi
echo "更新完成。如需回滚:备份在 $BACKUP_DIR/"