Plugins: Add a release delay between SVN commit of update, and publish#650
Plugins: Add a release delay between SVN commit of update, and publish#650dd32 wants to merge 15 commits into
Conversation
…releases. Introduces a release cooldown that holds new versions back from the update API for RELEASE_COOL_DOWN_DELAY (48hrs) after commit / final author confirmation. The previous version continues to be served during the window, giving scanners and reviewers time to flag supply-chain attacks before the new version ships to sites. Plugin reviewers can bypass the cooldown for urgent security fixes via a new Force-release control on the Plugin Controls metabox (requires a reason; logged via Tools::audit_log). Closed/disabled plugins, rebuild scripts, and other status-change paths bypass the cooldown so closures take effect immediately. release_time is re-anchored to the moment the version actually becomes public so the existing phased_rollout manual-updates-24hr window still measures from public availability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… just publish. Disabled plugins keep available=1 in update_source and continue to serve updates through the API. The cooldown gate was only checking for publish, so a new version committed to a disabled plugin would write through immediately, defeating the purpose for that subset. Closed plugins remain excluded -- available flips to 0, so there is nothing to gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hoist the force_released early-exit into the caller so the helper signature shrinks to (release_time, existing_version, new_version) plus an injectable now. Drop the post_status check from both the helper and the release_time anchor -- callers are responsible for bypassing when their context calls for it. Status_Transitions::flush_caches() now passes bypass=true so closures and disables take effect immediately rather than waiting for an in-flight cooldown window. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… is 0; tidy. When RELEASE_COOL_DOWN_DELAY is 0 the feature is off -- skip the deferral, the release_time anchor, the wp-admin Controls metabox section, the shortcode info banner, and the per-release status line. Sites running with the cooldown disabled see no related UI at all. Simplifications: - Inline get_cooldown_defer_time() (single caller; pure cooldown math is 4 lines). - Drop the metabox force-released audit line; Tools::audit_log writes an internal note that the Internal Notes metabox already renders. - Drop the "Serving to sites since X ago" line in the shortcode -- the existing "Released X ago by Y" line above already covers that state. - Drop the unused $user arg to audit_log() in force_release(); it defaults to wp_get_current_user() which is what we set $user to anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The form only renders for users with the plugin_review capability, the save_post handler re-checks the same capability, and the whole thing posts inside wp-admins post.php form which is already nonced by core. The extra _force_release_nonce was just noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ease_delay. Stores the cooldown delay on the release at creation time via add_release()s defaults, captured from the RELEASE_COOL_DOWN_DELAY constant. The cooldown gate and release_time anchor both read release_delay (?? 0) instead of the global constant -- pre-feature releases coalesce to 0 and write through unchanged, future constant changes do not retroactively affect in-flight releases. Force-release just sets release_delay = 0 on the release, replacing the four force_released, force_released_by, force_released_at, and force_released_reason fields. The reviewer, time, and reason are already captured by Tools::audit_log() so storing them twice was redundant. Drops the author-facing Force-released by a plugin reviewer X ago message along with that meta -- authors just see the cooldown countdown disappear. Verifies core post.php nonce explicitly in save_post to satisfy phpcs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cron schedule helper collapses to wp_clear_scheduled_hook +
wp_schedule_single_event -- the per-plugin hook name makes args unnecessary
for dedupe and the always-clear approach is shorter than the next-scheduled
dance. The cron handler recovers the slug from current_filter() so no args
need flow through.
bypass_cooldown is no longer load-bearing once release_delay is per-release:
- cron_trigger_release: fires at cooldown_until, gate check
(cooldown_until > time()) is false by definition.
- force_release: sets release_delay = 0 before update_single_plugin,
which short-circuits the gate.
- rebuild script: cooldown gate correctly preserves in-flight cooldowns
instead of stomping them.
- status_transitions: status changes that do not bump version match
existing_version == new_version and write through naturally. Status
changes that coincide with a deferred new version write through at
cooldown_until (worst-case 48h delay) -- the OLD safe version
continues to be served in the meantime, which is the right call.
Also reorders force_release() to call audit_log BEFORE the add_release that
zeroes release_delay, so the log line captures the original delay rather
than relying on a stale in-memory copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… the tests. Replaces the static info banner on the release-confirmation shortcode page with a targeted notice that only appears for committers when one of their plugins has a release currently inside the cooldown window. Matches the existing frontend_unconfirmed_releases_notice() pattern (same metabox plumbing via template-tags.php and plugin-single.php). Notice copy names the version, the relative time until it ships, the cooldown duration, and the plugins@wordpress.org contact for security expedites. Drops the Plugin_Update_Cooldown_Test.php file -- only covered compute_release_time() which is straightforward inline logic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
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 introduces a “release cooldown” window between when a plugin release is committed (or finally confirmed) and when the new version is written to update_source (and thus becomes available via the plugin update API). It also surfaces the cooldown state to plugin committers on the public plugin page and provides reviewers an audited “force release” escape hatch.
Changes:
- Add a per-release
release_delay(seeded fromRELEASE_COOL_DOWN_DELAY) and deferupdate_sourcewrites for new versions until the cooldown elapses. - Implement a per-plugin deferred cron hook (
release_to_update_api:{slug}) and a reviewer-only force-release flow with required reason + audit log entry. - Display cooldown status in release confirmation UI and show an informational notice to committers on the public plugin page during cooldown.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/template-parts/plugin-single.php | Renders the new cooldown notice block on plugin pages. |
| wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php | Adds a theme template tag wrapper for the cooldown notice. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php | Shows cooldown status in release confirmation output + adds frontend committer notice. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/plugin-directory.php | Defines the global cooldown constant. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-manager.php | Registers the new colon-hook cron handler (release_to_update_api). |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php | Implements deferral logic, scheduling, release-time computation, and reviewer force-release. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/class-plugin-directory.php | Persists release_delay onto newly created release records. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/metabox/class-controls.php | Adds admin cooldown status display + reviewer force-release controls + save handler. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/class-status-transitions.php | Minor comment punctuation adjustment. |
| wordpress.org/public_html/wp-content/plugins/plugin-directory/admin/class-customizations.php | Hooks the Controls metabox save handler. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| ); | ||
|
|
||
| $release_delay = (int) ( $release['release_delay'] ?? 0 ); |
| * and humans a window to flag bad releases. Plugin reviewers can bypass the cooldown | ||
| * via the wp-admin force-release action; see Jobs\API_Update_Updater::update_single_plugin(). | ||
| */ | ||
| define( __NAMESPACE__ . '\RELEASE_COOL_DOWN_DELAY', 24 * HOUR_IN_SECONDS ); |
…ASE_DELAY constant. Lets the plugin and theme directory cooldowns be tuned (or disabled) in lockstep from a single override point. Falls back to the hard-coded 24h limit when the shared constant isn't set. Mirrors the matching change on the theme side in WordPress#651. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce get_release_cooldown_delay( $plugin_slug ) which returns the RELEASE_COOL_DOWN_DELAY default passed through the new `wporg_plugins_release_cooldown_delay` filter, with the plugin slug passed along when known. The filter can shorten, extend, or remove (return 0) the delay per-plugin. Captured onto the release in add_release() exactly as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| * sites by the api.wordpress.org plugin update-check API. The previous version remains | ||
| * served until the cooldown elapses. Mitigates supply-chain attacks by giving scanners | ||
| * and humans a window to flag bad releases. Plugin reviewers can bypass the cooldown | ||
| * via the wp-admin force-release action; see Jobs\API_Update_Updater::update_single_plugin(). |
…verride. Default RELEASE_COOL_DOWN_DELAY to 0 (cooldown disabled) for now, to be raised once the workflow is ready, and wrap the define() in an if ( ! defined() ) guard so it can be pre-defined from global config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The problem
Today, a plugin release goes from SVN commit to "available via the update API" within seconds — as soon as the import pipeline finishes. There is no human-scale window between the commit and millions of sites pulling the new ZIP on their next
update check.
This narrow turnaround leaves the window open for a number of supply-chain risks:
This PR introduces a short cooldown between commit and serve, so there's a real window for both automated scanners and humans (the author included) to catch problems before the update reaches sites.
How this helps plugin authors
the broken version. Today, the broken version is already in the field by the time you spot it.
plugins@wordpress.org. A reviewer can force-release in seconds with the reason logged for audit.How this helps WordPress.org
manual-updates-24hrrollout strategy still applies on top — authors who opted in get the cooldown plus their auto-update delay. Closure and existing review tooling continue to work unchanged.update_source, with no changes required to the closed-source api.wordpress.org update-check endpoint or its caches. The previous version simply continues to be served until the new row lands.Test plan
update_sourcecontinues to show the previous version. Confirm arelease_to_update_api:{slug}cron event is scheduled forrelease_time + 24h.update_sourceand the cron event is cleared.plugin_reviewcap), confirm the "Force release" button + reason textarea render in the Controls metabox during cooldown. Submit with a reason. Confirm: row inupdate_sourceis rewritten immediately with the newversion,
release_delayon the release meta is zeroed, audit log entry includes the reason.RELEASE_COOL_DOWN_DELAYto0. Confirm: no notice on plugin pages, no force-release UI in metabox, no deferral on commit.disabledstatus. Confirm the cooldown applies the same way.manual-updates-24hr, confirm both gates apply in sequence: 24h cooldown (no version served), then 24h after release withdisable_autoupdate = true.