Skip to content

Conversation

@renovate
Copy link
Contributor

@renovate renovate bot commented Nov 13, 2025

This PR contains the following updates:

Package Type Update Change
actions/setup-node action major v5.0.0 -> v6.0.0

Release Notes

actions/setup-node (actions/setup-node)

v6.0.0

Compare Source

What's Changed

Breaking Changes

Dependency Upgrades

Full Changelog: actions/setup-node@v5...v6.0.0


Configuration

📅 Schedule: Branch creation - Tuesday through Thursday ( * * * * 2-4 ) (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added PR: Dependencies 🔩 Changes only update dependencies renovate labels Nov 13, 2025
@renovate renovate bot requested review from remusao and yshym as code owners November 13, 2025 15:02
@renovate renovate bot force-pushed the renovate/actions-setup-node-6-x branch from f6e44d9 to ed27192 Compare November 14, 2025 17:33
@github-actions
Copy link

[puLL-Merge] - actions/[email protected]

Diff
diff --git .github/workflows/check-dist.yml .github/workflows/check-dist.yml
index 64d40471d..aac4cd2f9 100644
--- .github/workflows/check-dist.yml
+++ .github/workflows/check-dist.yml
@@ -16,4 +16,4 @@ jobs:
     name: Check dist/
     uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
     with:
-      node-version: '20.x'
+      node-version: '24.x'
diff --git .github/workflows/e2e-cache.yml .github/workflows/e2e-cache.yml
index f1c1868bd..ddedf39b0 100644
--- .github/workflows/e2e-cache.yml
+++ .github/workflows/e2e-cache.yml
@@ -18,8 +18,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Clean global cache
@@ -41,8 +41,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Install pnpm
@@ -74,8 +74,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Yarn version
@@ -106,8 +106,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Update yarn
@@ -139,7 +139,7 @@ jobs:
     name: Test yarn subprojects
     strategy:
       matrix:
-        node-version: [18, 20, 22, 24]
+        node-version: [20, 22, 24]
     runs-on: ubuntu-latest
 
     steps:
@@ -166,7 +166,7 @@ jobs:
     name: Test yarn subprojects all locally managed
     strategy:
       matrix:
-        node-version: [18, 20, 22, 24]
+        node-version: [20, 22, 24]
     runs-on: ubuntu-latest
 
     steps:
@@ -193,7 +193,7 @@ jobs:
     name: Test yarn subprojects some locally managed
     strategy:
       matrix:
-        node-version: [18, 20, 22, 24]
+        node-version: [20, 22, 24]
     runs-on: ubuntu-latest
 
     steps:
@@ -220,7 +220,7 @@ jobs:
     name: Test yarn subprojects managed by git
     strategy:
       matrix:
-        node-version: [18, 20, 22, 24]
+        node-version: [20, 22, 24]
     runs-on: ubuntu-latest
 
     steps:
@@ -244,14 +244,14 @@ jobs:
             sub2/*.lock
             sub3/*.lock
 
-  node-npm-package-manager-cache:
-    name: Test enabling cache if package manager field is present (Node ${{ matrix.node-version }}, ${{ matrix.os }})
+  node-npm-packageManager-auto-cache:
+    name: Test auto cache with top-level packageManager
     runs-on: ${{ matrix.os }}
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Create package.json with packageManager field
@@ -268,3 +268,37 @@ jobs:
       - name: Verify node and npm
         run: __tests__/verify-node.sh "${{ matrix.node-version }}"
         shell: bash
+
+  node-npm-devEngines-auto-cache:
+    name: Test auto cache with devEngines.packageManager
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
+    steps:
+      - uses: actions/checkout@v5
+      - name: Create package.json with devEngines field
+        run: |
+          echo '{
+            "name": "test-project",
+            "version": "1.0.0",
+            "devEngines": {
+              "packageManager": {
+                "name": "npm",
+                "onFail": "error"
+              }
+            }
+          }' > package.json
+      - name: Clean global cache
+        run: npm cache clean --force
+      - name: Setup Node with caching enabled
+        uses: ./
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Install dependencies
+        run: npm install
+      - name: Verify node and npm
+        run: __tests__/verify-node.sh "${{ matrix.node-version }}"
+        shell: bash
diff --git .github/workflows/proxy.yml .github/workflows/proxy.yml
index ab52b8fd4..c5493b596 100644
--- .github/workflows/proxy.yml
+++ .github/workflows/proxy.yml
@@ -28,12 +28,12 @@ jobs:
       - uses: actions/checkout@v5
       - name: Clear tool cache
         run: rm -rf $RUNNER_TOOL_CACHE/*
-      - name: Setup node 14
+      - name: Setup node 24
         uses: ./
         with:
-          node-version: 14.x
+          node-version: 24.x
       - name: Verify node and npm
-        run: __tests__/verify-node.sh 14
+        run: __tests__/verify-node.sh 24
 
   test-bypass-proxy:
     runs-on: ubuntu-latest
@@ -44,9 +44,9 @@ jobs:
       - uses: actions/checkout@v5
       - name: Clear tool cache
         run: rm -rf $RUNNER_TOOL_CACHE/*
-      - name: Setup node 11
+      - name: Setup node 24
         uses: ./
         with:
-          node-version: 11
+          node-version: 24
       - name: Verify node and npm
-        run: __tests__/verify-node.sh 11
+        run: __tests__/verify-node.sh 24
diff --git .github/workflows/release-new-action-version.yml .github/workflows/release-new-action-version.yml
index 7e5de347a..959ef8d87 100644
--- .github/workflows/release-new-action-version.yml
+++ .github/workflows/release-new-action-version.yml
@@ -22,7 +22,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Update the ${{ env.TAG_NAME }} tag
-        uses: actions/[email protected]
+        uses: actions/[email protected]
         with:
           source-tag: ${{ env.TAG_NAME }}
           slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
diff --git .github/workflows/versions.yml .github/workflows/versions.yml
index ba56f66fb..333e3a5bc 100644
--- .github/workflows/versions.yml
+++ .github/workflows/versions.yml
@@ -17,8 +17,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Setup Node
@@ -34,7 +34,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest-large]
         node-version: [lts/dubnium, lts/erbium, lts/fermium, lts/*, lts/-1]
     steps:
       - uses: actions/checkout@v5
@@ -56,7 +56,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
         node-version:
           [
             '20-v8-canary',
@@ -81,8 +81,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [20-nightly, 21-nightly, 18.0.0-nightly]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20-nightly, 25-nightly, 24.0.0-nightly]
     steps:
       - uses: actions/checkout@v5
       - name: Setup Node
@@ -101,8 +101,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [20.0.0-rc.1, 18.0.0-rc.2, 19.0.0-rc.0]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20.0.0-rc.1, 22.14.0-rc.1, 24.0.0-rc.4]
     steps:
       - uses: actions/checkout@v5
       - name: Setup Node
@@ -121,8 +121,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18.20.0, 20.10.0, 22.0.0]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20.10.0, 22.0.0, 24.9.0]
     steps:
       - uses: actions/checkout@v5
       - name: Setup Node
@@ -138,8 +138,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
-        node-version: [18, 20, 22, 24]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
+        node-version: [20, 22, 24]
     steps:
       - uses: actions/checkout@v5
       - name: Setup Node and check latest
@@ -156,7 +156,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
         node-version-file:
           [.nvmrc, .tool-versions, .tool-versions-node, package.json]
     steps:
@@ -173,7 +173,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
     steps:
       - uses: actions/checkout@v5
       - name: Setup node from node version file
@@ -188,7 +188,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
     steps:
       - uses: actions/checkout@v5
       - name: Setup node from node version file
@@ -203,7 +203,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
         node-version: [17, 19]
     steps:
       - uses: actions/checkout@v5
@@ -220,7 +220,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest-large]
     steps:
       - uses: actions/checkout@v5
       # test old versions which didn't have npm and layout different
@@ -250,7 +250,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-latest, windows-latest, macos-latest, macos-13]
+        os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
         node-version: [current, latest, node]
     steps:
       - name: Get node version
diff --git .licenses/npm/semver-7.6.3.dep.yml .licenses/npm/semver-7.7.2.dep.yml
similarity index 98%
rename from .licenses/npm/semver-7.6.3.dep.yml
rename to .licenses/npm/semver-7.7.2.dep.yml
index 4e5e9d935..415789165 100644
--- .licenses/npm/semver-7.6.3.dep.yml
+++ .licenses/npm/semver-7.7.2.dep.yml
@@ -1,6 +1,6 @@
 ---
 name: semver
-version: 7.6.3
+version: 7.7.2
 type: npm
 summary: The semantic version parser used by npm.
 homepage:
diff --git README.md README.md
index e98158c67..24aa81d19 100644
--- README.md
+++ README.md
@@ -12,13 +12,27 @@ This action provides the following functionality for GitHub Actions users:
 - Registering problem matchers for error output
 - Configuring authentication for GPR or npm
 
+## Breaking changes in V6
+
+- Caching is now automatically enabled for npm projects when either the `devEngines.packageManager` field or the top-level `packageManager` field in `package.json` is set to `npm`. For other package managers, such as Yarn and pnpm, caching is disabled by default and must be configured manually using the `cache` input.
+
+## Breaking changes in V5 
+
+- Enabled caching by default with package manager detection if no cache input is provided.
+  > For workflows with elevated privileges or access to sensitive information, we recommend disabling automatic caching by setting `package-manager-cache: false` when caching is not needed for secure operation.
+
+- Upgraded action from node20 to node24.
+  > Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. [See Release Notes](https://github.com/actions/runner/releases/tag/v2.327.1)
+
+For more details, see the full release notes on the [releases page](https://github.com/actions/setup-node/releases/v5.0.0)
+
 ## Usage
 
 See [action.yml](action.yml)
 
 <!-- start usage -->
 ```yaml
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
     # Version Spec of the version to use in SemVer notation.
     # It also admits such aliases as lts/*, latest, nightly and canary builds
@@ -57,6 +71,11 @@ See [action.yml](action.yml)
     # Default: ''
     cache: ''
 
+    # Controls automatic caching for npm. By default, caching for npm is enabled if either the devEngines.packageManager field or the top-level packageManager field in package.json specifies npm and no explicit cache input is provided.
+    # To disable automatic caching for npm, set package-manager-cache to false.
+    # default: true
+    package-manager-cache: true
+
     # Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. 
     # It will generate hash from the target file for primary key. It works only If cache is specified.  
     # Supports wildcards or a list of file names for caching multiple dependencies.
@@ -99,9 +118,9 @@ See [action.yml](action.yml)
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: 18
+    node-version: 24
 - run: npm ci
 - run: npm test
 ```
@@ -118,9 +137,9 @@ The `node-version` input supports the Semantic Versioning Specification, for mor
 
 Examples:
 
- - Major versions: `18`, `20`
- - More specific versions: `10.15`, `16.15.1` , `18.4.0`
- - NVM LTS syntax: `lts/erbium`, `lts/fermium`, `lts/*`, `lts/-n`
+ - Major versions: `22`, `24`
+ - More specific versions: `20.19`, `22.17.1` , `24.8.0`
+ - NVM LTS syntax: `lts/iron`, `lts/jod`, `lts/*`, `lts/-n`
  - Latest release: `*` or `latest`/`current`/`node`
 
 **Note:** Like the other values, `*` will get the latest [locally-cached Node.js version](https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md#nodejs), or the latest version from [actions/node-versions](https://github.com/actions/node-versions/blob/main/versions-manifest.json), depending on the [`check-latest`](docs/advanced-usage.md#check-latest-version) input.
@@ -137,18 +156,6 @@ It's **always** recommended to commit the lockfile of your package manager for s
 
 The action has a built-in functionality for caching and restoring dependencies. It uses [actions/cache](https://github.com/actions/cache) under the hood for caching global packages data but requires less configuration settings. Supported package managers are `npm`, `yarn`, `pnpm` (v6.10+). The `cache` input is optional.
 
-Caching is turned on by default when a `packageManager` field is detected in the `package.json` file. The `package-manager-cache` input provides control over this automatic caching behavior. By default, `package-manager-cache` is set to `true`, which enables caching when a valid package manager field is detected in the `package.json` file. To disable this automatic caching, set the `package-manager-cache` input to `false`.
-
-```yaml
-steps:
-- uses: actions/checkout@v4
-- uses: actions/setup-node@v4
-  with:
-    package-manager-cache: false
-- run: npm ci
-```
-> If no valid `packageManager` field is detected in the `package.json` file, caching will remain disabled unless explicitly configured.
-
 The action defaults to search for the dependency file (`package-lock.json`, `npm-shrinkwrap.json` or `yarn.lock`) in the repository root, and uses its hash as a part of the cache key. Use `cache-dependency-path` for cases when multiple dependency files are used, or they are located in different subdirectories.
 
 **Note:** The action does not cache `node_modules`
@@ -160,9 +167,9 @@ See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` in
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: 20
+    node-version: 24
     cache: 'npm'
 - run: npm ci
 - run: npm test
@@ -173,15 +180,29 @@ steps:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: 20
+    node-version: 24
     cache: 'npm'
     cache-dependency-path: subdir/package-lock.json
 - run: npm ci
 - run: npm test
 ```
 
+Caching for npm dependencies is automatically enabled when your `package.json` contains either `devEngines.packageManager` field or top-level `packageManager` field set to `npm`, and no explicit cache input is provided.
+
+This behavior is controlled by the `package-manager-cache` input, which defaults to `true`. To turn off automatic caching, set `package-manager-cache` to `false`.
+
+```yaml
+steps:
+- uses: actions/checkout@v5
+- uses: actions/setup-node@v6
+  with:
+    package-manager-cache: false
+- run: npm ci
+```
+> If your `package.json` file does not include a `packageManager` field set to `npm`, caching will be disabled unless you explicitly enable it. For workflows with elevated privileges or access to sensitive information, we recommend disabling automatic caching for npm by setting `package-manager-cache: false` when caching is not required for secure operation.
+
 ## Matrix Testing
 
 ```yaml
@@ -190,12 +211,12 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node: [ 14, 16, 18 ]
+        node: [ 20, 22, 24 ]
     name: Node ${{ matrix.node }} sample
     steps:
       - uses: actions/checkout@v5
       - name: Setup node
-        uses: actions/setup-node@v5
+        uses: actions/setup-node@v6
         with:
           node-version: ${{ matrix.node }}
       - run: npm ci
@@ -209,10 +230,10 @@ jobs:
 To get a higher rate limit, you can [generate a personal access token on github.com](https://github.com/settings/tokens/new) and pass it as the `token` input for the action:
 
 ```yaml
-uses: actions/setup-node@v5
+uses: actions/setup-node@v6
 with:
   token: ${{ secrets.GH_DOTCOM_TOKEN }}
-  node-version: 20
+  node-version: 24
 ```
 
 If the runner is not able to access github.com, any Nodejs versions requested during a workflow run must come from the runner's tool cache. See "[Setting up the tool cache on self-hosted runners without internet access](https://docs.github.com/en/[email protected]/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)" for more information.
@@ -250,4 +271,4 @@ Contributions are welcome! See [Contributor's Guide](docs/contributors.md)
 
 ## Code of Conduct
 
-:wave: Be nice. See [our code of conduct](CODE_OF_CONDUCT.md)
+:wave: Be nice. See [our code of conduct](CODE_OF_CONDUCT.md)
\ No newline at end of file
diff --git __tests__/cache-save.test.ts __tests__/cache-save.test.ts
index 17899dfa3..79a28fd41 100644
--- __tests__/cache-save.test.ts
+++ __tests__/cache-save.test.ts
@@ -114,10 +114,10 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CachePrimaryKey || key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePaths
+              ? '["/foo/bar"]'
+              : 'not expected'
       );
 
       await run();
@@ -138,10 +138,10 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CachePrimaryKey || key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePaths
+              ? '["/foo/bar"]'
+              : 'not expected'
       );
 
       await run();
@@ -162,10 +162,10 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CachePrimaryKey || key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePaths
+              ? '["/foo/bar"]'
+              : 'not expected'
       );
       getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/npm`);
 
@@ -184,10 +184,10 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CachePrimaryKey || key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePaths
+              ? '["/foo/bar"]'
+              : 'not expected'
       );
 
       await run();
@@ -207,12 +207,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePrimaryKey
-          ? npmFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePrimaryKey
+              ? npmFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
 
       await run();
@@ -237,12 +237,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? yarnFileHash
-          : key === State.CachePrimaryKey
-          ? npmFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? yarnFileHash
+            : key === State.CachePrimaryKey
+              ? npmFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
 
       await run();
@@ -267,12 +267,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? npmFileHash
-          : key === State.CachePrimaryKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? npmFileHash
+            : key === State.CachePrimaryKey
+              ? yarnFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
 
       await run();
@@ -297,12 +297,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? pnpmFileHash
-          : key === State.CachePrimaryKey
-          ? npmFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? pnpmFileHash
+            : key === State.CachePrimaryKey
+              ? npmFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
 
       await run();
@@ -327,12 +327,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? npmFileHash
-          : key === State.CachePrimaryKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? npmFileHash
+            : key === State.CachePrimaryKey
+              ? yarnFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
       saveCacheSpy.mockImplementation(() => {
         return -1;
@@ -360,12 +360,12 @@ describe('run', () => {
         key === State.CachePackageManager
           ? inputs['cache']
           : key === State.CacheMatchedKey
-          ? npmFileHash
-          : key === State.CachePrimaryKey
-          ? yarnFileHash
-          : key === State.CachePaths
-          ? '["/foo/bar"]'
-          : 'not expected'
+            ? npmFileHash
+            : key === State.CachePrimaryKey
+              ? yarnFileHash
+              : key === State.CachePaths
+                ? '["/foo/bar"]'
+                : 'not expected'
       );
       saveCacheSpy.mockImplementation(() => {
         throw new cache.ValidationError('Validation failed');
diff --git __tests__/main.test.ts __tests__/main.test.ts
index 5af570929..ba26e64bd 100644
--- __tests__/main.test.ts
+++ __tests__/main.test.ts
@@ -285,34 +285,72 @@ describe('main tests', () => {
   });
 
   describe('cache feature tests', () => {
-    it('Should enable caching with the resolved package manager from packageManager field in package.json when the cache input is not provided', async () => {
+    it('Should enable caching when packageManager is npm and cache input is not provided', async () => {
       inputs['package-manager-cache'] = 'true';
-      inputs['cache'] = ''; // No cache input is provided
+      inputs['cache'] = '';
+      isCacheActionAvailable.mockImplementation(() => true);
 
       inSpy.mockImplementation(name => inputs[name]);
+      const readFileSpy = jest.spyOn(fs, 'readFileSync');
+      readFileSpy.mockImplementation(() =>
+        JSON.stringify({
+          packageManager: '[email protected]'
+        })
+      );
 
+      await main.run();
+
+      expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
+    });
+
+    it('Should enable caching when devEngines.packageManager.name is "npm" and cache input is not provided', async () => {
+      inputs['package-manager-cache'] = 'true';
+      inputs['cache'] = '';
+      isCacheActionAvailable.mockImplementation(() => true);
+
+      inSpy.mockImplementation(name => inputs[name]);
       const readFileSpy = jest.spyOn(fs, 'readFileSync');
       readFileSpy.mockImplementation(() =>
         JSON.stringify({
-          packageManager: '[email protected]'
+          devEngines: {
+            packageManager: {name: 'npm'}
+          }
         })
       );
 
       await main.run();
 
-      expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'yarn');
+      expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
     });
 
-    it('Should not enable caching if the packageManager field is missing in package.json and the cache input is not provided', async () => {
+    it('Should enable caching when devEngines.packageManager is array and one entry has name "npm"', async () => {
       inputs['package-manager-cache'] = 'true';
-      inputs['cache'] = ''; // No cache input is provided
+      inputs['cache'] = '';
+      isCacheActionAvailable.mockImplementation(() => true);
 
       inSpy.mockImplementation(name => inputs[name]);
+      const readFileSpy = jest.spyOn(fs, 'readFileSync');
+      readFileSpy.mockImplementation(() =>
+        JSON.stringify({
+          devEngines: {
+            packageManager: [{name: 'pnpm'}, {name: 'npm'}]
+          }
+        })
+      );
 
+      await main.run();
+
+      expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
+    });
+
+    it('Should not enable caching if packageManager is "[email protected]" and cache input is not provided', async () => {
+      inputs['package-manager-cache'] = 'true';
+      inputs['cache'] = '';
+      inSpy.mockImplementation(name => inputs[name]);
       const readFileSpy = jest.spyOn(fs, 'readFileSync');
       readFileSpy.mockImplementation(() =>
         JSON.stringify({
-          //packageManager field is not present
+          packageManager: '[email protected]'
         })
       );
 
@@ -321,26 +359,72 @@ describe('main tests', () => {
       expect(saveStateSpy).not.toHaveBeenCalled();
     });
 
-    it('Should skip caching when package-manager-cache is false', async () => {
-      inputs['package-manager-cache'] = 'false';
-      inputs['cache'] = ''; // No cache input is provided
-
+    it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
+      inputs['package-manager-cache'] = 'true';
+      inputs['cache'] = '';
       inSpy.mockImplementation(name => inputs[name]);
+      const readFileSpy = jest.spyOn(fs, 'readFileSync');
+      readFileSpy.mockImplementation(() =>
+        JSON.stringify({
+          devEngines: {
+            packageManager: {name: 'pnpm'}
+          }
+        })
+      );
 
       await main.run();
 
       expect(saveStateSpy).not.toHaveBeenCalled();
     });
 
-    it('Should enable caching with cache input explicitly provided', async () => {
+    it('Should not enable caching if devEngines.packageManager is array without "npm"', async () => {
       inputs['package-manager-cache'] = 'true';
-      inputs['cache'] = 'npm'; // Explicit cache input provided
+      inputs['cache'] = '';
+      inSpy.mockImplementation(name => inputs[name]);
+      const readFileSpy = jest.spyOn(fs, 'readFileSync');
+      readFileSpy.mockImplementation(() =>
+        JSON.stringify({
+          devEngines: {
+            packageManager: [{name: 'pnpm'}, {name: 'yarn'}]
+          }
+        })
+      );
 
+      await main.run();
+
+      expect(saveStateSpy).not.toHaveBeenCalled();
+    });
+
+    it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
+      inputs['package-manager-cache'] = 'true';
+      inputs['cache'] = '';
       inSpy.mockImplementation(name => inputs[name]);
-      isCacheActionAvailable.mockReturnValue(true);
+      const readFileSpy = jest.spyOn(fs, 'readFileSync');
+      readFileSpy.mockImplementation(() =>
+        JSON.stringify({
+          // packageManager field is not present
+        })
+      );
 
       await main.run();
 
+      expect(saveStateSpy).not.toHaveBeenCalled();
+    });
+
+    it('Should skip caching when package-manager-cache is false', async () => {
+      inputs['package-manager-cache'] = 'false';
+      inputs['cache'] = '';
+      inSpy.mockImplementation(name => inputs[name]);
+      await main.run();
+      expect(saveStateSpy).not.toHaveBeenCalled();
+    });
+
+    it('Should enable caching with cache input explicitly provided', async () => {
+      inputs['package-manager-cache'] = 'true';
+      inputs['cache'] = 'npm';
+      inSpy.mockImplementation(name => inputs[name]);
+      isCacheActionAvailable.mockImplementation(() => true);
+      await main.run();
       expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
     });
   });
diff --git action.yml action.yml
index fbc851b6e..73c766bc6 100644
--- action.yml
+++ action.yml
@@ -24,7 +24,7 @@ inputs:
   cache:
     description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.'
   package-manager-cache:
-    description: 'Set to false to disable automatic caching based on the package manager field in package.json. By default, caching is enabled if the package manager field is present.'
+    description: 'Set to false to disable automatic caching. By default, caching is enabled when either devEngines.packageManager or the top-level packageManager field in package.json specifies npm as the package manager.'
     default: true
   cache-dependency-path:
     description: 'Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. Supports wildcards or a list of file names for caching multiple dependencies.'
diff --git dist/setup/index.js dist/setup/index.js
index c6381da69..4aa29b266 100644
--- dist/setup/index.js
+++ dist/setup/index.js
@@ -65675,6 +65675,9 @@ function onceStrict (fn) {
 /***/ 89379:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const ANY = Symbol('SemVer ANY')
 // hoisted class for cyclic dependency
 class Comparator {
@@ -65823,6 +65826,9 @@ const Range = __nccwpck_require__(96782)
 /***/ 96782:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SPACE_CHARACTERS = /\s+/g
 
 // hoisted class for cyclic dependency
@@ -66384,6 +66390,9 @@ const testSet = (set, version, options) => {
 /***/ 7163:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const debug = __nccwpck_require__(1159)
 const { MAX_LENGTH, MAX_SAFE_INTEGER } = __nccwpck_require__(45101)
 const { safeRe: re, t } = __nccwpck_require__(95471)
@@ -66396,7 +66405,7 @@ class SemVer {
 
     if (version instanceof SemVer) {
       if (version.loose === !!options.loose &&
-          version.includePrerelease === !!options.includePrerelease) {
+        version.includePrerelease === !!options.includePrerelease) {
         return version
       } else {
         version = version.version
@@ -66562,6 +66571,19 @@ class SemVer {
   // preminor will bump the version up to the next minor release, and immediately
   // down to pre-release. premajor and prepatch work the same way.
   inc (release, identifier, identifierBase) {
+    if (release.startsWith('pre')) {
+      if (!identifier && identifierBase === false) {
+        throw new Error('invalid increment argument: identifier is empty')
+      }
+      // Avoid an invalid semver results
+      if (identifier) {
+        const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE])
+        if (!match || match[1] !== identifier) {
+          throw new Error(`invalid identifier: ${identifier}`)
+        }
+      }
+    }
+
     switch (release) {
       case 'premajor':
         this.prerelease.length = 0
@@ -66592,6 +66614,12 @@ class SemVer {
         }
         this.inc('pre', identifier, identifierBase)
         break
+      case 'release':
+        if (this.prerelease.length === 0) {
+          throw new Error(`version ${this.raw} is not a prerelease`)
+        }
+        this.prerelease.length = 0
+        break
 
       case 'major':
         // If this is a pre-major version, bump up to the same major version.
@@ -66635,10 +66663,6 @@ class SemVer {
       case 'pre': {
         const base = Number(identifierBase) ? 1 : 0
 
-        if (!identifier && identifierBase === false) {
-          throw new Error('invalid increment argument: identifier is empty')
-        }
-
         if (this.prerelease.length === 0) {
           this.prerelease = [base]
         } else {
@@ -66693,6 +66717,9 @@ module.exports = SemVer
 /***/ 1799:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const parse = __nccwpck_require__(16353)
 const clean = (version, options) => {
   const s = parse(version.trim().replace(/^[=v]+/, ''), options)
@@ -66706,6 +66733,9 @@ module.exports = clean
 /***/ 28646:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const eq = __nccwpck_require__(55082)
 const neq = __nccwpck_require__(4974)
 const gt = __nccwpck_require__(16599)
@@ -66765,6 +66795,9 @@ module.exports = cmp
 /***/ 35385:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const parse = __nccwpck_require__(16353)
 const { safeRe: re, t } = __nccwpck_require__(95471)
@@ -66832,6 +66865,9 @@ module.exports = coerce
 /***/ 37648:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const compareBuild = (a, b, loose) => {
   const versionA = new SemVer(a, loose)
@@ -66846,6 +66882,9 @@ module.exports = compareBuild
 /***/ 56874:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const compareLoose = (a, b) => compare(a, b, true)
 module.exports = compareLoose
@@ -66856,6 +66895,9 @@ module.exports = compareLoose
 /***/ 78469:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const compare = (a, b, loose) =>
   new SemVer(a, loose).compare(new SemVer(b, loose))
@@ -66868,6 +66910,9 @@ module.exports = compare
 /***/ 70711:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const parse = __nccwpck_require__(16353)
 
 const diff = (version1, version2) => {
@@ -66897,20 +66942,13 @@ const diff = (version1, version2) => {
       return 'major'
     }
 
-    // Otherwise it can be determined by checking the high version
-
-    if (highVersion.patch) {
-      // anything higher than a patch bump would result in the wrong version
+    // If the main part has no difference
+    if (lowVersion.compareMain(highVersion) === 0) {
+      if (lowVersion.minor && !lowVersion.patch) {
+        return 'minor'
+      }
       return 'patch'
     }
-
-    if (highVersion.minor) {
-      // anything higher than a minor bump would result in the wrong version
-      return 'minor'
-    }
-
-    // bumping major/minor/patch all have same result
-    return 'major'
   }
 
   // add the `pre` prefix if we are going to a prerelease version
@@ -66940,6 +66978,9 @@ module.exports = diff
 /***/ 55082:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const eq = (a, b, loose) => compare(a, b, loose) === 0
 module.exports = eq
@@ -66950,6 +66991,9 @@ module.exports = eq
 /***/ 16599:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const gt = (a, b, loose) => compare(a, b, loose) > 0
 module.exports = gt
@@ -66960,6 +67004,9 @@ module.exports = gt
 /***/ 41236:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const gte = (a, b, loose) => compare(a, b, loose) >= 0
 module.exports = gte
@@ -66970,6 +67017,9 @@ module.exports = gte
 /***/ 62338:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 
 const inc = (version, release, options, identifier, identifierBase) => {
@@ -66996,6 +67046,9 @@ module.exports = inc
 /***/ 3872:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const lt = (a, b, loose) => compare(a, b, loose) < 0
 module.exports = lt
@@ -67006,6 +67059,9 @@ module.exports = lt
 /***/ 56717:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const lte = (a, b, loose) => compare(a, b, loose) <= 0
 module.exports = lte
@@ -67016,6 +67072,9 @@ module.exports = lte
 /***/ 68511:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const major = (a, loose) => new SemVer(a, loose).major
 module.exports = major
@@ -67026,6 +67085,9 @@ module.exports = major
 /***/ 32603:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const minor = (a, loose) => new SemVer(a, loose).minor
 module.exports = minor
@@ -67036,6 +67098,9 @@ module.exports = minor
 /***/ 4974:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const neq = (a, b, loose) => compare(a, b, loose) !== 0
 module.exports = neq
@@ -67046,6 +67111,9 @@ module.exports = neq
 /***/ 16353:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const parse = (version, options, throwErrors = false) => {
   if (version instanceof SemVer) {
@@ -67069,6 +67137,9 @@ module.exports = parse
 /***/ 48756:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const patch = (a, loose) => new SemVer(a, loose).patch
 module.exports = patch
@@ -67079,6 +67150,9 @@ module.exports = patch
 /***/ 15714:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const parse = __nccwpck_require__(16353)
 const prerelease = (version, options) => {
   const parsed = parse(version, options)
@@ -67092,6 +67166,9 @@ module.exports = prerelease
 /***/ 32173:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compare = __nccwpck_require__(78469)
 const rcompare = (a, b, loose) => compare(b, a, loose)
 module.exports = rcompare
@@ -67102,6 +67179,9 @@ module.exports = rcompare
 /***/ 87192:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compareBuild = __nccwpck_require__(37648)
 const rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose))
 module.exports = rsort
@@ -67112,6 +67192,9 @@ module.exports = rsort
 /***/ 68011:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const Range = __nccwpck_require__(96782)
 const satisfies = (version, range, options) => {
   try {
@@ -67129,6 +67212,9 @@ module.exports = satisfies
 /***/ 29872:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const compareBuild = __nccwpck_require__(37648)
 const sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose))
 module.exports = sort
@@ -67139,6 +67225,9 @@ module.exports = sort
 /***/ 58780:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const parse = __nccwpck_require__(16353)
 const valid = (version, options) => {
   const v = parse(version, options)
@@ -67152,6 +67241,9 @@ module.exports = valid
 /***/ 62088:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 // just pre-load all the stuff that index.js lazily exports
 const internalRe = __nccwpck_require__(95471)
 const constants = __nccwpck_require__(45101)
@@ -67248,6 +67340,9 @@ module.exports = {
 /***/ 45101:
 /***/ ((module) => {
 
+"use strict";
+
+
 // Note: this is the semver.org version of the spec that it implements
 // Not necessarily the package version of this code.
 const SEMVER_SPEC_VERSION = '2.0.0'
@@ -67290,6 +67385,9 @@ module.exports = {
 /***/ 1159:
 /***/ ((module) => {
 
+"use strict";
+
+
 const debug = (
   typeof process === 'object' &&
   process.env &&
@@ -67306,6 +67404,9 @@ module.exports = debug
 /***/ 73348:
 /***/ ((module) => {
 
+"use strict";
+
+
 const numeric = /^[0-9]+$/
 const compareIdentifiers = (a, b) => {
   const anum = numeric.test(a)
@@ -67336,6 +67437,9 @@ module.exports = {
 /***/ 61383:
 /***/ ((module) => {
 
+"use strict";
+
+
 class LRUCache {
   constructor () {
     this.max = 1000
@@ -67383,6 +67487,9 @@ module.exports = LRUCache
 /***/ 70356:
 /***/ ((module) => {
 
+"use strict";
+
+
 // parse out just the options we care about
 const looseOption = Object.freeze({ loose: true })
 const emptyOpts = Object.freeze({ })
@@ -67405,6 +67512,9 @@ module.exports = parseOptions
 /***/ 95471:
 /***/ ((module, exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const {
   MAX_SAFE_COMPONENT_LENGTH,
   MAX_SAFE_BUILD_LENGTH,
@@ -67417,6 +67527,7 @@ exports = module.exports = {}
 const re = exports.re = []
 const safeRe = exports.safeRe = []
 const src = exports.src = []
+const safeSrc = exports.safeSrc = []
 const t = exports.t = {}
 let R = 0
 
@@ -67449,6 +67560,7 @@ const createToken = (name, value, isGlobal) => {
   debug(name, index, value)
   t[name] = index
   src[index] = value
+  safeSrc[index] = safe
   re[index] = new RegExp(value, isGlobal ? 'g' : undefined)
   safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)
 }
@@ -67481,12 +67593,14 @@ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
 
 // ## Pre-release Version Identifier
 // A numeric identifier, or a non-numeric identifier.
+// Non-numberic identifiers include numberic identifiers but can be longer.
+// Therefore non-numberic identifiers must go first.
 
-createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
-}|${src[t.NONNUMERICIDENTIFIER]})`)
+createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
+}|${src[t.NUMERICIDENTIFIER]})`)
 
-createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
-}|${src[t.NONNUMERICIDENTIFIER]})`)
+createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NONNUMERICIDENTIFIER]
+}|${src[t.NUMERICIDENTIFIERLOOSE]})`)
 
 // ## Pre-release Version
 // Hyphen, followed by one or more dot-separated pre-release version
@@ -67629,6 +67743,9 @@ createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$')
 /***/ 12276:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 // Determine if version is greater than all the versions possible in the range.
 const outside = __nccwpck_require__(10280)
 const gtr = (version, range, options) => outside(version, range, '>', options)
@@ -67640,6 +67757,9 @@ module.exports = gtr
 /***/ 23465:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const Range = __nccwpck_require__(96782)
 const intersects = (r1, r2, options) => {
   r1 = new Range(r1, options)
@@ -67654,6 +67774,9 @@ module.exports = intersects
 /***/ 15213:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const outside = __nccwpck_require__(10280)
 // Determine if version is less than all the versions possible in the range
 const ltr = (version, range, options) => outside(version, range, '<', options)
@@ -67665,6 +67788,9 @@ module.exports = ltr
 /***/ 73193:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const Range = __nccwpck_require__(96782)
 
@@ -67697,6 +67823,9 @@ module.exports = maxSatisfying
 /***/ 68595:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const Range = __nccwpck_require__(96782)
 const minSatisfying = (versions, range, options) => {
@@ -67728,6 +67857,9 @@ module.exports = minSatisfying
 /***/ 51866:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const Range = __nccwpck_require__(96782)
 const gt = __nccwpck_require__(16599)
@@ -67796,6 +67928,9 @@ module.exports = minVersion
 /***/ 10280:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const SemVer = __nccwpck_require__(7163)
 const Comparator = __nccwpck_require__(89379)
 const { ANY } = Comparator
@@ -67883,6 +68018,9 @@ module.exports = outside
 /***/ 82028:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 // given a set of versions and a range, create a "simplified" range
 // that includes the same versions that the original range does
 // If the original range is shorter than the simplified one, return that.
@@ -67937,6 +68075,9 @@ module.exports = (versions, range, options) => {
 /***/ 61489:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const Range = __nccwpck_require__(96782)
 const Comparator = __nccwpck_require__(89379)
 const { ANY } = Comparator
@@ -68191,6 +68332,9 @@ module.exports = subset
 /***/ 54750:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const Range = __nccwpck_require__(96782)
 
 // Mostly just for testing and legacy API reasons
@@ -68206,6 +68350,9 @@ module.exports = toComparators
 /***/ 64737:
 /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
 
+"use strict";
+
+
 const Range = __nccwpck_require__(96782)
 const validRange = (range, options) => {
   try {
@@ -99639,15 +99786,23 @@ function run() {
             if (registryUrl) {
                 auth.configAuthentication(registryUrl, alwaysAuth);
             }
-            const resolvedPackageManager = getNameFromPackageManagerField();
             const cacheDependencyPath = core.getInput('cache-dependency-path');
-            if (cache && (0, cache_utils_1.isCacheFeatureAvailable)()) {
-                core.saveState(constants_1.State.CachePackageManager, cache);
-                yield (0, cache_restore_1.restoreCache)(cache, cacheDependencyPath);
-            }
-            else if (resolvedPackageManager && packagemanagercache) {
-                core.saveState(constants_1.State.CachePackageManager, resolvedPackageManager);
-                yield (0, cache_restore_1.restoreCache)(resolvedPackageManager, cacheDependencyPath);
+            if ((0, cache_utils_1.isCacheFeatureAvailable)()) {
+                // if the cache input is provided, use it for caching.
+                if (cache) {
+                    core.saveState(constants_1.State.CachePackageManager, cache);
+                    yield (0, cache_restore_1.restoreCache)(cache, cacheDependencyPath);
+                    // package manager npm is detected from package.json, enable auto-caching for npm.
+                }
+                else if (packagemanagercache) {
+                    const resolvedPackageManager = getNameFromPackageManagerField();
+                    if (resolvedPackageManager) {
+                        core.info("Detected npm as the package manager from package.json's packageManager field. " +
+                            'Auto caching has been enabled for npm. If you want to disable it, set package-manager-cache input to false');
+                        core.saveState(constants_1.State.CachePackageManager, resolvedPackageManager);
+                        yield (0, cache_restore_1.restoreCache)(resolvedPackageManager, cacheDependencyPath);
+                    }
+                }
             }
             const matchersPath = path.join(__dirname, '../..', '.github');
             core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
@@ -99683,19 +99838,26 @@ function resolveVersionInput() {
     return version;
 }
 function getNameFromPackageManagerField() {
-    // Check packageManager field in package.json
-    const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'];
+    var _a;
+    const npmRegex = /^(\^)?npm(@.*)?$/; // matches "npm", "npm@...", "^npm@..."
     try {
         const packageJson = JSON.parse(fs_1.default.readFileSync(path.join(process.env.GITHUB_WORKSPACE, 'package.json'), 'utf-8'));
-        const pm = packageJson.packageManager;
-        if (typeof pm === 'string') {
-            const regex = new RegExp(`^(?:\\^)?(${SUPPORTED_PACKAGE_MANAGERS.join('|')})@`);
-            const match = pm.match(regex);
-            return match ? match[1] : undefined;
+        // Check devEngines.packageManager first (object or array)
+        const devPM = (_a = packageJson === null || packageJson === void 0 ? void 0 : packageJson.devEngines) === null || _a === void 0 ? void 0 : _a.packageManager;
+        const devPMArray = devPM ? (Array.isArray(devPM) ? devPM : [devPM]) : [];
+        for (const obj of devPMArray) {
+            if (typeof (obj === null || obj === void 0 ? void 0 : obj.name) === 'string' && npmRegex.test(obj.name)) {
+                return 'npm';
+            }
+        }
+        // Check top-level packageManager
+        const topLevelPM = packageJson === null || packageJson === void 0 ? void 0 : packageJson.packageManager;
+        if (typeof topLevelPM === 'string' && npmRegex.test(topLevelPM)) {
+            return 'npm';
         }
         return undefined;
     }
-    catch (err) {
+    catch (_b) {
         return undefined;
     }
 }
diff --git docs/advanced-usage.md docs/advanced-usage.md
index 97f977f71..f2a8a2f32 100644
--- docs/advanced-usage.md
+++ docs/advanced-usage.md
@@ -46,9 +46,9 @@ If `check-latest` is set to `true`, the action first checks if the cached versio
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '16'
+    node-version: '24'
     check-latest: true
 - run: npm ci
 - run: npm test
@@ -64,7 +64,7 @@ See [supported version syntax](https://github.com/actions/setup-node#supported-v
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
     node-version-file: '.nvmrc'
 - run: npm ci
@@ -98,9 +98,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '14'
+          node-version: '24'
           architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
       - run: npm ci
       - run: npm test
@@ -119,9 +119,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '20.0.0-v8-canary' # it will install the latest v8 canary release for node 20.0.0
+          node-version: '24.0.0-v8-canary' # it will install the latest v8 canary release for node 24.0.0
       - run: npm ci
       - run: npm test
 ```
@@ -134,9 +134,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '20-v8-canary' # it will install the latest v8 canary release for node 20
+          node-version: '24-v8-canary' # it will install the latest v8 canary release for node 24
       - run: npm ci
       - run: npm test
 ```
@@ -150,9 +150,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: 'v20.1.1-v8-canary20221103f7e2421e91'
+          node-version: 'v24.0.0-v8-canary2025030537242e55ac'
       - run: npm ci
       - run: npm test
 ```
@@ -170,9 +170,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '16-nightly' # it will install the latest nightly release for node 16
+          node-version: '24-nightly' # it will install the latest nightly release for node 24
       - run: npm ci
       - run: npm test
 ```
@@ -186,9 +186,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '16.0.0-nightly' # it will install the latest nightly release for node 16.0.0
+          node-version: '24.0.0-nightly' # it will install the latest nightly release for node 24.0.0
       - run: npm ci
       - run: npm test
 ```
@@ -202,9 +202,9 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '16.0.0-nightly20210420a0261d231c'
+          node-version: '24.0.0-nightly202505066102159fa1'
       - run: npm ci
       - run: npm test
 ```
@@ -220,26 +220,27 @@ jobs:
     name: Node sample
     steps:
       - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/setup-node@v6
         with:
-          node-version: '16.0.0-rc.1'
+          node-version: '24.0.0-rc.4'
       - run: npm ci
       - run: npm test
 ```
 
-**Note:** Unlike nightly versions, which support version range specifiers, you must specify the exact version for a release candidate: `16.0.0-rc.1`.
+**Note:** Unlike nightly versions, which support version range specifiers, you must specify the exact version for a release candidate: `24.0.0-rc.4`.
 
 ## Caching packages data
 The action follows [actions/cache](https://github.com/actions/cache/blob/main/examples.md#node---npm) guidelines, and caches global cache on the machine instead of `node_modules`, so cache can be reused between different Node.js versions.
 
 **Caching yarn dependencies:**
-Yarn caching handles both yarn versions: 1 or 2.
+Yarn caching handles both Yarn Classic (v1) and Yarn Berry (v2, v3, v4+).
+
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14'
+    node-version: '24'
     cache: 'yarn'
 - run: yarn install --frozen-lockfile # optional, --immutable
 - run: yarn test
@@ -256,12 +257,12 @@ steps:
 
 steps:
 - uses: actions/checkout@v5
-- uses: pnpm/action-setup@v2
+- uses: pnpm/action-setup@v4
   with:
-    version: 6.32.9
-- uses: actions/setup-node@v5
+    version: 10
+- uses: actions/setup-node@v6
   with:
-    node-version: '14'
+    node-version: '24'
     cache: 'pnpm'
 - run: pnpm install
 - run: pnpm test
@@ -275,9 +276,9 @@ steps:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14'
+    node-version: '24'
     cache: 'npm'
     cache-dependency-path: '**/package-lock.json'
 - run: npm ci
@@ -288,9 +289,9 @@ steps:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14'
+    node-version: '24'
     cache: 'npm'
     cache-dependency-path: |
       server/app/package-lock.json
@@ -312,21 +313,21 @@ jobs:
           - macos-latest
           - windows-latest
         node_version:
-          - 12
-          - 14
-          - 16
+          - 20
+          - 22
+          - 24
         architecture:
           - x64
         # an extra windows-x86 run:
         include:
-          - os: windows-2016
-            node_version: 12
+          - os: windows-latest
+            node_version: 24
             architecture: x86
     name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
     steps:
       - uses: actions/checkout@v5
       - name: Setup node
-        uses: actions/setup-node@v5
+        uses: actions/setup-node@v6
         with:
           node-version: ${{ matrix.node_version }}
           architecture: ${{ matrix.architecture }}
@@ -338,15 +339,15 @@ jobs:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14.x'
+    node-version: '24.x'
     registry-url: 'https://registry.npmjs.org'
 - run: npm ci
 - run: npm publish
   env:
     NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
     registry-url: 'https://npm.pkg.github.com'
 - run: npm publish
@@ -358,15 +359,15 @@ steps:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14.x'
+    node-version: '24.x'
     registry-url: <registry url>
 - run: yarn install --frozen-lockfile
 - run: yarn publish
   env:
     NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
     registry-url: 'https://npm.pkg.github.com'
 - run: yarn publish
@@ -378,9 +379,9 @@ steps:
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14.x'
+    node-version: '24.x'
     registry-url: 'https://registry.npmjs.org'
 # Skip post-install scripts here, as a malicious
 # script could steal NODE_AUTH_TOKEN.
@@ -398,9 +399,9 @@ Below you can find a sample "Setup .yarnrc.yml" step, that is going to allow you
 ```yaml
 steps:
 - uses: actions/checkout@v5
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14.x'
+    node-version: '24.x'
 - name: Setup .yarnrc.yml
   run: |
     yarn config set npmScopes.my-org.npmRegistryServer "https://npm.pkg.github.com"
@@ -427,9 +428,9 @@ It is possible to specify a token to authenticate with the mirror using the `mir
 The token will be passed as a bearer token in the `Authorization` header.
 
 ```yaml
-- uses: actions/setup-node@v5
+- uses: actions/setup-node@v6
   with:
-    node-version: '14.x'
+    node-version: '24.x'
     mirror: 'https://nodejs.org/dist'
     mirror-token: 'your-mirror-token'
 ```
diff --git package.json package.json
index 3399437ab..ef1cfc193 100644
--- package.json
+++ package.json
@@ -1,6 +1,6 @@
 {
   "name": "setup-node",
-  "version": "5.0.0",
+  "version": "6.0.0",
   "private": true,
   "description": "setup node action",
   "main": "lib/setup-node.js",
@@ -53,8 +53,8 @@
     "jest": "^29.7.0",
     "jest-circus": "^29.7.0",
     "jest-each": "^29.7.0",
-    "prettier": "^2.8.4",
-    "ts-jest": "^29.1.2",
+    "prettier": "^3.6.2",
+    "ts-jest": "^29.4.1",
     "typescript": "^5.4.2"
   }
 }
diff --git src/cache-utils.ts src/cache-utils.ts
index 89841bc10..6f45749a7 100644
--- src/cache-utils.ts
+++ src/cache-utils.ts
@@ -167,14 +167,12 @@ const getCacheDirectoriesFromCacheDependencyPath = async (
   packageManagerInfo: PackageManagerInfo,
   cacheDependencyPath: string
 ): Promise<string[]> => {
-  const projectDirectories = await getProjectDirectoriesFromCacheDependencyPath(
-    cacheDependencyPath
-  );
+  const projectDirectories =
+    await getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath);
   const cacheFoldersPaths = await Promise.all(
     projectDirectories.map(async projectDirectory => {
-      const cacheFolderPath = await packageManagerInfo.getCacheFolderPath(
-        projectDirectory
-      );
+      const cacheFolderPath =
+        await packageManagerInfo.getCacheFolderPath(projectDirectory);
       core.debug(
         `${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`
       );
diff --git src/distributions/official_builds/official_builds.ts src/distributions/official_builds/official_builds.ts
index 62999c334..14ad5612d 100644
--- src/distributions/official_builds/official_builds.ts
+++ src/distributions/official_builds/official_builds.ts
@@ -221,8 +221,8 @@ export default class OfficialBuilds extends BaseDistribution {
       alias === '*'
         ? numbered[numbered.length - 1]
         : n < 0
-        ? numbered[numbered.length - 1 + n]
-        : aliases[alias];
+          ? numbered[numbered.length - 1 + n]
+          : aliases[alias];
 
     if (!release) {
       throw new Error(
diff --git src/main.ts src/main.ts
index f169cef0e..078711506 100644
--- src/main.ts
+++ src/main.ts
@@ -67,14 +67,25 @@ export async function run() {
       auth.configAuthentication(registryUrl, alwaysAuth);
     }
 
-    const resolvedPackageManager = getNameFromPackageManagerField();
     const cacheDependencyPath = core.getInput('cache-dependency-path');
-    if (cache && isCacheFeatureAvailable()) {
-      core.saveState(State.CachePackageManager, cache);
-      await restoreCache(cache, cacheDependencyPath);
-    } else if (resolvedPackageManager && packagemanagercache) {
-      core.saveState(State.CachePackageManager, resolvedPackageManager);
-      await restoreCache(resolvedPackageManager, cacheDependencyPath);
+
+    if (isCacheFeatureAvailable()) {
+      // if the cache input is provided, use it for caching.
+      if (cache) {
+        core.saveState(State.CachePackageManager, cache);
+        await restoreCache(cache, cacheDependencyPath);
+        // package manager npm is detected from package.json, enable auto-caching for npm.
+      } else if (packagemanagercache) {
+        const resolvedPackageManager = getNameFromPackageManagerField();
+        if (resolvedPackageManager) {
+          core.info(
+            "Detected npm as the package manager from package.json's packageManager field. " +
+              'Auto caching has been enabled for npm. If you want to disable it, set package-manager-cache input to false'
+          );
+          core.saveState(State.CachePackageManager, resolvedPackageManager);
+          await restoreCache(resolvedPackageManager, cacheDependencyPath);
+        }
+      }
     }
 
     const matchersPath = path.join(__dirname, '../..', '.github');
@@ -127,8 +138,7 @@ function resolveVe,rsionInput(): string {
 }
 
 export function getNameFromPackageManagerField(): string | undefined {
-  // Check packageManager field in package.json
-  const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'yarn', 'pnpm'];
+  const npmRegex = /^(\^)?npm(@.*)?$/; // matches "npm", "npm@...", "^npm@..."
   try {
     const packageJson = JSON.parse(
       fs.readFileSync(
@@ -136,16 +146,24 @@ export function getNameFromPackageManagerField(): string | undefined {
         'utf-8'
       )
     );
-    const pm = packageJson.packageManager;
-    if (typeof pm === 'string') {
-      const regex = new RegExp(
-        `^(?:\\^)?(${SUPPORTED_PACKAGE_MANAGERS.join('|')})@`
-      );
-      const match = pm.match(regex);
-      return match ? match[1] : undefined;
+
+    // Check devEngines.packageManager first (object or array)
+    const devPM = packageJson?.devEngines?.packageManager;
+    const devPMArray = devPM ? (Array.isArray(devPM) ? devPM : [devPM]) : [];
+    for (const obj of devPMArray) {
+      if (typeof obj?.name === 'string' && npmRegex.test(obj.name)) {
+        return 'npm';
+      }
     }
+
+    // Check top-level packageManager
+    const topLevelPM = packageJson?.packageManager;
+    if (typeof topLevelPM === 'string' && npmRegex.test(topLevelPM)) {
+      return 'npm';
+    }
+
     return undefined;
-  } catch (err) {
+  } catch {
     return undefined;
   }
 }

Description

This PR upgrades the setup-node action from v5 to v6 with several major changes:

  1. Node.js version updates: Drops support for Node.js 18, updates test matrices to use Node 20, 22, and 24 as supported versions
  2. Action runtime upgrade: Upgrades the action itself from Node 20 to Node 24
  3. Auto-caching refinement: Changes automatic caching behavior to only enable by default for npm when detected via packageManager or devEngines.packageManager fields in package.json (instead of auto-enabling for all package managers)
  4. Test infrastructure updates:
    • Replaces macos-13 runners with macos-latest-large
    • Updates proxy tests to use Node 24 instead of older versions
    • Adds new test for devEngines.packageManager auto-caching
  5. Dependency updates: Updates semver from 7.6.3 to 7.7.2 and prettier/ts-jest
  6. Documentation updates: Updates all examples to reflect new version numbers and breaking changes

Possible Issues

  1. Breaking change impact: The change in auto-caching behavior (now only for npm by default) could break workflows that relied on automatic caching for yarn/pnpm. The README mentions this is a breaking change but users may miss it.

  2. Runner compatibility: The action now requires runner version v2.327.1+ due to the Node 24 upgrade. This could break older self-hosted runners that haven't been updated.

  3. Incomplete migration: Some test files still use nested ternary operators that could impact readability and maintainability (formatting changes in test files).

  4. Version detection logic complexity: The new package manager detection logic in getNameFromPackageManagerField() checks multiple locations (devEngines.packageManager as object/array, then top-level packageManager) which adds complexity and potential edge cases.

Security Hotspots

  1. Package manager detection regex (lines in src/main.ts and dist/setup/index.js):

    const npmRegex = /^(\^)?npm(@.*)?$/;

    This regex is used to validate package manager names from user-controlled package.json files. While the regex itself is relatively safe, it's processing untrusted input. The regex should be carefully reviewed to ensure it can't be exploited with ReDoS attacks (though this particular pattern appears safe).

  2. File system operations without validation (in src/main.ts):

    fs.readFileSync(path.join(process.env.GITHUB_WORKSPACE, 'package.json'), 'utf-8')

    Reading package.json directly from GITHUB_WORKSPACE without additional validation. While this is the expected workflow behavior, malicious repositories could provide crafted package.json files. The code does have try-catch blocks which mitigates this, but be aware this processes untrusted input.

Privacy Hotspots

  1. Cache state persistence (in src/main.ts):
    The action saves cache-related state using core.saveState() which persists between job steps. While this is intentional functionality, it's worth noting that package manager information is being stored in workflow state.
Changes

Changes

.github/workflows/

  • check-dist.yml: Updates Node version from 20.x to 24.x
  • e2e-cache.yml:
    • Replaces macos-13 with macos-latest-large across all test matrices
    • Removes Node 18 from test matrices, now tests Node 20, 22, 24
    • Renames node-npm-package-manager-cache job to node-npm-packageManager-auto-cache
    • Adds new node-npm-devEngines-auto-cache job to test devEngines.packageManager field
  • proxy.yml: Updates test versions from Node 14/11 to Node 24
  • release-new-action-version.yml: Bumps actions/publish-action from v0.3.0 to v0.4.0
  • versions.yml:
    • Updates all test matrices from Node 18/20/22/24 to 20/22/24
    • Replaces macos-13 with macos-latest-large
    • Updates nightly/RC version tests to use current versions

README.md and docs/advanced-usage.md

  • Adds breaking changes section documenting v5 and v6 changes
  • Updates all code examples to use actions/setup-node@v6 instead of v5
  • Updates example Node versions from 18/20 to 20/22/24
  • Updates documentation for package-manager-cache input to reflect npm-only auto-caching
  • Updates various example versions (pnpm, yarn) to current versions

src/main.ts

  • Major refactor of caching logic: Moves package manager detection inside conditional, only auto-enables for npm
  • New function behavior: getNameFromPackageManagerField() now:
    • Uses regex to specifically match npm (/^(\^)?npm(@.*)?$/)
    • Checks devEngines.packageManager field (supports both object and array)
    • Falls back to top-level packageManager field
    • Only returns 'npm' or undefined (no longer returns other package managers)

action.yml

  • Updates package-manager-cache description to clarify it only applies to npm auto-detection

package.json

  • Bumps version from 5.0.0 to 6.0.0
  • Updates prettier from ^2.8.4 to ^3.6.2
  • Updates ts-jest from ^29.1.2 to ^29.4.1

dist/setup/index.js (compiled output)

  • Reflects all changes from src/main.ts
  • Updates semver library (7.6.3 → 7.7.2) with various improvements including new "release" increment type

__tests__/

  • Reformats test files with updated prettier (nested ternaries)
  • Adds comprehensive tests for new npm-specific auto-caching behavior
  • Tests cover packageManager, devEngines.packageManager (object and array), and non-npm scenarios
sequenceDiagram
    participant User
    participant Action as setup-node Action
    participant FS as File System
    participant Cache as Cache API
    participant Installer as Node Installer
    
    User->>Action: Run setup-node@v6
    Action->>Action: Read inputs (version, cache, package-manager-cache)
    
    alt Node version specified
        Action->>Installer: Install Node version
        Installer-->>Action: Node installed
    end
    
    alt Cache input provided
        Action->>Cache: Save cache state (explicit)
        Action->>Cache: Restore cache
    else No cache input AND package-manager-cache=true
        Action->>FS: Read package.json
        FS-->>Action: package.json content
        Action->>Action: Check devEngines.packageManager
        alt devEngines has npm
            Action->>Cache: Auto-enable caching for npm
            Action->>Cache: Restore cache
        else Check top-level packageManager
            alt packageManager is npm
                Action->>Cache: Auto-enable caching for npm
                Action->>Cache: Restore cache
            else Not npm or missing
                Action->>Action: Skip auto-caching
            end
        end
    else package-manager-cache=false
        Action->>Action: Skip all auto-caching
    end
    
    Action->>Action: Configure authentication (if needed)
    Action-->>User: Setup complete
Loading

@renovate renovate bot force-pushed the renovate/actions-setup-node-6-x branch from ed27192 to 1d76065 Compare November 14, 2025 19:28
@mihaiplesa mihaiplesa merged commit f1c49cc into main Nov 15, 2025
10 of 12 checks passed
@mihaiplesa mihaiplesa deleted the renovate/actions-setup-node-6-x branch November 15, 2025 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

PR: Dependencies 🔩 Changes only update dependencies puLL-Merge renovate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants