From 68ac4abb58025a2da522a86355642d3f4b176400 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers Date: Fri, 5 Sep 2025 20:19:46 -0400 Subject: [PATCH 1/5] Fix Grunt task upgrading `certificate` dependency Because an exact version is pinned for `composer/ca-bundle`, the `composer update` command cannot update the dependency to the latest version. The command would not work for an individual dependency anyway due to the fact that there is no `composer.lock` file generated. --- Gruntfile.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 56862d96b9b50..d97c20743e6e4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,6 +4,7 @@ var webpackConfig = require( './webpack.config' ); var installChanged = require( 'install-changed' ); var json2php = require( 'json2php' ); +const fs = require("fs"); module.exports = function(grunt) { var path = require('path'), @@ -1584,10 +1585,69 @@ module.exports = function(grunt) { 'usebanner' ] ); - grunt.registerTask( 'certificates:update', 'Updates the Composer package responsible for root certificate updates.', function() { + grunt.registerTask( 'certificates:upgrade-package', 'Upgrades the package responsible for supplying the certificate authority certificate store bundled with WordPress.', function() { var done = this.async(); var flags = this.flags; - var args = [ 'update' ]; + var spawn = require( 'child_process' ).spawnSync; + var fs = require( 'fs' ); + + // Ensure that `composer update` has been run and the dependency is installed. + if ( ! fs.existsSync( 'vendor' ) || ! fs.existsSync( 'vendor/composer' ) || ! fs.existsSync( 'vendor/composer/ca-bundle' ) ) { + grunt.log.error( 'composer/ca-bundle dependency is missing. Please run `composer update` before attempting to upgrade the certificate bundle.' ); + done( false ); + return; + } + + /* + * Because the `composer/ca-bundle` is pinned to an exact version to ensure upgrades are applied intentionally, + * the `composer update` command will not upgrade the dependency. Instead, `composer require` must be called, + * but the specific version being upgraded to must be known and passed to the command. + */ + var outdatedResult = spawn( 'composer', [ 'outdated', 'composer/ca-bundle', '--format=json' ] ); + + if ( outdatedResult.status !== 0 ) { + grunt.log.error( 'Failed to get the package information for composer/ca-bundle.' ); + done( false ); + return; + } + + var packageInfo; + try { + var stdout = outdatedResult.stdout.toString().trim(); + if ( ! stdout ) { + grunt.log.writeln( 'The latest version is already installed.' ); + done( true ); + return; + } + packageInfo = JSON.parse( stdout ); + } catch ( e ) { + grunt.log.error( 'Failed to parse the package information for composer/ca-bundle.' ); + done( false ); + return; + } + + // Check for the version information needed to perform the necessary comparisons. + if ( ! packageInfo.versions || ! packageInfo.versions[0] || ! packageInfo.latest ) { + grunt.log.error( 'Could not determine version information for composer/ca-bundle.' ); + done( false ); + return; + } + + var currentVersion = packageInfo.versions[0]; + var latestVersion = packageInfo.latest; + + // Compare versions to ensure we actually need to update + if ( currentVersion === latestVersion ) { + grunt.log.writeln( 'The latest version is already installed: ' + latestVersion + '.' ); + done( true ); + return; + } + + grunt.log.writeln( 'Installed version: ' + currentVersion ); + grunt.log.writeln( 'New version found: ' + latestVersion ); + + // Upgrade to the latest version and change the pinned version in composer.json. + var args = [ 'require', 'composer/ca-bundle:' + latestVersion, '--dev' ]; grunt.util.spawn( { cmd: 'composer', @@ -1597,6 +1657,7 @@ module.exports = function(grunt) { if ( flags.error && error ) { done( false ); } else { + grunt.log.writeln( 'Successfully updated composer/ca-bundle to ' + latestVersion ); done( true ); } } ); @@ -1607,7 +1668,7 @@ module.exports = function(grunt) { ] ); grunt.registerTask( 'certificates:upgrade', [ - 'certificates:update', + 'certificates:upgrade-package', 'copy:certificates', 'build:certificates' ] ); From e49582eeb6cd98b3330302490bcc1f3c2d58902a Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers Date: Fri, 5 Sep 2025 20:27:29 -0400 Subject: [PATCH 2/5] Ensure updated certificate files are included --- .../reusable-test-core-build-process.yml | 26 ++++++++++++++++++- .github/workflows/test-build-processes.yml | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/reusable-test-core-build-process.yml b/.github/workflows/reusable-test-core-build-process.yml index 86f9e1ba1234a..e51646008ae9d 100644 --- a/.github/workflows/reusable-test-core-build-process.yml +++ b/.github/workflows/reusable-test-core-build-process.yml @@ -17,10 +17,15 @@ on: type: 'string' default: 'src' test-emoji: - description: 'Whether to run the grunt precommit:emoji script.' + description: 'Whether to run the precommit:emoji Grunt script.' required: false type: 'boolean' default: true + test-certificates: + description: 'Whether to run the certificate related Grunt scripts.' + required: false + type: 'boolean' + default: false save-build: description: 'Whether to save a ZIP of built WordPress as an artifact.' required: false @@ -69,6 +74,21 @@ jobs: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} persist-credentials: false + # This date is used to ensure that the PHPCS cache is cleared at least once every week. + # http://man7.org/linux/man-pages/man1/date.1.html + - name: "Get last Monday's date" + id: get-date + if: ${{ inputs.test-certificates }} + run: echo "date=$(/bin/date -u --date='last Mon' "+%F")" >> "$GITHUB_OUTPUT" + + # Since Composer dependencies are installed using `composer update` and no lock file is in version control, + # passing a custom cache suffix ensures that the cache is flushed at least once per week. + - name: Install Composer dependencies + if: ${{ inputs.test-certificates }} + uses: ramsey/composer-install@3cf229dc2919194e9e36783941438d17239e8520 # v3.1.1 + with: + custom-cache-suffix: ${{ steps.get-date.outputs.date }} + - name: Set up Node.js uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: @@ -91,6 +111,10 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Ensure certificates files are updated + if: ${{ inputs.test-certificates }} + run: npm run grunt copy:certificates && npm run grunt build:certificates + - name: Build WordPress to run from ${{ inputs.directory }} run: npm run ${{ inputs.directory == 'src' && 'build:dev' || 'build' }} diff --git a/.github/workflows/test-build-processes.yml b/.github/workflows/test-build-processes.yml index 587b6b02da290..065ebe805e64d 100644 --- a/.github/workflows/test-build-processes.yml +++ b/.github/workflows/test-build-processes.yml @@ -59,12 +59,14 @@ jobs: matrix: os: [ 'ubuntu-24.04' ] directory: [ 'src', 'build' ] + test-certificates: [ true ] include: # Only prepare artifacts for Playground once. - os: 'ubuntu-24.04' directory: 'build' save-build: true prepare-playground: ${{ github.event_name == 'pull_request' && true || '' }} + with: os: ${{ matrix.os }} directory: ${{ matrix.directory }} From e15125c3fa2a8086e70ad10a62bc3627ec80e716 Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 5 Sep 2025 20:39:00 -0400 Subject: [PATCH 3/5] Pass `test-certificate` value as an input. --- .github/workflows/test-build-processes.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-build-processes.yml b/.github/workflows/test-build-processes.yml index 065ebe805e64d..a30c7ee93d16c 100644 --- a/.github/workflows/test-build-processes.yml +++ b/.github/workflows/test-build-processes.yml @@ -70,6 +70,7 @@ jobs: with: os: ${{ matrix.os }} directory: ${{ matrix.directory }} + test-certificates: ${{ matrix.test-certificates || false }} save-build: ${{ matrix.save-build && matrix.save-build || false }} prepare-playground: ${{ matrix.prepare-playground && matrix.prepare-playground || false }} From 7a7895a66da8183a8d4c58fcbbfabfed4cbc6d8e Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 5 Sep 2025 20:41:36 -0400 Subject: [PATCH 4/5] =?UTF-8?q?Short=20syntax=20doesn=E2=80=99t=20appear?= =?UTF-8?q?=20to=20work.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test-build-processes.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test-build-processes.yml b/.github/workflows/test-build-processes.yml index a30c7ee93d16c..9096d19838d84 100644 --- a/.github/workflows/test-build-processes.yml +++ b/.github/workflows/test-build-processes.yml @@ -66,11 +66,10 @@ jobs: directory: 'build' save-build: true prepare-playground: ${{ github.event_name == 'pull_request' && true || '' }} - with: os: ${{ matrix.os }} directory: ${{ matrix.directory }} - test-certificates: ${{ matrix.test-certificates || false }} + test-certificates: ${{ matrix.test-certificates && true || false }} save-build: ${{ matrix.save-build && matrix.save-build || false }} prepare-playground: ${{ matrix.prepare-playground && matrix.prepare-playground || false }} From a4154f22cc9cdc912413f164b3f97f99d1553ddd Mon Sep 17 00:00:00 2001 From: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com> Date: Fri, 5 Sep 2025 20:45:30 -0400 Subject: [PATCH 5/5] Remove redundant `require` --- Gruntfile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index d97c20743e6e4..729f1117522e4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,7 +4,6 @@ var webpackConfig = require( './webpack.config' ); var installChanged = require( 'install-changed' ); var json2php = require( 'json2php' ); -const fs = require("fs"); module.exports = function(grunt) { var path = require('path'),