ブラウザ更新モードで除外プラグインが更新されてしまう罠 — Playwright 経路への設定反映と残余チェック誤警告の修正
症状
SSH を使わないブラウザ更新モード(Playwright 経由)でメンテナンスを実行すると、「更新チェック除外プラグイン」に指定したプラグインまで更新されていた。さらに更新後に「プラグイン更新が残っています」という WARNING メールが毎回届いていた。
除外プラグインを意図的に更新しないよう設定しているにもかかわらず、それが無視されて更新される。しかも除外プラグインが update-core.php に「更新あり」で残り続けるため、残余チェックが「未更新あり」と誤判定して WARNING を出す——という二重の問題だった。
SSH 経路と Playwright 経路の違い
SSH 経路では、WP-CLI の --skip-plugins オプションに ignored_plugins(除外プラグイン)のスラッグを渡すことで、更新対象から除外する仕組みが実装済みだった。
一方、Playwright 経路で使われる browser_update_remaining_plugins() は、update-core.php の「すべて選択」チェックボックスを Click して全プラグインを一括選択し、そのまま更新フォームを送信していた。除外プラグインを個別に外す処理は一切なかった。
# 修正前: Select-All で全件チェック(除外設定を無視)
page.check('input[name="action"][value="update-selected-plugins"]')
for cb in page.query_selector_all('input[name="checked[]"]'):
cb.check()
この関数はブラウザ単独での本体更新(run_browser_update_flow)だけでなく、SSH 完了後に走るブラウザ補完更新(run_browser_residual_update・既定 ON)でも呼ばれる。つまり SSH サイトでも補完更新フェーズで除外プラグインが更新されてしまう潜在リスクがあった。なお、ブラウザ経由の一括更新はピンポイントロールバックの対象外であるため、誤更新が起きても自動で戻せない。
修正① — _ignored_plugin_slugs() と _plugin_slug_from_checkbox_value()
除外指定がある場合は Select-All を使わず、チェックボックスを 1 件ずつ評価するように変更した。
def _ignored_plugin_slugs(site: dict) -> set[str]:
"""サイト設定から除外スラッグのセットを返す。"""
raw = site.get("ignored_plugins", "")
return {s.strip().lower() for s in raw.split(",") if s.strip()}
def _plugin_slug_from_checkbox_value(value: str) -> str:
"""'slug/file.php' 形式の value からスラッグ部分を取り出す。"""
return value.split("/")[0].strip().lower() if "/" in value else value.strip().lower()
update-core.php のプラグイン更新チェックボックスは value="contact-form-7/wp-contact-form-7.php" のような形式(スラッグ/メインファイル)になっている。そのため value の / より前を切り出してスラッグを正規化し、除外セットと照合する。
除外指定がある場合の処理:
ignored = _ignored_plugin_slugs(site)
if ignored:
for cb in page.query_selector_all('input[name="checked[]"]'):
val = cb.get_attribute("value") or ""
slug = _plugin_slug_from_checkbox_value(val)
if slug and slug not in ignored:
cb.check()
# value が取得できない場合は安全側(更新対象に残す)
除外指定がないサイトは従来どおり Select-All → 個別フォールバックの動作を完全に温存している。
修正② — _browser_plugin_residual_state() で残余チェックの誤警告を解消
更新後の残余チェックも同様の問題を抱えていた。修正前は update-core.php に更新フォームが存在するかどうかだけで「未更新あり」を判定していたため、除外プラグインが意図的に残っていても WARNING になっていた。
def _browser_plugin_residual_state(page, site: dict) -> bool:
"""除外スラッグを除いた残余プラグインが存在するか判定する。"""
ignored = _ignored_plugin_slugs(site)
checkboxes = page.query_selector_all('input[name="checked[]"]')
if not checkboxes:
return False # フォームなし = 残余なし
if not ignored:
return True # 除外なし = 従来どおりフォーム存在 = pending
for cb in checkboxes:
val = cb.get_attribute("value") or ""
slug = _plugin_slug_from_checkbox_value(val)
if slug not in ignored:
return True # 除外以外が 1 件でも残っていれば pending
return False # 残っているのが除外スラッグだけなら pending ではない
この変更で、除外プラグインのみが残っているケースは False(残余なし)と判定されるため、WARNING メールが飛ばなくなる。
テスト設計
tests/test_browser_ignored_plugins.py として 21 件の新規テストを追加した。
- スラッグ正規化 7 件: 単一ファイル形式 / 複数ファイル / 大小文字・空白ゆらぎ / value 欠落
- 除外反映 9 件: 単一除外 / 複数除外 / 全除外(更新スキップ)/ 除外なし全件更新 / 未存在除外スラッグの誤除外なし
- 残余チェック 5 件: 残余=除外のみ→非 pending / 残余=除外+非除外→pending / 除外なし従来動作 / フォームなし=非 pending
関連記事: 一括選択チェックボックスが「チェックしたつもり」で反映されない罠 / Playwright の生エラーをそのままユーザーに見せない設計