本站运行的聊天插件 AI Chat for Amazon Bedrock 是一个开源 WordPress 插件,源码在 GitHub,发布渠道是 wordpress.org 的 SVN。它的发布流程是一个脚本化的“门禁”,外加几步手工的 SVN 操作。本文整理这套流程里可以复用的部分,以及一个和 CloudFront 有关的教训:同一个版本号重新上传安装包,站点会继续提供旧的 CSS 和 JS。
版本号要在三处同时改
一个 WordPress 插件的版本至少出现在三个地方,任何一处漏改都会出问题:
- 主文件头部的
Version:,WordPress 用它判断已安装的版本; - 代码里的版本常量(这个插件是
AI_CHAT_BEDROCK_VERSION),入队脚本和样式时作为?ver=使用; readme.txt的Stable tag,wordpress.org 用 trunk 里这一行决定向用户分发哪个 tag。
此外还要在 readme.txt 里加一条 changelog 和一条 Upgrade Notice,并重新生成翻译模板 .pot。项目的 bin/prerelease.sh 会读出这三个版本号,要求完全一致,并检查 changelog 里有没有 = x.y.z = 这一节,缺了就判失败。
发布前的门禁脚本
bin/prerelease.sh 按“最快失败”的顺序跑以下检查,本地和 CI 跑的是同一个脚本:
- 每个 PHP 文件都能通过
php -l; - 上面说的版本一致性和 changelog;
- readme 的长度限制:简短描述不超过 150 字符,每条升级提示不超过 300 字符,描述加隐私政策少于 2500 词;
tests/下的全部测试套件;- 用固定版本的
phpcs跑 WordPress 编码规范; - 提供 WP-CLI 命令时,跑官方 Plugin Check;
- 从
.distignore算出将要发布的文件列表,并扫描其中形似凭证的内容。
README 特别说明了两条设计:
- 跑不了的检查报
skip,不报ok。开发期间门禁出过两次“假通过”:phpcs没能启动却被当成没有问题;凭证扫描在门禁实际调用的模式下根本没执行。之后专门写了tests/tooling.php:把项目复制到临时目录,每次故意破坏一处,断言对应的检查在FAIL行报错。 .distignore是唯一的排除清单。打包脚本bin/build-package.py读它,门禁也问打包脚本“会发什么”。原因是以前有三份手写清单互相不一致,一个新加的工具目录被算进了审计范围。
打包脚本拒绝打包含 AWS 访问密钥、SVN 令牌或私钥特征的文件,打完输出版本、文件数、字节数和 SHA-256。CI(GitHub Actions)在 PHP 7.4 和 8.3 上各跑一遍门禁,再单独跑工具测试、构建并校验安装包,并在真实的 WordPress 上跑 Plugin Check。7.4 是插件头部声明的最低版本。
wordpress.org 的 SVN 发布
门禁通过、安装包构建好之后,发布是一步单独的手工操作,脚本不碰 wordpress.org。README 里的步骤是:
- 把安装包的内容复制到 SVN 工作副本的
trunk/,提交; - 从 trunk 复制出新 tag 并提交,即 WordPress 手册推荐的
svn cp trunk tags/x.y.z; - 确认插件目录页显示了新版本,并且 downloads.wordpress.org 上的包和你构建的内容一致。
WordPress 手册提醒:每次向 SVN 推送,都会重建所有版本的 zip,所以更新有时要过最多 6 小时才会出现。截图也要跟着界面走:assets/ 里的截图必须是当前界面的真实截图,界面变了就重拍。
同一版本号重新上传:CloudFront 继续给旧文件
WordPress 入队脚本和样式时,把版本号作为查询参数拼到 URL 上,例如 …/admin/css/ai-chat-bedrock-admin.css?ver=1.47.6。这是 wp_enqueue_style() 的 $ver 参数,本来就是为了让缓存失效。本站的 CloudFront 也缓存插件的脚本和样式,缓存键包含查询字符串。实测同一个静态资源用两个不同的 ver 值请求,第一次都是 Miss;同一个 ver 值再请求一次就是 Hit。源站响应里没有 Cache-Control,缓存多久由分发上该缓存行为的默认 TTL 决定。
正常升级不受影响,因为版本号变了,URL 也就变了。问题出在 1.47.6 上。本站通过上传 zip 的方式升级插件,先上传了一次 1.47.6。之后仓库里又合入一个只改 CSS 的修复:表格行按钮里给屏幕阅读器用的文字是绝对定位的,没有被外层滚动容器裁剪,在 390px 宽的手机上把 MCP 页面撑到了 548px。这次修复没有改版本号。重新上传后,PHP 已经是新的,但 URL 还是 ?ver=1.47.6,CloudFront 按缓存键继续返回第一次缓存的旧 CSS,要等缓存过期才会换成新的。wordpress.org 上的 tags/1.47.6 里已经包含这个修复,这个问题只出在本站“同版本重新上传”的操作上。
避免的办法按推荐顺序:
- 改了任何发布出去的文件,就升一个补丁版本号。这和 AWS 文档的建议一致:频繁更新的文件优先用版本化的文件名,而不是靠失效。
- 非要复用版本号时,对插件路径做一次 CloudFront 失效:
aws cloudfront create-invalidation --distribution-id <分发 ID> \ --paths "/wp-content/plugins/<插件目录>/*" - 检查缓存策略是否真的把查询字符串放进了缓存键。托管策略
CachingOptimized不包含查询字符串,默认 TTL 是 24 小时。用它缓存wp-content时,连正常的版本升级都会拿到旧文件。
局限 / 注意
- 浏览器自己的缓存与 CloudFront 无关。清掉 CDN 缓存之后,个别访客可能仍会看到旧文件,版本号变更同样能解决这一层。
- 无障碍检查(
bin/audit-accessibility.py、bin/audit-frontend.py)需要驱动真实浏览器,是手工步骤,不在门禁里。按 README 的记录,它们发现过静态分析发现不了的问题,例如一次找出了二十个没有可访问名称的控件。



