#!/bin/sh # 构建管线(Phase 5c 裸 clone 改造):构建源 = origin/main 硬跟,非本地工作树。 # # 为什么:此前直接构建 ~/Code/Github/my-blog 工作树——分支/未提交状态会 # 意外上线(Sveltia 冒烟期实测发生过);且 Sveltia 的 main 提交无法自动 # 发布。改为独立构建仓 fetch+reset 硬跟 main:线上=远端 main,确定性闭环。 # blog 仓已公开,匿名 fetch 无需凭据。 set -e # 抓取 URL:默认公网;同机构建可由宿主环境变量覆盖为内网 # (2026-10-26 事故:ESA 边缘对 git.wait4x.online 522 时公网 fetch 失败, # set -e 下静默死、staging 冻结——内网通道必须可用) BLOG_GIT_URL="${BLOG_GIT_URL:-https://git.wait4x.online/wait4x/blog.git}" BUILD_REPO="$HOME/docker_apps/blog/build-repo" STAGING="$HOME/docker_apps/blog/www-staging" if [ ! -d "$BUILD_REPO/.git" ]; then echo "[build] 首次克隆构建仓" git clone "$BLOG_GIT_URL" "$BUILD_REPO" fi cd "$BUILD_REPO" # 每次构建把 origin 指向配置的 URL(同机构建走内网通道;公网 522 时仍可发布) git remote set-url origin "$BLOG_GIT_URL" git fetch origin main git reset --hard origin/main git clean -fd --exclude node_modules SHA=$(git rev-parse --short HEAD) echo "[build] 构建源: origin/main @ $SHA" # 5n B1:构建状态生命周期(后台 /admin/deploy-status.js 轮询 /admin/build-status.json)。 # building 态写在 rsync 前(此时 staging 仍服务上一版),ok 态写在末尾并解除 trap; # 任何中途退出(set -e 炸链)由 trap 兜底写 fail——状态文件每次 rsync 后重写,不依赖 dist mkdir -p "$STAGING/admin" 2>/dev/null || true write_status() { printf '{"sha":"%s","time":"%s","state":"%s","pages":%s}\n' "$SHA" "$(date -Is)" "$1" "${2:-0}" > "$STAGING/admin/build-status.json" 2>/dev/null || true; } trap 'write_status fail' EXIT write_status building pnpm install --frozen-lockfile >/dev/null # 5n A1:TMT 翻译先于构建(译文进 content/posts-en/,随同一次部署上线)。 # 守卫先行 + 守卫自身永不阻断(超时/无凭据/失败都只 WARN 并保留旧英文版) [ -f scripts/deploy/translate-step.sh ] && bash scripts/deploy/translate-step.sh || echo "[build] translate-step 未就绪,跳过" pnpm astro build # 按页字体 CSS 子集化(车道 A 任务 1):裁 @font-face 声明数,构建仓与本地同链; # 存在性守卫:新文件未推送到 main 前构建仓无此脚本,绝不让发布链因车道在途文件断掉 [ -f scripts/build-perf/subset-font-css.mjs ] && node scripts/build-perf/subset-font-css.mjs dist || echo "[build] subset 脚本未就绪,跳过" [ -f scripts/build-perf/config-check.py ] && python3 scripts/build-perf/config-check.py || echo "[build] config-check 未就绪,跳过" [ -f scripts/build-perf/contrast-check.mjs ] && node scripts/build-perf/contrast-check.mjs dist || echo "[build] contrast 脚本未就绪,跳过" # 首页阻塞 CSS 内联(车道 D):守卫先行同上——脚本未到 main 前绝不炸链 [ -f scripts/build-perf/gen-sw-precache.mjs ] && node scripts/build-perf/gen-sw-precache.mjs dist || echo "[build] sw-precache 生成器未就绪,跳过" [ -f scripts/build-perf/inline-home-css.mjs ] && node scripts/build-perf/inline-home-css.mjs || echo "[build] inline-home 脚本未就绪,跳过" # 5n C2:内容仪表盘(产物 dist/admin/stats.{html,json},随 rsync 上线) [ -f scripts/build-perf/gen-admin-stats.py ] && python3 scripts/build-perf/gen-admin-stats.py dist || echo "[build] stats 生成器未就绪,跳过" # README 动效资产(5i 引入、5j T2 退役):贡献蛇与 streak 快照已删——连续贡献改为 # 热力图同源本地计算(见 src/lib/heatmap.ts 的 streakStats),界面上标注"数据更新于"。 # fetch-readme-assets.mjs 保留骨架但 ASSETS 已清空,如将来要加新的自托管资产再启用。 # 内容 lint(T3):断链/缺图在部署前拦截;CONTENT_LINT=skip|warn 逃生门见脚本头注 [ -f scripts/build-perf/content-lint.mjs ] && node scripts/build-perf/content-lint.mjs dist || echo "[build] content-lint 未就绪,跳过" echo "[build] rsync -> $STAGING" mkdir -p "$STAGING" rsync -a --delete "$BUILD_REPO/dist/" "$STAGING/" ORIG="$HOME/docker_apps/blog/photos-originals" if [ -d "$ORIG" ]; then rsync -a "$ORIG/" "$STAGING/photos-originals/"; fi printf 'built %s at %s\n' "$SHA" "$(date -Is)" > "$STAGING/.build-stamp" PAGES=$(find "$STAGING" -name '*.html' | wc -l) echo "[build] done: $PAGES html files @ $SHA" write_status ok "$PAGES" trap - EXIT # 生产冒烟(T3):staging 三断言。首版容忍失败(robots 待 T1 落地,smoke FAIL 属预期), # T1 收编后改硬门:去掉 `|| echo` 让断言失败直接炸构建 if [ -f scripts/build-perf/prod-smoke.sh ] && [ "${SMOKE_BASE:-http://127.0.0.1:18743}" != "off" ]; then bash scripts/build-perf/prod-smoke.sh "${SMOKE_BASE:-http://127.0.0.1:18743}" || echo "[build] prod-smoke 未过(见上;T1 robots 落地后转硬门)" fi