Plugin Directory: Sync plugin status to the update API during a release cooldown - #783
Plugin Directory: Sync plugin status to the update API during a release cooldown#783obenland wants to merge 4 commits into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a release-cooldown edge case in the Plugin Directory’s Update API sync: while a new version is held back, plugin status/availability changes (close/disable/reopen) are now synced immediately to the existing update_source row, instead of being deferred along with the version bump.
Changes:
- Update cooldown handling so only the version bump is deferred; status-dependent fields (availability + closure meta) sync immediately via a new
update_row_availability(). - Extract shared logic into helpers (
get_close_meta(),clear_plugin_caches()) so both full writes and availability-only syncs stay consistent. - Add a PHPUnit test suite for cooldown behavior, and enhance the test bootstrap to define
PLUGINS_TABLE_PREFIXand create required stub tables.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php | Splits cooldown behavior into deferred version updates vs immediate availability/meta sync; extracts shared helpers. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/bootstrap.php | Defines PLUGINS_TABLE_PREFIX and creates stub external tables for tests using the shared SQL file. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/Update_Source_Cooldown_Test.php | Adds coverage for cooldown scenarios (deferred version bump, close/disable/reopen sync, and no-row-first-release behavior). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…se cooldown. update_single_plugin() used to defer the whole update_source write while a new version sat inside its release cooldown, so closing, disabling, or reopening the plugin never reached the row — sites kept being offered a closed plugin, and a reopened one stayed withdrawn — until the cooldown expired. Now only the version bump waits: status changes sync the row's availability, closure meta, and freshness immediately, while it keeps serving the previous release's data. The tests bootstrap defines PLUGINS_TABLE_PREFIX for runners whose test config lacks it; the stub tables themselves are created by the test environment's after-start script. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5cec7df to
8d32d6c
Compare
- Test row existence instead of version-string truthiness before syncing: a stored version of '0' (or '') read as "no row" and skipped the status sync — the exact gap the cooldown sync closes. - Fetch the row's version and meta in one query and pass the meta into update_row_availability(), dropping its redundant second SELECT. - Replace the hand-built plugin_information cache purge with Plugins_Info_API::flush_plugin_information_cache(): the hand-built keys used raw GP_Locales casing while the API stores lowercased locales, making the purge a no-op for every mixed-case locale. - Extract the availability predicate into is_available() instead of duplicating it (with strictness drift) across both writers. - Note in the cooldown comment that cron_trigger() keeps re-selecting the plugin until the cooldown expires; the re-pass is an idempotent no-op. - Add the missing native return type to the test helper. See WordPress#783. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The PLUGINS_TABLE_PREFIX fallback define is dropped from the tests bootstrap — wp-env writes the constant into wp-tests-config.php from .wp-env.test.json, and nothing in this suite reads it — which reverts the bootstrap to trunk. Also trims the test-runner exposition from the class docblock and tightens the cooldown gate comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php:96
$existing_rowcan be null when there is noupdate_sourcerow; using$existing_row->version ?? ''still attempts the property read and can emit a PHP warning. Use the nullsafe operator (or an explicit conditional) to avoid warnings in the common “no row yet” case.
$existing_version = (string) ( $existing_row->version ?? '' );
wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php:125
- During a release cooldown,
cron_trigger()will keep selecting the plugin (version mismatch is expected), so this call path runs every hour. Callingupdate_row_availability()unconditionally means an UPDATE query is executed on every pass even when availability/close meta/last_updated haven’t changed. Consider short-circuiting before the UPDATE when the computed availability + close meta match the current row, to reduce steady-state DB write load during long cooldowns.
}
return true;
…held. Stamping last_updated in the partial status sync consumed one of cron_trigger()'s staleness signals. Recovery from a lost deferred cron event then rested solely on the version clauses, which go blind when a stored version matches the new one's 128-character truncation. Leaving last_updated behind preserves the backup path; the repeated re-select is an idempotent no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99fc33b to
bd96084
Compare
Summary
Fixes a gap in the release cooldown: while a new version is being held back,
API_Update_Updater::update_single_plugin()defers the wholeupdate_sourcewrite — so a status change made during those hours never reaches the update API. Closing a plugin mid-cooldown keeps offering it to sites; reopening one keeps it withdrawn; disabling one never records its closure meta.The fix
Only the version bump waits for the cooldown. A status change made mid-cooldown syncs the existing row immediately — availability, closure meta (
closed_at/closed_reason), andlast_updated— via a newupdate_row_availability(), while the row keeps serving the previous release's data, so version-specific columns (version,stable_tag,release_time, rollout) never describe the held version. A plugin whose first-ever release is in cooldown has no row and gets none until the cooldown expires.Shared logic is extracted rather than duplicated:
get_close_meta()(closure fields, used by the full write and the sync) andclear_plugin_caches()(the update-check/info-API cache purge, which every row write must be followed by).Note that
cron_trigger()'s out-of-date query still re-selects an in-cooldown plugin every hourly run — the row'sversion/stable_tagdiffer from the post's by construction until the cooldown expires, and that mismatch can't be resolved without releasing the version. Each re-pass is an idempotent no-op: the status sync re-writes values the row already holds.Review fixes (second commit)
versionstring, so a stored'0'(or'') read as "no row" and skipped the status sync. It now tests row existence, and the row'sversionandmetaare fetched in one query, droppingupdate_row_availability()'s redundant second SELECT.clear_plugin_caches()now reusesPlugins_Info_API::flush_plugin_information_cache()instead of hand-buildingplugin_information:{slug}:{locale}keys from rawGP_Localescasing — the API stores those keys lowercased, so the hand-built purge was a no-op for every mixed-case locale (de_DE,pt_BR, …).is_available()instead of being duplicated (with strictness drift) across both writers.Testing
New
tests/Update_Source_Cooldown_Test.php: the version bump stays deferred; a closure withdraws the row immediately (still on the served version); a disable records closure meta with the row still available; a reopen restores the row; a first release in cooldown creates no row.The stub
update_source/svn_accesstables and thePLUGINS_TABLE_PREFIXconstant come from the wp-env test environment: the after-start script imports the shared SQL file on every start (locally and in CI), and the constant is written into wp-tests-config.php. The WP test installer drops only core tables, so the stub tables persist across runs, and the test clears leftover rows per plugin.Notes
This is split out of #777, which builds its scan-driven release block on the same "hold the version, sync the status" write path. #777 will be rebased onto this once it lands.
🤖 Generated with Claude Code