diff --git a/.github/actions/setup-npm/action.yml b/.github/actions/setup-npm/action.yml new file mode 100644 index 0000000000000..fba5bd0257bcc --- /dev/null +++ b/.github/actions/setup-npm/action.yml @@ -0,0 +1,55 @@ +name: 'Set up npm' +description: 'Installs the npm version required by the devEngines field of package.json, so every job resolves dependencies identically. Keeps the bundled npm when the field is absent.' + +inputs: + working-directory: + description: 'Optional. The directory holding the package.json to read the required npm version from. Defaults to the workspace root.' + required: false + default: '.' + cache: + description: 'Optional. Whether to restore and save the npm cache. Defaults to true.' + required: false + default: 'true' + +runs: + using: 'composite' + steps: + - name: Install npm + id: npm + env: + WORKING_DIRECTORY: ${{ inputs.working-directory }} + run: | + NPM_VERSION="$(jq -r '.devEngines.packageManager.version // empty' package.json)" + if [ -n "$NPM_VERSION" ]; then + # Contributors need the floor; CI stays on that major's newest release. + npm install --global "npm@${NPM_VERSION/>=/^}" + echo "Required npm version: $NPM_VERSION" + else + echo "No devEngines.packageManager.version, keeping the bundled npm." + fi + echo "Installed npm version: $(npm --version)" + + # actions/setup-node cannot restore the npm cache: it locates the + # directory with `npm config get cache`, which runs before this step + # and fails the devEngines check under the bundled npm. + echo "cache-dir=$(npm config get cache | tr -d '\r')" >> "$GITHUB_OUTPUT" + + # Scopes the cache key to this package and keeps the hashFiles patterns + # workspace-relative and free of a "./" prefix, so they match the same + # files as every other cache key. + PATH_PREFIX="" + if [ "$WORKING_DIRECTORY" != '.' ] && [ -n "$WORKING_DIRECTORY" ]; then + PATH_PREFIX="${WORKING_DIRECTORY%/}/" + fi + echo "path-prefix=$PATH_PREFIX" >> "$GITHUB_OUTPUT" + working-directory: ${{ inputs.working-directory }} + shell: bash + + - name: Cache npm downloads + if: ${{ inputs.cache == 'true' }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: ${{ steps.npm.outputs.cache-dir }} + key: npm-cache-${{ runner.os }}-${{ runner.arch }}-${{ steps.npm.outputs.path-prefix }}lock-${{ hashFiles(format('{0}package-lock.json', steps.npm.outputs.path-prefix), format('{0}patches/**', steps.npm.outputs.path-prefix)) }} + restore-keys: | + npm-cache-${{ runner.os }}-${{ runner.arch }}-${{ steps.npm.outputs.path-prefix }}lock- diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index e1526d54913f3..ad099ad58460a 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -68,6 +68,7 @@ on: - 'composer.*' - '**.jshintrc' - 'phpcs.xml.dist' + - '.github/actions/setup-npm/**' - '.github/workflows/coding-standards.yml' - '.github/workflows/reusable-coding-standards-*.yml' workflow_dispatch: diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index ca7fb3fae4066..e249f6e25475c 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -43,6 +43,7 @@ on: - 'tools/local-env/**' - 'tools/vendors/**' - 'tools/webpack/**' + - '.github/actions/setup-npm/**' - '.github/workflows/end-to-end-tests.yml' - '.github/workflows/reusable-end-to-end-tests*.yml' workflow_dispatch: diff --git a/.github/workflows/javascript-tests.yml b/.github/workflows/javascript-tests.yml index 7734f8fd8e98c..97c6f1099a43e 100644 --- a/.github/workflows/javascript-tests.yml +++ b/.github/workflows/javascript-tests.yml @@ -69,6 +69,7 @@ on: - 'tools/webpack/**' - '.jshintrc' - 'tests/qunit/**' + - '.github/actions/setup-npm/**' - '.github/workflows/javascript-tests.yml' - '.github/workflows/reusable-javascript-tests.yml' workflow_dispatch: diff --git a/.github/workflows/javascript-type-checking.yml b/.github/workflows/javascript-type-checking.yml index 65561cabbb1e1..0bf9288b06e82 100644 --- a/.github/workflows/javascript-type-checking.yml +++ b/.github/workflows/javascript-type-checking.yml @@ -24,6 +24,7 @@ on: # This directory contains TypeScript definitions. Changes could affect the outcome. - 'typings/**' # Confirm any changes to relevant workflow files. + - '.github/actions/setup-npm/**' - '.github/workflows/javascript-type-checking.yml' - '.github/workflows/reusable-javascript-type-checking-v1.yml' workflow_dispatch: diff --git a/.github/workflows/local-docker-environment.yml b/.github/workflows/local-docker-environment.yml index 894aeeebd779c..c636c695b6d44 100644 --- a/.github/workflows/local-docker-environment.yml +++ b/.github/workflows/local-docker-environment.yml @@ -28,6 +28,7 @@ on: # These files define the versions to test. - '.version-support-*.json' # Changes to this and related workflow files should always be verified. + - '.github/actions/setup-npm/**' - '.github/workflows/local-docker-environment.yml' - '.github/workflows/reusable-support-json-reader-v1.yml' - '.github/workflows/reusable-test-docker-environment-v1.yml' @@ -55,6 +56,7 @@ on: # These files define the versions to test. - '.version-support-*.json' # Changes to this and related workflow files should always be verified. + - '.github/actions/setup-npm/**' - '.github/workflows/local-docker-environment.yml' - '.github/workflows/reusable-support-json-reader-v1.yml' - '.github/workflows/reusable-test-docker-environment-v1.yml' diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index c4a3528a552f7..808f299208bc6 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -43,6 +43,7 @@ on: - 'tools/local-env/**' - 'tools/vendors/**' - 'tools/webpack/**' + - '.github/actions/setup-npm/**' - '.github/workflows/performance.yml' - '.github/workflows/reusable-performance-*.yml' workflow_dispatch: diff --git a/.github/workflows/phpunit-tests-full-matrix.yml b/.github/workflows/phpunit-tests-full-matrix.yml index a6b42f6401174..eedbbdb087e4e 100644 --- a/.github/workflows/phpunit-tests-full-matrix.yml +++ b/.github/workflows/phpunit-tests-full-matrix.yml @@ -42,6 +42,7 @@ on: - 'tools/vendors/**' - 'tools/webpack/**' - 'phpunit.xml.dist' + - '.github/actions/setup-npm/**' - '.github/workflows/phpunit-tests.yml' - '.github/workflows/phpunit-tests-full-matrix.yml' - '.github/workflows/reusable-phpunit-tests-*.yml' diff --git a/.github/workflows/phpunit-tests.yml b/.github/workflows/phpunit-tests.yml index 1e4a22bb61afc..23530363960c4 100644 --- a/.github/workflows/phpunit-tests.yml +++ b/.github/workflows/phpunit-tests.yml @@ -46,6 +46,7 @@ on: - 'tools/vendors/**' - 'tools/webpack/**' - 'phpunit.xml.dist' + - '.github/actions/setup-npm/**' - '.github/workflows/phpunit-tests.yml' - '.github/workflows/phpunit-tests-full-matrix.yml' - '.github/workflows/reusable-phpunit-tests-*.yml' diff --git a/.github/workflows/reusable-build-package.yml b/.github/workflows/reusable-build-package.yml index 9dbed3d8eea6b..bc01364cd28af 100644 --- a/.github/workflows/reusable-build-package.yml +++ b/.github/workflows/reusable-build-package.yml @@ -40,7 +40,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Install npm Dependencies run: npm ci diff --git a/.github/workflows/reusable-coding-standards-javascript.yml b/.github/workflows/reusable-coding-standards-javascript.yml index 9793d6a65e8df..c81ef7aa2c262 100644 --- a/.github/workflows/reusable-coding-standards-javascript.yml +++ b/.github/workflows/reusable-coding-standards-javascript.yml @@ -45,7 +45,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Log debug information run: | diff --git a/.github/workflows/reusable-end-to-end-tests.yml b/.github/workflows/reusable-end-to-end-tests.yml index 8c252273bfa89..4cd773fa6a6de 100644 --- a/.github/workflows/reusable-end-to-end-tests.yml +++ b/.github/workflows/reusable-end-to-end-tests.yml @@ -84,7 +84,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Log debug information run: | diff --git a/.github/workflows/reusable-javascript-tests.yml b/.github/workflows/reusable-javascript-tests.yml index fe40db119f87a..62b237a515e66 100644 --- a/.github/workflows/reusable-javascript-tests.yml +++ b/.github/workflows/reusable-javascript-tests.yml @@ -46,7 +46,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Log debug information run: | diff --git a/.github/workflows/reusable-javascript-type-checking-v1.yml b/.github/workflows/reusable-javascript-type-checking-v1.yml index 19032518b1545..e0bf36ce19c9c 100644 --- a/.github/workflows/reusable-javascript-type-checking-v1.yml +++ b/.github/workflows/reusable-javascript-type-checking-v1.yml @@ -44,7 +44,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Log debug information run: | diff --git a/.github/workflows/reusable-performance-report-v2.yml b/.github/workflows/reusable-performance-report-v2.yml index c97f053632321..56cc147491955 100644 --- a/.github/workflows/reusable-performance-report-v2.yml +++ b/.github/workflows/reusable-performance-report-v2.yml @@ -67,7 +67,7 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false - name: Download artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/reusable-performance-test-v2.yml b/.github/workflows/reusable-performance-test-v2.yml index 5063ecd6195ca..9d6f6c567797f 100644 --- a/.github/workflows/reusable-performance-test-v2.yml +++ b/.github/workflows/reusable-performance-test-v2.yml @@ -128,7 +128,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Set up PHP if: ${{ inputs.subject == 'base' }} diff --git a/.github/workflows/reusable-phpunit-tests-v3.yml b/.github/workflows/reusable-phpunit-tests-v3.yml index 88451b296b66d..cfb6b0ad12b37 100644 --- a/.github/workflows/reusable-phpunit-tests-v3.yml +++ b/.github/workflows/reusable-phpunit-tests-v3.yml @@ -219,7 +219,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm ## # This allows Composer dependencies to be installed using a single step. diff --git a/.github/workflows/reusable-prepare-gutenberg.yml b/.github/workflows/reusable-prepare-gutenberg.yml index 2d2c407ecdc7a..fd7381550f349 100644 --- a/.github/workflows/reusable-prepare-gutenberg.yml +++ b/.github/workflows/reusable-prepare-gutenberg.yml @@ -74,6 +74,7 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' + package-manager-cache: false - name: Download and verify Gutenberg build id: download diff --git a/.github/workflows/reusable-test-core-build-process.yml b/.github/workflows/reusable-test-core-build-process.yml index 70af6114fe944..addf6dd62c720 100644 --- a/.github/workflows/reusable-test-core-build-process.yml +++ b/.github/workflows/reusable-test-core-build-process.yml @@ -96,7 +96,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm - name: Log debug information run: | diff --git a/.github/workflows/reusable-test-local-docker-environment-v1.yml b/.github/workflows/reusable-test-local-docker-environment-v1.yml index 0e33daa553199..5d63fbc9aa4b4 100644 --- a/.github/workflows/reusable-test-local-docker-environment-v1.yml +++ b/.github/workflows/reusable-test-local-docker-environment-v1.yml @@ -95,7 +95,12 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: '.nvmrc' - cache: npm + package-manager-cache: false + + - name: Set up npm + # Release branches run this workflow from trunk, but some older branches may lack the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm ## # This allows Composer dependencies to be installed using a single step. diff --git a/.github/workflows/test-and-zip-default-themes.yml b/.github/workflows/test-and-zip-default-themes.yml index fc631c5852fef..75de38f8f700b 100644 --- a/.github/workflows/test-and-zip-default-themes.yml +++ b/.github/workflows/test-and-zip-default-themes.yml @@ -10,6 +10,7 @@ on: # Changing the preferred version of Node.js could affect themes with build processes. - '.npmrc' - '.nvmrc' + - '.github/actions/setup-npm/**' # Changes to any themes with a build script should be confirmed. - 'src/wp-content/themes/twentynineteen/**' - 'src/wp-content/themes/twentytwenty/**' @@ -27,6 +28,7 @@ on: # Changing the preferred version of Node.js could affect themes with build processes. - '.npmrc' - '.nvmrc' + - '.github/actions/setup-npm/**' # Changes to any themes with a build script should be confirmed. - 'src/wp-content/themes/twentynineteen/**' - 'src/wp-content/themes/twentytwenty/**' @@ -161,8 +163,14 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: 'src/wp-content/themes/${{ matrix.theme }}/.nvmrc' - cache: npm - cache-dependency-path: src/wp-content/themes/${{ matrix.theme }}/package-lock.json + package-manager-cache: false + + - name: Set up npm + # A workflow_dispatch run can check out a release branch, which lacks the action. + if: ${{ hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm + with: + working-directory: src/wp-content/themes/${{ matrix.theme }} - name: Install npm dependencies run: npm ci @@ -246,8 +254,14 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version-file: 'src/wp-content/themes/${{ matrix.theme }}/.nvmrc' - cache: npm - cache-dependency-path: src/wp-content/themes/${{ matrix.theme }}/package-lock.json + package-manager-cache: false + + - name: Set up npm + # A workflow_dispatch run can check out a release branch, which lacks the action. + if: ${{ ( matrix.theme == 'twentytwentytwo' || matrix.theme == 'twentytwentyfive' ) && hashFiles('.github/actions/setup-npm/action.yml') != '' }} + uses: ./.github/actions/setup-npm + with: + working-directory: src/wp-content/themes/${{ matrix.theme }} - name: Install npm dependencies if: matrix.theme == 'twentytwentytwo' || matrix.theme == 'twentytwentyfive' diff --git a/.github/workflows/test-build-processes.yml b/.github/workflows/test-build-processes.yml index 003fd80d08e08..ed970276e9833 100644 --- a/.github/workflows/test-build-processes.yml +++ b/.github/workflows/test-build-processes.yml @@ -43,6 +43,7 @@ on: - 'tools/gutenberg/**' - 'tools/vendors/**' - 'tools/webpack/**' + - '.github/actions/setup-npm/**' - '.github/workflows/test-build-processes.yml' - '.github/workflows/reusable-test-core-build-process.yml' workflow_dispatch: diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml index 191445ba7167c..c29be2fb4043e 100644 --- a/.github/workflows/test-coverage.yml +++ b/.github/workflows/test-coverage.yml @@ -6,6 +6,7 @@ on: branches: - trunk paths: + - '.github/actions/setup-npm/**' - '.github/workflows/test-coverage.yml' - '.github/workflows/reusable-phpunit-tests-v3.yml' - '.github/workflows/reusable-prepare-gutenberg.yml' @@ -19,6 +20,7 @@ on: branches: - trunk paths: + - '.github/actions/setup-npm/**' - '.github/workflows/test-coverage.yml' - '.github/workflows/reusable-phpunit-tests-v3.yml' - '.github/workflows/reusable-prepare-gutenberg.yml' diff --git a/.github/workflows/upgrade-develop-testing.yml b/.github/workflows/upgrade-develop-testing.yml index a5474e24620a5..a992ab2925a63 100644 --- a/.github/workflows/upgrade-develop-testing.yml +++ b/.github/workflows/upgrade-develop-testing.yml @@ -15,6 +15,7 @@ on: - '*.php' # PHP files in the root, like wp-config-sample.php. # Confirm any changes to relevant workflow files. - '.github/actions/smoke-check/**' + - '.github/actions/setup-npm/**' - '.github/workflows/upgrade-develop-testing.yml' - '.github/workflows/reusable-upgrade-testing.yml' pull_request: @@ -27,6 +28,7 @@ on: - 'src/**.php' - '*.php' # PHP files in the root, like wp-config-sample.php. # Confirm any changes to relevant workflow files. + - '.github/actions/setup-npm/**' - '.github/workflows/upgrade-develop-testing.yml' - '.github/workflows/reusable-upgrade-testing.yml' workflow_dispatch: diff --git a/package.json b/package.json index ca760568737a3..98ddced8db8ef 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,17 @@ "node": ">=24.18.0", "npm": ">=11.16.0" }, + "devEngines": { + "runtime": { + "name": "node", + "version": ">=24.18.0" + }, + "packageManager": { + "name": "npm", + "version": ">=11.16.0", + "onFail": "error" + } + }, "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", "browserslist": [