友链改完为什么还要跑一遍校验:10 月 23 日的发布返工记录

20 分钟友链 / GitHub Actions / 博客运维 / 自动化 / 开发记录
友链改完为什么还要跑一遍校验:10 月 23 日的发布返工记录
“一次小爱后台与静态博客的发布返工记录:从友链同步、构建竞态和重复部署,到最后撤回自动管理,只保留评论 AI 审核回复与已有遮罩。”
4086 字
20 分钟
友链改完为什么还要跑一遍校验:10 月 23 日的发布返工记录

今天折腾的起点,是一个很具体的问题:我已经在小爱后台改了友链,再点 GitHub 的运行工作流,最后到底会以哪一边为准?
这听起来像是“把服务器数据复制到仓库”就能结束的事。真正往下查才发现,友链页面不是直接读取小爱服务的数据库,而是 Astro 根据仓库里的数据构建成静态文件。于是一次后台修改要经过服务器导出、仓库同步、静态构建、服务器校验和目录替换。只要其中某一步拿到旧版本,页面就可能和后台显示的不一样;如果自动化又在失败后重试,还可能多跑几次部署,白白消耗 Actions 额度。
我把这一天的修改按实际发生顺序记下来。下面前几轮说的是当时尝试过的同步方案,这些方案最终已经撤回,不是当前功能说明。现在友链资料回到仓库手工维护,小爱继续在评论区提供完整 AI 审核回复,页面保留已有违规与短暂失联遮罩。做过哪些保护、为什么最后仍决定收回范围,都值得留下记录。
先弄清楚哪份数据才算数
在撤回前的方案中,小爱服务维护申请、AI 审核和站长决定,也负责记录友链的临时下线或违规遮罩。博客则是静态站:当时仓库中的 friends-auto.json 和 friend-visibility.json 是构建输入,构建产物里的 friends/directory.json 和 friends/auto-manifest.json 是页面实际发布的数据。
整条链路可以画成这样:
小爱后台的审批与状态 │ 导出当前发布快照 ▼GitHub 仓库中的友链与遮罩数据 │ Astro 构建 ▼静态目录与发布清单 │ 服务器复核后原子替换 ▼线上友链页面这几步主要落在 .github/workflows/sync-friend-applications.yml、.github/workflows/refresh-and-deploy.yml、ops/waline-ai-reviewer/friends.py、scripts/sync-friend-applications.ts 和 scripts/publish-static-site.py。前两个工作流分别负责按需同步与构建发布;服务端校验审批、遮罩和当前线上版本;脚本负责把服务端快照映射进仓库,并保护静态目录替换。
这几份东西不是天然同步的。只运行博客部署工作流,理论上只是拿仓库快照重新构建;只运行友链同步工作流,也不等于页面已经成功更新。我们需要先回答两个问题:构建时使用的数据是不是小爱后台当前认可的状态?发布到服务器的文件是不是刚刚构建出来的那一份?
第一轮:不能只对比“新加的申请”
早期的同步思路容易把重点放在新增申请上:读出通过审核的申请,合并进静态友链文件,再触发部署。但这不足以表示完整状态。
如果站长修改了已收录友链资料、撤回或移除了某条友链,或者给友链设置了临时下线和违规遮罩,单看“有没有新申请”就会漏掉这些变化。尤其是遮罩状态,它影响卡片是否可点击;如果仓库没有同步这个文件,下一次构建就可能把被下线的链接重新变成普通可点链接。
所以同步快照扩展为同时包含当前已批准资料和遮罩状态。静态目录里手工维护的老友链仍从 friendsConfig.ts 保留;小爱管理的自动友链则按服务器的完整当前发布列表生成。这样,新增、修改、移除和遮罩都可以作为状态差异处理,而不是把“新增”当成唯一事件。
这一轮还补了版本核对:申请的版本号变了,意味着站长后来又修改过资料,旧审批就不能被当成当前版本直接发布。站长可以明确覆盖 AI 的审核结论,但发布的数据仍必须和当前被批准的申请版本完全对应。
第二轮:构建成功,不代表构建内容正确
接下来遇到的是更隐蔽的一层:Astro 命令返回成功,只能说明页面构建完成,不能证明构建出的友链目录和输入数据一一对应。
工作流当时会记录本次构建使用的友链与遮罩数据哈希,再检查构建后的发布清单、目录条目和资料字段。站点名称、网址、头像、描述、订阅地址、标签和截图都要与输入快照一致;没有截图时,也要正确落到默认图片。发布前会再把准备部署的审批和遮罩交给小爱服务复核。如果构建期间后台审批已经变化,服务器会拒绝这个旧快照。
静态目录替换也加了校验。发布脚本在切换线上目录前检查必需页面和友链目录哈希;替换后再核对发布状态记录中的提交、目录及数据哈希。验证不匹配时,工作流应该失败,而不是把“上传命令成功”当成“版本已上线”。
这也回答了我最担心的场景:如果我先在小爱后台改了状态,期间又有人提交了页面代码,旧构建不能悄悄覆盖新的审批或遮罩。最坏的结果应该是这次部署中止,等待使用最新状态重新构建。
下次加页面,会不会被后台友链卡住
在当时的设计里,友链没同步不会永久挡住页面代码上传,但本地和 GitHub 的基线仍要处理好。下面这段描述的是撤回前的部署链路。
如果小爱后台的变化已经同步到了 GitHub,而本地还停在旧提交,直接推送时可能遇到非快进拒绝。先把本地更新到最新 main,再接上页面修改即可;如果页面代码和友链数据改的是同一个文件,还要处理正常的 Git 冲突。
如果后台状态还没写进仓库,普通页面代码仍然可以推上去。代码推送启动发布工作流后,工作流会先读取小爱当前的批准快照和遮罩,再把它们与仓库数据合并,最后构建页面。若构建期间 main 又前进,或小爱审批、遮罩状态发生变化,发布前的比较会让这一轮停止,最新版本再重新构建。因此“代码已推上 GitHub”和“这一轮网站已经成功发布”是两个不同状态,失败时先看 Actions,再基于最新 main 重跑即可。
第三轮:一次同步不该意外变成两次完整部署
第一次把后台差异推回 main 时,GitHub 的 push 事件也会启动博客部署。如果同步工作流随后再显式启动一次部署,同一份友链变更就可能触发两次完整构建和上传。
这不只是界面上多一条 Actions 记录。博客构建要安装依赖、生成页面、检查并上传静态目录;重复执行会占用额度,也会让人分不清到底哪一次才是最终发布。
当时调整为友链数据文件的推送跳过普通 push 部署触发,友链同步工作流只在发现数据变化、或确认线上发布版本过期时,明确请求一次博客部署。博客发布、友链同步和小爱服务部署也使用同一组并发控制,避免它们同时改写或校验发布状态。
因此,两种按钮的职责仍然不同:**友链同步负责把小爱后台的当前发布状态写进仓库,并在需要时请求部署;博客部署负责读取服务器当前状态、构建并发布,不应把旧仓库数据直接覆盖到线上。**没有状态变化且线上版本一致时,就不需要再启动一次完整发布。
第四轮:构建自己改出来的文件,挡住了 RSS 快照提交
博客构建会重新生成一些本地文件。一个部署工作流既要保存 RSS 快照,又会经过构建步骤;如果把构建时产生的其他文件留在工作区,之后提交 RSS 并 rebase 最新分支时,Git 可能因为工作区状态冲突而中止。
第一次看到这种失败,很容易以为是友链数据冲突。实际上,失败发生在“保存构建副产物”和“把它安全地写回分支”之间。处理方式是只提交真正要保存的 RSS 快照,把构建生成的其他工作区变更清理掉,再进行 rebase。之后还要比较构建开始时的提交与最新 main:如果期间有页面源码或友链数据变化,就停止发布,让更新的工作流负责构建。
这样保留下来的不是“尽可能把旧构建推上去”,而是“只允许构建结果对应它实际读取的那份源码”。
第五轮:服务器文件存在,服务账号却读不到
小爱服务部署还有一个权限问题。工作流以 root 身份解压源码,但有些发布校验会切换到 waline-ai-reviewer 服务账号执行。如果暂存目录权限设为 700,root 看得到,服务账号却连目录都进不去。
处理时把权限拆开:保存回滚资料的备份目录继续保持 700,不让普通服务账号读取;只把本次部署用的暂存目录设为可遍历、可读取的权限,然后在复制到服务目录前完成校验。权限边界应该按用途设置,不能因为部署程序最初由 root 解压,就默认后续所有命令也以 root 执行。
最后把异步发布也纳入一致性判断
小爱服务把申请或站长设置提交给 GitHub 后,GitHub 接受工作流请求只代表“任务已排队”,不代表静态页面已经变成新版本。如果没有继续核对线上发布状态,后台可能显示已经请求过同步,实际页面却还停留在旧版本。
当时服务端加入了线上友链清单检查,会核对目录、目录和遮罩的发布标记。哈希仍旧时,状态不会提前标成发布完成;如果 GitHub 已接受请求,但线上版本持续落后,后台会按退避间隔再次检查和请求同步。这个机制把“请求成功”和“发布成功”拆成两个状态,避免一次 HTTP 204 就被误读成友链已经上线。
今天留下的验证边界
本轮覆盖了小爱申请及遮罩状态测试、静态目录发布测试、友链数据导入、TypeScript 检查和工作流结构检查。测试重点不只是正常路径,也包括审批变化、状态哈希不匹配、无效线上清单、重复申请、发布目录哈希不对以及服务端仍旧时不得报成功。
有些目录切换测试依赖 Linux 环境,本机 Windows 无法完整执行;GitHub Actions 上的 Linux 工作流才是这部分集成测试的执行环境。还有一个容易忽略的边界:代码检查通过、提交推到 GitHub,甚至上传命令返回成功,都不能单独证明线上就是目标版本。最后还要看到发布工作流核对公开状态接口中的提交和哈希,才算这次发布闭环。
回头看,真正修的是发布链路的承诺
今天不是简单给友链列表加一个同步按钮,而是把几句原本默认成立的话变成了可验证的条件:小爱后台说通过的版本,应该是构建输入;站长设置的遮罩,应该随同一份发布一起生效;构建期间发生的新改动,不应该被旧产物覆盖;工作流报告成功时,线上文件应该能证明它对应哪个提交和哪组数据。
每次失败都把边界往前推了一点:先发现少同步了状态,再发现构建结果需要核验;接着发现 GitHub 事件会重复部署、构建副产物会妨碍 rebase,最后发现服务器上的权限也会让一个看似正确的校验无法执行。
这就是今天这轮返工最实在的收获:静态博客的线上状态不是仓库文件的自动镜像。只有把后台状态、仓库快照、构建产物和服务器发布标记逐层对上,才能知道页面实际展示的究竟是哪一版。
最终决定:收回自动管理的范围
排查到最后,我选择撤回友链后台管理、每日回链巡检和审批后的自动发布。原因很具体:一个个人静态博客,不应该为了增加一条友链,就必须同时理解后台状态、仓库快照、工作流队列和服务器发布标记。保护机制可以继续堆,但维护成本也会跟着上涨。
现在友链资料恢复由 src/config/friendsConfig.ts 手工维护,随正常博客构建发布。小爱仍然负责评论区的完整 AI 审核:标准化名称、网址、描述和头像,实际读取站点与回链页面,给出内容分析、规则评分、预计通过率、处理时间预测以及互换规则。这些是给申请者和站长看的建议,是否添加仍由站长决定,服务不会再自动修改博客友链资料或向仓库提交友链数据。
已有两条违规状态和一条短暂失联状态继续保留。页面通过小爱的只读接口读取已有遮罩,被遮罩的友链不可点击;违规整改倒计时到期后显示“将于3日内清理”。这句文字是处理提示,不会自行删除友链。友链审核页面和回链检查页面则一并撤下。
删除工作流,还差一次服务器更新
这次收尾还有一个很直观的遗漏:仓库已经删掉管理页面,博客也部署了新版本,小爱后台却仍显示“友链审核”和“回链检查”。这不是刷新几次就能解决的问题。静态博客和 Python 小爱服务是两个部署对象,博客目录替换不会重启 /opt/waline-ai-reviewer 中运行的服务;把小爱部署工作流删掉,也不会让服务器自动获得删除后的源码。
因此撤回功能时,还需要给服务器做一次对应的源码更新,并核实旧路由不再可用、评论收件箱和 AI 审核回复继续工作、已有遮罩记录没有被清空。临时更新入口在完成后移除,避免为了撤掉一组自动化,又长期留下另一组不需要的工作流。
另一个边界也补清楚了:站长账号发公告可以跳过审核,但站长账号提交第三方站点资料,仍应该获得完整审核。账号身份不能代替留言内容的判断,更不应该把一份完整申请降级成一句“收到申请”。
这轮返工最终留下的是更清楚的维护边界:友链资料在仓库里改,AI 分析在评论里看,已有状态用只读遮罩展示。提交是否成功、博客是否更新、小爱服务是否更新,则分别核实。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
友链改完为什么还要跑一遍校验:10 月 23 日的发布返工记录
https://rainzt.cn/posts/friend-release-consistency-2026-10-23/相关文章智能推荐
1
月刊 · 第二期
随笔记录 8 月 27 日到 9 月 27 日之间的博客更新:Aemeath v4.0.0、自建 Umami、评论区 Lottie 表情、被撤回的视频壁纸,以及本期推荐的博友。
2
月刊
随笔记录 8 月 7 日到 26 日之间的博客更新、自动上架项目、被我停掉的功能、刚搭好的小家,以及本期想认真推荐的几位博友。
3
最近,来交作业了
随笔接着上周的《最近》,记录我为自动上架加入图片哈希值校验,也兑现了国庆后改版的打算,让博客精简主题 Aemeath-Pure 正式上线。
4
最近
随笔聊聊最近的疲惫和懒散、暂时推迟的创业计划,以及重新调整作息、捡起锻炼、继续开发自动上架 Agents 的打算。
5
小爱Agents来啦~
项目作品把原本独立的小爱 Agents 接入自动上架,让同事通过 AI 聊天排查报错,也能由面向跨境电商的 DeepSeek Harness 主控规划任务、调用 Browser Use 等工具并检查结果。文章介绍新增的 11 项上架工具,以及本轮上传提速和容错更新。
随机文章随机推荐




























































评论区
分享你的想法,与大家交流讨论