From a8b26af11d0915a8ec734b6fae22b554cd81af84 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 17 Aug 2026 14:02:46 -0700 Subject: [PATCH 01/11] Bump WordPress "Tested up to" versions of all plugins to 7.1 WordPress 7.1 is imminent, so update the readme header for each of the ten plugins published to the plugin directory ahead of the upcoming releases. The unreleased od-* plugins have no readme.txt and are unaffected, and the "Requires at least" floor is left at 6.9. Co-Authored-By: Claude Opus 5 --- plugins/auto-sizes/readme.txt | 2 +- plugins/dominant-color-images/readme.txt | 2 +- plugins/embed-optimizer/readme.txt | 2 +- plugins/image-prioritizer/readme.txt | 2 +- plugins/optimization-detective/readme.txt | 2 +- plugins/performance-lab/readme.txt | 2 +- plugins/speculation-rules/readme.txt | 2 +- plugins/view-transitions/readme.txt | 2 +- plugins/web-worker-offloading/readme.txt | 2 +- plugins/webp-uploads/readme.txt | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index 259c8b84d3..33ffe8e0f2 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -1,7 +1,7 @@ === Enhanced Responsive Images === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.7.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index 1c1e9c2a50..dc617fd6f9 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -1,7 +1,7 @@ === Image Placeholders === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.2.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 47de6656a9..acd1ed017e 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -1,7 +1,7 @@ === Embed Optimizer === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.0.0-beta5 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index f50adea92f..6a8c2ddbc7 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -1,7 +1,7 @@ === Image Prioritizer === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.0.0-beta3 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 31df6fbdd1..a3cbdae1dd 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -1,7 +1,7 @@ === Optimization Detective === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.0.0-beta6 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/performance-lab/readme.txt b/plugins/performance-lab/readme.txt index 718c97313f..076c600d99 100644 --- a/plugins/performance-lab/readme.txt +++ b/plugins/performance-lab/readme.txt @@ -1,7 +1,7 @@ === Performance Lab === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 4.2.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/speculation-rules/readme.txt b/plugins/speculation-rules/readme.txt index 5216f41bbb..8b87004eae 100644 --- a/plugins/speculation-rules/readme.txt +++ b/plugins/speculation-rules/readme.txt @@ -1,7 +1,7 @@ === Speculative Loading === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.6.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/view-transitions/readme.txt b/plugins/view-transitions/readme.txt index 429f93962f..6cb568bb16 100644 --- a/plugins/view-transitions/readme.txt +++ b/plugins/view-transitions/readme.txt @@ -1,7 +1,7 @@ === View Transitions === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 1.2.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index 3cbe343e1b..73097275bf 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -1,7 +1,7 @@ === Web Worker Offloading === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 0.2.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html diff --git a/plugins/webp-uploads/readme.txt b/plugins/webp-uploads/readme.txt index 59b7ddc992..fb1f1218fc 100644 --- a/plugins/webp-uploads/readme.txt +++ b/plugins/webp-uploads/readme.txt @@ -1,7 +1,7 @@ === Modern Image Formats === Contributors: wordpressdotorg -Tested up to: 7.0 +Tested up to: 7.1 Stable tag: 2.7.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html From 7a570e2d9f13851c4f4c7f2acb9fa5ae846e8eae Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 17 Aug 2026 14:15:33 -0700 Subject: [PATCH 02/11] Bump Optimization Detective to 1.0.0-beta7 --- plugins/optimization-detective/load.php | 4 ++-- plugins/optimization-detective/readme.txt | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/optimization-detective/load.php b/plugins/optimization-detective/load.php index 652a037318..0b072e7111 100644 --- a/plugins/optimization-detective/load.php +++ b/plugins/optimization-detective/load.php @@ -5,7 +5,7 @@ * Description: Provides a framework for leveraging real user metrics to detect optimizations for improving page performance. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.0.0-beta6 + * Version: 1.0.0-beta7 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -73,7 +73,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'optimization_detective_pending_plugin', - '1.0.0-beta6', + '1.0.0-beta7', static function ( string $version ): void { if ( defined( 'OPTIMIZATION_DETECTIVE_VERSION' ) ) { return; diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index a3cbdae1dd..b25a7d5313 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.0.0-beta6 +Stable tag: 1.0.0-beta7 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, rum @@ -55,6 +55,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta7 = + = 1.0.0-beta6 = **Security** From e13d7ba324226d822f6cb9d538e55874455f86f7 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 17 Aug 2026 16:19:10 -0700 Subject: [PATCH 03/11] Add --increment and --set-version to the bump-versions command Bumping a plugin version previously required an open, dated release milestone titled " ". That is a lot of ceremony for a release that only carries changes which are already merged, so add two ways to bump without one: * --increment derives the target version from the plugin's current "Stable tag". * --set-version sets an explicit version for a single plugin. In either mode the plugins are named as positional arguments, matching the calling convention of generate-pending-release-diffs.sh. The list is required rather than inferred because the appropriate increment level differs per plugin, so no single auto-detected set would be correct. Passing --all targets every plugin for the rare case where one level does apply to all of them. The "prerelease" level increments the trailing number of the prerelease component, so 1.0.0-beta5 becomes 1.0.0-beta6. This is hand-rolled rather than delegated to semver, which yields 1.0.0-beta5.0 because it treats "beta5" as a single alphanumeric identifier. Incrementing a version that has a prerelease component by major, minor, or patch is refused, since that would silently graduate a beta to a stable release. All target versions are resolved before any file is written, so that guard aborts the whole run rather than leaving some plugins bumped and others not. The milestone-based behaviour is unchanged when neither new option is passed. Co-Authored-By: Claude Opus 5 --- bin/plugin/cli.js | 7 +- bin/plugin/commands/bump-versions.js | 265 ++++++++++++++++++++++++++- 2 files changed, 260 insertions(+), 12 deletions(-) diff --git a/bin/plugin/cli.js b/bin/plugin/cli.js index 9a72c8e09e..880b0bb288 100755 --- a/bin/plugin/cli.js +++ b/bin/plugin/cli.js @@ -85,10 +85,13 @@ withOptions( program.command( 'verify-version-consistency' ), versionsOptions ) .description( 'Verifies consistency of versions in plugins' ) .action( catchException( versionsHandler ) ); -withOptions( program.command( 'bump-plugin-versions' ), bumpVersionsOptions ) +withOptions( + program.command( 'bump-plugin-versions [plugins...]' ), + bumpVersionsOptions +) .alias( 'bump-versions' ) .description( - 'Bumps plugin versions based on open, dated release milestones (titled "$plugin_slug $version" without "n.e.x.t")' + 'Bumps plugin versions based on open, dated release milestones (titled "$plugin_slug $version" without "n.e.x.t"); pass --increment or --set-version with a list of plugin slugs to bump without a milestone' ) .action( catchException( bumpVersionsHandler ) ); diff --git a/bin/plugin/commands/bump-versions.js b/bin/plugin/commands/bump-versions.js index 05ab108d91..295ec7dec3 100644 --- a/bin/plugin/commands/bump-versions.js +++ b/bin/plugin/commands/bump-versions.js @@ -18,9 +18,15 @@ const { plugins } = require( '../../../plugins.json' ); /** * @typedef WPBumpVersionsCommandOptions * - * @property {string=} plugin Optional plugin slug to limit bumping to a single plugin. - * @property {string=} token Optional personal GitHub access token. - * @property {boolean=} dryRun Whether to only report what would change without writing. + * @property {string=} plugin Optional plugin slug to limit bumping to a single plugin. + * @property {string=} increment Optional increment level ("major", "minor", "patch", or + * "prerelease") to derive the target version from the current + * one instead of from a release milestone. + * @property {string=} setVersion Optional explicit target version, bypassing both release + * milestones and --increment. + * @property {boolean=} all Whether to target every plugin; requires --increment. + * @property {string=} token Optional personal GitHub access token. + * @property {boolean=} dryRun Whether to only report what would change without writing. */ /** @@ -35,12 +41,31 @@ const { plugins } = require( '../../../plugins.json' ); const VERSION_PATTERN = '\\d+\\.\\d+\\.\\d+(?:-[\\w.]+)?'; +const INCREMENT_LEVELS = [ 'major', 'minor', 'patch', 'prerelease' ]; + exports.options = [ { argname: '-p, --plugin ', description: 'The plugin slug to bump; if omitted, all plugins with a matching milestone are bumped', }, + { + argname: '-i, --increment ', + description: `Derive the target version from the current one rather than from a release milestone: ${ INCREMENT_LEVELS.join( + ', ' + ) }`, + }, + { + argname: '-s, --set-version ', + description: + 'Set an explicit target version, bypassing both release milestones and --increment', + }, + { + argname: '-a, --all', + description: + 'Target every plugin; only valid together with --increment', + defaults: false, + }, { argname: '-t, --token ', description: 'GitHub token', @@ -64,27 +89,65 @@ exports.options = [ * - the "Stable tag" in readme.txt, and * - a new (empty) changelog entry is inserted so the readme is ready for `npm run readme`. * + * Alternatively, passing --increment or --set-version skips the milestone lookup entirely + * and derives the target version from each plugin's current "Stable tag". In that mode the + * plugins to bump are named explicitly (as positional arguments), because the appropriate + * increment level differs per plugin and must not be guessed. + * + * @param {string[]} pluginSlugs Plugin slugs given as positional arguments; + * commander passes an empty array when none. * @param {WPBumpVersionsCommandOptions} opt */ -exports.handler = async ( opt ) => { - if ( opt.plugin && ! plugins.includes( opt.plugin ) ) { +exports.handler = async ( pluginSlugs, opt ) => { + const requestedSlugs = [ ...new Set( pluginSlugs ) ]; + if ( opt.plugin && ! requestedSlugs.includes( opt.plugin ) ) { + requestedSlugs.push( opt.plugin ); + } + + for ( const slug of requestedSlugs ) { + if ( ! plugins.includes( slug ) ) { + throw new Error( + `The plugin "${ slug }" is not a valid plugin managed as part of this project.` + ); + } + } + + if ( opt.increment && opt.setVersion ) { + throw new Error( + 'The --increment and --set-version options are mutually exclusive.' + ); + } + + if ( opt.increment || opt.setVersion ) { + await bumpWithoutMilestone( requestedSlugs, opt ); + return; + } + + if ( opt.all ) { throw new Error( - `The plugin "${ opt.plugin }" is not a valid plugin managed as part of this project.` + 'The --all option requires --increment, since milestones already determine which plugins to bump.' ); } + if ( requestedSlugs.length > 1 ) { + throw new Error( + `Milestone-based bumping targets at most one plugin, but ${ requestedSlugs.length } were given. Pass --increment or --set-version to bump several plugins at once.` + ); + } + + const milestonePlugin = requestedSlugs[ 0 ]; const milestones = await getReleaseMilestones( opt.token ); let selected = milestones; - if ( opt.plugin ) { - selected = milestones.filter( ( m ) => m.slug === opt.plugin ); + if ( milestonePlugin ) { + selected = milestones.filter( ( m ) => m.slug === milestonePlugin ); } if ( selected.length === 0 ) { log( formats.warning( - opt.plugin - ? `⚠ No release milestone (open, dated, without "n.e.x.t") found for ${ opt.plugin }.` + milestonePlugin + ? `⚠ No release milestone (open, dated, without "n.e.x.t") found for ${ milestonePlugin }.` : '⚠ No release milestones (open, dated, without "n.e.x.t") found.' ) ); @@ -155,6 +218,188 @@ exports.handler = async ( opt ) => { } }; +/** + * Bumps the given plugins without consulting release milestones, deriving each target + * version from the plugin's current "Stable tag" via --increment, or using --set-version + * verbatim. + * + * Every target version is resolved before anything is written, so a validation failure + * (most importantly the prerelease guard) aborts the whole run rather than leaving some + * plugins bumped and others not. + * + * @param {string[]} requestedSlugs Explicitly named plugin slugs. + * @param {WPBumpVersionsCommandOptions} opt + */ +async function bumpWithoutMilestone( requestedSlugs, opt ) { + if ( opt.increment && ! INCREMENT_LEVELS.includes( opt.increment ) ) { + throw new Error( + `Unknown --increment level "${ + opt.increment + }"; expected one of: ${ INCREMENT_LEVELS.join( ', ' ) }.` + ); + } + + if ( + opt.setVersion && + ! new RegExp( `^${ VERSION_PATTERN }$` ).test( opt.setVersion ) + ) { + throw new Error( + `"${ opt.setVersion }" is not a valid version (expected e.g. "1.2.3" or "1.0.0-beta4").` + ); + } + + let targetSlugs = requestedSlugs; + if ( opt.all ) { + if ( requestedSlugs.length > 0 ) { + throw new Error( + 'The --all option cannot be combined with an explicit list of plugins.' + ); + } + targetSlugs = plugins; + } + + if ( targetSlugs.length === 0 ) { + throw new Error( + 'No plugins were named. List the plugin slugs to bump (e.g. `bump-versions --increment minor auto-sizes webp-uploads`), or pass --all for every plugin.' + ); + } + + if ( opt.setVersion && targetSlugs.length > 1 ) { + throw new Error( + `The --set-version option applies to a single plugin, but ${ targetSlugs.length } were given.` + ); + } + + const pluginRoot = path.resolve( __dirname, '../../../' ); + + // Resolve first, write second. + const targets = [ ...targetSlugs ].sort().map( ( slug ) => { + const pluginDirectory = path.resolve( pluginRoot, 'plugins', slug ); + const currentVersion = readCurrentVersion( pluginDirectory, slug ); + const version = + opt.setVersion ?? + incrementVersion( currentVersion, opt.increment ?? '', slug ); + return { slug, pluginDirectory, currentVersion, version }; + } ); + + for ( const { + slug, + pluginDirectory, + currentVersion, + version, + } of targets ) { + try { + const changes = await bumpPlugin( + pluginDirectory, + version, + opt.dryRun + ); + const prefix = opt.dryRun ? '🔍 [dry-run] ' : '💃 '; + log( + formats.success( + `${ prefix }${ slug }: ${ currentVersion } → ${ version }:` + ) + ); + for ( const change of changes ) { + log( ` ${ change }` ); + } + } catch ( error ) { + const message = + error instanceof Error ? error.message : 'Unknown error'; + log( formats.error( `❌ ${ slug }: ${ message }` ) ); + process.exitCode = 1; + } + } +} + +/** + * Reads a plugin's current version from the "Stable tag" in its readme.txt. + * + * @param {string} pluginDirectory Absolute path to the plugin directory. + * @param {string} slug Plugin slug, for error messages. + * @return {string} The current version. + */ +function readCurrentVersion( pluginDirectory, slug ) { + const readmeFile = path.resolve( pluginDirectory, 'readme.txt' ); + if ( ! fs.existsSync( readmeFile ) ) { + throw new Error( `Unable to locate readme.txt for ${ slug }.` ); + } + const matches = fs + .readFileSync( readmeFile, 'utf-8' ) + .match( new RegExp( `^Stable tag:\\s*(${ VERSION_PATTERN })$`, 'm' ) ); + if ( ! matches ) { + throw new Error( `Unable to locate "Stable tag" in ${ readmeFile }.` ); + } + return matches[ 1 ]; +} + +/** + * Increments a version by the given level. + * + * The "prerelease" level increments the trailing number of the prerelease component, so + * "1.0.0-beta5" becomes "1.0.0-beta6". This is deliberately hand-rolled rather than + * delegated to semver: `semver.inc( '1.0.0-beta5', 'prerelease' )` yields "1.0.0-beta5.0", + * because semver treats "beta5" as a single alphanumeric identifier. + * + * Incrementing a version that has a prerelease component by major/minor/patch is refused, + * since that would silently graduate a beta to a stable release. + * + * @param {string} currentVersion Current version. + * @param {string} level Increment level. + * @param {string} slug Plugin slug, for error messages. + * @return {string} The incremented version. + */ +function incrementVersion( currentVersion, level, slug ) { + const matches = currentVersion.match( + /^(\d+)\.(\d+)\.(\d+)(?:-([\w.]+))?$/ + ); + if ( ! matches ) { + throw new Error( + `Unable to parse the current version "${ currentVersion }" of ${ slug }.` + ); + } + const [ , major, minor, patch, prerelease ] = matches; + + if ( 'prerelease' === level ) { + if ( ! prerelease ) { + throw new Error( + `${ slug } is at ${ currentVersion }, which has no prerelease component to increment. Use --increment patch/minor/major, or --set-version.` + ); + } + const prereleaseMatches = prerelease.match( /^(.*?)(\d+)$/ ); + if ( ! prereleaseMatches ) { + throw new Error( + `Unable to increment the prerelease component of ${ slug }'s version "${ currentVersion }" because it does not end in a number. Use --set-version.` + ); + } + const [ , label, number ] = prereleaseMatches; + return `${ major }.${ minor }.${ patch }-${ label }${ + Number( number ) + 1 + }`; + } + + if ( prerelease ) { + throw new Error( + `${ slug } is at ${ currentVersion }, so --increment ${ level } would graduate it to a stable release. Use --increment prerelease for the next prerelease, or --set-version to set it explicitly.` + ); + } + + switch ( level ) { + case 'major': + return `${ Number( major ) + 1 }.0.0`; + case 'minor': + return `${ major }.${ Number( minor ) + 1 }.0`; + case 'patch': + return `${ major }.${ minor }.${ Number( patch ) + 1 }`; + default: + throw new Error( + `Unknown --increment level "${ level }"; expected one of: ${ INCREMENT_LEVELS.join( + ', ' + ) }.` + ); + } +} + /** * Fetches the release milestones: open milestones with a due date whose title is * " " (i.e. without "n.e.x.t"). From fffe7829c1b97ab75d0d19df346d8746e07eb311 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 21 Aug 2026 08:25:32 -0700 Subject: [PATCH 04/11] Bump minor versions for auto-sizes, dominant-color-images, speculation-rules, web-worker-offloading Run via: ```bash npm run bump-versions -- --increment minor auto-sizes dominant-color-images speculation-rules web-worker-offloading ``` --- plugins/auto-sizes/auto-sizes.php | 4 ++-- plugins/auto-sizes/readme.txt | 4 +++- plugins/dominant-color-images/load.php | 4 ++-- plugins/dominant-color-images/readme.txt | 4 +++- plugins/speculation-rules/load.php | 4 ++-- plugins/speculation-rules/readme.txt | 4 +++- plugins/web-worker-offloading/load.php | 4 ++-- plugins/web-worker-offloading/readme.txt | 4 +++- 8 files changed, 20 insertions(+), 12 deletions(-) diff --git a/plugins/auto-sizes/auto-sizes.php b/plugins/auto-sizes/auto-sizes.php index 1342544dbc..b4c34ab7e3 100644 --- a/plugins/auto-sizes/auto-sizes.php +++ b/plugins/auto-sizes/auto-sizes.php @@ -5,7 +5,7 @@ * Description: Improves responsive images with better sizes calculations and auto-sizes for lazy-loaded images. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.7.0 + * Version: 1.8.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -27,7 +27,7 @@ return; } -define( 'IMAGE_AUTO_SIZES_VERSION', '1.7.0' ); +define( 'IMAGE_AUTO_SIZES_VERSION', '1.8.0' ); require_once __DIR__ . '/includes/improve-calculate-sizes.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index 33ffe8e0f2..e4007bf264 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.7.0 +Stable tag: 1.8.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, auto-sizes @@ -52,6 +52,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.8.0 = + = 1.7.0 = **Enhancements** diff --git a/plugins/dominant-color-images/load.php b/plugins/dominant-color-images/load.php index 5640401957..f064954de8 100644 --- a/plugins/dominant-color-images/load.php +++ b/plugins/dominant-color-images/load.php @@ -5,7 +5,7 @@ * Description: Displays placeholders based on an image's dominant color while the image is loading. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.2.1 + * Version: 1.3.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -28,7 +28,7 @@ return; } -define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.2.1' ); +define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.3.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index dc617fd6f9..62c7fa85d3 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.2.1 +Stable tag: 1.3.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, dominant color @@ -47,6 +47,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.3.0 = + = 1.2.1 = **Bug Fixes** diff --git a/plugins/speculation-rules/load.php b/plugins/speculation-rules/load.php index 0910a2a1d2..20f4ff6f85 100644 --- a/plugins/speculation-rules/load.php +++ b/plugins/speculation-rules/load.php @@ -5,7 +5,7 @@ * Description: Enables browsers to speculatively prerender or prefetch pages to achieve near-instant loads based on user interaction. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.6.0 + * Version: 1.7.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -67,7 +67,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'plsr_pending_plugin_info', - '1.6.0', + '1.7.0', static function ( string $version ): void { // Define the constant. diff --git a/plugins/speculation-rules/readme.txt b/plugins/speculation-rules/readme.txt index 8b87004eae..c360a23eb2 100644 --- a/plugins/speculation-rules/readme.txt +++ b/plugins/speculation-rules/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.6.0 +Stable tag: 1.7.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, javascript, speculation rules, prerender, prefetch @@ -122,6 +122,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.7.0 = + = 1.6.0 = **Enhancements** diff --git a/plugins/web-worker-offloading/load.php b/plugins/web-worker-offloading/load.php index 60979fedec..9848aea319 100644 --- a/plugins/web-worker-offloading/load.php +++ b/plugins/web-worker-offloading/load.php @@ -5,7 +5,7 @@ * Description: Offloads select JavaScript execution to a Web Worker to reduce work on the main thread and improve the Interaction to Next Paint (INP) metric. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 0.2.1 + * Version: 0.3.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -45,7 +45,7 @@ ); } -define( 'WEB_WORKER_OFFLOADING_VERSION', '0.2.1' ); +define( 'WEB_WORKER_OFFLOADING_VERSION', '0.3.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index 73097275bf..8144d9811c 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 0.2.1 +Stable tag: 0.3.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, JavaScript, web worker, partytown, analytics @@ -94,6 +94,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.3.0 = + = 0.2.1 = * Intend to sunset. ([2404](https://github.com/WordPress/performance/pull/2404)) From 68be9016cf320933ab386d16201f8be8b3059ee6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 21 Aug 2026 08:26:16 -0700 Subject: [PATCH 05/11] Bump prerelease versions for embed-optimizer and image-prioritizer Run via: ```bash npm run bump-versions -- --increment prerelease embed-optimizer image-prioritizer ``` --- plugins/embed-optimizer/load.php | 4 ++-- plugins/embed-optimizer/readme.txt | 4 +++- plugins/image-prioritizer/load.php | 4 ++-- plugins/image-prioritizer/readme.txt | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/embed-optimizer/load.php b/plugins/embed-optimizer/load.php index ebfbf11b0f..b40583f878 100644 --- a/plugins/embed-optimizer/load.php +++ b/plugins/embed-optimizer/load.php @@ -5,7 +5,7 @@ * Description: Optimizes the performance of embeds through lazy-loading, adding dns-prefetch links, and reserving space to reduce layout shifts. * Requires at least: 6.9 * Requires PHP: 7.4 - * Version: 1.0.0-beta5 + * Version: 1.0.0-beta6 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -73,7 +73,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'embed_optimizer_pending_plugin', - '1.0.0-beta5', + '1.0.0-beta6', static function ( string $version ): void { if ( defined( 'EMBED_OPTIMIZER_VERSION' ) ) { return; diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index acd1ed017e..6683d23f4c 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.0.0-beta5 +Stable tag: 1.0.0-beta6 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, embeds, optimization-detective @@ -67,6 +67,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta6 = + = 1.0.0-beta5 = **Bug Fixes** diff --git a/plugins/image-prioritizer/load.php b/plugins/image-prioritizer/load.php index 90b991a018..2e6b1a2865 100644 --- a/plugins/image-prioritizer/load.php +++ b/plugins/image-prioritizer/load.php @@ -6,7 +6,7 @@ * Requires at least: 6.9 * Requires PHP: 7.4 * Requires Plugins: optimization-detective - * Version: 1.0.0-beta3 + * Version: 1.0.0-beta4 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -74,7 +74,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'image_prioritizer_pending_plugin', - '1.0.0-beta3', + '1.0.0-beta4', static function ( string $version ): void { if ( defined( 'IMAGE_PRIORITIZER_VERSION' ) ) { return; diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 6a8c2ddbc7..b44bda8718 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 7.1 -Stable tag: 1.0.0-beta3 +Stable tag: 1.0.0-beta4 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, image, optimization-detective @@ -72,6 +72,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 1.0.0-beta4 = + = 1.0.0-beta3 = **Enhancements** From 0aff6d536a313f6a00b6e94cfac8f676d60e5905 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 21 Aug 2026 15:47:53 -0700 Subject: [PATCH 06/11] Add changelog entries for the pending plugin releases Fill in the empty changelog entries that bump-versions stubbed out for the seven plugins being released. The entries are written by hand because the changelog and prepare-release-notes commands derive their content from a release milestone, and these releases deliberately skip milestones. The bulk of each entry is the same across all seven plugins, since what is pending is a single cross-cutting sweep: strict types, native property types, and the raised WordPress 6.9 and PHP 7.4 minimums. Only three plugins carry anything beyond that: * Optimization Detective moved the URL Metrics storage HMAC validation into the REST endpoint callback and dropped its deprecated constants. * Speculative Loading hardened the JSON encoding of its inline scripts. * Image Prioritizer fixed TypeScript 6 type errors in the video lazy-loading script. Co-Authored-By: Claude Opus 5 --- plugins/auto-sizes/readme.txt | 7 +++++++ plugins/dominant-color-images/readme.txt | 7 +++++++ plugins/embed-optimizer/readme.txt | 7 +++++++ plugins/image-prioritizer/readme.txt | 11 +++++++++++ plugins/optimization-detective/readme.txt | 9 +++++++++ plugins/speculation-rules/readme.txt | 8 ++++++++ plugins/web-worker-offloading/readme.txt | 7 +++++++ 7 files changed, 56 insertions(+) diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index e4007bf264..6000a114af 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -54,6 +54,13 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 1.8.0 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.7.0 = **Enhancements** diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index 62c7fa85d3..97e97f51bf 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -49,6 +49,13 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 1.3.0 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.2.1 = **Bug Fixes** diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 6683d23f4c..d8f846010a 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -69,6 +69,13 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 1.0.0-beta6 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 1.0.0-beta5 = **Bug Fixes** diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index b44bda8718..e8f397ec60 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -74,6 +74,17 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 1.0.0-beta4 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + +**Bug Fixes** + +* Fix TypeScript 6 type errors in the video lazy-loading script. ([2434](https://github.com/WordPress/performance/pull/2434)) + = 1.0.0-beta3 = **Enhancements** diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index b25a7d5313..102d073cea 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -57,6 +57,15 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 1.0.0-beta7 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) +* Move URL Metrics storage HMAC validation into the REST endpoint callback so it runs once all parameters have been validated and sanitized, returning a `rest_invalid_param` error. ([2436](https://github.com/WordPress/performance/pull/2436)) +* Remove the deprecated constants along with the `deprecated.php` file that contained them. + = 1.0.0-beta6 = **Security** diff --git a/plugins/speculation-rules/readme.txt b/plugins/speculation-rules/readme.txt index c360a23eb2..f2e25889f7 100644 --- a/plugins/speculation-rules/readme.txt +++ b/plugins/speculation-rules/readme.txt @@ -124,6 +124,14 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 1.7.0 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) +* Harden the JSON encoding of the inline speculation rules script and add a `sourceURL` to the authentication admin notice script. ([2169](https://github.com/WordPress/performance/pull/2169)) + = 1.6.0 = **Enhancements** diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index 8144d9811c..6a2f0b4a05 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -96,6 +96,13 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 0.3.0 = +**Enhancements** + +* Add `declare( strict_types = 1 )` to all PHP files. ([2424](https://github.com/WordPress/performance/pull/2424)) +* Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) +* Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) +* Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) + = 0.2.1 = * Intend to sunset. ([2404](https://github.com/WordPress/performance/pull/2404)) From 753f134b1451149a7496f0cb55c8c5a076fcc219 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 21 Aug 2026 16:01:33 -0700 Subject: [PATCH 07/11] Fix pending release diffs missing same-length readme changes The script could report a plugin as having no pending changes when it actually had some. Bumping "Tested up to: 7.0" to "7.1" replaces a line with one of exactly the same length, and the working copy is reused between runs, so a previous run's rsync leaves the build file's mtime on the copy. Both rsync's default quick check and SVN's own stat cache compare size and mtime rather than content, so each concludes the file is unchanged and "svn status" comes back clean. The failure is intermittent, since it only bites once a prior run has left a matching mtime behind, and it is silent in the worst way: the affected plugin is reported under a "No changes." note, which reads as confirmation that there is nothing to release. Pass -c so rsync compares checksums, and --no-times so the files it copies get a fresh mtime that invalidates SVN's stat cache. Both are needed; -c alone still leaves SVN unable to see the change. Verified against a cold checkout and a warm one, with identical results. Co-Authored-By: Claude Opus 5 --- bin/generate-pending-release-diffs.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/generate-pending-release-diffs.sh b/bin/generate-pending-release-diffs.sh index d66555ba5a..1a208e94d7 100755 --- a/bin/generate-pending-release-diffs.sh +++ b/bin/generate-pending-release-diffs.sh @@ -45,7 +45,11 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p # Exclude minified assets: they are build artifacts derived from their sources, so # diffing them just adds noise. Excluding them here also protects the stable copies # from --delete, so svn status/diff below will not report them. - rsync -avz --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 + # Compare by checksum (-c) and stamp copied files with a fresh mtime (--no-times). + # Both are required: "Tested up to: 7.0" -> "7.1" is a same-length edit, and rsync's + # default quick check (size+mtime) as well as SVN's own stat cache would each conclude + # the file is unchanged, so the plugin gets wrongly reported as having nothing to release. + rsync -avzc --no-times --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 cd "$stable_dir/$plugin_slug/" From afa479a53f835d037ebbcfffe6bb4b9f87e53f18 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 22 Aug 2026 14:38:56 -0700 Subject: [PATCH 08/11] Omit generated JS bundles from the pending release diffs The two web-vitals bundles under build/ accounted for 35% of the whole diff, 42 KB out of 124 KB, for what amounts to a library upgrade. They are minified despite not carrying a .min.js suffix, so the existing *.min.js exclusion misses them, and each is a single line, meaning any change at all renders as a whole-file rewrite. Exclude build/*.js, which brings the diff down from 127 KB to 85 KB. The sibling build/*.asset.php is deliberately left in, since its 'version' is the compact signal that the bundled library changed: two lines showing 5.1.0 becoming 6.1.0, in place of 42 KB of unreadable bundle. That signal turned out to be worth keeping. It surfaced that web-vitals is going from 5.1.0 to 6.1.0, across a major release, which was missing from Optimization Detective's changelog entry and is now added. Co-Authored-By: Claude Opus 5 --- bin/generate-pending-release-diffs.sh | 10 +++++++--- plugins/optimization-detective/readme.txt | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/generate-pending-release-diffs.sh b/bin/generate-pending-release-diffs.sh index 1a208e94d7..1e9d4c74ed 100755 --- a/bin/generate-pending-release-diffs.sh +++ b/bin/generate-pending-release-diffs.sh @@ -42,14 +42,18 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p remote_stable_tag=$( grep "Stable tag:" "$stable_dir/$plugin_slug/readme.txt" | awk '{print $3}' ) local_stable_tag=$( grep "Stable tag:" "build/$plugin_slug/readme.txt" | awk '{print $3}' ) - # Exclude minified assets: they are build artifacts derived from their sources, so + # Exclude generated assets: they are build artifacts derived from their sources, so # diffing them just adds noise. Excluding them here also protects the stable copies - # from --delete, so svn status/diff below will not report them. + # from --delete, so svn status/diff below will not report them. The build/*.js bundles + # are minified despite not carrying a .min.js suffix, and each is a single line, so any + # change to one renders as a whole-file rewrite: the two web-vitals bundles alone were + # 35% of the entire diff. Their sibling build/*.asset.php is deliberately NOT excluded, + # since its 'version' is the compact signal that the bundled library changed. # Compare by checksum (-c) and stamp copied files with a fresh mtime (--no-times). # Both are required: "Tested up to: 7.0" -> "7.1" is a same-length edit, and rsync's # default quick check (size+mtime) as well as SVN's own stat cache would each conclude # the file is unchanged, so the plugin gets wrongly reported as having nothing to release. - rsync -avzc --no-times --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 + rsync -avzc --no-times --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" --exclude="build/*.js" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 cd "$stable_dir/$plugin_slug/" diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 102d073cea..10d960cc3c 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -63,6 +63,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu * Bump minimum required WordPress version to 6.9. ([2517](https://github.com/WordPress/performance/pull/2517)) * Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) * Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) +* Bump the bundled web-vitals library from 5.1.0 to 6.1.0, including its 6.0.0 major release. ([2606](https://github.com/WordPress/performance/pull/2606), [2629](https://github.com/WordPress/performance/pull/2629)) * Move URL Metrics storage HMAC validation into the REST endpoint callback so it runs once all parameters have been validated and sanitized, returning a `rest_invalid_param` error. ([2436](https://github.com/WordPress/performance/pull/2436)) * Remove the deprecated constants along with the `deprecated.php` file that contained them. From 4f1d9fed5540ce60382117b3602185f7988bf478 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 22 Aug 2026 14:59:24 -0700 Subject: [PATCH 09/11] Suppress built file contents in the diff instead of excluding the files Excluding generated assets from the rsync kept the diff readable but blinded "svn status" to them, which is the wrong trade: status is the view that answers which files are being added, removed, and modified, and it should be complete. Worse, the exclusion also protected those files from --delete, so a stray minified asset dropped into an already-tracked directory was reported nowhere at all. Only one landing in a brand new directory showed up, and then merely as its parent directory, with no indication of what was inside. Copy everything instead, and filter "svn diff" so that generated files keep their header but have their contents replaced with a placeholder. They stay visible as changed without dragging in tens of KB of unreadable bundle: the whole report goes from 127 KB to 88 KB. The *.asset.php files are left intact, since their 'version' is the readable signal that a bundled library changed. Also expand unversioned directories in the status output, so that a batch of added files is listed file by file rather than collapsing into its parent. This supersedes the build/*.js exclusion added in the previous commit, whose carve-out for *.asset.php is preserved here by the same reasoning. Both edge cases are now covered: a stray min file in a tracked directory shows as "? stray.min.js", and a new directory lists its contents. It immediately turned up a real pending change that the exclusion had been hiding, a modified view-transitions .min.css, which on inspection is only a minifier emitting #0000 where it used to emit transparent. Co-Authored-By: Claude Opus 5 --- bin/generate-pending-release-diffs.sh | 55 ++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/bin/generate-pending-release-diffs.sh b/bin/generate-pending-release-diffs.sh index 1e9d4c74ed..af081dd5ed 100755 --- a/bin/generate-pending-release-diffs.sh +++ b/bin/generate-pending-release-diffs.sh @@ -42,18 +42,17 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p remote_stable_tag=$( grep "Stable tag:" "$stable_dir/$plugin_slug/readme.txt" | awk '{print $3}' ) local_stable_tag=$( grep "Stable tag:" "build/$plugin_slug/readme.txt" | awk '{print $3}' ) - # Exclude generated assets: they are build artifacts derived from their sources, so - # diffing them just adds noise. Excluding them here also protects the stable copies - # from --delete, so svn status/diff below will not report them. The build/*.js bundles - # are minified despite not carrying a .min.js suffix, and each is a single line, so any - # change to one renders as a whole-file rewrite: the two web-vitals bundles alone were - # 35% of the entire diff. Their sibling build/*.asset.php is deliberately NOT excluded, - # since its 'version' is the compact signal that the bundled library changed. + # Copy everything, including generated assets. They are excluded from the *diff* further + # below rather than from the copy, so that "svn status" reports the true set of files + # being added, removed, and modified. Excluding them here instead would protect them + # from --delete and make them invisible to svn entirely: a stray minified asset dropped + # into an already-tracked directory would then be reported nowhere at all. + # # Compare by checksum (-c) and stamp copied files with a fresh mtime (--no-times). # Both are required: "Tested up to: 7.0" -> "7.1" is a same-length edit, and rsync's # default quick check (size+mtime) as well as SVN's own stat cache would each conclude # the file is unchanged, so the plugin gets wrongly reported as having nothing to release. - rsync -avzc --no-times --delete --exclude=".svn" --exclude="*.min.js" --exclude="*.min.css" --exclude="build/*.js" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 + rsync -avzc --no-times --delete --exclude=".svn" "build/$plugin_slug/" "$stable_dir/$plugin_slug/" >&2 cd "$stable_dir/$plugin_slug/" @@ -75,13 +74,49 @@ for plugin_slug in $( if [ $# -gt 0 ]; then echo "$@"; else jq '.plugins[]' -r p echo "\`svn status\`:" echo '```' - svn status + # SVN reports an unversioned directory without listing what is inside it, so a batch + # of added files would otherwise surface only as their parent directory. Expand those + # so every file being added is named. + svn status | while IFS= read -r status_line; do + echo "$status_line" + if [ "${status_line:0:1}" = "?" ]; then + status_path="${status_line:8}" + if [ -d "$status_path" ]; then + find "$status_path" -type f | sort | sed 's|^|? |' + fi + fi + done echo '```' echo echo '
svn diff' echo echo '```diff' - svn diff + # Keep generated files in the diff so it is clear they changed, but replace their + # contents with a placeholder. They are minified or bundled build output, so their + # diffs are unreadable and enormous: the two web-vitals bundles alone were 35% of + # the entire report, for what amounts to a library version bump. The sibling + # *.asset.php is left intact, since its 'version' is the readable signal of that. + svn diff | awk ' + /^Index: / { + path = substr( $0, 8 ) + suppress = ( path ~ /\.min\.(js|css)$/ || path ~ /(^|\/)build\// ) + if ( path ~ /\.asset\.php$/ ) { + suppress = 0 + } + placed = 0 + print + next + } + suppress && /^(=+|--- |\+\+\+ )/ { print; next } + suppress { + if ( ! placed ) { + print "(Built file content suppressed.)" + placed = 1 + } + next + } + { print } + ' echo '```' echo '
' fi From 42401c8dc7f3c617a01dab6d15cad30241026de8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 22 Aug 2026 15:27:38 -0700 Subject: [PATCH 10/11] Ship the source maps for the vendored web-vitals bundles web-vitals began publishing source maps in 6.0.0, and emits a sourceMappingURL comment alongside them. That reference is correct inside the package, where the map sits next to the bundle, but the webpack config copies only the bundles out of node_modules. The upgrade would therefore have shipped two files pointing at maps that do not exist, making browsers request a URL that 404s whenever devtools is open. Copy the maps too, rather than stripping the comment. Upstream publishes them deliberately for exactly this case, and Optimization Detective is a plugin whose entire purpose is measuring real user performance, so readable web-vitals frames are worth having when debugging it or an extension built on it. Both maps carry sourcesContent, so they are self-contained and need nothing else fetched. The Gutenberg plugin ships source maps for the same reason; WordPress core does not, but core is not distributed as a plugin. The cost is 49 KB on a 106 KB zip, and nothing at runtime: browsers only request a map once devtools is open, so no visitor ever pays for it. Two renames had to be handled, since web-vitals.attribution.js is copied in as web-vitals-attribution.js. Its map is now named to match, the sourceMappingURL comment in the bundle is repointed at the new name, and the map's own "file" field is repointed at the renamed bundle. Both transformers are written as factories over a file name rather than hardcoded, since any vendored bundle renamed on the way in needs the same treatment. Note that the two added .map files are visible in the pending release diff as untracked additions only because generated assets are now copied and suppressed rather than excluded. Under the previous exclusion they would have been added to the plugin without appearing anywhere in the report. Co-Authored-By: Claude Opus 5 --- plugins/optimization-detective/readme.txt | 1 + tools/webpack/utils.js | 39 +++++++++++++++++++++++ webpack.config.js | 31 ++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 10d960cc3c..8c8fd5213d 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -64,6 +64,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu * Bump minimum required PHP version from 7.2 to 7.4. ([2469](https://github.com/WordPress/performance/pull/2469)) * Improve code quality with native property types and stricter static analysis. ([1729](https://github.com/WordPress/performance/pull/1729)) * Bump the bundled web-vitals library from 5.1.0 to 6.1.0, including its 6.0.0 major release. ([2606](https://github.com/WordPress/performance/pull/2606), [2629](https://github.com/WordPress/performance/pull/2629)) +* Ship the source maps for the bundled web-vitals library, which it began publishing in 6.0.0. * Move URL Metrics storage HMAC validation into the REST endpoint callback so it runs once all parameters have been validated and sanitized, returning a `rest_invalid_param` error. ([2436](https://github.com/WordPress/performance/pull/2436)) * Remove the deprecated constants along with the `deprecated.php` file that contained them. diff --git a/tools/webpack/utils.js b/tools/webpack/utils.js index e20c6f7f77..92fc0778eb 100644 --- a/tools/webpack/utils.js +++ b/tools/webpack/utils.js @@ -81,6 +81,43 @@ const generateBuildManifest = ( slug, from ) => { fs.writeFileSync( manifestPath, JSON.stringify( manifest, null, 2 ) ); }; +/** + * Creates a transformer that repoints a bundle's sourceMappingURL at a given file name. + * + * A bundle vendored out of node_modules keeps the sourceMappingURL its publisher emitted, + * which names the map as it is called inside that package. Where the bundle is renamed on + * the way in, that reference stops matching the map shipped beside it, so it has to be + * rewritten to the name the map is given here. + * + * @param {string} mapFileName File name the source map is shipped under. + * + * @return {function(Buffer): string} Transformer. + */ +const sourceMappingUrlTransformer = ( mapFileName ) => ( content ) => + content + .toString() + .replace( + /\/\/# sourceMappingURL=\S+/, + `//# sourceMappingURL=${ mapFileName }` + ); + +/** + * Creates a transformer that repoints a source map's "file" field at a given file name. + * + * The field names the generated file a map belongs to, as that file is called inside the + * package the map was published in. Where the bundle is renamed on the way in, the field + * has to be repointed at the new name to stay consistent with it. + * + * @param {string} bundleFileName File name the bundle is shipped under. + * + * @return {function(Buffer): string} Transformer. + */ +const sourceMapFileTransformer = ( bundleFileName ) => ( content ) => { + const map = JSON.parse( content.toString() ); + map.file = bundleFileName; + return JSON.stringify( map ); +}; + /** * Transformer to get version from package.json and return it as a PHP file. * @@ -293,6 +330,8 @@ module.exports = { getPluginVersion, generateBuildManifest, assetDataTransformer, + sourceMappingUrlTransformer, + sourceMapFileTransformer, cssMinifyTransformer, getZipContentFingerprint, getUntrackedIncludedFiles, diff --git a/webpack.config.js b/webpack.config.js index a21df0cedd..1d9f0a41a9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,6 +12,8 @@ const { plugins: standalonePlugins } = require( './plugins.json' ); const { createPluginZip, assetDataTransformer, + sourceMappingUrlTransformer, + sourceMapFileTransformer, cssMinifyTransformer, deleteFileOrDirectory, generateBuildManifest, @@ -194,11 +196,40 @@ const optimizationDetective = ( env ) => { from: `${ source }/dist/web-vitals.js`, to: `${ destination }/build/web-vitals.js`, info: { minimized: true }, + transform: { + transformer: + sourceMappingUrlTransformer( + 'web-vitals.js.map' + ), + cache: false, + }, + }, + { + from: `${ source }/dist/web-vitals.js.map`, + to: `${ destination }/build/web-vitals.js.map`, + info: { minimized: true }, }, { from: `${ source }/dist/web-vitals.attribution.js`, to: `${ destination }/build/web-vitals-attribution.js`, info: { minimized: true }, + transform: { + transformer: sourceMappingUrlTransformer( + 'web-vitals-attribution.js.map' + ), + cache: false, + }, + }, + { + from: `${ source }/dist/web-vitals.attribution.js.map`, + to: `${ destination }/build/web-vitals-attribution.js.map`, + info: { minimized: true }, + transform: { + transformer: sourceMapFileTransformer( + 'web-vitals-attribution.js' + ), + cache: false, + }, }, { from: `${ source }/package.json`, From 8fb3ddd6f660b82ef1e28497e8c981158497686f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 25 Aug 2026 21:01:15 -0700 Subject: [PATCH 11/11] Allow preparing release notes without a release milestone The release notes were the last step still requiring milestones, and it failed quietly rather than loudly. Only one of the seven plugins being released has a dated milestone, so prepare-release-notes produced notes for that one alone, and create-draft-release checked only that the notes file was non-empty. A draft release would therefore have been created covering one plugin out of seven, with nothing to indicate the other six were missing. The milestone was never the source of the content: getReadmeChangelogEntry() already reads each plugin's changelog from its readme.txt, and the milestone only decided which plugins to include. So accept plugin slugs as positional arguments and skip the lookup entirely when any are given, matching what bump-versions already does. There is deliberately no option to select every plugin here, unlike bump-versions where one exists but is rarely useful. A plugin that is not being released still has a changelog entry for its current stable tag, so including it would repeat an already-published entry in the new release notes. When plugins are named explicitly, a failure on any of them is now fatal and nothing at all is written, rather than the previous per-plugin tolerance emitting a subset. Asking for specific plugins and silently getting fewer is the exact failure this commit exists to prevent. Milestone selection keeps the old behaviour, since there the set was never asserted by hand. create-draft-release forwards any slugs through, then verifies each one is present in the generated notes before creating the draft. Where no slugs are given it now lists which plugins the notes actually cover, so an omission is visible rather than implied by silence. Co-Authored-By: Claude Opus 5 --- bin/create-draft-release.sh | 23 +++++- bin/plugin/cli.js | 4 +- bin/plugin/commands/prepare-release-notes.js | 87 ++++++++++++++------ 3 files changed, 86 insertions(+), 28 deletions(-) diff --git a/bin/create-draft-release.sh b/bin/create-draft-release.sh index 24d4499f6f..7e63d1c457 100755 --- a/bin/create-draft-release.sh +++ b/bin/create-draft-release.sh @@ -15,6 +15,12 @@ # RELEASE_DATE=$(date +%Y-%m-%d) ./create-draft-release.sh # RELEASE_DATE=2026-06-30 npm run create-draft-release # npm run create-draft-release +# +# Any plugin slugs given are forwarded to `npm run prepare-release-notes`, which then reads +# each plugin's changelog straight from its readme.txt rather than looking for a release +# milestone. Use this for a release that does not use milestones: +# +# RELEASE_DATE=2026-06-30 npm run create-draft-release -- auto-sizes speculation-rules set -e @@ -60,13 +66,28 @@ if github_token="$(gh auth token 2> /dev/null)" && [ -n "$github_token" ]; then fi echo "Generating release notes for $tag..." >&2 -npm run prepare-release-notes --silent -- "${notes_args[@]}" > "$notes_file" +npm run prepare-release-notes --silent -- "${notes_args[@]}" "$@" > "$notes_file" if [ ! -s "$notes_file" ]; then echo "Error: No release notes were generated; aborting." >&2 exit 1 fi +# Guard against notes that cover only some of what is being released. Without plugin slugs +# the selection comes from release milestones, and a plugin missing its milestone is simply +# skipped, which would otherwise yield a release whose notes quietly omit it. +if [ $# -gt 0 ]; then + for plugin_slug in "$@"; do + if ! grep -qF "## \`$plugin_slug\`" "$notes_file"; then + echo "Error: No release notes were generated for \"$plugin_slug\"; aborting." >&2 + exit 1 + fi + done +else + echo "Note: No plugin slugs were given, so the release notes cover only plugins with an open, dated release milestone. Check that every plugin being released is present below." >&2 + grep -oE '^## `[^`]+` .*' "$notes_file" | sed 's/^/ /' >&2 +fi + echo "Creating draft release \"$tag\" targeting \"$target\"..." >&2 gh release create "$tag" \ --draft \ diff --git a/bin/plugin/cli.js b/bin/plugin/cli.js index 880b0bb288..3e26a0ced7 100755 --- a/bin/plugin/cli.js +++ b/bin/plugin/cli.js @@ -96,11 +96,11 @@ withOptions( .action( catchException( bumpVersionsHandler ) ); withOptions( - program.command( 'prepare-release-notes' ), + program.command( 'prepare-release-notes [plugins...]' ), prepareReleaseNotesOptions ) .description( - 'Prints the combined per-plugin changelogs (release notes) for plugins with an open, dated release milestone; progress goes to STDERR so STDOUT can be piped' + 'Prints the combined per-plugin changelogs (release notes) for plugins with an open, dated release milestone, or for a list of plugin slugs given without a milestone; progress goes to STDERR so STDOUT can be piped' ) .action( catchException( prepareReleaseNotesHandler ) ); diff --git a/bin/plugin/commands/prepare-release-notes.js b/bin/plugin/commands/prepare-release-notes.js index e1d9daa8ef..11a82c892a 100644 --- a/bin/plugin/commands/prepare-release-notes.js +++ b/bin/plugin/commands/prepare-release-notes.js @@ -39,74 +39,111 @@ exports.options = [ * Progress and warnings are written to STDERR so that STDOUT (the Markdown release * notes) can be piped to a file or the clipboard. * + * Naming plugin slugs explicitly skips the milestone lookup entirely, for releases that do + * not use milestones. The notes come from the same place either way, since the milestone + * only ever decided which plugins to include. + * + * There is deliberately no option to select every plugin. A plugin that is not being + * released still has a changelog entry for its current stable tag, so including it would + * silently repeat an already-published entry in the new release notes. + * + * @param {string[]} pluginSlugs Plugin slugs given as positional arguments. * @param {WPPrepareReleaseNotesCommandOptions} opt */ -exports.handler = async ( opt ) => { - if ( opt.plugin && ! plugins.includes( opt.plugin ) ) { - throw new Error( - `The plugin "${ opt.plugin }" is not a valid plugin managed as part of this project.` - ); +exports.handler = async ( pluginSlugs, opt ) => { + const requestedSlugs = [ ...new Set( pluginSlugs ) ]; + if ( opt.plugin && ! requestedSlugs.includes( opt.plugin ) ) { + requestedSlugs.push( opt.plugin ); + } + + for ( const slug of requestedSlugs ) { + if ( ! plugins.includes( slug ) ) { + throw new Error( + `The plugin "${ slug }" is not a valid plugin managed as part of this project.` + ); + } + } + + // With slugs named, the milestone lookup is skipped and their readmes are read directly. + if ( requestedSlugs.length > 0 ) { + writeSections( [ ...requestedSlugs ].sort(), true ); + return; } const milestones = await getReleaseMilestones( opt.token ); - const selected = ( - opt.plugin - ? milestones.filter( - ( milestone ) => milestone.slug === opt.plugin - ) - : milestones - ).sort( ( a, b ) => a.slug.localeCompare( b.slug ) ); + const selected = milestones + .map( ( milestone ) => milestone.slug ) + .sort( ( a, b ) => a.localeCompare( b ) ); if ( selected.length === 0 ) { process.stderr.write( formats.warning( - opt.plugin - ? `⚠ Skipping ${ opt.plugin }: no open release milestone with a due date (and without "n.e.x.t") was found.\n` - : '⚠ No plugins have an open release milestone with a due date (and without "n.e.x.t"); nothing to prepare.\n' + '⚠ No plugins have an open release milestone with a due date (and without "n.e.x.t"); nothing to prepare. Name the plugin slugs explicitly to prepare notes without a milestone.\n' ) ); return; } + writeSections( selected, false ); +}; + +/** + * Writes the release notes for the given plugins to STDOUT. + * + * When the plugins were named explicitly, any failure is fatal and nothing is written: the + * caller asked for those specific plugins, so quietly emitting notes for a subset would + * produce a release whose notes silently omit part of what is being released. Milestone + * selection keeps the original per-plugin tolerance. + * + * @param {string[]} slugs Plugin slugs, already sorted. + * @param {boolean} isExplicit Whether the plugins were named explicitly. + */ +function writeSections( slugs, isExplicit ) { const pluginRoot = path.resolve( __dirname, '../../../' ); const sections = []; + const failures = []; - for ( const milestone of selected ) { + for ( const slug of slugs ) { try { const readmeFilePath = path.resolve( pluginRoot, 'plugins', - milestone.slug, + slug, 'readme.txt' ); const { version, changelog } = getReadmeChangelogEntry( readmeFilePath ); - sections.push( - `## \`${ milestone.slug }\` ${ version }\n\n${ changelog }` - ); + sections.push( `## \`${ slug }\` ${ version }\n\n${ changelog }` ); process.stderr.write( formats.success( - `✔ Prepared release notes for ${ milestone.slug } ${ version }.\n` + `✔ Prepared release notes for ${ slug } ${ version }.\n` ) ); } catch ( error ) { const message = error instanceof Error ? error.message : 'Unknown error'; + failures.push( `${ slug }: ${ message }` ); process.stderr.write( - formats.error( - `${ milestone.slug } failed to prepare: ${ message }\n` - ) + formats.error( `${ slug } failed to prepare: ${ message }\n` ) ); } } + if ( isExplicit && failures.length > 0 ) { + throw new Error( + `Release notes could not be prepared for every plugin named, so none were written:\n ${ failures.join( + '\n ' + ) }` + ); + } + if ( sections.length === 0 ) { return; } log( sections.join( '\n\n' ) ); -}; +} /** * Reads the changelog entry for a plugin's stable tag from its `readme.txt`.