WordPress プラグインのリリースで得た教訓:バージョン番号、リリースゲート、SVN、CloudFront キャッシュ

AI Chat for Amazon Bedrock のリリース手順:バージョン一致、skip を報告するゲート、SVN、同一バージョン再アップロードで旧 CSS が残った教訓。

本サイトで動いているチャットプラグイン AI Chat for Amazon Bedrock はオープンソースの WordPress プラグインで、ソースコードは GitHub にあり、配布チャネルは wordpress.org の SVN です。リリース手順は、スクリプト化された「ゲート」と、いくつかの手作業の SVN 操作で構成されています。本記事では、この手順のうち再利用できる部分と、CloudFront に関する 1 つの教訓をまとめます。その教訓とは、同じバージョン番号でインストールパッケージを再アップロードすると、サイトが古い CSS と JS を配信し続けるというものです。

バージョン番号は 3 か所を同時に変える

WordPress プラグインのバージョンは少なくとも 3 か所に現れ、どれか 1 か所でも変え忘れると問題が起きます。

  • メインファイルのヘッダーにある Version:。WordPress はこれでインストール済みのバージョンを判断します。
  • コード内のバージョン定数(このプラグインでは AI_CHAT_BEDROCK_VERSION)。スクリプトとスタイルをエンキューする際に ?ver= として使われます。
  • readme.txt の Stable tag。wordpress.org は trunk にあるこの行で、ユーザーにどの tag を配布するかを決めます。

さらに readme.txt に changelog と Upgrade Notice を 1 件ずつ追加し、翻訳テンプレート .pot を再生成する必要があります。プロジェクトの bin/prerelease.sh は 3 つのバージョン番号を読み取って完全に一致することを求め、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 では、2 つの設計を特に説明しています。

  • 実行できなかったチェックは skip と報告し、ok とは報告しない。開発中、ゲートで「偽の合格」が 2 回起きました。phpcs が起動できなかったのに問題なしと扱われたケースと、認証情報のスキャンが、ゲートが実際に呼び出すモードではまったく実行されていなかったケースです。その後、専用に tests/tooling.php を書きました。プロジェクトを一時ディレクトリにコピーし、毎回 1 か所を意図的に壊して、対応するチェックが FAIL 行でエラーを報告することをアサートします。
  • .distignore を唯一の除外リストにする。パッケージングスクリプト bin/build-package.py がこれを読み、ゲートもパッケージングスクリプトに「何を公開するか」を問い合わせます。以前は手書きのリストが 3 つあって互いに食い違っており、新しく追加したツールのディレクトリが監査範囲に含まれてしまったためです。

パッケージングスクリプトは、AWS アクセスキー、SVN トークン、秘密鍵の特徴を含むファイルがあるとパッケージ化を拒否し、完了するとバージョン、ファイル数、バイト数、SHA-256 を出力します。CI(GitHub Actions)では PHP 7.4 と 8.3 のそれぞれでゲートを実行し、さらにツールのテストと、インストールパッケージのビルドおよび検証を個別に行い、実際の WordPress 上で Plugin Check を実行します。7.4 はプラグインのヘッダーで宣言している最低バージョンです。

wordpress.org の SVN へのリリース

ゲートを通過してインストールパッケージをビルドした後のリリースは独立した手作業で、スクリプトは wordpress.org に触れません。README に書かれた手順は次のとおりです。

  1. インストールパッケージの内容を SVN 作業コピーの trunk/ にコピーし、コミットする。
  2. trunk から新しい tag をコピーしてコミットする。WordPress ハンドブックが推奨する svn cp trunk tags/x.y.z です。
  3. プラグインディレクトリのページに新しいバージョンが表示され、downloads.wordpress.org のパッケージが自分でビルドした内容と一致することを確認する。

WordPress ハンドブックによると、SVN にプッシュするたびにすべてのバージョンの zip が再生成されるため、更新が表示されるまで最大 6 時間かかることがあります。スクリーンショットも UI に合わせて更新する必要があります。assets/ のスクリーンショットは現在の UI の実際の画面でなければならず、UI が変わったら撮り直します。

同じバージョン番号での再アップロード:CloudFront が古いファイルを配信し続ける

WordPress はスクリプトとスタイルをエンキューする際、バージョン番号をクエリパラメータとして URL に付けます。例:…/admin/css/ai-chat-bedrock-admin.css?ver=1.47.6。これは wp_enqueue_style() の $ver 引数で、もともとキャッシュを無効化するためのものです。本サイトの CloudFront はプラグインのスクリプトとスタイルもキャッシュしており、キャッシュキーにはクエリ文字列が含まれます。実測では、同じ静的リソースを異なる 2 つの 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 にはこの修正がすでに含まれており、この問題は本サイトでの「同じバージョンでの再アップロード」という操作でのみ起きたものです。

回避策を推奨順に挙げます。

  1. 公開したファイルを 1 つでも変更したら、パッチバージョンを上げる。これは AWS ドキュメントの推奨とも一致します。頻繁に更新するファイルには、無効化に頼るよりもバージョン付きのファイル名を優先して使うというものです。
  2. どうしてもバージョン番号を再利用する場合は、プラグインのパスに対して CloudFront の無効化を実行する。
    aws cloudfront create-invalidation --distribution-id <ディストリビューション ID> \
      --paths "/wp-content/plugins/<プラグインのディレクトリ>/*"
  3. キャッシュポリシーが本当にクエリ文字列をキャッシュキーに含めているかを確認する。マネージドポリシー CachingOptimized はクエリ文字列を含まず、デフォルト TTL は 24 時間です。これで wp-content をキャッシュすると、通常のバージョン更新でさえ古いファイルが返されます。

制約と注意点

  • ブラウザ自体のキャッシュは CloudFront とは無関係です。CDN のキャッシュを消しても、一部の訪問者には古いファイルが表示され続ける可能性があります。この層もバージョン番号の変更で解決できます。
  • アクセシビリティのチェック(bin/audit-accessibility.py、bin/audit-frontend.py)は実際のブラウザを操作する必要があるため手作業の手順で、ゲートには含まれていません。README の記録によると、これらは静的解析では見つからない問題を見つけたことがあり、例えば一度に、アクセシブルな名前のないコントロールを 20 個見つけました。

リンク