区分开发部署与全量部署,减少样式迭代发布负担

Constraint: 需要保留公网可见的真实部署路径,同时降低日常样式改动的部署成本
Rejected: 仅保留 --fast | 不会重建镜像,无法可靠看到代码/样式变更
Confidence: high
Scope-risk: narrow
Directive: 日常页面/样式迭代优先使用 --dev-deploy,资源或日志更新再使用 --full-deploy
Tested: bash -n scripts/deploy-homepage.sh; scripts/deploy-homepage.sh --help; ./scripts/deploy-homepage.sh --dev-deploy; ./scripts/deploy-homepage.sh --full-deploy
Not-tested: 生产域名切换后的反代行为
This commit is contained in:
SepComet 2026-05-10 15:27:08 +08:00
parent 84159db417
commit 811f8199e0
4 changed files with 155 additions and 19 deletions

View File

@ -36,6 +36,14 @@ RUN_LOCAL_REBUILD="${RUN_LOCAL_REBUILD:-true}"
# 默认值true # 默认值true
RUN_STATIC_ASSET_SYNC="${RUN_STATIC_ASSET_SYNC:-true}" RUN_STATIC_ASSET_SYNC="${RUN_STATIC_ASSET_SYNC:-true}"
# 是否执行日志同步
# 默认值true
RUN_LOG_SYNC="${RUN_LOG_SYNC:-true}"
# 刷新 Docker 服务时是否强制重新构建镜像
# 默认值true
DOCKER_FORCE_BUILD="${DOCKER_FORCE_BUILD:-true}"
# 锁文件路径,避免定时任务重入 # 锁文件路径,避免定时任务重入
# 默认值:/tmp/personal-homepage-deploy.lock # 默认值:/tmp/personal-homepage-deploy.lock
LOCK_FILE="${LOCK_FILE:-/tmp/personal-homepage-deploy.lock}" LOCK_FILE="${LOCK_FILE:-/tmp/personal-homepage-deploy.lock}"
@ -53,6 +61,33 @@ fail() {
exit 1 exit 1
} }
usage() {
cat <<'EOF'
用法:
deploy-homepage.sh [options]
选项:
--preset <dev|full|fast> 使用预设部署档位
--dev-deploy 等价于 --preset dev
--full-deploy 等价于 --preset full
--run-static-asset-sync <true|false> 是否同步静态资源
--run-log-sync <true|false> 是否同步日志目录
--run-local-rebuild <true|false> 是否在宿主机执行 npm run rebuild
--docker-force-build <true|false> docker compose up 时是否加 --build
--rsync-delete <true|false> rsync 时是否启用 --delete
--fast 快速模式(仅刷新容器,不做同步与宿主机 rebuild
-h, --help 显示帮助
说明:
- 预设档位:
dev = 仅代码/样式部署(不做静态资源同步与日志同步;仍会 docker --build
full = 全量部署(静态资源 + 日志 + 宿主机 rebuild + docker --build
fast = 仅快速刷新容器(不做同步、不本地 rebuild、且不 docker --build
- 命令行参数优先级高于环境变量。
- 布尔值支持: true/false/1/0/yes/no/on/off
EOF
}
require_command() { require_command() {
command -v "$1" >/dev/null 2>&1 || fail "缺少命令: $1" command -v "$1" >/dev/null 2>&1 || fail "缺少命令: $1"
} }
@ -64,6 +99,93 @@ is_true() {
esac esac
} }
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--preset)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
case "${2,,}" in
dev)
RUN_STATIC_ASSET_SYNC=false
RUN_LOG_SYNC=false
RUN_LOCAL_REBUILD=false
DOCKER_FORCE_BUILD=true
;;
full)
RUN_STATIC_ASSET_SYNC=true
RUN_LOG_SYNC=true
RUN_LOCAL_REBUILD=true
DOCKER_FORCE_BUILD=true
;;
fast)
RUN_STATIC_ASSET_SYNC=false
RUN_LOG_SYNC=false
RUN_LOCAL_REBUILD=false
DOCKER_FORCE_BUILD=false
;;
*)
fail "不支持的 preset: $2(可选: dev|full|fast"
;;
esac
shift 2
;;
--dev-deploy)
RUN_STATIC_ASSET_SYNC=false
RUN_LOG_SYNC=false
RUN_LOCAL_REBUILD=false
DOCKER_FORCE_BUILD=true
shift
;;
--full-deploy)
RUN_STATIC_ASSET_SYNC=true
RUN_LOG_SYNC=true
RUN_LOCAL_REBUILD=true
DOCKER_FORCE_BUILD=true
shift
;;
--run-static-asset-sync)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
RUN_STATIC_ASSET_SYNC="$2"
shift 2
;;
--run-log-sync)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
RUN_LOG_SYNC="$2"
shift 2
;;
--run-local-rebuild)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
RUN_LOCAL_REBUILD="$2"
shift 2
;;
--docker-force-build)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
DOCKER_FORCE_BUILD="$2"
shift 2
;;
--rsync-delete)
[[ $# -ge 2 ]] || fail "参数缺少值: $1"
RSYNC_DELETE="$2"
shift 2
;;
--fast)
RUN_STATIC_ASSET_SYNC=false
RUN_LOG_SYNC=false
RUN_LOCAL_REBUILD=false
DOCKER_FORCE_BUILD=false
shift
;;
-h|--help)
usage
exit 0
;;
*)
fail "未知参数: $1(使用 --help 查看可用参数)"
;;
esac
done
}
sync_logs() { sync_logs() {
mkdir -p "$LOG_TARGET_DIR" mkdir -p "$LOG_TARGET_DIR"
@ -108,7 +230,11 @@ refresh_docker_service() {
log "刷新 Docker 服务: $DOCKER_SERVICE_NAME" log "刷新 Docker 服务: $DOCKER_SERVICE_NAME"
( (
cd "$SITE_DIR" cd "$SITE_DIR"
if is_true "$DOCKER_FORCE_BUILD"; then
docker compose -f "$DOCKER_COMPOSE_FILE" up -d --build "$DOCKER_SERVICE_NAME" docker compose -f "$DOCKER_COMPOSE_FILE" up -d --build "$DOCKER_SERVICE_NAME"
else
docker compose -f "$DOCKER_COMPOSE_FILE" up -d "$DOCKER_SERVICE_NAME"
fi
) )
} }
@ -135,8 +261,10 @@ main_impl() {
log "DOCKER_COMPOSE_FILE=$DOCKER_COMPOSE_FILE" log "DOCKER_COMPOSE_FILE=$DOCKER_COMPOSE_FILE"
log "DOCKER_SERVICE_NAME=$DOCKER_SERVICE_NAME" log "DOCKER_SERVICE_NAME=$DOCKER_SERVICE_NAME"
log "RSYNC_DELETE=$RSYNC_DELETE" log "RSYNC_DELETE=$RSYNC_DELETE"
log "RUN_LOG_SYNC=$RUN_LOG_SYNC"
log "RUN_STATIC_ASSET_SYNC=$RUN_STATIC_ASSET_SYNC" log "RUN_STATIC_ASSET_SYNC=$RUN_STATIC_ASSET_SYNC"
log "RUN_LOCAL_REBUILD=$RUN_LOCAL_REBUILD" log "RUN_LOCAL_REBUILD=$RUN_LOCAL_REBUILD"
log "DOCKER_FORCE_BUILD=$DOCKER_FORCE_BUILD"
log "LOCK_FILE=$LOCK_FILE" log "LOCK_FILE=$LOCK_FILE"
if is_true "$RUN_STATIC_ASSET_SYNC"; then if is_true "$RUN_STATIC_ASSET_SYNC"; then
@ -145,7 +273,11 @@ main_impl() {
log "跳过静态资源同步RUN_STATIC_ASSET_SYNC=false" log "跳过静态资源同步RUN_STATIC_ASSET_SYNC=false"
fi fi
if is_true "$RUN_LOG_SYNC"; then
sync_logs sync_logs
else
log "跳过日志同步RUN_LOG_SYNC=false"
fi
if is_true "$RUN_LOCAL_REBUILD"; then if is_true "$RUN_LOCAL_REBUILD"; then
run_local_rebuild run_local_rebuild
@ -159,9 +291,11 @@ main_impl() {
} }
main() { main() {
parse_args "$@"
if ! command -v flock >/dev/null 2>&1; then if ! command -v flock >/dev/null 2>&1; then
log "未找到 flock跳过锁保护" log "未找到 flock跳过锁保护"
main_impl "$@" main_impl
return 0 return 0
fi fi
@ -173,7 +307,7 @@ main() {
fi fi
log "已获取部署锁: $LOCK_FILE" log "已获取部署锁: $LOCK_FILE"
main_impl "$@" main_impl
} }
main "$@" main "$@"

View File

@ -1,12 +1,12 @@
{ {
"projects": [ "projects": [
{ {
"project_repo": "basil/personal-homepage", "project_repo": "basil/biography-of-lijie",
"downloads": [ "downloads": [
{ {
"name": "Windows 构建包", "name": "Windows 构建包",
"description": "项目打包文件(可选)", "description": "项目 李诫传 打包文件",
"url": "http://106.12.111.150:8000/f/5eb1877a7212488ca0aa/?dl=1", "url": "http://106.12.111.150:8000/f/c6e397439b174fe39106/?dl=1",
"repo_id": "", "repo_id": "",
"path": "", "path": "",
"type": "build", "type": "build",
@ -17,9 +17,17 @@
], ],
"shares": [ "shares": [
{ {
"name": "简历 PDF", "name": "Unity 游戏客户端开发简历 PDF",
"description": "独立公开资源示例,可不绑定项目。", "description": "求职 Unity 游戏客户端开发岗位的简历",
"url": "", "url": "http://106.12.111.150:8000/f/937ec9f34ec94d528a52/?dl=1",
"repo_id": "",
"path": "",
"type": "document"
},
{
"name": "游戏构建开发简历 PDF",
"description": "求职游戏构建开发岗位的简历",
"url": "http://106.12.111.150:8000/f/04deda4586a742a6b5a7/?dl=1",
"repo_id": "", "repo_id": "",
"path": "", "path": "",
"type": "document" "type": "document"

View File

@ -1,14 +1,8 @@
[ [
{ {
"name": "个人主页需求文档", "name": "配置样例结构",
"description": "站点范围、页面结构、数据模型与部署思路的初稿。", "description": "站点范围、页面结构、数据模型与部署思路的初稿。",
"url": "#", "url": "#",
"time": "2026-05-03" "time": "2026-05-01"
},
{
"name": "首页设计说明",
"description": "工程编辑部风格的首页设计约束与组件边界。",
"url": "#",
"time": "2026-05-03"
} }
] ]

View File

@ -44,7 +44,7 @@ const featuredProjectsUpdatedAt = getLatestDate(
<code>106.12.111.150/logs</code> <code>106.12.111.150/logs</code>
这类 这类
<strong>不带 :2000</strong> <strong>不带 :2000</strong>
的地址并出现 502,请手动把地址改成 的地址并出现 404,请手动把地址改成
<code>106.12.111.150:2000/对应路径</code> <code>106.12.111.150:2000/对应路径</code>
即可正常访问其他视图。这个问题是当前 Nginx 反代导致的临时现象,等域名备案完成后会恢复正常。 即可正常访问其他视图。这个问题是当前 Nginx 反代导致的临时现象,等域名备案完成后会恢复正常。
</p> </p>