mirror of
https://github.com/actions/setup-node.git
synced 2026-11-09 22:53:41 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
101b347414 | ||
|
|
949feb2413 | ||
|
|
7207c4f118 | ||
|
|
54337e0d02 | ||
|
|
fb54fa8c14 | ||
|
|
7f84f1d52d | ||
|
|
0c54c7b074 | ||
|
|
a829419004 | ||
|
|
680d1e489b | ||
|
|
db0fb3a98e | ||
|
|
94196ee1d1 | ||
|
|
d1fcc24e82 | ||
|
|
ae0d4ed088 | ||
|
|
3d7870f621 | ||
|
|
1acbd4c02f | ||
|
|
12b7ff3796 | ||
|
|
e51e5fe84f | ||
|
|
32f57ac043 | ||
|
|
b789df37d6 | ||
|
|
1b7c366466 | ||
|
|
05e62e6009 | ||
|
|
391b8c7809 | ||
|
|
363be39a57 |
No files matched your search
@@ -18,10 +18,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Clean global cache
|
||||
run: npm cache clean --force
|
||||
- name: Setup Node
|
||||
@@ -41,14 +41,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
||||
with:
|
||||
version: 6.10.0
|
||||
version: 10
|
||||
- name: Generate pnpm file
|
||||
run: pnpm install
|
||||
- name: Remove dependencies
|
||||
@@ -74,10 +74,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Yarn version
|
||||
run: yarn --version
|
||||
- name: Generate yarn file
|
||||
@@ -106,10 +106,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Update yarn
|
||||
run: yarn set version 3.6.4
|
||||
- name: Yarn version
|
||||
@@ -139,11 +139,11 @@ jobs:
|
||||
name: Test yarn subprojects
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22, 24]
|
||||
node-version: [22, 24, 26]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: prepare sub-projects
|
||||
run: __tests__/prepare-yarn-subprojects.sh yarn1
|
||||
@@ -166,11 +166,11 @@ jobs:
|
||||
name: Test yarn subprojects all locally managed
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22, 24]
|
||||
node-version: [22, 24, 26]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: prepare sub-projects
|
||||
run: __tests__/prepare-yarn-subprojects.sh keepcache keepcache
|
||||
@@ -193,11 +193,11 @@ jobs:
|
||||
name: Test yarn subprojects some locally managed
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22, 24]
|
||||
node-version: [22, 24, 26]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: prepare sub-projects
|
||||
run: __tests__/prepare-yarn-subprojects.sh global
|
||||
@@ -220,11 +220,11 @@ jobs:
|
||||
name: Test yarn subprojects managed by git
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20, 22, 24]
|
||||
node-version: [22, 24, 26]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: prepare sub-projects
|
||||
run: /bin/bash __tests__/prepare-yarn-subprojects.sh keepcache
|
||||
@@ -250,10 +250,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Create package.json with packageManager field
|
||||
run: |
|
||||
echo '{ "name": "test-project", "version": "1.0.0", "packageManager": "npm@8.0.0" }' > package.json
|
||||
@@ -275,10 +275,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Create package.json with devEngines field
|
||||
run: |
|
||||
echo '{
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
env:
|
||||
https_proxy: http://squid-proxy:3128
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Clear tool cache
|
||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||
- name: Setup node 24
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
https_proxy: http://no-such-proxy:3128
|
||||
no_proxy: api.github.com,github.com,nodejs.org,registry.npmjs.org,*.s3.amazonaws.com,s3.amazonaws.com
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Clear tool cache
|
||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||
- name: Setup node 24
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checking out
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Publish
|
||||
id: publish
|
||||
uses: actions/publish-immutable-action@v0.0.4
|
||||
uses: actions/publish-immutable-action@4bc8754ffc40f27910afb20287dbbbb675a4e978 # v0.0.4
|
||||
@@ -17,10 +17,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -34,10 +34,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest-large]
|
||||
node-version: [lts/dubnium, lts/erbium, lts/fermium, lts/*, lts/-1]
|
||||
os: [ubuntu-latest, windows-latest, macos-15-intel]
|
||||
node-version: [lts/*, lts/-1]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -56,15 +56,15 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version:
|
||||
[
|
||||
'20-v8-canary',
|
||||
'20.0.0-v8-canary',
|
||||
'20.0.0-v8-canary20221101e50e45c9f8'
|
||||
'26-v8-canary',
|
||||
'26.0.0-v8-canary',
|
||||
'26.0.0-v8-canary20251016054584e5cf'
|
||||
]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -81,10 +81,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20-nightly, 25-nightly, 24.0.0-nightly]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [26-nightly, 25-nightly, 24.0.0-nightly]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -101,10 +101,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
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]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [26.0.0-rc.1, 22.14.0-rc.1, 24.0.0-rc.4]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -121,10 +121,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20.10.0, 22.0.0, 24.9.0]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [26.5.0, 22.0.0, 24.9.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
@@ -138,10 +138,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
node-version: [20, 22, 24]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [22, 24, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node and check latest
|
||||
uses: ./
|
||||
with:
|
||||
@@ -156,11 +156,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version-file:
|
||||
[.nvmrc, .tool-versions, .tool-versions-node, package.json]
|
||||
[.nvmrc, .tool-versions, .tool-versions-node, package.json, mise.toml]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup node from node version file
|
||||
uses: ./
|
||||
with:
|
||||
@@ -175,22 +175,22 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup node from node version file
|
||||
uses: ./
|
||||
with:
|
||||
node-version-file: '__tests__/data/package-dev-engines.json'
|
||||
- name: Verify node
|
||||
run: __tests__/verify-node.sh 20
|
||||
run: __tests__/verify-node.sh 26
|
||||
|
||||
version-file-volta:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup node from node version file
|
||||
uses: ./
|
||||
with:
|
||||
@@ -203,9 +203,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup node from node version file
|
||||
uses: ./
|
||||
with:
|
||||
@@ -218,10 +218,10 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [21, 23]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node from dist
|
||||
uses: ./
|
||||
with:
|
||||
@@ -235,9 +235,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-15-intel]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# test old versions which didn't have npm and layout different
|
||||
- name: Setup node 0.12.18 from dist
|
||||
uses: ./
|
||||
@@ -250,11 +250,11 @@ jobs:
|
||||
arch:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Setup node 20 x86 from dist
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup node 22 x86 from dist
|
||||
uses: ./
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '22'
|
||||
architecture: 'x86'
|
||||
- name: Verify node
|
||||
run: __tests__/verify-arch.sh "ia32"
|
||||
@@ -265,7 +265,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||
node-version: [current, latest, node]
|
||||
steps:
|
||||
- name: Get node version
|
||||
@@ -274,7 +274,7 @@ jobs:
|
||||
echo "LATEST_NODE_VERSION=$latestNodeVersion" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
shell: bash
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Setup Node
|
||||
uses: ./
|
||||
with:
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ allowed:
|
||||
- mit
|
||||
- cc0-1.0
|
||||
- unlicense
|
||||
- blueoak-1.0.0
|
||||
- BlueOak-1.0.0
|
||||
|
||||
reviewed:
|
||||
npm:
|
||||
|
||||
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
File renamed without changes.
BIN
Binary file not shown.
File renamed without changes.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
@@ -12,9 +12,13 @@ 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 V7
|
||||
## What's new in V7
|
||||
|
||||
- **Migrated to ESM** to enable support for the latest `@actions/*` package versions.
|
||||
- Migrated action internals to ESM for compatibility with latest `@actions/*` packages. No changes to action inputs, outputs, or behavior.
|
||||
|
||||
### Breaking change
|
||||
|
||||
- The dummy `NODE_AUTH_TOKEN` fallback has been removed, as it could unintentionally affect the generated `.npmrc` with a non-functional token. With this change, if `registry-url` is set without `NODE_AUTH_TOKEN`, legacy Yarn Classic (1.x) and older Node/npm versions may fail, and pnpm may warn. npm Trusted Publishing (OIDC) is not affected, since it does not use `NODE_AUTH_TOKEN`.
|
||||
|
||||
## Breaking changes in V6
|
||||
|
||||
@@ -30,7 +34,7 @@ This action provides the following functionality for GitHub Actions users:
|
||||
- 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)
|
||||
For more details, see the full release notes on the [releases page](https://github.com/actions/setup-node/releases)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -38,14 +42,14 @@ See [action.yml](action.yml)
|
||||
|
||||
<!-- start usage -->
|
||||
```yaml
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
# Version Spec of the version to use in SemVer notation.
|
||||
# It also admits such aliases as lts/*, latest, nightly and canary builds
|
||||
# Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node
|
||||
node-version: ''
|
||||
|
||||
# File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions.
|
||||
# File containing the version Spec of the version to use. Examples: package.json, mise.toml, .nvmrc, .node-version, .tool-versions.
|
||||
# If node-version and node-version-file are both provided the action will use version from node-version.
|
||||
node-version-file: ''
|
||||
|
||||
@@ -119,8 +123,8 @@ See [action.yml](action.yml)
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -169,8 +173,8 @@ See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` in
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
@@ -182,8 +186,8 @@ steps:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
@@ -198,8 +202,8 @@ This behavior is controlled by the `package-manager-cache` input, which defaults
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
package-manager-cache: false
|
||||
- run: npm ci
|
||||
@@ -217,9 +221,9 @@ jobs:
|
||||
node: [ 20, 22, 24 ]
|
||||
name: Node ${{ matrix.node }} sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -229,12 +233,16 @@ jobs:
|
||||
|
||||
## Using `setup-node` on GHES
|
||||
|
||||
`setup-node` comes pre-installed on the appliance with GHES if Actions is enabled. When dynamically downloading Nodejs distributions, `setup-node` downloads distributions from [`actions/node-versions`](https://github.com/actions/node-versions) on github.com (outside of the appliance). These calls to `actions/node-versions` are made via unauthenticated requests, which are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). If more requests are made within the time frame, then you will start to see rate-limit errors during downloading that looks like: `##[error]API rate limit exceeded for...`. After that error the action will try to download versions directly from the official site, but it also can have rate limit so it's better to put token.
|
||||
`setup-node` comes pre-installed on the appliance with GHES if Actions is enabled. It downloads Node.js distributions from [`actions/node-versions`](https://github.com/actions/node-versions) on github.com, outside the appliance.
|
||||
|
||||
Manifest requests use the GitHub API first, with the `token` input for authentication when supplied. If API retrieval fails, the action tries the manifest on `raw.githubusercontent.com`. This fallback also applies when `check-latest` is enabled. If both manifest sources are unavailable, numeric versions can fall back to nodejs.org. LTS aliases require a manifest to resolve the requested version.
|
||||
|
||||
Unauthenticated API requests are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). Supplying a token increases the API rate limit.
|
||||
|
||||
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@v6
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
||||
node-version: 24
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[tools]
|
||||
node = "24.0.0"
|
||||
@@ -5,7 +5,7 @@
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"version": "^20"
|
||||
"version": "^26"
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-8
@@ -1,16 +1,15 @@
|
||||
import {
|
||||
jest,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
afterAll,
|
||||
afterEach,
|
||||
afterAll
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
jest
|
||||
} from '@jest/globals';
|
||||
import {fileURLToPath} from 'url';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import osm from 'os';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -178,6 +177,10 @@ describe('main tests', () => {
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
each`
|
||||
contents | expected
|
||||
${'12'} | ${'12'}
|
||||
@@ -197,6 +200,11 @@ describe('main tests', () => {
|
||||
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
||||
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
||||
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
||||
${'[tools]\ngo="latest"\nnode = "24.10"'} | ${'24.10'}
|
||||
${'[tools]\nnode = "22.12"'} | ${'22.12'}
|
||||
${'[tools]\ngo="latest"\nnode = "24.10"'} | ${'24.10'}
|
||||
${'[tools]\nnode = { version = "22.20" }'} | ${'22.20'}
|
||||
${'[tools]\nnode = { postinstall = "corepack enable" }'} | ${null}
|
||||
`.it('parses "$contents"', ({contents, expected}: any) => {
|
||||
const existsSpy = jest.spyOn(fs, 'existsSync');
|
||||
existsSpy.mockImplementation(() => true);
|
||||
@@ -311,11 +319,39 @@ describe('main tests', () => {
|
||||
|
||||
// Assert
|
||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||
// A relative input is still resolved against the workspace.
|
||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalledWith(
|
||||
path.join(process.env['GITHUB_WORKSPACE']!, '.nvmrc')
|
||||
);
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
`Resolved ${inputs['node-version-file']} as ${expectedVersionSpec}`
|
||||
);
|
||||
}, 10000);
|
||||
|
||||
it('reads node-version-file given as an absolute path outside the workspace', async () => {
|
||||
// Arrange: a composite action passes `${{ github.action_path }}/.nvmrc`,
|
||||
// which is absolute and may sit outside GITHUB_WORKSPACE.
|
||||
// The workspace deliberately points at a different existing directory,
|
||||
// so the absolute version file lies outside it.
|
||||
const workspace = path.join(__dirname, 'mock');
|
||||
process.env['GITHUB_WORKSPACE'] = workspace;
|
||||
const versionFilePath = path.join(__dirname, 'data', '.nvmrc');
|
||||
// Guard the premise: the version file lives outside the workspace.
|
||||
expect(path.relative(workspace, versionFilePath).startsWith('..')).toBe(
|
||||
true
|
||||
);
|
||||
inputs['node-version-file'] = versionFilePath;
|
||||
|
||||
// Act
|
||||
await main.run();
|
||||
|
||||
// Assert: the path is used as provided and the real file is read.
|
||||
// The expected `24` comes from `__tests__/data/.nvmrc` (`v24`).
|
||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalledWith(versionFilePath);
|
||||
expect(infoSpy).toHaveBeenCalledWith(`Resolved ${versionFilePath} as 24`);
|
||||
expect(core.setFailed as jest.Mock).not.toHaveBeenCalled();
|
||||
}, 10000);
|
||||
|
||||
it('should throw an error if node-version-file is not accessible', async () => {
|
||||
// Arrange
|
||||
inputs['node-version-file'] = 'non-existing-file';
|
||||
|
||||
@@ -14,6 +14,8 @@ import osm from 'os';
|
||||
import path from 'path';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const nodeVersionsManifestUrl =
|
||||
'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json';
|
||||
|
||||
// Mock @actions modules before importing anything that depends on them
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
@@ -129,6 +131,21 @@ const {default: nodeV8CanaryTestDist} = await import(
|
||||
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||
import type {IToolRelease} from '@actions/tool-cache';
|
||||
|
||||
function useRetryTimers() {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
}
|
||||
|
||||
async function runWithRetries() {
|
||||
const setup = main.run();
|
||||
await jest.runAllTimersAsync();
|
||||
await setup;
|
||||
}
|
||||
|
||||
describe('setup-node', () => {
|
||||
let build: InstanceType<typeof OfficialBuilds>;
|
||||
let inputs = {} as any;
|
||||
@@ -233,7 +250,9 @@ describe('setup-node', () => {
|
||||
|
||||
getJsonSpy.mockImplementation((url: any) => {
|
||||
let res: any;
|
||||
if (url.includes('/rc')) {
|
||||
if (url === nodeVersionsManifestUrl) {
|
||||
throw new Error('Unable to download raw manifest');
|
||||
} else if (url.includes('/rc')) {
|
||||
res = <INodeVersion[]>nodeTestDistRc;
|
||||
} else if (url.includes('/nightly')) {
|
||||
res = <INodeVersion[]>nodeTestDistNightly;
|
||||
@@ -258,7 +277,11 @@ describe('setup-node', () => {
|
||||
|
||||
// @actions/exec
|
||||
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||
getExecOutputSpy.mockImplementation(async () => ({
|
||||
stdout: 'v16.15.0',
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -379,6 +402,16 @@ describe('setup-node', () => {
|
||||
whichSpy.mockImplementation((cmd: any) => {
|
||||
return `some/${cmd}/path`;
|
||||
});
|
||||
getExecOutputSpy.mockImplementation(async (cmd: string) => ({
|
||||
stdout:
|
||||
cmd === 'node'
|
||||
? `v${resolvedVersion}`
|
||||
: cmd === 'npm'
|
||||
? '11.12.1'
|
||||
: '4.17.1',
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
|
||||
await main.run();
|
||||
|
||||
@@ -635,7 +668,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Attempt to resolve the latest version from manifest...'
|
||||
);
|
||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
@@ -663,7 +696,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Attempt to resolve the latest version from manifest...'
|
||||
);
|
||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
@@ -702,7 +735,7 @@ describe('setup-node', () => {
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Attempt to resolve the latest version from manifest...'
|
||||
);
|
||||
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
@@ -756,10 +789,12 @@ describe('setup-node', () => {
|
||||
`Attempting to download ${versionSpec}...`
|
||||
);
|
||||
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||
});
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
describe('LTS version', () => {
|
||||
useRetryTimers();
|
||||
|
||||
beforeEach(() => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
@@ -915,7 +950,7 @@ describe('setup-node', () => {
|
||||
});
|
||||
|
||||
// act
|
||||
await main.run();
|
||||
await runWithRetries();
|
||||
|
||||
// assert
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
@@ -924,11 +959,15 @@ describe('setup-node', () => {
|
||||
expect(dbgSpy).toHaveBeenCalledWith(
|
||||
'Getting manifest from actions/node-versions@main'
|
||||
);
|
||||
expect(setFailedSpy).toHaveBeenCalledWith('Unable to download manifest');
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
'Failed to fetch a valid manifest from the GitHub API and raw URL. API: Unable to download manifest. Raw: Unable to download raw manifest'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('latest alias syntax', () => {
|
||||
useRetryTimers();
|
||||
|
||||
it.each(['latest', 'current', 'node'])(
|
||||
'download the %s version if alias is provided',
|
||||
async inputVersion => {
|
||||
@@ -944,10 +983,12 @@ describe('setup-node', () => {
|
||||
});
|
||||
|
||||
// Act
|
||||
await main.run();
|
||||
await runWithRetries();
|
||||
|
||||
// assert
|
||||
expect(logSpy).toHaveBeenCalledWith('Unable to download manifest');
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Failed to fetch a valid manifest from the GitHub API and raw URL. API: Unable to download manifest. Raw: Unable to download raw manifest'
|
||||
);
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
|
||||
}
|
||||
@@ -1020,4 +1061,310 @@ describe('setup-node', () => {
|
||||
);
|
||||
}
|
||||
}, 100000);
|
||||
|
||||
describe('manifest retrieval', () => {
|
||||
useRetryTimers();
|
||||
let installedVersion: string;
|
||||
|
||||
beforeEach(() => {
|
||||
installedVersion = '12.16.2';
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
inputs['node-version'] = 'lts/erbium';
|
||||
inputs['token'] = 'manifest-token';
|
||||
whichSpy.mockImplementation(async (tool: string) => tool);
|
||||
findSpy.mockImplementation(() => '');
|
||||
getJsonSpy.mockImplementation((url: string) => ({
|
||||
result:
|
||||
url === nodeVersionsManifestUrl ? nodeTestManifest : nodeTestDist
|
||||
}));
|
||||
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||
cacheSpy.mockImplementation(
|
||||
async (
|
||||
_sourceDirectory: string,
|
||||
_tool: string,
|
||||
version: string,
|
||||
arch: string
|
||||
) => {
|
||||
installedVersion = version;
|
||||
return path.normalize(`/cache/node/${version}/${arch}`);
|
||||
}
|
||||
);
|
||||
getExecOutputSpy.mockImplementation(async () => ({
|
||||
stdout: `v${installedVersion}\n`,
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
});
|
||||
|
||||
describe.each([
|
||||
{versionSpec: 'lts/erbium', checkLatest: 'false'},
|
||||
{versionSpec: 'lts/erbium', checkLatest: 'true'},
|
||||
{versionSpec: '12', checkLatest: 'false'},
|
||||
{versionSpec: '12', checkLatest: 'true'}
|
||||
])('$versionSpec, check-latest: $checkLatest', mode => {
|
||||
beforeEach(() => {
|
||||
inputs['node-version'] = mode.versionSpec;
|
||||
inputs['check-latest'] = mode.checkLatest;
|
||||
});
|
||||
|
||||
it('uses the authenticated API without requesting raw', async () => {
|
||||
await runWithRetries();
|
||||
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getManifestSpy).toHaveBeenCalledWith(
|
||||
'actions',
|
||||
'node-versions',
|
||||
'token manifest-token',
|
||||
'main'
|
||||
);
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.16.2');
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([403, 429])(
|
||||
'falls back to raw immediately after API HTTP %i',
|
||||
async statusCode => {
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
throw Object.assign(new Error('API unavailable'), {statusCode});
|
||||
});
|
||||
const startedAt = Date.now();
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getJsonSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||
expect(getManifestSpy.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
getJsonSpy.mock.invocationCallOrder[0]
|
||||
);
|
||||
expect(Date.now()).toBe(startedAt);
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'node-version',
|
||||
'v12.16.2'
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('refreshes the manifest for a later action invocation', async () => {
|
||||
inputs['node-version'] = '12';
|
||||
await runWithRetries();
|
||||
getManifestSpy.mockClear();
|
||||
|
||||
const latestVersion = '12.17.0';
|
||||
getManifestSpy.mockImplementation(() => [
|
||||
{
|
||||
version: latestVersion,
|
||||
stable: true,
|
||||
release_url: `https://github.com/actions/node-versions/releases/tag/${latestVersion}`,
|
||||
files: [
|
||||
{
|
||||
filename: `node-${latestVersion}-linux-x64.tar.gz`,
|
||||
arch: 'x64',
|
||||
platform: 'linux',
|
||||
download_url: `https://github.com/actions/node-versions/releases/download/${latestVersion}/node-${latestVersion}-linux-x64.tar.gz`
|
||||
}
|
||||
]
|
||||
},
|
||||
...nodeTestManifest
|
||||
]);
|
||||
inputs['check-latest'] = 'true';
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(logSpy).toHaveBeenCalledWith(`Resolved as '${latestVersion}'`);
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.17.0');
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not fetch a manifest for an installed numeric version', async () => {
|
||||
inputs['node-version'] = '12';
|
||||
findSpy.mockReturnValue(path.normalize('/cache/node/12.16.2/x64'));
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getManifestSpy).not.toHaveBeenCalled();
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.16.2');
|
||||
});
|
||||
|
||||
it('checks the manifest before using an installed version with check-latest', async () => {
|
||||
inputs['node-version'] = '12';
|
||||
inputs['check-latest'] = 'true';
|
||||
findSpy.mockReturnValue(path.normalize('/cache/node/12.16.2/x64'));
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getManifestSpy.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
findSpy.mock.invocationCallOrder[0]
|
||||
);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('uses an installed version when check-latest finds no matching release', async () => {
|
||||
os.arch = 'arm64';
|
||||
inputs['node-version'] = '12';
|
||||
inputs['check-latest'] = 'true';
|
||||
findSpy.mockReturnValue(path.normalize('/cache/node/12.16.2/arm64'));
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(logSpy).toHaveBeenCalledWith(
|
||||
'Failed to resolve version 12 from manifest'
|
||||
);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.16.2');
|
||||
});
|
||||
|
||||
it('retries a transient API failure before requesting raw', async () => {
|
||||
getManifestSpy.mockImplementationOnce(() => {
|
||||
throw new Error('transient network failure');
|
||||
});
|
||||
const startedAt = Date.now();
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getJsonSpy).not.toHaveBeenCalled();
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(2);
|
||||
expect(Date.now() - startedAt).toBe(1000);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(['empty', 'unavailable'])(
|
||||
'uses raw after retrying an %s API manifest',
|
||||
async failure => {
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
if (failure === 'unavailable') {
|
||||
throw new Error('API unavailable');
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const startedAt = Date.now();
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(3);
|
||||
expect(getJsonSpy).toHaveBeenCalledTimes(1);
|
||||
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||
expect(Date.now() - startedAt).toBe(3000);
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.16.2');
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
['empty', []],
|
||||
['malformed', null]
|
||||
])(
|
||||
'fails LTS setup when the API fails and raw is %s',
|
||||
async (_, result) => {
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
throw Object.assign(new Error('API unavailable'), {statusCode: 429});
|
||||
});
|
||||
getJsonSpy.mockImplementationOnce(() => ({result}));
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||
expect(getManifestSpy).toHaveBeenCalledTimes(1);
|
||||
expect(dlSpy).not.toHaveBeenCalled();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API: API unavailable. Raw: The manifest')
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('retains both source errors when LTS setup cannot fetch a manifest', async () => {
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
throw Object.assign(new Error('API unavailable'), {statusCode: 403});
|
||||
});
|
||||
getJsonSpy.mockImplementationOnce(() => {
|
||||
throw new Error('Raw unavailable');
|
||||
});
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(dlSpy).not.toHaveBeenCalled();
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API: API unavailable. Raw: Raw unavailable')
|
||||
);
|
||||
});
|
||||
|
||||
it.each(['false', 'true'])(
|
||||
'uses Node dist when both sources fail with check-latest: %s',
|
||||
async checkLatest => {
|
||||
inputs['node-version'] = '12';
|
||||
inputs['check-latest'] = checkLatest;
|
||||
getManifestSpy.mockImplementation(() => {
|
||||
throw Object.assign(new Error('API unavailable'), {statusCode: 403});
|
||||
});
|
||||
getJsonSpy.mockImplementation((url: string) => {
|
||||
if (url === nodeVersionsManifestUrl) {
|
||||
throw new Error('Raw unavailable');
|
||||
}
|
||||
return {result: nodeTestDist};
|
||||
});
|
||||
|
||||
await runWithRetries();
|
||||
|
||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||
expect(getJsonSpy).toHaveBeenCalledWith(nodeVersionsManifestUrl);
|
||||
expect(dlSpy).toHaveBeenCalledWith(
|
||||
'https://nodejs.org/dist/v12.16.3/node-v12.16.3-linux-x64.tar.gz',
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
expect(core.setOutput).toHaveBeenCalledWith('node-version', 'v12.16.3');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('node version verification', () => {
|
||||
beforeEach(() => {
|
||||
os.platform = 'linux';
|
||||
os.arch = 'x64';
|
||||
inputs['node-version'] = '12';
|
||||
});
|
||||
|
||||
it('fails when the installed node version does not match the expected version', async () => {
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockReturnValue(toolPath);
|
||||
getExecOutputSpy.mockImplementation(async () => ({
|
||||
stdout: 'v22.22.3\n',
|
||||
stderr: '',
|
||||
exitCode: 0
|
||||
}));
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Node v12.16.1 installation failed, likely due to an incomplete or corrupted download.`
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when the node executable cannot be invoked', async () => {
|
||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||
findSpy.mockReturnValue(toolPath);
|
||||
getExecOutputSpy.mockImplementation(async () => {
|
||||
throw new Error('node not found');
|
||||
});
|
||||
|
||||
await main.run();
|
||||
|
||||
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||
`Node installation failed. Node may not be installed or not on PATH: node not found`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -5,7 +5,7 @@ inputs:
|
||||
node-version:
|
||||
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0.'
|
||||
node-version-file:
|
||||
description: 'File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions.'
|
||||
description: 'File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions, mise.toml.'
|
||||
architecture:
|
||||
description: 'Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.'
|
||||
check-latest:
|
||||
|
||||
Vendored
+4495
-4947
File diff suppressed because it is too large.
Load diff
Vendored
+4737
-5121
File diff suppressed because it is too large.
Load diff
+52
-52
@@ -45,8 +45,8 @@ To run without a lockfile:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
package-manager-cache: false # Explicitly disable caching if you don't have a lockfile
|
||||
@@ -64,8 +64,8 @@ If `check-latest` is set to `true`, the action first checks if the cached versio
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
check-latest: true
|
||||
@@ -76,15 +76,15 @@ steps:
|
||||
|
||||
## Node version file
|
||||
|
||||
The `node-version-file` input accepts a path to a file containing the version of Node.js to be used by a project, for example `.nvmrc`, `.node-version`, `.tool-versions`, or `package.json`. If both the `node-version` and the `node-version-file` inputs are provided then the `node-version` input is used.
|
||||
The `node-version-file` input accepts a path to a file containing the version of Node.js to be used by a project, for example `.nvmrc`, `.node-version`, `.tool-versions`, `mise.toml`, or `package.json`. If both the `node-version` and the `node-version-file` inputs are provided then the `node-version` input is used.
|
||||
See [supported version syntax](https://github.com/actions/setup-node#supported-version-syntax).
|
||||
|
||||
> The action will search for the node version file relative to the repository root.
|
||||
> The action resolves a relative `node-version-file` path against `GITHUB_WORKSPACE`, which is the repository root by default. If the input is a full absolute path, the action uses that path directly instead of appending it to the workspace.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -128,8 +128,8 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
|
||||
@@ -150,8 +150,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.0.0-v8-canary' # it will install the latest v8 canary release for node 24.0.0
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -166,8 +166,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24-v8-canary' # it will install the latest v8 canary release for node 24
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -183,8 +183,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'v24.0.0-v8-canary2025030537242e55ac'
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -204,8 +204,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24-nightly' # it will install the latest nightly release for node 24
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -221,8 +221,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.0.0-nightly' # it will install the latest nightly release for node 24.0.0
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -238,8 +238,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.0.0-nightly202505066102159fa1'
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -257,8 +257,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Node sample
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.0.0-rc.4'
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -276,8 +276,8 @@ Yarn caching handles both Yarn Classic (v1) and Yarn Berry (v2, v3, v4+).
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'yarn'
|
||||
@@ -295,11 +295,11 @@ steps:
|
||||
# NOTE: pnpm caching support requires pnpm version >= 6.10.0
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/checkout@v7
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: 10
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'pnpm'
|
||||
@@ -314,8 +314,8 @@ steps:
|
||||
**Using wildcard patterns to cache dependencies**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'npm'
|
||||
@@ -327,8 +327,8 @@ steps:
|
||||
**Using a list of file paths to cache dependencies**
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'npm'
|
||||
@@ -345,13 +345,13 @@ You can restore caches without saving new entries, which helps reduce cache writ
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
# - uses: pnpm/action-setup@v6
|
||||
# with:
|
||||
# version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
@@ -397,9 +397,9 @@ jobs:
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
node_version:
|
||||
- 20
|
||||
- 22
|
||||
- 24
|
||||
- 26
|
||||
architecture:
|
||||
- x64
|
||||
# an extra windows-x86 run:
|
||||
@@ -409,9 +409,9 @@ jobs:
|
||||
architecture: x86
|
||||
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v6
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
@@ -423,8 +423,8 @@ jobs:
|
||||
## Publish to npmjs and GPR with npm
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -433,7 +433,7 @@ steps:
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||
@@ -445,8 +445,8 @@ steps:
|
||||
## Publish to npmjs and GPR with yarn
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
registry-url: <registry url>
|
||||
@@ -455,7 +455,7 @@ steps:
|
||||
- run: yarn publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||
@@ -467,8 +467,8 @@ steps:
|
||||
## Use private packages
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -488,8 +488,8 @@ Below you can find a sample "Setup .yarnrc.yml" step, that is going to allow you
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||
@@ -533,9 +533,9 @@ You must also configure a **Trusted Publisher** in npm for your package/scope th
|
||||
id-token: write # Required for OIDC
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
@@ -558,10 +558,10 @@ It is possible to specify a token to authenticate with the mirror using the `mir
|
||||
The token will be passed in the `Authorization` header.
|
||||
|
||||
```yaml
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.x'
|
||||
mirror: 'https://nodejs.org/dist'
|
||||
mirror-token: 'your-mirror-token'
|
||||
cache-package-manager: false # Disable automatic npm caching if not required
|
||||
package-manager-cache: false # Disable automatic npm caching if not required
|
||||
```
|
||||
Generated
+659
-536
File diff suppressed because it is too large.
Load diff
+15
-11
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "setup-node",
|
||||
"version": "7.0.0",
|
||||
"version": "7.1.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"description": "setup node action",
|
||||
@@ -29,7 +29,7 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^6.1.0",
|
||||
"@actions/cache": "^6.3.0",
|
||||
"@actions/core": "^3.0.1",
|
||||
"@actions/exec": "^3.0.0",
|
||||
"@actions/github": "^9.1.1",
|
||||
@@ -37,24 +37,28 @@
|
||||
"@actions/http-client": "^4.0.1",
|
||||
"@actions/io": "^3.0.2",
|
||||
"@actions/tool-cache": "^4.0.0",
|
||||
"semver": "^7.8.5"
|
||||
"semver": "^7.8.5",
|
||||
"smol-toml": "^1.7.1"
|
||||
},
|
||||
"overrides": {
|
||||
"brace-expansion": "5.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@jest/globals": "^30.4.1",
|
||||
"@types/node": "^26.0.0",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/node": "^24.1.0",
|
||||
"@types/semver": "^7.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
||||
"@typescript-eslint/parser": "^8.62.0",
|
||||
"@vercel/ncc": "^0.44.0",
|
||||
"eslint": "^10.5.0",
|
||||
"@typescript-eslint/parser": "^8.70.0",
|
||||
"@vercel/ncc": "^0.45.0",
|
||||
"eslint": "^10.11.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jest": "^29.15.2",
|
||||
"eslint-plugin-n": "^18.1.0",
|
||||
"eslint-plugin-n": "^18.3.0",
|
||||
"globals": "^17.7.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-each": "^30.4.1",
|
||||
"prettier": "^3.8.4",
|
||||
"jest-each": "^30.5.2",
|
||||
"prettier": "^3.9.6",
|
||||
"ts-jest": "^29.4.11",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import * as core from '@actions/core';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import path from 'path';
|
||||
|
||||
import BaseDistribution from '../base-distribution.js';
|
||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models.js';
|
||||
@@ -9,6 +11,15 @@ interface INodeRelease extends tc.IToolRelease {
|
||||
lts?: string;
|
||||
}
|
||||
|
||||
const nodeVersionsManifestUrl =
|
||||
'https://raw.githubusercontent.com/actions/node-versions/main/versions-manifest.json';
|
||||
const invalidManifestMessage =
|
||||
'The manifest fetched is empty, truncated, or does not contain any valid tool release entries.';
|
||||
|
||||
function isValidManifest(manifest: unknown): manifest is tc.IToolRelease[] {
|
||||
return Array.isArray(manifest) && manifest.length > 0;
|
||||
}
|
||||
|
||||
export default class OfficialBuilds extends BaseDistribution {
|
||||
constructor(nodeInfo: NodeInputs) {
|
||||
super(nodeInfo);
|
||||
@@ -42,12 +53,20 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
|
||||
if (this.nodeInfo.checkLatest) {
|
||||
core.info('Attempt to resolve the latest version from manifest...');
|
||||
const resolvedVersion = await this.resolveVersionFromManifest(
|
||||
let resolvedVersion: string | undefined;
|
||||
try {
|
||||
manifest ??= await this.getManifest();
|
||||
const info = await this.getInfoFromManifest(
|
||||
this.nodeInfo.versionSpec,
|
||||
this.nodeInfo.stable,
|
||||
osArch,
|
||||
manifest
|
||||
);
|
||||
resolvedVersion = info?.resolvedVersion;
|
||||
} catch (error) {
|
||||
core.info('Unable to resolve version from manifest...');
|
||||
core.debug((error as Error).message);
|
||||
}
|
||||
if (resolvedVersion) {
|
||||
this.nodeInfo.versionSpec = resolvedVersion;
|
||||
core.info(`Resolved as '${resolvedVersion}'`);
|
||||
@@ -62,7 +81,9 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
|
||||
if (toolPath) {
|
||||
core.info(`Found in cache @ ${toolPath}`);
|
||||
const installedDir = toolPath;
|
||||
this.addToolPath(toolPath);
|
||||
await this.verifyNodeVersion(installedDir);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -123,11 +144,13 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
toolPath = await this.downloadDirectlyFromNode();
|
||||
}
|
||||
|
||||
const installedDir = toolPath;
|
||||
if (this.osPlat != 'win32') {
|
||||
toolPath = path.join(toolPath, 'bin');
|
||||
}
|
||||
|
||||
core.addPath(toolPath);
|
||||
await this.verifyNodeVersion(installedDir);
|
||||
}
|
||||
|
||||
protected addToolPath(toolPath: string) {
|
||||
@@ -185,9 +208,13 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
return `${url}/dist`;
|
||||
}
|
||||
|
||||
private getManifest(): Promise<tc.IToolRelease[]> {
|
||||
private async getManifest(): Promise<tc.IToolRelease[]> {
|
||||
let lastError: Error | undefined;
|
||||
const maxAttempts = 3;
|
||||
core.debug('Getting manifest from actions/node-versions@main');
|
||||
return tc.getManifestFromRepo(
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
const manifest = await tc.getManifestFromRepo(
|
||||
'actions',
|
||||
'node-versions',
|
||||
this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||
@@ -195,6 +222,48 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
: this.nodeInfo.auth,
|
||||
'main'
|
||||
);
|
||||
if (isValidManifest(manifest)) {
|
||||
return manifest;
|
||||
}
|
||||
lastError = new Error(invalidManifestMessage);
|
||||
} catch (error) {
|
||||
lastError = error instanceof Error ? error : new Error(String(error));
|
||||
const status = (error as {statusCode?: number})?.statusCode;
|
||||
if (status === 403 || status === 429) {
|
||||
core.debug(
|
||||
`GitHub API returned HTTP ${status}; trying raw manifest.`
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
core.debug(
|
||||
`Attempt ${attempt}/${maxAttempts} to fetch the manifest failed: ${lastError.message}`
|
||||
);
|
||||
if (attempt < maxAttempts) {
|
||||
core.info('Retrying to fetch the manifest...');
|
||||
await new Promise(resolve =>
|
||||
setTimeout(resolve, 1000 * 2 ** (attempt - 1))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
core.debug(`GitHub API manifest fetch failed: ${lastError?.message}`);
|
||||
core.debug(`Getting manifest from ${nodeVersionsManifestUrl}`);
|
||||
try {
|
||||
const {result} = await this.httpClient.getJson<tc.IToolRelease[]>(
|
||||
nodeVersionsManifestUrl
|
||||
);
|
||||
if (!isValidManifest(result)) {
|
||||
throw new Error(invalidManifestMessage);
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
const message = (error as Error).message;
|
||||
throw new Error(
|
||||
`Failed to fetch a valid manifest from the GitHub API and raw URL. API: ${lastError?.message}. Raw: ${message}`,
|
||||
{cause: error}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private resolveLtsAliasFromManifest(
|
||||
@@ -241,26 +310,6 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
return release.version.split('.')[0];
|
||||
}
|
||||
|
||||
private async resolveVersionFromManifest(
|
||||
versionSpec: string,
|
||||
stable: boolean,
|
||||
osArch: string,
|
||||
manifest: tc.IToolRelease[] | undefined
|
||||
): Promise<string | undefined> {
|
||||
try {
|
||||
const info = await this.getInfoFromManifest(
|
||||
versionSpec,
|
||||
stable,
|
||||
osArch,
|
||||
manifest
|
||||
);
|
||||
return info?.resolvedVersion;
|
||||
} catch (err) {
|
||||
core.info('Unable to resolve version from manifest...');
|
||||
core.debug((err as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
private async getInfoFromManifest(
|
||||
versionSpec: string,
|
||||
stable: boolean,
|
||||
@@ -298,4 +347,30 @@ export default class OfficialBuilds extends BaseDistribution {
|
||||
private isLatestSyntax(versionSpec): boolean {
|
||||
return ['current', 'latest', 'node'].includes(versionSpec);
|
||||
}
|
||||
|
||||
private async verifyNodeVersion(installedDir: string) {
|
||||
// tool-cache layout: <root>/node/<version>/<arch>
|
||||
const expectedVersion = 'v' + path.basename(path.dirname(installedDir));
|
||||
let actualVersion = '';
|
||||
try {
|
||||
const {stdout} = await exec.getExecOutput('node', ['--version'], {
|
||||
silent: true
|
||||
});
|
||||
actualVersion = stdout.trim();
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`Node installation failed. Node may not be installed or not on PATH: ${(err as Error).message}`,
|
||||
{cause: err}
|
||||
);
|
||||
}
|
||||
if (actualVersion !== expectedVersion) {
|
||||
core.debug(
|
||||
`Node installation failed: expected ${expectedVersion} but "node --version" reported ${actualVersion || '(empty)'} (installedDir: ${installedDir}).`
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Node ${expectedVersion} installation failed, likely due to an incomplete or corrupted download.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -120,7 +120,9 @@ function resolveVersionInput(): string {
|
||||
}
|
||||
|
||||
if (versionFileInput) {
|
||||
const versionFilePath = path.join(
|
||||
// `path.resolve` (unlike `path.join`) keeps an already-absolute input as-is,
|
||||
// so a composite action can pass `${{ github.action_path }}/.nvmrc`.
|
||||
const versionFilePath = path.resolve(
|
||||
process.env.GITHUB_WORKSPACE!,
|
||||
versionFileInput
|
||||
);
|
||||
|
||||
+21
@@ -1,6 +1,7 @@
|
||||
import * as core from '@actions/core';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as io from '@actions/io';
|
||||
import {parse} from 'smol-toml';
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
@@ -68,6 +69,26 @@ export function getNodeVersionFromFile(versionFilePath: string): string | null {
|
||||
core.info('Node version file is not JSON file');
|
||||
}
|
||||
|
||||
// Try parsing the file as a mise `mise.toml` file.
|
||||
try {
|
||||
const manifest: Record<string, any> = parse(contents);
|
||||
if (manifest?.tools?.node) {
|
||||
const node = manifest.tools.node;
|
||||
|
||||
if (typeof node === 'object' && node?.version) {
|
||||
return node.version;
|
||||
}
|
||||
|
||||
if (typeof node === 'string') {
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
} catch {
|
||||
core.info('Node version file is not TOML file');
|
||||
}
|
||||
|
||||
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
|
||||
return found?.groups?.version ?? contents.trim();
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user