From a89397e7eea8274f20b6c30cacfaca1f89e5a24e Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Mon, 7 Jul 2025 03:48:04 +0000 Subject: [PATCH 01/30] Add the phased rollout general option to plugin advanced section. --- .../plugin-directory/class-template.php | 16 +++++ .../wporg-plugins-2024/inc/template-tags.php | 59 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php index 1125025e68..e0bad66253 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php @@ -1001,6 +1001,22 @@ public static function get_release_confirmation_link( $tag, $post = null, $what ); } + /** + * Generates a link to enable Release Confirmations. + * + * @param int|\WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. + * @return string URL to enable confirmations. + */ + public static function get_phased_rollout_link( $post = null ) { + $post = get_post( $post ); + + return add_query_arg( + array( '_wpnonce' => wp_create_nonce( 'wp_rest' ) ), + home_url( 'wp-json/plugins/v1/plugin/' . $post->post_name . '/phased-rollout' ) + ); + } + + /** * Returns the reasons for closing or disabling a plugin. * diff --git a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php index 91a022adbc..6a3282fa6c 100644 --- a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php +++ b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php @@ -478,6 +478,9 @@ function the_plugin_danger_zone() { // Output the Release Confirmation form. the_plugin_release_confirmation_form(); + // Output the phased rollout settings. + the_phased_rollout_settings(); + if ( 'publish' != $post->post_status ) { // A reminder of the closed status. the_active_plugin_notice(); @@ -772,3 +775,59 @@ function the_author_notice( $post = null ) { ); } } + +/** + * Displays the "phased rollout" settings for a plugin. + */ +function the_phased_rollout_settings() { + $post = get_post(); + if ( ! current_user_can( 'plugin_manage_releases', $post ) ) { + return; + } + $rollout = $post->phased_rollout ?: ''; + + $options = [ + '' => __( 'Immediate (default)', 'wporg-plugins' ), + 'slow' => __( 'Slow rollout', 'wporg-plugins' ), + 'extra-slow' => __( 'Extra slow rollout', 'wporg-plugins' ), + 'cautious' => __( 'Cautious rollout', 'wporg-plugins' ), + 'custom' => __( 'Custom rollout', 'wporg-plugins' ), + ]; + + $descriptions = [ + '' => __( 'Plugin updates will be made available to sites as soon as they ask about updates.', 'wporg-plugins' ), + 'slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), + 'extra-slow'=> __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), + 'cautious' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), +// 'custom' => __( 'Plugin updates will be released to a custom percentage of sites, as defined by the plugin author.', 'wporg-plugins' ), + ]; + + echo '

' . __( 'Phased rollout', 'wporg-plugins' ) . '

'; + + echo '

' . + __( 'Phased rollout of a plugin initially delivers updates to a small selection of sites, increasing over time.', 'wporg-plugins' ) . + ' ' . + __( 'This allows for the plugin author to limit the impact of a change in a plugin which may negatively impact user experience, to receive that feedback, and resolve the issue before the plugin update is delivered to all websites.', 'wporg-plugins' ) . + '

'; + + echo '
'; + echo ''; + + echo '
' . esc_html( $descriptions[ $rollout ] ?? '' ) . '
'; + echo ''; + + echo '

'; + echo ''; + echo '

'; + echo '
'; +} From c89df5379d8cb6974941f7e2beb317b10889534d Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 01:12:55 +0000 Subject: [PATCH 02/30] Add functions that would otherwise live in the closed-source plugins update api. --- .../standalone/plugin-update-helpers.php | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php new file mode 100644 index 0000000000..46a4f58d53 --- /dev/null +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -0,0 +1,141 @@ +percentage ) && $phase_details->percentage >= $site_percent ) { + return true; + } + + if ( empty( $phase_details['time'] ) ) { + // If no time is set, assume the update is available. + return true; + } + + $hours_since_release = ( time() - $phase_details['time'] ) / 3600; + + switch( $phase_details['strategy'] ?? 'immediate' ) { + default: + case 'immediate': + // Immediate updates are always applied. + return true; + + // Start at 5%, increases to 100% over the next 48hrs. + case 'slow': + $a = -1037.12128; + $b = -0.00391789; + $c = 0.988961; + break; + + // Starts at 5%, increases to 100% over the next 72hrs + case 'extra-slow': + $a = 58.4925; + $b = -0.0111363; + $c = 0.821332; + break; + + // Starts at 1%, + case 'cautious': + $a = -41.46565; + $b = 0.0078509; + $c = 0.945984; + break; + + case 'should-switch-to-static-map'; + $map = [ + 0 => 0.01, // 1% + 1 => 0.05, // 5% + 2 => 0.10, // 10% + 3 => 0.20, // 20% + 4 => 0.30, // 30% + 5 => 0.40, // 40% + 6 => 0.50, // 50% + 7 => 0.60, // 60% + 8 => 0.70, // 70% + 9 => 0.80, // 80% + 10 => 0.90, // 90% + ]; + } + + // There's a better formula for this I'm sure, but this will do to start with. + // y = a . log(bx+c) + $percent = $a * log( $b * $hours_since_release + $c ); + + return ( $site_percent <= $percent ); +} From 42aadbb07dace620b7d23443a8f8ba4f8b5ebde7 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 01:13:31 +0000 Subject: [PATCH 03/30] format the code for easier reading. --- .../themes/pub/wporg-plugins-2024/inc/template-tags.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php index 6a3282fa6c..f44964a09a 100644 --- a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php +++ b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php @@ -795,7 +795,7 @@ function the_phased_rollout_settings() { ]; $descriptions = [ - '' => __( 'Plugin updates will be made available to sites as soon as they ask about updates.', 'wporg-plugins' ), + '' => __( 'Plugin updates will be released to all sites as soon as they check for updates.', 'wporg-plugins' ), 'slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), 'extra-slow'=> __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), 'cautious' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), @@ -813,7 +813,12 @@ function the_phased_rollout_settings() { echo '
'; echo ''; From 8e9377117b1b62b1c61216beb7ca8287f11b7bdc Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 01:24:46 +0000 Subject: [PATCH 04/30] Clarify comments and TODOs. --- .../jobs/class-api-update-updater.php | 9 +++++++++ .../shortcodes/class-release-confirmation.php | 2 ++ .../standalone/plugin-update-helpers.php | 12 +++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php index 95267f9ce4..f832181c8a 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php @@ -92,6 +92,15 @@ public static function update_single_plugin( $plugin_slug, $self_loop = false ) } } + if ( $post->phased_rollout ) { + $meta['phased_rollout'] = array( + 'strategy' => $post->phased_rollout, + // TODO: This should be the release time of the update, is this correct? + 'time' => strtotime( $post->post_modified ), + ); + } + // TODO: Per-release phased rollout details from Release Confirmation should override the above. + $data = array( 'plugin_id' => $post->ID, 'plugin_slug' => $post->post_name, diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php index baeffcdda0..fc3b2e0a12 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php @@ -164,6 +164,8 @@ static function single_plugin( $plugin ) { self::get_approval_text( $plugin, $data ), self::get_actions( $plugin, $data ) ); + + // TODO: per-release phased rollout settings should be available here. } echo ''; diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 46a4f58d53..ad4712bb8d 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -76,15 +76,24 @@ function phased_rollout_should_update( $phase_details ) { return true; } + /* + * $phase_details are expected to be in the format of... + * { + * "strategy": "slow", + * "time": Release time in seconds since the epoch, + * } + */ + $site_percent = get_site_percentage(); // If the phased percentage is set directly in the details. if ( isset( $phase_details->percentage ) && $phase_details->percentage >= $site_percent ) { + // TODO: This is the 'custom' strategy, which is not yet implemented. return true; } + // If no time is set, assume the update is available. if ( empty( $phase_details['time'] ) ) { - // If no time is set, assume the update is available. return true; } @@ -135,6 +144,7 @@ function phased_rollout_should_update( $phase_details ) { // There's a better formula for this I'm sure, but this will do to start with. // y = a . log(bx+c) + // TODO: This is totally broken and does not return a percentage at all. Should probably switch to a map instead. $percent = $a * log( $b * $hours_since_release + $c ); return ( $site_percent <= $percent ); From c5ca4a75e7e2b51a055be500bfc239aee71f561e Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 01:28:21 +0000 Subject: [PATCH 05/30] Somewhat cleaner get_site_percentage(). --- .../standalone/plugin-update-helpers.php | 33 ++++--------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index ad4712bb8d..64a38bfae7 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -20,11 +20,6 @@ function get_site_percentage() { global $wp_url; - static $site_percent; - if ( $site_percent ) { - return $site_percent; - } - /* * If the site URL hasn't been extracted already, pull it from the global. * NOTE: This may be set by the tests or other codepaths that run before this function. @@ -33,33 +28,17 @@ function get_site_percentage() { $wp_url = $m[1]; } - // If no URL is set, delay the update until fully rolled out. - if ( ! $wp_url ) { - return 100; - } - - $site_url = strtolower( $wp_url ); + $site_domain = strtolower( parse_url( $wp_url, PHP_URL_HOST ) ); - // Strip off the scheme. - if ( $pos = strpos( $site_url, '://' ) ) { - $site_url = substr( $site_url, $pos + 3 ); - } - // ... and path. - if ( - ( $pos = strpos( $site_url, '/' ) ) || - ( $pos = strpos( $site_url, '?' ) ) || - ( $pos = strpos( $site_url, '#' ) ) - ) { - $site_url = substr( $site_url, 0, $pos ); - } - - // Again, if we've reached this point and have no URL, delay the update. - if ( ! $site_url ) { + // If we've reached this point and have no URL, delay the update until 100% is reached. + if ( ! $site_domain ) { return 100; } + // TODO: We probably need to have this be based on ( site_domain, plugin_slug, version ) instead. + // $site_step represents an integer from 0 to 4095. - $site_step = base_convert( substr( md5( $site_url ), 0, 3 ), 16, 10 ); + $site_step = base_convert( substr( md5( $site_domain ), 0, 3 ), 16, 10 ); $site_percent = $site_step / 4095 * 100; return $site_percent; From 7614815073e980149fe9c16dbb013828c6af7200 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 03:08:48 +0000 Subject: [PATCH 06/30] Use the plugin slug/version in the percent calc. --- .../standalone/plugin-update-helpers.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 64a38bfae7..5bb677f0e2 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -15,9 +15,13 @@ * Return the current sites update-percentage. * * @global $wp_url + * + * @param string $slug The plugin slug. + * @param string $version The plugin version. + * * @return float 0...100.00 */ -function get_site_percentage() { +function get_site_percentage( string $slug = '', string $version = '' ) { global $wp_url; /* @@ -35,10 +39,8 @@ function get_site_percentage() { return 100; } - // TODO: We probably need to have this be based on ( site_domain, plugin_slug, version ) instead. - // $site_step represents an integer from 0 to 4095. - $site_step = base_convert( substr( md5( $site_domain ), 0, 3 ), 16, 10 ); + $site_step = base_convert( substr( md5( "{$site_domain}|{$slug}|{$version}" ), 0, 3 ), 16, 10 ); $site_percent = $site_step / 4095 * 100; return $site_percent; @@ -47,10 +49,11 @@ function get_site_percentage() { /** * Determine if the site should update based on the phased rollout details. * - * @param object|array $phase_details The details of the phased rollout. + * @param object $update_details The plugin update details. * @return bool True if the site should update, false otherwise. */ -function phased_rollout_should_update( $phase_details ) { +function phased_rollout_should_update( object $update_details ) { + $phase_details = $update_details->meta->phased_rollout ?? false; if ( ! $phase_details ) { return true; } @@ -63,7 +66,7 @@ function phased_rollout_should_update( $phase_details ) { * } */ - $site_percent = get_site_percentage(); + $site_percent = get_site_percentage( $update_details->plugin_slug, $update_details->version ); // If the phased percentage is set directly in the details. if ( isset( $phase_details->percentage ) && $phase_details->percentage >= $site_percent ) { From 5d14a8c9ccc9adca91e0eefdb604cf982d83020e Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 04:58:59 +0000 Subject: [PATCH 07/30] Fix the release curves. --- .../standalone/plugin-update-helpers.php | 68 +++++++------------ 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 5bb677f0e2..a999b5bd62 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -49,12 +49,14 @@ function get_site_percentage( string $slug = '', string $version = '' ) { /** * Determine if the site should update based on the phased rollout details. * + * @link https://www.desmos.com/calculator/59sl7efajq + * * @param object $update_details The plugin update details. * @return bool True if the site should update, false otherwise. */ function phased_rollout_should_update( object $update_details ) { $phase_details = $update_details->meta->phased_rollout ?? false; - if ( ! $phase_details ) { + if ( ! $phase_details || empty( $phase_details['time'] ) ) { return true; } @@ -63,71 +65,47 @@ function phased_rollout_should_update( object $update_details ) { * { * "strategy": "slow", * "time": Release time in seconds since the epoch, + * "percentage": If the strategy is "custom", this is the percentage of sites that should receive the update. * } */ - $site_percent = get_site_percentage( $update_details->plugin_slug, $update_details->version ); - - // If the phased percentage is set directly in the details. - if ( isset( $phase_details->percentage ) && $phase_details->percentage >= $site_percent ) { - // TODO: This is the 'custom' strategy, which is not yet implemented. - return true; - } + $site_percent = get_site_percentage( $update_details->plugin_slug, $update_details->version ); + $hours_since_release = ( time() - $phase_details['time'] ) / 3600; - // If no time is set, assume the update is available. - if ( empty( $phase_details['time'] ) ) { + if ( $hours_since_release >= 120 ) { + // If more than 5 days have passed, assume the update is available. return true; } - $hours_since_release = ( time() - $phase_details['time'] ) / 3600; - switch( $phase_details['strategy'] ?? 'immediate' ) { default: case 'immediate': - // Immediate updates are always applied. - return true; + $percent = 100; + break; + + // Custom defined by the plugin author. + case 'custom': + $percent = $phase_details['percentage'] ?? 100; - // Start at 5%, increases to 100% over the next 48hrs. + // Straight curve, start at 5%, increases to 100% over the next 48hrs (2d). case 'slow': - $a = -1037.12128; - $b = -0.00391789; - $c = 0.988961; + $percent = 5 + ( $hours_since_release / 48 ) * 95; break; - // Starts at 5%, increases to 100% over the next 72hrs + // Polynomial curve, starts at 5%, increases to 100% over the next 72hrs (3d). case 'extra-slow': - $a = 58.4925; - $b = -0.0111363; - $c = 0.821332; + $percent = 9 * ( 1.0345 ** $hours_since_release ) - 3.9; break; - // Starts at 1%, + // Polynomial curve, starts at 1%, with an increase to 100% over the next 120hrs (5d). case 'cautious': - $a = -41.46565; - $b = 0.0078509; - $c = 0.945984; + $percent = 11 * ( 1.0195 ** $hours_since_release ) - 10; break; - - case 'should-switch-to-static-map'; - $map = [ - 0 => 0.01, // 1% - 1 => 0.05, // 5% - 2 => 0.10, // 10% - 3 => 0.20, // 20% - 4 => 0.30, // 30% - 5 => 0.40, // 40% - 6 => 0.50, // 50% - 7 => 0.60, // 60% - 8 => 0.70, // 70% - 9 => 0.80, // 80% - 10 => 0.90, // 90% - ]; } - // There's a better formula for this I'm sure, but this will do to start with. - // y = a . log(bx+c) - // TODO: This is totally broken and does not return a percentage at all. Should probably switch to a map instead. - $percent = $a * log( $b * $hours_since_release + $c ); + if ( $percent >= 100 ) { + return true; + } return ( $site_percent <= $percent ); } From 086708b71a504f28ec5b7f13d78ca87cdaa91965 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 05:08:26 +0000 Subject: [PATCH 08/30] Somewhat document the curve.. --- .../standalone/plugin-update-helpers.php | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index a999b5bd62..c5e393eb60 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -87,17 +87,47 @@ function phased_rollout_should_update( object $update_details ) { case 'custom': $percent = $phase_details['percentage'] ?? 100; - // Straight curve, start at 5%, increases to 100% over the next 48hrs (2d). + /* + * Straight curve, start at 5%, increases to 100% over the next 48hrs (2d). + * + * At 6 hours, the percentage is 5 + (6/48) * 95 = 16.875% + * At 12 hours, the percentage is 5 + (12/48) * 95 = 28.75% + * At 24 hours, the percentage is 5 + (24/48) * 95 = 52.5% + * At 36 hours, the percentage is 5 + (36/48) * 95 = 72.25% + * At 48 hours, the percentage is 5 + (48/48) * 95 = 100% + */ case 'slow': $percent = 5 + ( $hours_since_release / 48 ) * 95; break; - // Polynomial curve, starts at 5%, increases to 100% over the next 72hrs (3d). + /* + * Polynomial curve, starts at 5%, increases to 100% over the next 72hrs (3d). + * + * At 6 hours, the percentage is 9 * ( 1.0345 ** 6 ) - 3.9 = 7.13% + * At 12 hours, the percentage is 9 * ( 1.0345 ** 12 ) - 3.9 = 9.62% + * At 24 hours, the percentage is 9 * ( 1.0345 ** 24 ) - 3.9 = 16.41% + * At 36 hours, the percentage is 9 * ( 1.0345 ** 36 ) - 3.9 = 26.61% + * At 48 hours, the percentage is 9 * ( 1.0345 ** 48 ) - 3.9 = 41.95% + * At 60 hours, the percentage is 9 * ( 1.0345 ** 60 ) - 3.9 = 64.97% + * At 72 hours, the percentage is 9 * ( 1.0345 ** 72 ) - 3.9 = 100% + */ case 'extra-slow': $percent = 9 * ( 1.0345 ** $hours_since_release ) - 3.9; break; - // Polynomial curve, starts at 1%, with an increase to 100% over the next 120hrs (5d). + /* + * Polynomial curve, starts at 1%, with an increase to 100% over the next 120hrs (5d). + * + * At 6 hours, the percentage is 11 * ( 1.0195 ** 6 ) - 10 = 2.35% + * At 12 hours, the percentage is 11 * ( 1.0195 ** 12 ) - 10 = 3.87% + * At 24 hours, the percentage is 11 * ( 1.0195 ** 24 ) - 10 = 7.49% + * At 36 hours, the percentage is 11 * ( 1.0195 ** 36 ) - 10 = 12% + * At 48 hours, the percentage is 11 * ( 1.0195 ** 48 ) - 10 = 17.8% + * At 72 hours, the percentage is 11 * ( 1.0195 ** 72 ) - 10 = 34.18% + * At 96 hours, the percentage is 11 * ( 1.0195 ** 96 ) - 10 = 60.24% + * At 120 hours, the percentage is 11 * ( 1.0195 ** 120 ) - 10 = 101.65 ~= 100% + * + */ case 'cautious': $percent = 11 * ( 1.0195 ** $hours_since_release ) - 10; break; From 4ae19380e0750ace0958ed1808083dd5b42629f5 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 05:09:47 +0000 Subject: [PATCH 09/30] Don't need to check for time availability. --- .../plugin-directory/standalone/plugin-update-helpers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index c5e393eb60..f2d6282885 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -56,7 +56,7 @@ function get_site_percentage( string $slug = '', string $version = '' ) { */ function phased_rollout_should_update( object $update_details ) { $phase_details = $update_details->meta->phased_rollout ?? false; - if ( ! $phase_details || empty( $phase_details['time'] ) ) { + if ( ! $phase_details ) { return true; } @@ -83,7 +83,7 @@ function phased_rollout_should_update( object $update_details ) { $percent = 100; break; - // Custom defined by the plugin author. + // Custom defined by the plugin author, they must update this value in settings. case 'custom': $percent = $phase_details['percentage'] ?? 100; From 99404d64c072f7383d6f063fbc400cd1772ef607 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 05:48:28 +0000 Subject: [PATCH 10/30] Use the Release Confirmation confirmed time if it's known, use the per-release strategy if present. --- .../jobs/class-api-update-updater.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php index f832181c8a..75d0b0c205 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php @@ -92,14 +92,19 @@ public static function update_single_plugin( $plugin_slug, $self_loop = false ) } } - if ( $post->phased_rollout ) { + $release = Plugin_Directory::get_release( $post->ID, $version ); + if ( $release && $release['phased_rollout'] ) { + // If the release has a phased rollout, use that. + $meta['phased_rollout'] = array( + 'strategy' => $release['phased_rollout'], + 'time' => max( $release['confirmations'] ), + ); + } elseif ( $post->phased_rollout ) { $meta['phased_rollout'] = array( 'strategy' => $post->phased_rollout, - // TODO: This should be the release time of the update, is this correct? - 'time' => strtotime( $post->post_modified ), + 'time' => $release ? max( $release['confirmations'] ) : strtotime( $post->post_modified ), ); } - // TODO: Per-release phased rollout details from Release Confirmation should override the above. $data = array( 'plugin_id' => $post->ID, From 52f94e2d889f08dd53aeb76c4f9bc265a0790875 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 06:14:24 +0000 Subject: [PATCH 11/30] parse_url() returning false, strtolower() only liking strings. --- .../plugin-directory/standalone/plugin-update-helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index f2d6282885..77bbd35abf 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -32,7 +32,7 @@ function get_site_percentage( string $slug = '', string $version = '' ) { $wp_url = $m[1]; } - $site_domain = strtolower( parse_url( $wp_url, PHP_URL_HOST ) ); + $site_domain = strtolower( parse_url( $wp_url, PHP_URL_HOST ) ?: '' ); // If we've reached this point and have no URL, delay the update until 100% is reached. if ( ! $site_domain ) { From 3c220da80e27a558840e60d0a0ba7b0c6f2ef1d9 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 9 Jul 2025 06:14:55 +0000 Subject: [PATCH 12/30] Avoid Notice when $wp_url hasn't been set. --- .../plugin-directory/standalone/plugin-update-helpers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 77bbd35abf..d5203f2e98 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -28,7 +28,7 @@ function get_site_percentage( string $slug = '', string $version = '' ) { * If the site URL hasn't been extracted already, pull it from the global. * NOTE: This may be set by the tests or other codepaths that run before this function. */ - if ( ! $wp_url && preg_match( '#^WordPress/.+; (http.+)$#i', $_SERVER['HTTP_USER_AGENT'] ?? '', $m ) ) { + if ( empty( $wp_url ) && preg_match( '#^WordPress/.+; (http.+)$#i', $_SERVER['HTTP_USER_AGENT'] ?? '', $m ) ) { $wp_url = $m[1]; } From 1b9ac9612f88fa77b196e02f0b1dd14b0ff94497 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Tue, 15 Jul 2025 04:34:42 +0000 Subject: [PATCH 13/30] Store the previous version / stable tag upon version increase. --- .../plugins/plugin-directory/cli/class-import.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php index 9e09e7c0d4..a6bf6d543e 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php @@ -516,6 +516,14 @@ public function import_from_svn( $plugin_slug, $svn_changed_tags = array( 'trunk $this->rebuild_affected_zips( $plugin_slug, $stable_tag, $current_stable_tag, $svn_changed_tags, $svn_revision_triggered ); + // If we've got a new version, store the last version in the plugin meta. + if ( $version && $version !== $post->version ) { + update_post_meta( $plugin->ID, 'last_version', wp_slash( $post->version ) ); + } + if ( $stable_tag && $stable_tag !== $current_stable_tag ) { + update_post_meta( $plugin->ID, 'last_stable_tag', wp_slash( $current_stable_tag ) ); + } + // Finally, set the new version live. update_post_meta( $plugin->ID, 'stable_tag', wp_slash( $stable_tag ) ); update_post_meta( $plugin->ID, 'version', wp_slash( $version ) ); From d0f48fabdb4da6494a7f5927ca9db32f09ced954 Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Tue, 15 Jul 2025 04:38:15 +0000 Subject: [PATCH 14/30] Sync through the metadata updates. --- .../jobs/class-api-update-updater.php | 11 ++++++++--- .../standalone/plugin-update-helpers.php | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php index 75d0b0c205..50bab7c692 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/jobs/class-api-update-updater.php @@ -79,7 +79,11 @@ public static function update_single_plugin( $plugin_slug, $self_loop = false ) $version = get_post_meta( $post->ID, 'version', true ); $requires_plugins = get_post_meta( $post->ID, 'requires_plugins', true ); - $meta = array(); + $meta = array( + 'release_time' => strtotime( $post->post_modified ), + 'last_version' => $post->last_version ?? '', + 'last_stable_tag' => $post->last_stable_tag ?? '', + ); if ( in_array( $post->post_status, array( 'disabled', 'closed' ) ) ) { $closed_data = Template::get_close_data( $post ); @@ -93,16 +97,17 @@ public static function update_single_plugin( $plugin_slug, $self_loop = false ) } $release = Plugin_Directory::get_release( $post->ID, $version ); + if ( $release ) { + $meta['release_time'] = max( $release['confirmations'] ); + } if ( $release && $release['phased_rollout'] ) { // If the release has a phased rollout, use that. $meta['phased_rollout'] = array( 'strategy' => $release['phased_rollout'], - 'time' => max( $release['confirmations'] ), ); } elseif ( $post->phased_rollout ) { $meta['phased_rollout'] = array( 'strategy' => $post->phased_rollout, - 'time' => $release ? max( $release['confirmations'] ) : strtotime( $post->post_modified ), ); } diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index d5203f2e98..3cde66422d 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -14,7 +14,7 @@ /** * Return the current sites update-percentage. * - * @global $wp_url + * @global string $wp_url The WordPress site URL. Extracted from the HTTP User Agent header. * * @param string $slug The plugin slug. * @param string $version The plugin version. From b5f0da7942f63812139db747c9e8991131485e8f Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Tue, 15 Jul 2025 04:39:52 +0000 Subject: [PATCH 15/30] Reorganise how this adjusts the API, by filtering the response data for a plugin rather than being a boolean. --- .../standalone/plugin-update-helpers.php | 125 ++++++++++++------ 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 3cde66422d..627d7344bb 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -47,45 +47,102 @@ function get_site_percentage( string $slug = '', string $version = '' ) { } /** - * Determine if the site should update based on the phased rollout details. + * This function acts as a filter on the update that's presented to the site. * - * @link https://www.desmos.com/calculator/59sl7efajq + * @global string $wp_url The WordPress site URL. Extracted from the HTTP User Agent header. + * @global string $req_wp_version The WordPress client version. * - * @param object $update_details The plugin update details. - * @return bool True if the site should update, false otherwise. + * @param object $plugin_info The plugin update details. + * @param object $plugin_details The plugin details. + * @param string $installed_version The currently installed version of the plugin. + * @param string $wp_version The WordPress version. Empty if not a WordPress client. + * @return object The updated plugin update details. */ -function phased_rollout_should_update( object $update_details ) { - $phase_details = $update_details->meta->phased_rollout ?? false; - if ( ! $phase_details ) { - return true; +function phased_rollout_alter_update( $plugin_info, $plugin_details, $installed_version ) { + global $wp_url, $req_wp_version_base; + + $strategy = $phase_details['strategy'] ?? false; + if ( ! $strategy ) { + return $plugin_info; } - /* - * $phase_details are expected to be in the format of... - * { - * "strategy": "slow", - * "time": Release time in seconds since the epoch, - * "percentage": If the strategy is "custom", this is the percentage of sites that should receive the update. - * } - */ + // This is effectively a NOOP strategy, no changes. + if ( 'immediate' === $strategy ) { + return $plugin_info; + } + + // Calculate the number of hours since the plugin was released. + $hours_since_release = ( time() - $plugin_details->meta->release_time ) / 3600; + if ( $hours_since_release > 120 ) { + // If more than 5 days have passed, always assume the update is available. + return $plugin_info; + } + + $do_not_offer_update = false; + + // Handle the percent-based strategies. + $plugin_percent_rollout = phased_rollout_get_plugin_percent( $strategy, $hours_since_release, $plugin_details ); + if ( $plugin_percent_rollout !== false ) { + + $site_percent = get_site_percentage( $plugin_details->plugin_slug, $plugin_details->version ); + + if ( $site_percent > $plugin_percent_rollout ) { + $do_not_offer_update = true; + } + } - $site_percent = get_site_percentage( $update_details->plugin_slug, $update_details->version ); - $hours_since_release = ( time() - $phase_details['time'] ) / 3600; + // If the site should not update, we'll return the last-version if possible. + if ( $do_not_offer_update ) { + $plugin_info->version = $plugin_details->meta->last_version ?? $installed_version; + + // Match update-check API. + unset( + $plugin_info->tested, + $plugin_info->requires_php, + $plugin_info->requires_plugins, + $plugin_info->compatibility, + $plugin_info->upgrade_notice + ); + } + + return $plugin_info; +} + +/** + * Get the percentage of sites that should receive the update for the plugin. + * + * @link https://www.desmos.com/calculator/59sl7efajq + * + * @param string $strategy The rollout strategy. + * @param float $hours_since_release The number of hours since the plugin was released. + * @param object $update_details The plugin update details. + * + * @return float|false The percentage of sites that should receive the update, or false invalid details. + */ +function phased_rollout_get_plugin_percent( string $strategy, float $hours_since_release, object $update_details ) { + $percent_based_strategies = [ + 'custom', + 'slow', + 'extra-slow', + 'cautious', + ]; - if ( $hours_since_release >= 120 ) { - // If more than 5 days have passed, assume the update is available. - return true; + $phase_details = $update_details->meta->phased_rollout ?? false; + + if ( + ! $phase_details || + ! in_array( $strategy, $percent_based_strategies, true ) + ) { + return false; } - switch( $phase_details['strategy'] ?? 'immediate' ) { + switch( $strategy ) { default: - case 'immediate': - $percent = 100; - break; + return false; // Custom defined by the plugin author, they must update this value in settings. case 'custom': - $percent = $phase_details['percentage'] ?? 100; + return $phase_details['percentage'] ?? 100; /* * Straight curve, start at 5%, increases to 100% over the next 48hrs (2d). @@ -97,8 +154,7 @@ function phased_rollout_should_update( object $update_details ) { * At 48 hours, the percentage is 5 + (48/48) * 95 = 100% */ case 'slow': - $percent = 5 + ( $hours_since_release / 48 ) * 95; - break; + return 5 + ( $hours_since_release / 48 ) * 95; /* * Polynomial curve, starts at 5%, increases to 100% over the next 72hrs (3d). @@ -112,8 +168,7 @@ function phased_rollout_should_update( object $update_details ) { * At 72 hours, the percentage is 9 * ( 1.0345 ** 72 ) - 3.9 = 100% */ case 'extra-slow': - $percent = 9 * ( 1.0345 ** $hours_since_release ) - 3.9; - break; + return 9 * ( 1.0345 ** $hours_since_release ) - 3.9; /* * Polynomial curve, starts at 1%, with an increase to 100% over the next 120hrs (5d). @@ -129,13 +184,9 @@ function phased_rollout_should_update( object $update_details ) { * */ case 'cautious': - $percent = 11 * ( 1.0195 ** $hours_since_release ) - 10; - break; - } - - if ( $percent >= 100 ) { - return true; + return 11 * ( 1.0195 ** $hours_since_release ) - 10; } - return ( $site_percent <= $percent ); + // If we reach this point, something is wrong. + return false; } From 9455a126052307b175bd817d9be46486539ebe1d Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Tue, 15 Jul 2025 04:43:16 +0000 Subject: [PATCH 16/30] Add a phased rollout of 'Manual user-initiated updates for first 24hrs, then automatic updates. --- .../standalone/plugin-update-helpers.php | 16 ++++++++++++++ .../wporg-plugins-2024/inc/template-tags.php | 22 ++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index 627d7344bb..9310085593 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -78,6 +78,22 @@ function phased_rollout_alter_update( $plugin_info, $plugin_details, $installed_ return $plugin_info; } + // If the strategy is manual updates only for the first 24hrs, then we can just disable the sites ability to perform autoupdates. + if ( 'manual-updates-24hr' === $strategy ) { + + // The flag used here is only operable in WordPress 6.6+. + // See https://core.trac.wordpress.org/ticket/52796 + $meets_wp_version = $req_wp_version_base && version_compare( $req_wp_version_base, '6.6', '>=' ); + + // If less than 24 hours have passed, do not update. + if ( $meets_wp_version && $hours_since_release <= 24 ) { + $plugin_info->disable_autoupdates = true; + } + // Else: The plugin update is unchanged, sites will update. + + return $plugin_info; + } + $do_not_offer_update = false; // Handle the percent-based strategies. diff --git a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php index f44964a09a..acf41c9eb5 100644 --- a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php +++ b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php @@ -787,19 +787,21 @@ function the_phased_rollout_settings() { $rollout = $post->phased_rollout ?: ''; $options = [ - '' => __( 'Immediate (default)', 'wporg-plugins' ), - 'slow' => __( 'Slow rollout', 'wporg-plugins' ), - 'extra-slow' => __( 'Extra slow rollout', 'wporg-plugins' ), - 'cautious' => __( 'Cautious rollout', 'wporg-plugins' ), - 'custom' => __( 'Custom rollout', 'wporg-plugins' ), + '' => __( 'Immediate (default)', 'wporg-plugins' ), + 'manual-updates-24hr' => __( 'Manual updates only (24 hours)', 'wporg-plugins' ), + 'slow' => __( 'Slow rollout', 'wporg-plugins' ), + 'extra-slow' => __( 'Extra slow rollout', 'wporg-plugins' ), + 'cautious' => __( 'Cautious rollout', 'wporg-plugins' ), + 'custom' => __( 'Custom rollout', 'wporg-plugins' ), ]; $descriptions = [ - '' => __( 'Plugin updates will be released to all sites as soon as they check for updates.', 'wporg-plugins' ), - 'slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), - 'extra-slow'=> __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), - 'cautious' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), -// 'custom' => __( 'Plugin updates will be released to a custom percentage of sites, as defined by the plugin author.', 'wporg-plugins' ), + '' => __( 'Plugin updates will be released to all sites as soon as they check for updates.', 'wporg-plugins' ), + 'manual-updates-24hr' => __( 'Plugin updates will be released to all sites, but automatic updates will be disabled for 24 hours. After that, sites will receive the update as normal.', 'wporg-plugins' ), + 'slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), + 'extra-slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), + 'cautious' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), +// 'custom' => __( 'Plugin updates will be released to a custom percentage of sites, as defined by the plugin author.', 'wporg-plugins' ), ]; echo '

' . __( 'Phased rollout', 'wporg-plugins' ) . '

'; From b768acbd6017a310077d744964061bf3fa01328a Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Wed, 16 Jul 2025 04:31:20 +0000 Subject: [PATCH 17/30] Tweak when the last_stable_tag record is done, as we only want it for version changes. Also record the date. --- .../wp-content/plugins/plugin-directory/cli/class-import.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php index a6bf6d543e..391daaec67 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/cli/class-import.php @@ -519,9 +519,9 @@ public function import_from_svn( $plugin_slug, $svn_changed_tags = array( 'trunk // If we've got a new version, store the last version in the plugin meta. if ( $version && $version !== $post->version ) { update_post_meta( $plugin->ID, 'last_version', wp_slash( $post->version ) ); - } - if ( $stable_tag && $stable_tag !== $current_stable_tag ) { update_post_meta( $plugin->ID, 'last_stable_tag', wp_slash( $current_stable_tag ) ); + // Keep the date of the last version change, this often differs from the last_updated/post_modified dates. + update_post_meta( $plugin->ID, 'version_date', wp_slash( current_time( 'mysql' ) ) ); } // Finally, set the new version live. From 57bcb7e53e8961f650eb63abacca94b1e152f0da Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Tue, 22 Jul 2025 03:12:23 +0000 Subject: [PATCH 18/30] Move strategies to a helper method. --- .../plugin-directory/class-template.php | 30 ++++++++++++++++ .../wporg-plugins-2024/inc/template-tags.php | 34 +++++-------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php index e0bad66253..1f07fa9c4d 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php @@ -1385,4 +1385,34 @@ public static function get_screenshots( $plugin = null, $locale = null ) { return $sorted; } + + static function get_rollout_strategies() { + return [ + '' => [ + 'name' => __( 'Immediate (default)', 'wporg-plugins' ), + 'description' => __( 'Plugin updates will be released to all sites as soon as they check for updates.', 'wporg-plugins' ), + ], + 'manual-updates-24hr' => [ + 'name' => __( 'Manual updates only (24 hours)', 'wporg-plugins' ), + 'description' => __( 'Plugin updates will be released to all sites, but automatic updates will be disabled for 24 hours. After that, sites will receive the update as normal.', 'wporg-plugins' ), + ], + /* + [ + 'name' => __( 'Slow rollout', 'wporg-plugins' ), + 'slug' => 'slow', + 'description' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), + ], + [ + 'name' => __( 'Extra slow rollout', 'wporg-plugins' ), + 'slug' => 'extra-slow', + 'description' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), + ], + [ + 'name' => __( 'Cautious rollout', 'wporg-plugins' ), + 'slug' => 'cautious', + 'description' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), + ] + */ + ]; + } } diff --git a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php index acf41c9eb5..045f13d820 100644 --- a/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php +++ b/wordpress.org/public_html/wp-content/themes/pub/wporg-plugins-2024/inc/template-tags.php @@ -749,7 +749,7 @@ function the_author_notice( $post = null ) { printf( '
%s
', esc_attr( $notice['type'] ), - '

' . __( 'A note from the Plugin Review team, visible only to the plugin author & committers.', 'wporg-plugins' ) . '

' . + '

' . __( 'A note from the Plugins Team, visible only to the plugin author & committers.', 'wporg-plugins' ) . '

' . wp_kses_post( $notice['html'] ) // Should have wrapping

tags. ); } @@ -786,24 +786,6 @@ function the_phased_rollout_settings() { } $rollout = $post->phased_rollout ?: ''; - $options = [ - '' => __( 'Immediate (default)', 'wporg-plugins' ), - 'manual-updates-24hr' => __( 'Manual updates only (24 hours)', 'wporg-plugins' ), - 'slow' => __( 'Slow rollout', 'wporg-plugins' ), - 'extra-slow' => __( 'Extra slow rollout', 'wporg-plugins' ), - 'cautious' => __( 'Cautious rollout', 'wporg-plugins' ), - 'custom' => __( 'Custom rollout', 'wporg-plugins' ), - ]; - - $descriptions = [ - '' => __( 'Plugin updates will be released to all sites as soon as they check for updates.', 'wporg-plugins' ), - 'manual-updates-24hr' => __( 'Plugin updates will be released to all sites, but automatic updates will be disabled for 24 hours. After that, sites will receive the update as normal.', 'wporg-plugins' ), - 'slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 2 days.', 'wporg-plugins' ), - 'extra-slow' => __( 'Plugin updates will be released to 5% of sites for the first 6 hours, increasing to 100% over the next 3 days.', 'wporg-plugins' ), - 'cautious' => __( 'Plugin updates will be released to 1% of sites for the first 6 hours, increasing to 10% by day 2, and to 100% of sites within 5 days.', 'wporg-plugins' ), -// 'custom' => __( 'Plugin updates will be released to a custom percentage of sites, as defined by the plugin author.', 'wporg-plugins' ), - ]; - echo '

' . __( 'Phased rollout', 'wporg-plugins' ) . '

'; echo '

' . @@ -814,17 +796,17 @@ function the_phased_rollout_settings() { echo ''; echo ''; - echo '

' . esc_html( $descriptions[ $rollout ] ?? '' ) . '
'; + echo '
' . esc_html( Template::get_rollout_strategies()[ $rollout ]['description'] ?? '' ) . '
'; echo ''; echo '

'; echo ''; From 9c4a31acfacccfb7dd7f872d26d8dce462b06eda Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Thu, 31 Jul 2025 03:17:51 +0000 Subject: [PATCH 29/30] Docblocks. --- .../wp-content/plugins/plugin-directory/class-template.php | 5 +++++ .../shortcodes/class-release-confirmation.php | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php index 1f07fa9c4d..ab8bb330cc 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/class-template.php @@ -1386,6 +1386,11 @@ public static function get_screenshots( $plugin = null, $locale = null ) { return $sorted; } + /** + * Get the available rollout strategies for plugin updates. + * + * @return array + */ static function get_rollout_strategies() { return [ '' => [ diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php index 25ffeab382..a70828d199 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/shortcodes/class-release-confirmation.php @@ -342,6 +342,13 @@ static function get_actions( $plugin, $data ) { return implode( ' ', $buttons ); } + /** + * Display the Rollout Strategy options for a given plugin release. + * + * @param WP_Post $plugin The plugin post object. + * @param array $data The release data. + * @return string HTML for the rollout strategy options. + */ static function get_rollout_strategy( $plugin, $data ) { if ( ! current_user_can( 'plugin_manage_releases', $plugin ) ) { return ''; From b113ce03a83705364e54f5cfce0526bf6bec63fb Mon Sep 17 00:00:00 2001 From: Dion Hulse Date: Thu, 31 Jul 2025 03:22:42 +0000 Subject: [PATCH 30/30] Use the last_stable_tag too if possible. --- .../plugin-directory/standalone/plugin-update-helpers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php index afb38e72e1..2f40d62272 100644 --- a/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php +++ b/wordpress.org/public_html/wp-content/plugins/plugin-directory/standalone/plugin-update-helpers.php @@ -82,8 +82,8 @@ function phased_rollout( $plugin_info, $plugin_details, $installed_version ) { // If the site should not update, we'll return the last-version if possible. if ( $do_not_offer_update ) { - $last_version = $plugin_details->meta->last_version ?? ''; - $plugin_info->version = $last_version ?: $installed_version; + $plugin_info->version = ( $plugin_details->meta->last_version ?? '' ) ?: $installed_version; + $plugin_info->stable_tag = ( $plugin_details->meta->last_stable_tag ?? '' ) ?: $installed_version; // Match update-check API. unset(