#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LOG_FILE="$SCRIPT_DIR/deploy.log"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[INFO]${NC} $(date '+%H:%M:%S') $*" | tee -a "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $(date '+%H:%M:%S') $*" | tee -a "$LOG_FILE"; }
err() { echo -e "${RED}[ERROR]${NC} $(date '+%H:%M:%S') $*" | tee -a "$LOG_FILE"; }
MODE="build"
NGINX_PORT=80
INSTALL_NODE=false
SERVE_PORT=38090
while [[ $# -gt 0 ]]; do
case "$1" in
--nginx) MODE="nginx"; shift ;;
--port) NGINX_PORT="$2"; shift 2 ;;
--preview) MODE="preview"; shift ;;
--serve) MODE="serve"; SERVE_PORT="$2"; shift 2 ;;
--help|-h)
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " (无参数) 构建静态站点到docs/.vitepress/dist"
echo " --nginx 构建并配置Nginx,注册为systemd开机自启服务"
echo " --port <端口> Nginx监听端口 (默认 80)"
echo " --preview 构建并以预览模式启动 (开发用)"
echo " --serve <端口> 构建并在指定端口提供静态服务 (默认 38090)"
echo " --help, -h 显示帮助"
exit 0
;;
*) err "未知参数: $1"; exit 1 ;;
esac
done
> "$LOG_FILE"
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
elif [ -f /etc/redhat-release ]; then
OS="centos"
OS_VERSION=$(rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release) 2>/dev/null || echo "7")
else
OS="unknown"
OS_VERSION="unknown"
fi
log "检测到系统: $OS $OS_VERSION"
}
install_nodejs() {
if command -v node &>/dev/null; then
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_VER" -ge 18 ]; then
log "Node.js $(node -v)已满足要求 (>=18)"
return 0
fi
warn "当前Node.js $(node -v)版本过低,需要 >=18,正在升级..."
fi
log "安装Node.js 18 LTS..."
case "$OS" in
ubuntu|debian)
log "使用NodeSource安装Node.js 18..."
if ! command -v curl &>/dev/null; then
apt-get update -y && apt-get install -y curl gnupg
fi
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt-get install -y nodejs
;;
centos|rhel|fedora|tencentos|anolis|openEuler)
log "使用NodeSource安装Node.js 18..."
if ! command -v curl &>/dev/null; then
yum install -y curl
fi
curl -fsSL https://rpm.nodesource.com/setup_18.x | bash -
yum install -y nodejs
;;
*)
err "不支持的系统: $OS,请手动安装Node.js >=18"
exit 1
;;
esac
log "Node.js安装完成: $(node -v)"
log "npm版本: $(npm -v)"
}
install_deps() {
cd "$SCRIPT_DIR"
if [ ! -f "package.json" ]; then
err "未找到package.json,请在vitepress目录下执行本脚本"
exit 1
fi
if [ ! -d "node_modules" ]; then
log "安装项目依赖..."
if command -v pnpm &>/dev/null; then
pnpm install 2>&1 | tee -a "$LOG_FILE"
else
log "pnpm未安装,使用npm..."
npm install --legacy-peer-deps 2>&1 | tee -a "$LOG_FILE"
fi
log "依赖安装完成"
else
log "依赖已存在,跳过安装"
fi
}
install_python_deps() {
log "检查Python依赖 (cmarkgfm, pygments)..."
local missing=0
if ! python3 -c "import cmarkgfm" &>/dev/null; then missing=1; fi
if ! python3 -c "import pygments" &>/dev/null; then missing=1; fi
if [ "$missing" -eq 0 ]; then
log "Python依赖已满足"
return 0
fi
log "安装Python依赖..."
cd "$SCRIPT_DIR"
local pip_cmd=""
if command -v pip3 &>/dev/null; then
pip_cmd="pip3"
elif command -v pip &>/dev/null; then
pip_cmd="pip"
else
err "未找到pip,请手动安装: pip install -r requirements.txt"
exit 1
fi
$pip_cmd install -r requirements.txt --break-system-packages -i https://mirrors.huaweicloud.com/repository/pypi/simple 2>&1 | tee -a "$LOG_FILE"
log "Python依赖安装完成"
}
build_docs() {
cd "$SCRIPT_DIR"
log "开始构建VitePress站点..."
npm run docs:prebuild 2>&1 | tee -a "$LOG_FILE"
npx cross-env NODE_OPTIONS=--max-old-space-size=14336 vitepress build docs 2>&1 | tee -a "$LOG_FILE"
DIST_DIR="$SCRIPT_DIR/docs/.vitepress/dist"
if [ ! -d "$DIST_DIR" ]; then
err "构建失败: 未生成 $DIST_DIR"
exit 1
fi
log "构建成功! 产物目录: $DIST_DIR"
}
deploy_nginx() {
DIST_DIR="$SCRIPT_DIR/docs/.vitepress/dist"
NGINX_RELEASES_ROOT="/usr/share/nginx/html/ascendc-docs-releases"
NGINX_CURRENT="/usr/share/nginx/html/ascendc-docs-current"
NGINX_PREVIOUS="/usr/share/nginx/html/ascendc-docs-previous"
NGINX_CONF="/etc/nginx/conf.d/ascendc-docs.conf"
log "=== Nginx部署模式 ==="
if ! command -v nginx &>/dev/null; then
log "安装Nginx..."
case "$OS" in
ubuntu|debian) apt-get update -y && apt-get install -y nginx ;;
centos|rhel|fedora|tencentos|anolis|openEuler)
yum install -y epel-release 2>/dev/null || true
yum install -y nginx
;;
*) err "请手动安装Nginx"; exit 1 ;;
esac
fi
RELEASE_ID="$(date '+%Y%m%d%H%M%S')-$$"
RELEASE_DIR="$NGINX_RELEASES_ROOT/$RELEASE_ID"
NEXT_LINK="${NGINX_CURRENT}.next"
PREVIOUS_NEXT_LINK="${NGINX_PREVIOUS}.next"
log "拷贝构建产物到 $RELEASE_DIR"
mkdir -p "$NGINX_RELEASES_ROOT"
mkdir "$RELEASE_DIR"
cp -a "$DIST_DIR"/. "$RELEASE_DIR"/
if [ -e "$NEXT_LINK" ] || [ -L "$NEXT_LINK" ] || \
[ -e "$PREVIOUS_NEXT_LINK" ] || [ -L "$PREVIOUS_NEXT_LINK" ]; then
err "临时发布链接已存在,请检查 $NEXT_LINK 和 $PREVIOUS_NEXT_LINK"
exit 1
fi
if [ -L "$NGINX_CURRENT" ]; then
CURRENT_RELEASE="$(readlink -f "$NGINX_CURRENT")"
ln -s "$CURRENT_RELEASE" "$PREVIOUS_NEXT_LINK"
mv -Tf "$PREVIOUS_NEXT_LINK" "$NGINX_PREVIOUS"
fi
ln -s "$RELEASE_DIR" "$NEXT_LINK"
mv -Tf "$NEXT_LINK" "$NGINX_CURRENT"
log "文件发布完成, 共 $(find "$RELEASE_DIR" -type f | wc -l) 个文件"
log "生成Nginx配置: $NGINX_CONF"
cat > "$NGINX_CONF" << NGINX_EOF
# Ascend C Documentation - VitePress Static Site
server {
listen ${NGINX_PORT};
server_name _;
root ${NGINX_CURRENT};
index index.html;
charset utf-8;
# 日志
access_log /var/log/nginx/ascendc-docs-access.log;
error_log /var/log/nginx/ascendc-docs-error.log;
# HTML和扩展名省略路由必须重新验证,避免客户端拿到旧路由表。
location / {
try_files \$uri \$uri.html \$uri/ \$uri/index.html =404;
add_header Cache-Control "no-cache, must-revalidate" always;
}
# VitePress在页面模块加载失败时会重新请求该文件,禁止缓存旧版本。
location = /hashmap.json {
try_files \$uri =404;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
}
# VitePress assets文件名包含内容哈希,可以长期缓存。
location ^~ /assets/ {
try_files \$uri @previous_assets;
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
location @previous_assets {
root ${NGINX_PREVIOUS};
try_files \$uri =404;
add_header Cache-Control "public, max-age=31536000, immutable" always;
}
# Pagefind索引路径不含构建哈希,必须跟随当前release更新。
location ^~ /pagefind/ {
try_files \$uri =404;
add_header Cache-Control "no-cache, must-revalidate" always;
}
# 非哈希媒体资源使用有限时长缓存。
location ~* \.(png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
try_files \$uri =404;
add_header Cache-Control "public, max-age=2592000" always;
}
error_page 404 /404.html;
location = /404.html {
internal;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
}
# Gzip压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml;
gzip_min_length 1000;
}
NGINX_EOF
nginx -t 2>&1 | tee -a "$LOG_FILE"
if ! nginx -t &>/dev/null; then
err "Nginx配置测试失败,请检查配置"
exit 1
fi
systemctl enable nginx 2>/dev/null || true
systemctl restart nginx 2>/dev/null || service nginx restart 2>/dev/null || nginx -s reload
CURRENT_RELEASE="$(readlink -f "$NGINX_CURRENT")"
PREVIOUS_RELEASE="$(readlink -f "$NGINX_PREVIOUS" 2>/dev/null || true)"
for candidate in "$NGINX_RELEASES_ROOT"/*; do
[ -d "$candidate" ] || continue
if [ "$candidate" != "$CURRENT_RELEASE" ] && [ "$candidate" != "$PREVIOUS_RELEASE" ]; then
rm -rf -- "$candidate"
fi
done
sleep 1
if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:${NGINX_PORT}/" | grep -q "200\|301\|302"; then
log "部署成功! 访问地址: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo '127.0.0.1'):${NGINX_PORT}"
else
warn "Nginx可能未正常启动,请手动检查"
fi
}
run_preview() {
cd "$SCRIPT_DIR"
log "启动预览模式 (Ctrl+C退出)..."
if command -v pnpm &>/dev/null; then
pnpm docs:preview --port "$SERVE_PORT" --host 0.0.0.0
else
npm run docs:preview -- --port "$SERVE_PORT" --host 0.0.0.0
fi
}
run_serve() {
cd "$SCRIPT_DIR"
DIST_DIR="docs/.vitepress/dist"
if [ ! -d "$DIST_DIR" ]; then
err "请先构建: $0"
exit 1
fi
log "在 ${SERVE_PORT} 端口提供静态服务..."
if command -v npx &>/dev/null; then
log "使用http-server提供服务..."
npx http-server "$DIST_DIR" -p "$SERVE_PORT" -c-1 --gzip -a 0.0.0.0
elif command -v python3 &>/dev/null; then
log "使用python3提供服务..."
python3 -m http.server "$SERVE_PORT" --directory "$DIST_DIR" --bind 0.0.0.0
else
err "未找到http-server或python3"
exit 1
fi
}
detect_os
case "$MODE" in
nginx)
if [ "$(id -u)" -ne 0 ]; then
err "Nginx部署需要root权限,请使用sudo执行"
exit 1
fi
install_nodejs
install_deps
install_python_deps
build_docs
deploy_nginx
;;
build)
install_nodejs
install_deps
install_python_deps
build_docs
log "构建完成! 使用以下命令启动服务:"
log " sudo $0 --nginx # Nginx生产部署"
log " $0 --serve # 临时静态服务 (端口 $SERVE_PORT)"
log " $0 --serve 8080 # 指定端口"
log " $0 --preview # VitePress预览模式"
;;
preview)
install_nodejs
install_deps
install_python_deps
build_docs
run_preview
;;
serve)
install_nodejs
install_deps
install_python_deps
build_docs
run_serve
;;
esac