From 5a50df6e232cd82edfdf00074a71fda75a9e6adf Mon Sep 17 00:00:00 2001 From: zqlit Date: Mon, 5 Oct 2026 17:39:59 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=B8=BB=E9=A2=98/layouts/assets=20?= =?UTF-8?q?=E7=AD=89=E5=85=A8=E5=B1=80=E6=94=B9=E5=8A=A8=E6=97=B6=EF=BC=8C?= =?UTF-8?q?purge=20=E6=B8=85=E5=8D=95=E8=BF=BD=E5=8A=A0=E5=85=A8=E7=AB=99?= =?UTF-8?q?=20HTML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:模板用 resources.Fingerprint 生成带 hash 的 JS/CSS 文件名,而 upx sync 带 --delete(远端不存在的文件会被删)。只改主题、不动 content/*.md 时, purge_list 只输出固定入口页 + content 页 → 文章页的 HTML 仍留在 CDN(TTL 8 天), 里面引用的是**已被删掉的旧指纹 JS** → 评论区/交互断链(实测文章页仍引用 page-only.min.1e47477b…,该文件本轮已不在源站,只是还在 CDN 缓存里)。 修法:diff 命中 themes/ layouts/ assets/ static/ data/ archetypes/ 或 hugo.toml/config.* 时,walk public/ 把所有 .html 加入刷新清单 (本机验证 1336/1336 覆盖)。 --- scripts/purge_list.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/scripts/purge_list.js b/scripts/purge_list.js index 5f3a417e..808faac9 100644 --- a/scripts/purge_list.js +++ b/scripts/purge_list.js @@ -8,7 +8,9 @@ * 即使源站已更新,CDN 仍会发最多 8 天的旧副本**:本次 about.html 就是这样 * 卡在旧副本上(源站 47935B / CDN 41454B)。 * - * 策略:固定入口页(保底,逻辑不变)+ 本次改动过的 content/*.md 对应的输出页。 + * 策略:固定入口页(保底,逻辑不变) + * + 本次改动过的 content/*.md 对应的输出页 + * + **全局性改动(themes/layouts/assets/static/data、hugo/config)时,追加全站所有 HTML**。 * - 输出页靠「frontmatter 的 url / slug / 目录名 / 文件名」推导,并**校验 * public/ 下确实存在该文件**才加入,避免刷不存在的 URL(刷了也无害,但脏)。 * - 拿不到 git 基线(单提交仓库 / 浅克隆)时自动降级:只输出固定入口页。 @@ -49,8 +51,40 @@ const base = process.argv[2] || 'HEAD~1'; let changed = sh(`git -c core.quotepath=false diff --name-only -z ${base} HEAD`).split('\0').filter(Boolean); if (!changed.length) changed = sh('git -c core.quotepath=false diff --name-only -z HEAD').split('\0').filter(Boolean); +// 递归列出 public 下所有 .html +function walkHtml(dir, out = []) { + let entries; + try { + entries = fs.readdirSync(dir, { withFileTypes: true }); + } catch { + return out; + } + for (const e of entries) { + const p = path.join(dir, e.name); + if (e.isDirectory()) walkHtml(p, out); + else if (e.name.endsWith('.html')) out.push(p); + } + return out; +} + const urls = new Set(FIXED.map((p) => SITE + '/' + p.replace(/^\//, ''))); +// ★ 全局性改动 → 刷新**全部** HTML(2026-10-05 新增) +// 为什么必须这么做:模板用 resources.Fingerprint 给 JS/CSS 生成带 hash 的文件名, +// 而 upx sync 带 --delete(远端不存在的文件会被删)。主题一改,page-only / core 等 +// 包的文件名就变了 → 老指纹文件被删。文章页的 HTML 若还留在 CDN(TTL 8 天), +// 它引用的就是**已被删掉的旧 JS** → 评论区/交互直接断链。 +// 所以凡是会改变「全站 HTML 引用资源」的改动,都必须让所有 HTML 重新拉取。 +const GLOBAL_DIRS = /^(themes|layouts|assets|static|data|archetypes)\//; +const GLOBAL_FILES = /^(hugo|config)\.(toml|yaml|yml|json)$/; +const isGlobal = changed.some((f) => GLOBAL_DIRS.test(f) || GLOBAL_FILES.test(f)); +if (isGlobal) { + for (const p of walkHtml('public')) { + const rel = p.replace(/^public[\\/]/, '').split(path.sep).join('/'); + if (rel) urls.add(SITE + '/' + rel); + } +} + for (const f of changed) { if (!/^content\/.*\.md$/.test(f) || !fs.existsSync(f)) continue; const src = fs.readFileSync(f, 'utf8');