mirror of
https://gitea.com/actions/setup-node.git
synced 2026-08-15 01:51:41 +08:00
Compare commits
77
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e51e5fe84f | ||
|
|
32f57ac043 | ||
|
|
b789df37d6 | ||
|
|
1b7c366466 | ||
|
|
05e62e6009 | ||
|
|
391b8c7809 | ||
|
|
363be39a57 | ||
|
|
8207627860 | ||
|
|
04be95cf35 | ||
|
|
7c2c68d20d | ||
|
|
6a61c0375d | ||
|
|
30eb73b41d | ||
|
|
4e1a87a501 | ||
|
|
360237f0c0 | ||
|
|
4f8aac5beb | ||
|
|
f4a67bbeca | ||
|
|
0355742c94 | ||
|
|
ad1b57eb81 | ||
|
|
670825a89d | ||
|
|
48b55a011b | ||
|
|
ab72c7e7eb | ||
|
|
53b83947a5 | ||
|
|
54045abd5d | ||
|
|
c882bffdbd | ||
|
|
774c1d6296 | ||
|
|
efcb663fc6 | ||
|
|
d02c89dce7 | ||
|
|
6044e13b5d | ||
|
|
8e494633d0 | ||
|
|
621ac41091 | ||
|
|
2951748f4c | ||
|
|
21ddc7bc1f | ||
|
|
65d868f8d4 | ||
|
|
395ad32622 | ||
|
|
a4d2e2bbca | ||
|
|
b9b25d45f7 | ||
|
|
633bb92bc0 | ||
|
|
dda4788290 | ||
|
|
2028fbc5c2 | ||
|
|
13427813f7 | ||
|
|
89d709d423 | ||
|
|
cd2651c462 | ||
|
|
a0853c2454 | ||
|
|
b7234cc9fe | ||
|
|
d7a11313b5 | ||
|
|
5e2628c959 | ||
|
|
65beceff8e | ||
|
|
7e24a656e1 | ||
|
|
08f58d1471 | ||
|
|
49933ea528 | ||
|
|
e3ce749e20 | ||
|
|
40337cb8f7 | ||
|
|
1ccdddc9b8 | ||
|
|
cdca7365b2 | ||
|
|
22c0e7494f | ||
|
|
a7c2d9473e | ||
|
|
802632921f | ||
|
|
1d0ff469b7 | ||
|
|
574f09a9fa | ||
|
|
260f8702db | ||
|
|
111c4beff8 | ||
|
|
0bc26deefe | ||
|
|
8f9cc178b6 | ||
|
|
5eef37b631 | ||
|
|
fbeca22fb2 | ||
|
|
48b90677b6 | ||
|
|
39370e3970 | ||
|
|
abb238b131 | ||
|
|
aca7b64a59 | ||
|
|
88de2a3d99 | ||
|
|
0a44ba7841 | ||
|
|
d6ebc7b438 | ||
|
|
97ca147735 | ||
|
|
aa363ded8f | ||
|
|
1c7b2db920 | ||
|
|
26961cf329 | ||
|
|
1e60f620b9 |
@@ -1,6 +0,0 @@
|
|||||||
# Ignore list
|
|
||||||
/*
|
|
||||||
|
|
||||||
# Do not ignore these folders:
|
|
||||||
!__tests__/
|
|
||||||
!src/
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:eslint-plugin-jest/recommended',
|
|
||||||
'eslint-config-prettier'
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint', 'eslint-plugin-node', 'eslint-plugin-jest'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-require-imports': 'error',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'@typescript-eslint/no-explicit-any': 'off',
|
|
||||||
'@typescript-eslint/no-empty-function': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
'ts-ignore': 'allow-with-description'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-console': 'error',
|
|
||||||
'yoda': 'error',
|
|
||||||
'prefer-const': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
destructuring: 'all'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'no-control-regex': 'off',
|
|
||||||
'no-constant-condition': ['error', {checkLoops: false}],
|
|
||||||
'node/no-extraneous-import': 'error'
|
|
||||||
},
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: ['**/*{test,spec}.ts'],
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-unused-vars': 'off',
|
|
||||||
'jest/no-standalone-expect': 'off',
|
|
||||||
'jest/no-conditional-expect': 'off',
|
|
||||||
'no-console': 'off',
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
env: {
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
'jest/globals': true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
# Enable version updates for npm
|
||||||
|
- package-ecosystem: 'npm'
|
||||||
|
# Look for `package.json` and `lock` files in the `root` directory
|
||||||
|
directory: '/'
|
||||||
|
# Check the npm registry for updates every day (weekdays)
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
|
|
||||||
|
# Enable version updates for GitHub Actions
|
||||||
|
- package-ecosystem: 'github-actions'
|
||||||
|
# Workflow files stored in the default location of `.github/workflows`
|
||||||
|
# You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: 'weekly'
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"owner": "eslint-compact",
|
"owner": "eslint-compact",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s(Error|Warning|Info)\\s-\\s(.+)\\s\\((.+)\\)$",
|
"regexp": "^(.+):\\sline\\s(\\d+),\\scol\\s(\\d+),\\s([Ee]rror|[Ww]arning|[Ii]nfo)\\s-\\s(.+)\\s\\((.+)\\)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"column": 3,
|
"column": 3,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"owner": "eslint-stylish",
|
"owner": "eslint-stylish",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^([^\\s].*)$",
|
"regexp": "^\\s*([^\\s].*)$",
|
||||||
"file": 1
|
"file": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ jobs:
|
|||||||
name: Basic validation
|
name: Basic validation
|
||||||
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main
|
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main
|
||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '24.x'
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ jobs:
|
|||||||
name: Check dist/
|
name: Check dist/
|
||||||
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
|
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
|
||||||
with:
|
with:
|
||||||
node-version: '20.x'
|
node-version: '24.x'
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Clean global cache
|
- name: Clean global cache
|
||||||
run: npm cache clean --force
|
run: npm cache clean --force
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
@@ -41,14 +41,14 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@v2
|
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
|
||||||
with:
|
with:
|
||||||
version: 6.10.0
|
version: 10
|
||||||
- name: Generate pnpm file
|
- name: Generate pnpm file
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
- name: Remove dependencies
|
- name: Remove dependencies
|
||||||
@@ -74,14 +74,14 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Yarn version
|
- name: Yarn version
|
||||||
run: yarn --version
|
run: yarn --version
|
||||||
- name: Generate yarn file
|
- name: Generate yarn file
|
||||||
run: yarn install
|
run: yarn install --ignore-engines
|
||||||
- name: Remove dependencies
|
- name: Remove dependencies
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: Remove-Item node_modules -Force -Recurse
|
run: Remove-Item node_modules -Force -Recurse
|
||||||
@@ -106,10 +106,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Update yarn
|
- name: Update yarn
|
||||||
run: yarn set version 3.6.4
|
run: yarn set version 3.6.4
|
||||||
- name: Yarn version
|
- name: Yarn version
|
||||||
@@ -139,11 +139,11 @@ jobs:
|
|||||||
name: Test yarn subprojects
|
name: Test yarn subprojects
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: prepare sub-projects
|
- name: prepare sub-projects
|
||||||
run: __tests__/prepare-yarn-subprojects.sh yarn1
|
run: __tests__/prepare-yarn-subprojects.sh yarn1
|
||||||
@@ -166,11 +166,11 @@ jobs:
|
|||||||
name: Test yarn subprojects all locally managed
|
name: Test yarn subprojects all locally managed
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: prepare sub-projects
|
- name: prepare sub-projects
|
||||||
run: __tests__/prepare-yarn-subprojects.sh keepcache keepcache
|
run: __tests__/prepare-yarn-subprojects.sh keepcache keepcache
|
||||||
@@ -193,11 +193,11 @@ jobs:
|
|||||||
name: Test yarn subprojects some locally managed
|
name: Test yarn subprojects some locally managed
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: prepare sub-projects
|
- name: prepare sub-projects
|
||||||
run: __tests__/prepare-yarn-subprojects.sh global
|
run: __tests__/prepare-yarn-subprojects.sh global
|
||||||
@@ -220,11 +220,11 @@ jobs:
|
|||||||
name: Test yarn subprojects managed by git
|
name: Test yarn subprojects managed by git
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
|
||||||
- name: prepare sub-projects
|
- name: prepare sub-projects
|
||||||
run: /bin/bash __tests__/prepare-yarn-subprojects.sh keepcache
|
run: /bin/bash __tests__/prepare-yarn-subprojects.sh keepcache
|
||||||
@@ -243,3 +243,62 @@ jobs:
|
|||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
sub2/*.lock
|
sub2/*.lock
|
||||||
sub3/*.lock
|
sub3/*.lock
|
||||||
|
|
||||||
|
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-15-intel]
|
||||||
|
node-version: [22, 24, 26]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
- name: Create package.json with packageManager field
|
||||||
|
run: |
|
||||||
|
echo '{ "name": "test-project", "version": "1.0.0", "packageManager": "npm@8.0.0" }' > 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
|
||||||
|
|
||||||
|
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-15-intel]
|
||||||
|
node-version: [22, 24, 26]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
- 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
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
https_proxy: http://squid-proxy:3128
|
https_proxy: http://squid-proxy:3128
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Clear tool cache
|
- name: Clear tool cache
|
||||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||||
- name: Setup node 14
|
- name: Setup node 24
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version: 14.x
|
node-version: 24.x
|
||||||
- name: Verify node and npm
|
- name: Verify node and npm
|
||||||
run: __tests__/verify-node.sh 14
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
test-bypass-proxy:
|
test-bypass-proxy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -41,12 +41,12 @@ jobs:
|
|||||||
https_proxy: http://no-such-proxy:3128
|
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
|
no_proxy: api.github.com,github.com,nodejs.org,registry.npmjs.org,*.s3.amazonaws.com,s3.amazonaws.com
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Clear tool cache
|
- name: Clear tool cache
|
||||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||||
- name: Setup node 11
|
- name: Setup node 24
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version: 11
|
node-version: 24
|
||||||
- name: Verify node and npm
|
- name: Verify node and npm
|
||||||
run: __tests__/verify-node.sh 11
|
run: __tests__/verify-node.sh 24
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: 'Publish Immutable Action Version'
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checking out
|
||||||
|
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
- name: Publish
|
||||||
|
id: publish
|
||||||
|
uses: actions/publish-immutable-action@4bc8754ffc40f27910afb20287dbbbb675a4e978 # v0.0.4
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Update the ${{ env.TAG_NAME }} tag
|
- name: Update the ${{ env.TAG_NAME }} tag
|
||||||
uses: actions/publish-action@v0.2.2
|
uses: actions/publish-action@v0.4.0
|
||||||
with:
|
with:
|
||||||
source-tag: ${{ env.TAG_NAME }}
|
source-tag: ${{ env.TAG_NAME }}
|
||||||
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
|
|||||||
@@ -17,10 +17,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -34,10 +34,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-13]
|
os: [ubuntu-latest, windows-latest, macos-15-intel]
|
||||||
node-version: [lts/dubnium, lts/erbium, lts/fermium, lts/*, lts/-1]
|
node-version: [lts/*, lts/-1]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -56,15 +56,15 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version:
|
node-version:
|
||||||
[
|
[
|
||||||
'20-v8-canary',
|
'26-v8-canary',
|
||||||
'20.0.0-v8-canary',
|
'26.0.0-v8-canary',
|
||||||
'20.0.0-v8-canary20221101e50e45c9f8'
|
'26.0.0-v8-canary20251016054584e5cf'
|
||||||
]
|
]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -81,11 +81,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version:
|
node-version: [26-nightly, 25-nightly, 24.0.0-nightly]
|
||||||
[20.11.0-nightly202312211a0be537da, 21-nightly, 18.0.0-nightly]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -102,10 +101,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [20.0.0-rc.1, 18.0.0-rc.2, 19.0.0-rc.0]
|
node-version: [26.0.0-rc.1, 22.14.0-rc.1, 24.0.0-rc.4]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -122,10 +121,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18.20.0, 20.10.0, 22.0.0]
|
node-version: [26.5.0, 22.0.0, 24.9.0]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -139,10 +138,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [18, 20, 22]
|
node-version: [22, 24, 26]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node and check latest
|
- name: Setup Node and check latest
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -157,57 +156,72 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version-file:
|
node-version-file:
|
||||||
[.nvmrc, .tool-versions, .tool-versions-node, package.json]
|
[.nvmrc, .tool-versions, .tool-versions-node, package.json]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup node from node version file
|
- name: Setup node from node version file
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version-file: '__tests__/data/${{ matrix.node-version-file }}'
|
node-version-file: '__tests__/data/${{ matrix.node-version-file }}'
|
||||||
- name: Verify node
|
- name: Verify node
|
||||||
run: __tests__/verify-node.sh 20
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
|
version-file-dev-engines:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
|
- 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 26
|
||||||
|
|
||||||
version-file-volta:
|
version-file-volta:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup node from node version file
|
- name: Setup node from node version file
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version-file: '__tests__/data/package-volta.json'
|
node-version-file: '__tests__/data/package-volta.json'
|
||||||
- name: Verify node
|
- name: Verify node
|
||||||
run: __tests__/verify-node.sh 20
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
version-file-volta-extends:
|
version-file-volta-extends:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup node from node version file
|
- name: Setup node from node version file
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version-file: '__tests__/data/package-volta-extends.json'
|
node-version-file: '__tests__/data/package-volta-extends.json'
|
||||||
- name: Verify node
|
- name: Verify node
|
||||||
run: __tests__/verify-node.sh 20
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
node-dist:
|
node-dist:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [17, 19]
|
node-version: [21, 23]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node from dist
|
- name: Setup Node from dist
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
@@ -221,9 +235,9 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-13]
|
os: [ubuntu-latest, windows-latest, macos-15-intel]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
# test old versions which didn't have npm and layout different
|
# test old versions which didn't have npm and layout different
|
||||||
- name: Setup node 0.12.18 from dist
|
- name: Setup node 0.12.18 from dist
|
||||||
uses: ./
|
uses: ./
|
||||||
@@ -236,11 +250,11 @@ jobs:
|
|||||||
arch:
|
arch:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup node 20 x86 from dist
|
- name: Setup node 22 x86 from dist
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: '22'
|
||||||
architecture: 'x86'
|
architecture: 'x86'
|
||||||
- name: Verify node
|
- name: Verify node
|
||||||
run: __tests__/verify-arch.sh "ia32"
|
run: __tests__/verify-arch.sh "ia32"
|
||||||
@@ -251,7 +265,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-15-intel]
|
||||||
node-version: [current, latest, node]
|
node-version: [current, latest, node]
|
||||||
steps:
|
steps:
|
||||||
- name: Get node version
|
- name: Get node version
|
||||||
@@ -260,7 +274,7 @@ jobs:
|
|||||||
echo "LATEST_NODE_VERSION=$latestNodeVersion" >> $GITHUB_OUTPUT
|
echo "LATEST_NODE_VERSION=$latestNodeVersion" >> $GITHUB_OUTPUT
|
||||||
id: version
|
id: version
|
||||||
shell: bash
|
shell: bash
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
|
|||||||
+15
@@ -93,3 +93,18 @@ typings/
|
|||||||
|
|
||||||
# DynamoDB Local files
|
# DynamoDB Local files
|
||||||
.dynamodb/
|
.dynamodb/
|
||||||
|
|
||||||
|
# Ignore everything inside fixture package-manager folders
|
||||||
|
__tests__/data/npm/**
|
||||||
|
__tests__/data/pnpm/**
|
||||||
|
__tests__/data/yarn/**
|
||||||
|
|
||||||
|
# Keep only fixture manifests + lockfiles
|
||||||
|
!__tests__/data/npm/package.json
|
||||||
|
!__tests__/data/npm/package-lock.json
|
||||||
|
|
||||||
|
!__tests__/data/pnpm/package.json
|
||||||
|
!__tests__/data/pnpm/pnpm-lock.yaml
|
||||||
|
|
||||||
|
!__tests__/data/yarn/package.json
|
||||||
|
!__tests__/data/yarn/yarn.lock
|
||||||
|
|||||||
+6
-1
@@ -10,6 +10,11 @@ allowed:
|
|||||||
- mit
|
- mit
|
||||||
- cc0-1.0
|
- cc0-1.0
|
||||||
- unlicense
|
- unlicense
|
||||||
|
- blueoak-1.0.0
|
||||||
|
|
||||||
reviewed:
|
reviewed:
|
||||||
npm:
|
npm:
|
||||||
|
- "@actions/http-client"
|
||||||
|
- balanced-match
|
||||||
|
- brace-expansion
|
||||||
|
- fast-content-type-parse
|
||||||
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Generated
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.
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
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.
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
Generated
BIN
Binary file not shown.
@@ -1,11 +0,0 @@
|
|||||||
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
|
||||||
module.exports = {
|
|
||||||
printWidth: 80,
|
|
||||||
tabWidth: 2,
|
|
||||||
useTabs: false,
|
|
||||||
semi: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'none',
|
|
||||||
bracketSpacing: false,
|
|
||||||
arrowParens: 'avoid'
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
@@ -12,36 +12,60 @@ This action provides the following functionality for GitHub Actions users:
|
|||||||
- Registering problem matchers for error output
|
- Registering problem matchers for error output
|
||||||
- Configuring authentication for GPR or npm
|
- Configuring authentication for GPR or npm
|
||||||
|
|
||||||
|
## What's new in V7
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
- The `always-auth` input has been removed, as it is deprecated and will no longer be supported in future npm releases. To ensure your workflows continue to run without warnings or errors, please remove any references to `always-auth` from your configuration.
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
See [action.yml](action.yml)
|
See [action.yml](action.yml)
|
||||||
|
|
||||||
<!-- start usage -->
|
<!-- start usage -->
|
||||||
```yaml
|
```yaml
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
# Version Spec of the version to use in SemVer notation.
|
# Version Spec of the version to use in SemVer notation.
|
||||||
# It also emits such aliases as lts, latest, nightly and canary builds
|
# 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
|
# Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node
|
||||||
node-version: ''
|
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, .nvmrc, .node-version, .tool-versions.
|
||||||
# If node-version and node-version-file are both provided the action will use version from node-version.
|
# If node-version and node-version-file are both provided the action will use version from node-version.
|
||||||
node-version-file: ''
|
node-version-file: ''
|
||||||
|
|
||||||
# Set this option if you want the action to check for the latest available version
|
# Set this option if you want the action to check for the latest available version
|
||||||
# that satisfies the version spec.
|
# that satisfies the version spec.
|
||||||
# It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen).
|
# It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen).
|
||||||
# Default: false
|
# Default: false
|
||||||
check-latest: false
|
check-latest: false
|
||||||
|
|
||||||
# Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.
|
# Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.
|
||||||
# Default: ''. The action use system architecture by default
|
# Default: ''. The action use system architecture by default
|
||||||
architecture: ''
|
architecture: ''
|
||||||
|
|
||||||
# Used to pull node distributions from https://github.com/actions/node-versions.
|
# Used to pull node distributions from https://github.com/actions/node-versions.
|
||||||
# Since there's a default, this is typically not supplied by the user.
|
# Since there's a default, this is typically not supplied by the user.
|
||||||
# When running this action on github.com, the default value is sufficient.
|
# When running this action on github.com, the default value is sufficient.
|
||||||
# When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting.
|
# When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting.
|
||||||
#
|
#
|
||||||
# We recommend using a service account with the least permissions necessary. Also
|
# We recommend using a service account with the least permissions necessary. Also
|
||||||
@@ -57,25 +81,41 @@ See [action.yml](action.yml)
|
|||||||
# Default: ''
|
# Default: ''
|
||||||
cache: ''
|
cache: ''
|
||||||
|
|
||||||
# Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc.
|
# 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.
|
||||||
# It will generate hash from the target file for primary key. It works only If cache is specified.
|
# 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.
|
# Supports wildcards or a list of file names for caching multiple dependencies.
|
||||||
# Default: ''
|
# Default: ''
|
||||||
cache-dependency-path: ''
|
cache-dependency-path: ''
|
||||||
|
|
||||||
# Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file,
|
# Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file,
|
||||||
# and set up auth to read in from env.NODE_AUTH_TOKEN.
|
# and set up auth to read in from env.NODE_AUTH_TOKEN.
|
||||||
# Default: ''
|
# Default: ''
|
||||||
registry-url: ''
|
registry-url: ''
|
||||||
|
|
||||||
# Optional scope for authenticating against scoped registries.
|
# Optional scope for authenticating against scoped registries.
|
||||||
# Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/).
|
# Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/).
|
||||||
# Default: ''
|
# Default: ''
|
||||||
scope: ''
|
scope: ''
|
||||||
|
|
||||||
# Set always-auth option in npmrc file.
|
# Optional mirror to download binaries from.
|
||||||
|
# Artifacts need to match the official Node.js
|
||||||
|
# Example:
|
||||||
|
# V8 Canary Build: <mirror_url>/download/v8-canary
|
||||||
|
# RC Build: <mirror_url>/download/rc
|
||||||
|
# Official: Build <mirror_url>/dist
|
||||||
|
# Nightly build: <mirror_url>/download/nightly
|
||||||
# Default: ''
|
# Default: ''
|
||||||
always-auth: ''
|
mirror: ''
|
||||||
|
|
||||||
|
# Optional mirror token.
|
||||||
|
# The token will be used as a bearer token in the Authorization header
|
||||||
|
# Default: ''
|
||||||
|
mirror-token: ''
|
||||||
```
|
```
|
||||||
<!-- end usage -->
|
<!-- end usage -->
|
||||||
|
|
||||||
@@ -83,15 +123,16 @@ See [action.yml](action.yml)
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 24
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
The `node-version` input is optional. If not supplied, the node version from PATH will be used. However, it is recommended to always specify Node.js version and don't rely on the system one.
|
The `node-version` input is optional. If not supplied, the node version from PATH will be used. However, it is recommended to always specify Node.js version and not rely on the system one.
|
||||||
|
|
||||||
The action will first check the local cache for a semver match. If unable to find a specific version in the cache, the action will attempt to download a version of Node.js. It will pull LTS versions from [node-versions releases](https://github.com/actions/node-versions/releases) and on miss or failure will fall back to the previous behavior of downloading directly from [node dist](https://nodejs.org/dist/).
|
The action will first check the local cache for a semver match. If unable to find a specific version in the cache, the action will attempt to download a version of Node.js. It will pull LTS versions from [node-versions releases](https://github.com/actions/node-versions/releases) and on miss or failure will fall back to the previous behavior of downloading directly from [node dist](https://nodejs.org/dist/).
|
||||||
|
|
||||||
@@ -103,9 +144,9 @@ The `node-version` input supports the Semantic Versioning Specification, for mor
|
|||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- Major versions: `18`, `20`
|
- Major versions: `22`, `24`
|
||||||
- More specific versions: `10.15`, `16.15.1` , `18.4.0`
|
- More specific versions: `20.19`, `22.17.1` , `24.8.0`
|
||||||
- NVM LTS syntax: `lts/erbium`, `lts/fermium`, `lts/*`, `lts/-n`
|
- NVM LTS syntax: `lts/iron`, `lts/jod`, `lts/*`, `lts/-n`
|
||||||
- Latest release: `*` or `latest`/`current`/`node`
|
- 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.
|
**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.
|
||||||
@@ -116,11 +157,11 @@ Since it will not be cached always, there is possibility of hitting rate limit w
|
|||||||
|
|
||||||
### Checking in lockfiles
|
### Checking in lockfiles
|
||||||
|
|
||||||
It's **always** recommended to commit the lockfile of your package manager for security and performance reasons. For more information consult the "Working with lockfiles" section of the [Advanced usage](docs/advanced-usage.md#working-with-lockfiles) guide.
|
It's **strongly recommended** to commit the lockfile of your package manager for security and performance reasons. For more information consult the "Working with lockfiles" section of the [Advanced usage](docs/advanced-usage.md#working-with-lockfiles) guide.
|
||||||
|
|
||||||
## Caching global packages data
|
## Caching global packages data
|
||||||
|
|
||||||
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, and caching is turned off by default.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -132,10 +173,10 @@ See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` in
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
@@ -145,16 +186,30 @@ steps:
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 24
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: subdir/package-lock.json
|
cache-dependency-path: subdir/package-lock.json
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- 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@v7
|
||||||
|
- uses: actions/setup-node@v7
|
||||||
|
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
|
## Matrix Testing
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -163,14 +218,15 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [ 14, 16, 18 ]
|
node: [ 20, 22, 24 ]
|
||||||
name: Node ${{ matrix.node }} sample
|
name: Node ${{ matrix.node }} sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -182,10 +238,11 @@ 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:
|
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
|
```yaml
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
||||||
node-version: 20
|
node-version: 24
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
```
|
```
|
||||||
|
|
||||||
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/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)" for more information.
|
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/enterprise-server@3.2/admin/github-actions/managing-access-to-actions-from-githubcom/setting-up-the-tool-cache-on-self-hosted-runners-without-internet-access)" for more information.
|
||||||
@@ -203,6 +260,17 @@ If the runner is not able to access github.com, any Nodejs versions requested du
|
|||||||
- [Publishing to npmjs and GPR with npm](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-npm)
|
- [Publishing to npmjs and GPR with npm](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-npm)
|
||||||
- [Publishing to npmjs and GPR with yarn](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-yarn)
|
- [Publishing to npmjs and GPR with yarn](docs/advanced-usage.md#publish-to-npmjs-and-gpr-with-yarn)
|
||||||
- [Using private packages](docs/advanced-usage.md#use-private-packages)
|
- [Using private packages](docs/advanced-usage.md#use-private-packages)
|
||||||
|
- [Publishing to npm with Trusted Publisher (OIDC)](docs/advanced-usage.md#publishing-to-npm-with-trusted-publisher-oidc)
|
||||||
|
- [Using private mirror](docs/advanced-usage.md#use-private-mirror)
|
||||||
|
|
||||||
|
## Recommended permissions
|
||||||
|
|
||||||
|
When using the `setup-node` action in your GitHub Actions workflow, it is recommended to set the following permissions to ensure proper functionality:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
permissions:
|
||||||
|
contents: read # access to check out code and install dependencies
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+90
-53
@@ -1,19 +1,57 @@
|
|||||||
|
import {
|
||||||
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as core from '@actions/core';
|
|
||||||
import * as io from '@actions/io';
|
import * as io from '@actions/io';
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn((name: string) => {
|
||||||
|
const val =
|
||||||
|
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
||||||
|
return val.trim();
|
||||||
|
}),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn())
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports AFTER mocking so authutil sees the mocked core.
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
let rcFile: string;
|
let rcFile: string;
|
||||||
|
|
||||||
describe('authutil tests', () => {
|
describe('authutil tests', () => {
|
||||||
const _runnerDir = path.join(__dirname, 'runner');
|
const _runnerDir = path.join(__dirname, 'runner');
|
||||||
|
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.SpiedFunction<typeof console.log>;
|
||||||
let dbgSpy: jest.SpyInstance;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const randPath = path.join(Math.random().toString(36).substring(7));
|
const randPath = path.join(Math.random().toString(36).substring(7));
|
||||||
@@ -37,19 +75,8 @@ describe('authutil tests', () => {
|
|||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(console, 'log');
|
logSpy = jest.spyOn(console, 'log');
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
cnSpy.mockImplementation(() => true);
|
||||||
cnSpy.mockImplementation(line => {
|
logSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('write:' + line + '\n');
|
|
||||||
});
|
|
||||||
logSpy.mockImplementation(line => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
function dbg(message: string) {
|
function dbg(message: string) {
|
||||||
@@ -76,115 +103,125 @@ describe('authutil tests', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('Sets up npmrc for npmjs', async () => {
|
it('Sets up npmrc for npmjs', async () => {
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'false');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
const rc = readRcFile(rcFile);
|
const rc = readRcFile(rcFile);
|
||||||
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
||||||
expect(rc['always-auth']).toBe('false');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Appends trailing slash to registry', async () => {
|
it('Appends trailing slash to registry', async () => {
|
||||||
await auth.configAuthentication('https://registry.npmjs.org', 'false');
|
await auth.configAuthentication('https://registry.npmjs.org');
|
||||||
|
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
const rc = readRcFile(rcFile);
|
const rc = readRcFile(rcFile);
|
||||||
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
||||||
expect(rc['always-auth']).toBe('false');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Configures scoped npm registries', async () => {
|
it('Configures scoped npm registries', async () => {
|
||||||
process.env['INPUT_SCOPE'] = 'myScope';
|
process.env['INPUT_SCOPE'] = 'myScope';
|
||||||
await auth.configAuthentication('https://registry.npmjs.org', 'false');
|
await auth.configAuthentication('https://registry.npmjs.org');
|
||||||
|
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
const rc = readRcFile(rcFile);
|
const rc = readRcFile(rcFile);
|
||||||
expect(rc['@myscope:registry']).toBe('https://registry.npmjs.org/');
|
expect(rc['@myscope:registry']).toBe('https://registry.npmjs.org/');
|
||||||
expect(rc['always-auth']).toBe('false');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Automatically configures GPR scope', async () => {
|
it('Automatically configures GPR scope', async () => {
|
||||||
await auth.configAuthentication('npm.pkg.github.com', 'false');
|
await auth.configAuthentication('npm.pkg.github.com');
|
||||||
|
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
const rc = readRcFile(rcFile);
|
const rc = readRcFile(rcFile);
|
||||||
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
||||||
expect(rc['always-auth']).toBe('false');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Sets up npmrc for always-auth true', async () => {
|
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
|
||||||
const rc = readRcFile(rcFile);
|
|
||||||
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
|
||||||
expect(rc['always-auth']).toBe('true');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is already set the NODE_AUTH_TOKEN export it', async () => {
|
it('is already set the NODE_AUTH_TOKEN export it', async () => {
|
||||||
process.env.NODE_AUTH_TOKEN = 'foobar';
|
process.env.NODE_AUTH_TOKEN = 'foobar';
|
||||||
await auth.configAuthentication('npm.pkg.github.com', 'false');
|
await auth.configAuthentication('npm.pkg.github.com');
|
||||||
expect(fs.statSync(rcFile)).toBeDefined();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
const rc = readRcFile(rcFile);
|
const rc = readRcFile(rcFile);
|
||||||
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
||||||
expect(rc['always-auth']).toBe('false');
|
|
||||||
expect(process.env.NODE_AUTH_TOKEN).toEqual('foobar');
|
expect(process.env.NODE_AUTH_TOKEN).toEqual('foobar');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not export NODE_AUTH_TOKEN if not set in environment', async () => {
|
||||||
|
const exportSpy = core.exportVariable as jest.Mock;
|
||||||
|
exportSpy.mockClear();
|
||||||
|
delete process.env.NODE_AUTH_TOKEN;
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
||||||
|
expect(exportSpy).not.toHaveBeenCalledWith(
|
||||||
|
'NODE_AUTH_TOKEN',
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should export NODE_AUTH_TOKEN if set to empty string', async () => {
|
||||||
|
const exportSpy = core.exportVariable as jest.Mock;
|
||||||
|
exportSpy.mockClear();
|
||||||
|
process.env.NODE_AUTH_TOKEN = '';
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
|
expect(exportSpy).toHaveBeenCalledWith('NODE_AUTH_TOKEN', '');
|
||||||
|
});
|
||||||
|
|
||||||
it('configAuthentication should overwrite non-scoped with non-scoped', async () => {
|
it('configAuthentication should overwrite non-scoped with non-scoped', async () => {
|
||||||
fs.writeFileSync(rcFile, 'registry=NNN');
|
fs.writeFileSync(rcFile, 'registry=NNN');
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should overwrite only non-scoped', async () => {
|
it('configAuthentication should overwrite only non-scoped', async () => {
|
||||||
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`@myscope:registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`@myscope:registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should add non-scoped to scoped', async () => {
|
it('configAuthentication should add non-scoped to scoped', async () => {
|
||||||
fs.writeFileSync(rcFile, '@myscope:registry=NNN');
|
fs.writeFileSync(rcFile, '@myscope:registry=NNN');
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`@myscope:registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`@myscope:registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should overwrite scoped with scoped', async () => {
|
it('configAuthentication should overwrite scoped with scoped', async () => {
|
||||||
process.env['INPUT_SCOPE'] = 'myscope';
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
fs.writeFileSync(rcFile, `@myscope:registry=NNN`);
|
fs.writeFileSync(rcFile, `@myscope:registry=NNN`);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should overwrite only scoped', async () => {
|
it('configAuthentication should overwrite only scoped', async () => {
|
||||||
process.env['INPUT_SCOPE'] = 'myscope';
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should add scoped to non-scoped', async () => {
|
it('configAuthentication should add scoped to non-scoped', async () => {
|
||||||
process.env['INPUT_SCOPE'] = 'myscope';
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
fs.writeFileSync(rcFile, `registry=MMM`);
|
fs.writeFileSync(rcFile, `registry=MMM`);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -194,20 +231,20 @@ describe('authutil tests', () => {
|
|||||||
rcFile,
|
rcFile,
|
||||||
`@otherscope:registry=NNN${os.EOL}@myscope:registry=MMM`
|
`@otherscope:registry=NNN${os.EOL}@myscope:registry=MMM`
|
||||||
);
|
);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`@otherscope:registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`@otherscope:registry=NNN${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('configAuthentication should add scoped to another scoped', async () => {
|
it('configAuthentication should add scoped to another scoped', async () => {
|
||||||
process.env['INPUT_SCOPE'] = 'myscope';
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
fs.writeFileSync(rcFile, `@otherscope:registry=MMM`);
|
fs.writeFileSync(rcFile, `@otherscope:registry=MMM`);
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
expect(contents).toBe(
|
expect(contents).toBe(
|
||||||
`@otherscope:registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/${os.EOL}always-auth=true`
|
`@otherscope:registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+159
-63
@@ -1,17 +1,81 @@
|
|||||||
import * as core from '@actions/core';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as cache from '@actions/cache';
|
import {fileURLToPath} from 'url';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as glob from '@actions/glob';
|
import osm from 'os';
|
||||||
|
|
||||||
import * as utils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import {restoreCache} from '../src/cache-restore';
|
|
||||||
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const {restoreCache} = await import('../src/cache-restore.js');
|
||||||
|
|
||||||
describe('cache-restore', () => {
|
describe('cache-restore', () => {
|
||||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
const packageManagers = ['yarn', 'npm', 'pnpm'] as const;
|
||||||
|
type PackageManager = (typeof packageManagers)[number];
|
||||||
|
|
||||||
|
const setWorkspaceFor = (pm: PackageManager) => {
|
||||||
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data', pm);
|
||||||
|
};
|
||||||
|
const originalGithubWorkspace = process.env['GITHUB_WORKSPACE'];
|
||||||
if (!process.env.RUNNER_OS) {
|
if (!process.env.RUNNER_OS) {
|
||||||
process.env.RUNNER_OS = 'Linux';
|
process.env.RUNNER_OS = 'Linux';
|
||||||
}
|
}
|
||||||
const platform = process.env.RUNNER_OS;
|
const platform = process.env.RUNNER_OS;
|
||||||
|
const arch = 'arm64';
|
||||||
const commonPath = '/some/random/path';
|
const commonPath = '/some/random/path';
|
||||||
const npmCachePath = `${commonPath}/npm`;
|
const npmCachePath = `${commonPath}/npm`;
|
||||||
const pnpmCachePath = `${commonPath}/pnpm`;
|
const pnpmCachePath = `${commonPath}/pnpm`;
|
||||||
@@ -23,7 +87,7 @@ describe('cache-restore', () => {
|
|||||||
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
|
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
|
||||||
const pnpmFileHash =
|
const pnpmFileHash =
|
||||||
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
|
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
|
||||||
const cachesObject = {
|
const cachesObject: Record<string, string> = {
|
||||||
[npmCachePath]: npmFileHash,
|
[npmCachePath]: npmFileHash,
|
||||||
[pnpmCachePath]: pnpmFileHash,
|
[pnpmCachePath]: pnpmFileHash,
|
||||||
[yarn1CachePath]: yarnFileHash,
|
[yarn1CachePath]: yarnFileHash,
|
||||||
@@ -45,63 +109,70 @@ describe('cache-restore', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let saveStateSpy: jest.SpyInstance;
|
let saveStateSpy: jest.Mock;
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let setOutputSpy: jest.SpyInstance;
|
let setOutputSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let restoreCacheSpy: jest.SpyInstance;
|
let restoreCacheSpy: jest.Mock;
|
||||||
let hashFilesSpy: jest.SpyInstance;
|
let hashFilesSpy: jest.Mock;
|
||||||
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// core
|
// core
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => undefined);
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = core.debug as jest.Mock;
|
||||||
debugSpy.mockImplementation(() => undefined);
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
setOutputSpy = core.setOutput as jest.Mock;
|
||||||
setOutputSpy.mockImplementation(() => undefined);
|
setOutputSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
saveStateSpy = jest.spyOn(core, 'saveState');
|
saveStateSpy = core.saveState as jest.Mock;
|
||||||
saveStateSpy.mockImplementation(() => undefined);
|
saveStateSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
// glob
|
// glob
|
||||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
(hashFilesSpy as jest.Mock<typeof glob.hashFiles>).mockImplementation(
|
||||||
if (pattern.includes('package-lock.json')) {
|
async (pattern: string) => {
|
||||||
return npmFileHash;
|
if (pattern.includes('package-lock.json')) {
|
||||||
} else if (pattern.includes('pnpm-lock.yaml')) {
|
return npmFileHash;
|
||||||
return pnpmFileHash;
|
} else if (pattern.includes('pnpm-lock.yaml')) {
|
||||||
} else if (pattern.includes('yarn.lock')) {
|
return pnpmFileHash;
|
||||||
return yarnFileHash;
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
} else {
|
return yarnFileHash;
|
||||||
return '';
|
} else {
|
||||||
}
|
return '';
|
||||||
});
|
|
||||||
|
|
||||||
// cache
|
|
||||||
restoreCacheSpy = jest.spyOn(cache, 'restoreCache');
|
|
||||||
restoreCacheSpy.mockImplementation(
|
|
||||||
(cachePaths: Array<string>, key: string) => {
|
|
||||||
if (!cachePaths || cachePaths.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachPath = cachePaths[0];
|
|
||||||
const fileHash = cachesObject[cachPath];
|
|
||||||
|
|
||||||
if (key.includes(fileHash)) {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// cache-utils
|
// cache
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
restoreCacheSpy = cache.restoreCache as jest.Mock;
|
||||||
|
(
|
||||||
|
restoreCacheSpy as jest.Mock<typeof cache.restoreCache>
|
||||||
|
).mockImplementation(async (cachePaths: string[], key: string) => {
|
||||||
|
if (!cachePaths || cachePaths.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachPath = cachePaths[0];
|
||||||
|
const fileHash = cachesObject[cachPath];
|
||||||
|
|
||||||
|
if (key.includes(fileHash)) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
// exec
|
||||||
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
|
// os
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => arch);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Validate provided package manager', () => {
|
describe('Validate provided package manager', () => {
|
||||||
@@ -124,23 +195,33 @@ describe('cache-restore', () => {
|
|||||||
])(
|
])(
|
||||||
'restored dependencies for %s',
|
'restored dependencies for %s',
|
||||||
async (packageManager, toolVersion, fileHash) => {
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
const expectedCacheKey = `node-cache-${platform}-${arch}-${packageManager}-${fileHash}`;
|
||||||
if (command.includes('version')) {
|
setWorkspaceFor(packageManager as PackageManager);
|
||||||
return toolVersion;
|
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||||
} else {
|
stdout: command.includes('version')
|
||||||
return findCacheFolder(command);
|
? toolVersion
|
||||||
}
|
: findCacheFolder(command),
|
||||||
});
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
|
||||||
await restoreCache(packageManager, '');
|
await restoreCache(packageManager, '');
|
||||||
expect(hashFilesSpy).toHaveBeenCalled();
|
expect(hashFilesSpy).toHaveBeenCalled();
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache restored from key: node-cache-${platform}-${packageManager}-${fileHash}`
|
`Cache restored from key: ${expectedCacheKey}`
|
||||||
);
|
);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`${packageManager} cache is not found`
|
`${packageManager} cache is not found`
|
||||||
);
|
);
|
||||||
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
|
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'cache-primary-key',
|
||||||
|
expectedCacheKey
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'cache-matched-key',
|
||||||
|
expectedCacheKey
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -154,13 +235,15 @@ describe('cache-restore', () => {
|
|||||||
])(
|
])(
|
||||||
'dependencies are changed %s',
|
'dependencies are changed %s',
|
||||||
async (packageManager, toolVersion, fileHash) => {
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
getCommandOutputSpy.mockImplementation((command: string) => {
|
const expectedCacheKey = `node-cache-${platform}-${arch}-${packageManager}-${fileHash}`;
|
||||||
if (command.includes('version')) {
|
setWorkspaceFor(packageManager as PackageManager);
|
||||||
return toolVersion;
|
getExecOutputSpy.mockImplementation(async (command: any) => ({
|
||||||
} else {
|
stdout: command.includes('version')
|
||||||
return findCacheFolder(command);
|
? toolVersion
|
||||||
}
|
: findCacheFolder(command),
|
||||||
});
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
|
||||||
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
||||||
await restoreCache(packageManager, '');
|
await restoreCache(packageManager, '');
|
||||||
@@ -169,11 +252,24 @@ describe('cache-restore', () => {
|
|||||||
`${packageManager} cache is not found`
|
`${packageManager} cache is not found`
|
||||||
);
|
);
|
||||||
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false);
|
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'cache-primary-key',
|
||||||
|
expectedCacheKey
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'cache-matched-key',
|
||||||
|
undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
if (originalGithubWorkspace === undefined) {
|
||||||
|
delete process.env['GITHUB_WORKSPACE'];
|
||||||
|
} else {
|
||||||
|
process.env['GITHUB_WORKSPACE'] = originalGithubWorkspace;
|
||||||
|
}
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|||||||
+170
-104
@@ -1,12 +1,74 @@
|
|||||||
import * as core from '@actions/core';
|
import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals';
|
||||||
import * as cache from '@actions/cache';
|
import {fileURLToPath} from 'url';
|
||||||
import * as glob from '@actions/glob';
|
|
||||||
import fs from 'fs';
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
import * as utils from '../src/cache-utils';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import {run} from '../src/cache-save';
|
|
||||||
import {State} from '../src/constants';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn(),
|
||||||
|
ValidationError: class ValidationError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ValidationError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const {run} = await import('../src/cache-save.js');
|
||||||
|
const {State} = await import('../src/constants.js');
|
||||||
|
|
||||||
describe('run', () => {
|
describe('run', () => {
|
||||||
const yarnFileHash =
|
const yarnFileHash =
|
||||||
@@ -20,42 +82,42 @@ describe('run', () => {
|
|||||||
|
|
||||||
const inputs = {} as any;
|
const inputs = {} as any;
|
||||||
|
|
||||||
let getInputSpy: jest.SpyInstance;
|
let getInputSpy: jest.Mock;
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let setFailedSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let getStateSpy: jest.SpyInstance;
|
let getStateSpy: jest.Mock;
|
||||||
let saveCacheSpy: jest.SpyInstance;
|
let saveCacheSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let hashFilesSpy: jest.SpyInstance;
|
let hashFilesSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
getInputSpy = jest.spyOn(core, 'getInput');
|
getInputSpy = core.getInput as jest.Mock;
|
||||||
getInputSpy.mockImplementation((name: string) => inputs[name]);
|
getInputSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => undefined);
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
warningSpy.mockImplementation(() => undefined);
|
warningSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
setFailedSpy = jest.spyOn(core, 'setFailed');
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
setFailedSpy.mockImplementation(() => undefined);
|
setFailedSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
debugSpy = core.debug as jest.Mock;
|
||||||
debugSpy.mockImplementation(() => undefined);
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
getStateSpy = jest.spyOn(core, 'getState');
|
getStateSpy = core.getState as jest.Mock;
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
saveCacheSpy = jest.spyOn(cache, 'saveCache');
|
saveCacheSpy = cache.saveCache as jest.Mock;
|
||||||
saveCacheSpy.mockImplementation(() => undefined);
|
saveCacheSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
// glob
|
// glob
|
||||||
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
hashFilesSpy = glob.hashFiles as jest.Mock;
|
||||||
hashFilesSpy.mockImplementation((pattern: string) => {
|
hashFilesSpy.mockImplementation((pattern: any) => {
|
||||||
if (pattern.includes('package-lock.json')) {
|
if (pattern.includes('package-lock.json')) {
|
||||||
return npmFileHash;
|
return npmFileHash;
|
||||||
} else if (pattern.includes('yarn.lock')) {
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
@@ -68,17 +130,20 @@ describe('run', () => {
|
|||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
// utils
|
// exec
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
existsSpy.mockRestore();
|
existsSpy.mockRestore();
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Package manager validation', () => {
|
describe('Package manager validation', () => {
|
||||||
it('Package manager is not provided, skip caching', async () => {
|
it('Package manager is not provided, skip caching', async () => {
|
||||||
inputs['cache'] = '';
|
inputs['cache'] = '';
|
||||||
|
getStateSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
@@ -114,17 +179,17 @@ describe('run', () => {
|
|||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@@ -138,17 +203,17 @@ describe('run', () => {
|
|||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@@ -162,18 +227,22 @@ describe('run', () => {
|
|||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/npm`);
|
getExecOutputSpy.mockImplementationOnce(() => ({
|
||||||
|
stdout: `${commonPath}/npm`,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@@ -184,17 +253,17 @@ describe('run', () => {
|
|||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(setFailedSpy).not.toHaveBeenCalled();
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@@ -203,23 +272,23 @@ describe('run', () => {
|
|||||||
describe('action saves the cache', () => {
|
describe('action saves the cache', () => {
|
||||||
it('saves cache from yarn 1', async () => {
|
it('saves cache from yarn 1', async () => {
|
||||||
inputs['cache'] = 'yarn';
|
inputs['cache'] = 'yarn';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@@ -233,23 +302,23 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from yarn 2', async () => {
|
it('saves cache from yarn 2', async () => {
|
||||||
inputs['cache'] = 'yarn';
|
inputs['cache'] = 'yarn';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
@@ -263,23 +332,23 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from npm', async () => {
|
it('saves cache from npm', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
@@ -293,23 +362,23 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves cache from pnpm', async () => {
|
it('saves cache from pnpm', async () => {
|
||||||
inputs['cache'] = 'pnpm';
|
inputs['cache'] = 'pnpm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? pnpmFileHash
|
? pnpmFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
|
|
||||||
await run();
|
await run();
|
||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
||||||
@@ -323,16 +392,16 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('save with -1 cacheId , should not fail workflow', async () => {
|
it('save with -1 cacheId , should not fail workflow', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
saveCacheSpy.mockImplementation(() => {
|
saveCacheSpy.mockImplementation(() => {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -342,8 +411,10 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache was not saved for the key: ${yarnFileHash}`
|
||||||
|
);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
);
|
);
|
||||||
@@ -356,16 +427,16 @@ describe('run', () => {
|
|||||||
|
|
||||||
it('saves with error from toolkit, should fail workflow', async () => {
|
it('saves with error from toolkit, should fail workflow', async () => {
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
getStateSpy.mockImplementation((key: string) =>
|
getStateSpy.mockImplementation((key: any) =>
|
||||||
key === State.CachePackageManager
|
key === State.CachePackageManager
|
||||||
? inputs['cache']
|
? inputs['cache']
|
||||||
: key === State.CacheMatchedKey
|
: key === State.CacheMatchedKey
|
||||||
? npmFileHash
|
? npmFileHash
|
||||||
: key === State.CachePrimaryKey
|
: key === State.CachePrimaryKey
|
||||||
? yarnFileHash
|
? yarnFileHash
|
||||||
: key === State.CachePaths
|
: key === State.CachePaths
|
||||||
? '["/foo/bar"]'
|
? '["/foo/bar"]'
|
||||||
: 'not expected'
|
: 'not expected'
|
||||||
);
|
);
|
||||||
saveCacheSpy.mockImplementation(() => {
|
saveCacheSpy.mockImplementation(() => {
|
||||||
throw new cache.ValidationError('Validation failed');
|
throw new cache.ValidationError('Validation failed');
|
||||||
@@ -375,7 +446,7 @@ describe('run', () => {
|
|||||||
|
|
||||||
expect(getInputSpy).not.toHaveBeenCalled();
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(debugSpy).toHaveBeenCalledTimes(0);
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
expect(infoSpy).not.toHaveBeenCalledWith(
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
@@ -384,9 +455,4 @@ describe('run', () => {
|
|||||||
expect(setFailedSpy).toHaveBeenCalled();
|
expect(setFailedSpy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
jest.resetAllMocks();
|
|
||||||
jest.clearAllMocks();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
+215
-105
@@ -1,45 +1,133 @@
|
|||||||
import * as core from '@actions/core';
|
|
||||||
import * as cache from '@actions/cache';
|
|
||||||
import path from 'path';
|
|
||||||
import * as utils from '../src/cache-utils';
|
|
||||||
import {
|
import {
|
||||||
PackageManagerInfo,
|
jest,
|
||||||
|
describe,
|
||||||
|
it,
|
||||||
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// Mock @actions modules before importing anything that depends on them
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/glob', () => ({
|
||||||
|
hashFiles: jest.fn(),
|
||||||
|
create: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const glob = await import('@actions/glob');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const utils = await import('../src/cache-utils.js');
|
||||||
|
const cacheUtils = await import('../src/cache-utils.js');
|
||||||
|
// @ts-ignore - test file outside rootDir
|
||||||
|
const {MockGlobber} = await import('./mock/glob-mock.js');
|
||||||
|
|
||||||
|
import type {PackageManagerInfo} from '../src/cache-utils.js';
|
||||||
|
|
||||||
|
const {
|
||||||
isCacheFeatureAvailable,
|
isCacheFeatureAvailable,
|
||||||
supportedPackageManagers,
|
supportedPackageManagers,
|
||||||
getCommandOutput,
|
isGhes,
|
||||||
resetProjectDirectoriesMemoized
|
resetProjectDirectoriesMemoized
|
||||||
} from '../src/cache-utils';
|
} = utils;
|
||||||
import fs from 'fs';
|
|
||||||
import * as cacheUtils from '../src/cache-utils';
|
// Helper: mock exec.getExecOutput to simulate getCommandOutput behavior
|
||||||
import * as glob from '@actions/glob';
|
function mockGetCommandOutput(
|
||||||
import {Globber} from '@actions/glob';
|
spy: jest.Mock,
|
||||||
import {MockGlobber} from './mock/glob-mock';
|
fn: (command: string, cwd?: string) => string
|
||||||
|
) {
|
||||||
|
spy.mockImplementation(async (command: any, _args: any, options: any) => ({
|
||||||
|
stdout: fn(command, options?.cwd),
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockGetCommandOutputOnce(spy: jest.Mock, result: string) {
|
||||||
|
spy.mockImplementationOnce(async () => ({
|
||||||
|
stdout: result,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
describe('cache-utils', () => {
|
describe('cache-utils', () => {
|
||||||
const versionYarn1 = '1.2.3';
|
const versionYarn1 = '1.2.3';
|
||||||
|
|
||||||
let debugSpy: jest.SpyInstance;
|
let debugSpy: jest.Mock;
|
||||||
let getCommandOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
let isFeatureAvailable: jest.SpyInstance;
|
let isFeatureAvailable: jest.Mock;
|
||||||
let info: jest.SpyInstance;
|
let info: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let fsRealPathSyncSpy: jest.SpyInstance;
|
let fsRealPathSyncSpy: jest.SpiedFunction<typeof fs.realpathSync>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
console.log('::stop-commands::stoptoken');
|
console.log('::stop-commands::stoptoken');
|
||||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
debugSpy = jest.spyOn(core, 'debug');
|
|
||||||
debugSpy.mockImplementation(msg => {});
|
|
||||||
|
|
||||||
info = jest.spyOn(core, 'info');
|
debugSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
debugSpy.mockImplementation((_msg: any) => {});
|
||||||
|
|
||||||
isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
info = core.info as jest.Mock;
|
||||||
|
warningSpy = core.warning as jest.Mock;
|
||||||
|
|
||||||
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
isFeatureAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
||||||
fsRealPathSyncSpy.mockImplementation(dirName => {
|
fsRealPathSyncSpy.mockImplementation((dirName: any) => {
|
||||||
return dirName;
|
return dirName;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -47,7 +135,6 @@ describe('cache-utils', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.resetAllMocks();
|
jest.resetAllMocks();
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
//jest.restoreAllMocks();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@@ -64,7 +151,7 @@ describe('cache-utils', () => {
|
|||||||
['yarn2', null],
|
['yarn2', null],
|
||||||
['npm7', null]
|
['npm7', null]
|
||||||
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => versionYarn1);
|
mockGetCommandOutputOnce(getExecOutputSpy, versionYarn1);
|
||||||
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
@@ -92,7 +179,6 @@ describe('cache-utils', () => {
|
|||||||
|
|
||||||
it('isCacheFeatureAvailable for GHES is available', () => {
|
it('isCacheFeatureAvailable for GHES is available', () => {
|
||||||
isFeatureAvailable.mockImplementation(() => true);
|
isFeatureAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,24 +189,22 @@ describe('cache-utils', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('getCacheDirectoriesPaths', () => {
|
describe('getCacheDirectoriesPaths', () => {
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let lstatSpy: jest.SpyInstance;
|
let lstatSpy: jest.SpiedFunction<typeof fs.lstatSync>;
|
||||||
let globCreateSpy: jest.SpyInstance;
|
let globCreateSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
||||||
lstatSpy.mockImplementation(arg => ({
|
lstatSpy.mockImplementation(
|
||||||
isDirectory: () => true
|
(_arg: any) => ({isDirectory: () => true}) as any
|
||||||
}));
|
);
|
||||||
|
|
||||||
globCreateSpy = jest.spyOn(glob, 'create');
|
globCreateSpy = glob.create as jest.Mock;
|
||||||
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
globCreateSpy.mockImplementation(
|
MockGlobber.create(['/foo', '/bar'])
|
||||||
(pattern: string): Promise<Globber> =>
|
|
||||||
MockGlobber.create(['/foo', '/bar'])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
resetProjectDirectoriesMemoized();
|
resetProjectDirectoriesMemoized();
|
||||||
@@ -129,7 +213,6 @@ describe('cache-utils', () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
existsSpy.mockRestore();
|
existsSpy.mockRestore();
|
||||||
lstatSpy.mockRestore();
|
lstatSpy.mockRestore();
|
||||||
globCreateSpy.mockRestore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
@@ -142,22 +225,18 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should return one dir for non yarn',
|
'getCacheDirectoriesPaths should return one dir for non yarn',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
cacheDependency
|
cacheDependency
|
||||||
);
|
);
|
||||||
expect(dirs).toEqual(['foo']);
|
expect(dirs).toEqual(['foo']);
|
||||||
// to do not call for a version
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(1);
|
||||||
// call once for get cache folder
|
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
||||||
getCommandOutputSpy.mockImplementation(() => 'foo');
|
mockGetCommandOutput(getExecOutputSpy, () => 'foo');
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
''
|
''
|
||||||
@@ -178,15 +257,12 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
// return empty string to indicate getCacheFolderPath failed
|
|
||||||
// --version still works
|
|
||||||
command.includes('version') ? '1.' : ''
|
command.includes('version') ? '1.' : ''
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
||||||
).rejects.toThrow(); //'Could not get cache folder path for /dir');
|
).rejects.toThrow();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -196,10 +272,9 @@ describe('cache-utils', () => {
|
|||||||
])(
|
])(
|
||||||
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
||||||
async (packageManagerInfo, cacheDependency) => {
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
lstatSpy.mockImplementation(arg => ({
|
lstatSpy.mockImplementation(
|
||||||
isDirectory: () => false
|
(_arg: any) => ({isDirectory: () => false}) as any
|
||||||
}));
|
);
|
||||||
|
|
||||||
await cacheUtils.getCacheDirectories(
|
await cacheUtils.getCacheDirectories(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
cacheDependency
|
cacheDependency
|
||||||
@@ -214,9 +289,8 @@ describe('cache-utils', () => {
|
|||||||
it.each(['1.1.1', '2.2.2'])(
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
||||||
async version => {
|
async version => {
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => version);
|
mockGetCommandOutputOnce(getExecOutputSpy, version);
|
||||||
getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`);
|
mockGetCommandOutputOnce(getExecOutputSpy, `foo${version}`);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
''
|
''
|
||||||
@@ -229,14 +303,12 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
||||||
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
@@ -249,18 +321,16 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir1/file'
|
||||||
'/tmp/dir1/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
@@ -273,55 +343,59 @@ describe('cache-utils', () => {
|
|||||||
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
||||||
async version => {
|
async version => {
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version')
|
command.includes('version')
|
||||||
? version
|
? version
|
||||||
: `file_${version}_${dirNo++ % 2}`
|
: `file_${version}_${dirNo++ % 2}`
|
||||||
);
|
);
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir3/file'
|
||||||
'/tmp/dir3/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
supportedPackageManagers.yarn,
|
supportedPackageManagers.yarn,
|
||||||
'/tmp/**/file'
|
'/tmp/**/file'
|
||||||
);
|
);
|
||||||
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledTimes(6);
|
expect(getExecOutputSpy).toHaveBeenCalledTimes(6);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir1'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir2'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
'yarn --version',
|
'yarn --version',
|
||||||
'/tmp/dir3'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir1'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir1'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir2'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir2'})
|
||||||
);
|
);
|
||||||
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
version.startsWith('1.')
|
version.startsWith('1.')
|
||||||
? 'yarn cache dir'
|
? 'yarn cache dir'
|
||||||
: 'yarn config get cacheFolder',
|
: 'yarn config get cacheFolder',
|
||||||
'/tmp/dir3'
|
undefined,
|
||||||
|
expect.objectContaining({cwd: '/tmp/dir3'})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -329,22 +403,20 @@ describe('cache-utils', () => {
|
|||||||
it.each(['1.1.1', '2.2.2'])(
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
||||||
async version => {
|
async version => {
|
||||||
// simulate wrong indents
|
|
||||||
const cacheDependencyPath = `/tmp/dir1/file
|
const cacheDependencyPath = `/tmp/dir1/file
|
||||||
/tmp/dir2/file
|
/tmp/dir2/file
|
||||||
/tmp/**/file
|
/tmp/**/file
|
||||||
`;
|
`;
|
||||||
globCreateSpy.mockImplementation(
|
globCreateSpy.mockImplementation((_pattern: any) =>
|
||||||
(pattern: string): Promise<Globber> =>
|
MockGlobber.create([
|
||||||
MockGlobber.create([
|
'/tmp/dir1/file',
|
||||||
'/tmp/dir1/file',
|
'/tmp/dir2/file',
|
||||||
'/tmp/dir2/file',
|
'/tmp/dir3/file',
|
||||||
'/tmp/dir3/file',
|
'/tmp/dir4/file'
|
||||||
'/tmp/dir4/file'
|
])
|
||||||
])
|
|
||||||
);
|
);
|
||||||
let dirNo = 1;
|
let dirNo = 1;
|
||||||
getCommandOutputSpy.mockImplementation((command: string) =>
|
mockGetCommandOutput(getExecOutputSpy, (command: string) =>
|
||||||
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
);
|
);
|
||||||
const dirs = await cacheUtils.getCacheDirectories(
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
@@ -361,3 +433,41 @@ describe('cache-utils', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isGhes', () => {
|
||||||
|
const pristineEnv = process.env;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
process.env = {...pristineEnv};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
process.env = pristineEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when the GITHUB_SERVER_URL environment variable is not defined', () => {
|
||||||
|
delete process.env['GITHUB_SERVER_URL'];
|
||||||
|
expect(isGhes()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when the GITHUB_SERVER_URL environment variable is set to github.com', () => {
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://github.com';
|
||||||
|
expect(isGhes()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when the GITHUB_SERVER_URL environment variable is set to a GitHub Enterprise Cloud-style URL', () => {
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://contoso.ghe.com';
|
||||||
|
expect(isGhes()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false when the GITHUB_SERVER_URL environment variable has a .localhost suffix', () => {
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://mock-github.localhost';
|
||||||
|
expect(isGhes()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns true when the GITHUB_SERVER_URL environment variable is set to some other URL', () => {
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://src.onpremise.fabrikam.com';
|
||||||
|
expect(isGhes()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,51 +1,162 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real authutil before mocking
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
import type {IToolRelease} from '@actions/tool-cache';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let getDistSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getDistSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let readFileSyncSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let readFileSyncSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let parseNodeVersionSpy: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let parseNodeVersionSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@@ -53,8 +164,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@@ -65,34 +176,35 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
// gets
|
// gets
|
||||||
getManifestSpy.mockImplementation(
|
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||||
() => <tc.IToolRelease[]>nodeTestManifest
|
|
||||||
);
|
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@@ -109,28 +221,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -178,7 +280,7 @@ describe('setup-node', () => {
|
|||||||
inputs['node-version'] = '20-v8-canary';
|
inputs['node-version'] = '20-v8-canary';
|
||||||
os['arch'] = 'x64';
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize(
|
const toolPath = path.normalize(
|
||||||
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
||||||
@@ -193,7 +295,7 @@ describe('setup-node', () => {
|
|||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@@ -213,7 +315,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@@ -227,7 +329,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '11.15.0';
|
const versionSpec = '11.15.0';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -250,7 +351,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@@ -269,8 +370,8 @@ describe('setup-node', () => {
|
|||||||
]);
|
]);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -283,7 +384,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '19.0.0-v8-canary';
|
const versionSpec = '19.0.0-v8-canary';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
@@ -297,7 +397,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@@ -321,10 +421,8 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
const expectedUrl = `https://nodejs.org/download/v8-canary/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
const expectedUrl = `https://nodejs.org/download/v8-canary/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
||||||
@@ -396,9 +494,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -431,9 +527,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -493,9 +587,69 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'20.0.0-v8-canary',
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'https://my_mirror.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'20-v8-canary',
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'https://my_mirror.org/download/v8-canary/v20.0.0-v8-canary20221103f7e2421e91/node-v20.0.0-v8-canary20221103f7e2421e91-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'19.0.0-v8-canary',
|
||||||
|
'19.0.0-v8-canary202210187d6960f23f',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'https://my_mirror.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'19-v8-canary',
|
||||||
|
'19.0.0-v8-canary202210187d6960f23f',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'https://my_mirror.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
|
||||||
|
]
|
||||||
|
])(
|
||||||
|
'get %s version from dist if check-latest is true',
|
||||||
|
async (input, expectedVersion, foundVersion, expectedUrl) => {
|
||||||
|
const foundToolPath = path.normalize(`/cache/node/${foundVersion}/x64`);
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
inputs['mirror'] = 'https://my_mirror.org';
|
||||||
|
inputs['mirror-token'] = 'faketoken';
|
||||||
|
|
||||||
|
findSpy.mockReturnValue(foundToolPath);
|
||||||
|
findAllVersionsSpy.mockReturnValue([
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
|
||||||
);
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -505,7 +659,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = 'v20-v8-canary';
|
const versionSpec = 'v20-v8-canary';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
os.platform = 'linux';
|
os.platform = 'linux';
|
||||||
@@ -515,13 +668,11 @@ describe('setup-node', () => {
|
|||||||
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
||||||
|
|
||||||
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
||||||
findSpy.mockImplementation(version => toolPath);
|
findSpy.mockImplementation((version: any) => toolPath);
|
||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(`${toolPath}${path.sep}bin`);
|
||||||
`::add-path::${toolPath}${path.sep}bin${osm.EOL}`
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(dlSpy).not.toHaveBeenCalled();
|
expect(dlSpy).not.toHaveBeenCalled();
|
||||||
expect(exSpy).not.toHaveBeenCalled();
|
expect(exSpy).not.toHaveBeenCalled();
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v20
|
v24
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
nodejs 20.0.0
|
nodejs 24.0.0
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
node 20.0.0
|
node 24.0.0
|
||||||
|
|||||||
Generated
+109
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
"name": "npm-fixture",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "npm-fixture",
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "^1.3.8",
|
||||||
|
"array-flatten": "^3.0.0",
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-disposition": "^0.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-types": "~2.1.34",
|
||||||
|
"negotiator": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts/node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/accepts/node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/array-flatten": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/bytes": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/content-disposition": {
|
||||||
|
"version": "0.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "5.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/negotiator": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "npm-fixture",
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "^1.3.8",
|
||||||
|
"array-flatten": "^3.0.0",
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-disposition": "^0.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"engines": {
|
||||||
|
"node": "^19"
|
||||||
|
},
|
||||||
|
"devEngines": {
|
||||||
|
"runtime": {
|
||||||
|
"name": "node",
|
||||||
|
"version": "^26"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
-395
@@ -1,395 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "test",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"requires": true,
|
|
||||||
"dependencies": {
|
|
||||||
"accepts": {
|
|
||||||
"version": "1.3.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
|
||||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
|
||||||
"requires": {
|
|
||||||
"mime-types": "~2.1.24",
|
|
||||||
"negotiator": "0.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"array-flatten": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
|
||||||
},
|
|
||||||
"body-parser": {
|
|
||||||
"version": "1.19.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
|
|
||||||
"integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.1.0",
|
|
||||||
"content-type": "~1.0.4",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"http-errors": "1.7.2",
|
|
||||||
"iconv-lite": "0.4.24",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"qs": "6.7.0",
|
|
||||||
"raw-body": "2.4.0",
|
|
||||||
"type-is": "~1.6.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bytes": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
|
|
||||||
"integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
|
|
||||||
},
|
|
||||||
"content-disposition": {
|
|
||||||
"version": "0.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
|
||||||
"integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "5.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content-type": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
|
||||||
},
|
|
||||||
"cookie": {
|
|
||||||
"version": "0.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
|
|
||||||
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
|
|
||||||
},
|
|
||||||
"cookie-signature": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
|
||||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"depd": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
|
|
||||||
},
|
|
||||||
"destroy": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
|
||||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
|
||||||
},
|
|
||||||
"ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
|
||||||
},
|
|
||||||
"encodeurl": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
|
|
||||||
},
|
|
||||||
"escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
|
||||||
},
|
|
||||||
"etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
|
||||||
},
|
|
||||||
"express": {
|
|
||||||
"version": "4.17.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
|
|
||||||
"integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
|
|
||||||
"requires": {
|
|
||||||
"accepts": "~1.3.7",
|
|
||||||
"array-flatten": "1.1.1",
|
|
||||||
"body-parser": "1.19.0",
|
|
||||||
"content-disposition": "0.5.3",
|
|
||||||
"content-type": "~1.0.4",
|
|
||||||
"cookie": "0.4.0",
|
|
||||||
"cookie-signature": "1.0.6",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"finalhandler": "~1.1.2",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"merge-descriptors": "1.0.1",
|
|
||||||
"methods": "~1.1.2",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"path-to-regexp": "0.1.7",
|
|
||||||
"proxy-addr": "~2.0.5",
|
|
||||||
"qs": "6.7.0",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"safe-buffer": "5.1.2",
|
|
||||||
"send": "0.17.1",
|
|
||||||
"serve-static": "1.14.1",
|
|
||||||
"setprototypeof": "1.1.1",
|
|
||||||
"statuses": "~1.5.0",
|
|
||||||
"type-is": "~1.6.18",
|
|
||||||
"utils-merge": "1.0.1",
|
|
||||||
"vary": "~1.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"finalhandler": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
|
|
||||||
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"statuses": "~1.5.0",
|
|
||||||
"unpipe": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forwarded": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
|
||||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
|
||||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
|
||||||
},
|
|
||||||
"http-errors": {
|
|
||||||
"version": "1.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
|
|
||||||
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
|
|
||||||
"requires": {
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"setprototypeof": "1.1.1",
|
|
||||||
"statuses": ">= 1.5.0 < 2",
|
|
||||||
"toidentifier": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
|
||||||
"version": "0.4.24",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
|
||||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
|
||||||
"requires": {
|
|
||||||
"safer-buffer": ">= 2.1.2 < 3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inherits": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
|
||||||
},
|
|
||||||
"ipaddr.js": {
|
|
||||||
"version": "1.9.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
|
||||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
|
|
||||||
},
|
|
||||||
"lru-cache": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
|
||||||
"requires": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"media-typer": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
|
||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
|
||||||
},
|
|
||||||
"merge-descriptors": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
|
||||||
},
|
|
||||||
"methods": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
|
||||||
},
|
|
||||||
"mime": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
|
||||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
|
|
||||||
},
|
|
||||||
"mime-db": {
|
|
||||||
"version": "1.47.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz",
|
|
||||||
"integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw=="
|
|
||||||
},
|
|
||||||
"mime-types": {
|
|
||||||
"version": "2.1.30",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz",
|
|
||||||
"integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==",
|
|
||||||
"requires": {
|
|
||||||
"mime-db": "1.47.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
|
||||||
},
|
|
||||||
"negotiator": {
|
|
||||||
"version": "0.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
|
||||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
|
||||||
},
|
|
||||||
"on-finished": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
|
||||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
|
||||||
"requires": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parseurl": {
|
|
||||||
"version": "1.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
|
||||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
|
|
||||||
},
|
|
||||||
"path-to-regexp": {
|
|
||||||
"version": "0.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
|
||||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
|
||||||
},
|
|
||||||
"proxy-addr": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
|
|
||||||
"requires": {
|
|
||||||
"forwarded": "~0.1.2",
|
|
||||||
"ipaddr.js": "1.9.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"qs": {
|
|
||||||
"version": "6.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
|
||||||
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
|
|
||||||
},
|
|
||||||
"range-parser": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
|
|
||||||
},
|
|
||||||
"raw-body": {
|
|
||||||
"version": "2.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
|
|
||||||
"integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.1.0",
|
|
||||||
"http-errors": "1.7.2",
|
|
||||||
"iconv-lite": "0.4.24",
|
|
||||||
"unpipe": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"safe-buffer": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
|
||||||
},
|
|
||||||
"safer-buffer": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
|
||||||
},
|
|
||||||
"semver": {
|
|
||||||
"version": "7.3.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
|
||||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
|
||||||
"requires": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"version": "0.17.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
|
|
||||||
"integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "~1.1.2",
|
|
||||||
"destroy": "~1.0.4",
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"etag": "~1.8.1",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"http-errors": "~1.7.2",
|
|
||||||
"mime": "1.6.0",
|
|
||||||
"ms": "2.1.1",
|
|
||||||
"on-finished": "~2.3.0",
|
|
||||||
"range-parser": "~1.2.1",
|
|
||||||
"statuses": "~1.5.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ms": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve-static": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
|
|
||||||
"requires": {
|
|
||||||
"encodeurl": "~1.0.2",
|
|
||||||
"escape-html": "~1.0.3",
|
|
||||||
"parseurl": "~1.3.3",
|
|
||||||
"send": "0.17.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"setprototypeof": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
|
|
||||||
},
|
|
||||||
"statuses": {
|
|
||||||
"version": "1.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
|
||||||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
|
|
||||||
},
|
|
||||||
"toidentifier": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
|
||||||
},
|
|
||||||
"type-is": {
|
|
||||||
"version": "1.6.18",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
|
||||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
|
||||||
"requires": {
|
|
||||||
"media-typer": "0.3.0",
|
|
||||||
"mime-types": "~2.1.24"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unpipe": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
|
||||||
},
|
|
||||||
"utils-merge": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
|
||||||
},
|
|
||||||
"vary": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
|
||||||
},
|
|
||||||
"yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
"node": "^14.0.0"
|
"node": "^14.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "20.0.0"
|
"node": "24.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.0.0"
|
"node": "^24.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-455
@@ -1,455 +0,0 @@
|
|||||||
lockfileVersion: 5.3
|
|
||||||
|
|
||||||
specifiers:
|
|
||||||
express: ^4.17.1
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
express: 4.17.1
|
|
||||||
|
|
||||||
packages:
|
|
||||||
/accepts/1.3.7:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-types: 2.1.31
|
|
||||||
negotiator: 0.6.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/array-flatten/1.1.1:
|
|
||||||
resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/body-parser/1.19.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.0
|
|
||||||
content-type: 1.0.4
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 1.1.2
|
|
||||||
http-errors: 1.7.2
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
on-finished: 2.3.0
|
|
||||||
qs: 6.7.0
|
|
||||||
raw-body: 2.4.0
|
|
||||||
type-is: 1.6.18
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/bytes/3.1.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/content-disposition/0.5.3:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
safe-buffer: 5.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/content-type/1.0.4:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie-signature/1.0.6:
|
|
||||||
resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/cookie/0.4.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/debug/2.6.9:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
|
||||||
}
|
|
||||||
dependencies:
|
|
||||||
ms: 2.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/depd/1.1.2:
|
|
||||||
resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/destroy/1.0.4:
|
|
||||||
resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ee-first/1.1.1:
|
|
||||||
resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/encodeurl/1.0.2:
|
|
||||||
resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/escape-html/1.0.3:
|
|
||||||
resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/etag/1.8.1:
|
|
||||||
resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/express/4.17.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.10.0'}
|
|
||||||
dependencies:
|
|
||||||
accepts: 1.3.7
|
|
||||||
array-flatten: 1.1.1
|
|
||||||
body-parser: 1.19.0
|
|
||||||
content-disposition: 0.5.3
|
|
||||||
content-type: 1.0.4
|
|
||||||
cookie: 0.4.0
|
|
||||||
cookie-signature: 1.0.6
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 1.1.2
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
etag: 1.8.1
|
|
||||||
finalhandler: 1.1.2
|
|
||||||
fresh: 0.5.2
|
|
||||||
merge-descriptors: 1.0.1
|
|
||||||
methods: 1.1.2
|
|
||||||
on-finished: 2.3.0
|
|
||||||
parseurl: 1.3.3
|
|
||||||
path-to-regexp: 0.1.7
|
|
||||||
proxy-addr: 2.0.7
|
|
||||||
qs: 6.7.0
|
|
||||||
range-parser: 1.2.1
|
|
||||||
safe-buffer: 5.1.2
|
|
||||||
send: 0.17.1
|
|
||||||
serve-static: 1.14.1
|
|
||||||
setprototypeof: 1.1.1
|
|
||||||
statuses: 1.5.0
|
|
||||||
type-is: 1.6.18
|
|
||||||
utils-merge: 1.0.1
|
|
||||||
vary: 1.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/finalhandler/1.1.2:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
debug: 2.6.9
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
on-finished: 2.3.0
|
|
||||||
parseurl: 1.3.3
|
|
||||||
statuses: 1.5.0
|
|
||||||
unpipe: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/forwarded/0.2.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/fresh/0.5.2:
|
|
||||||
resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/http-errors/1.7.2:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
depd: 1.1.2
|
|
||||||
inherits: 2.0.3
|
|
||||||
setprototypeof: 1.1.1
|
|
||||||
statuses: 1.5.0
|
|
||||||
toidentifier: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/http-errors/1.7.3:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
depd: 1.1.2
|
|
||||||
inherits: 2.0.4
|
|
||||||
setprototypeof: 1.1.1
|
|
||||||
statuses: 1.5.0
|
|
||||||
toidentifier: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/iconv-lite/0.4.24:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
|
||||||
}
|
|
||||||
engines: {node: '>=0.10.0'}
|
|
||||||
dependencies:
|
|
||||||
safer-buffer: 2.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/inherits/2.0.3:
|
|
||||||
resolution: {integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/inherits/2.0.4:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
|
||||||
}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ipaddr.js/1.9.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/media-typer/0.3.0:
|
|
||||||
resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/merge-descriptors/1.0.1:
|
|
||||||
resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/methods/1.1.2:
|
|
||||||
resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-db/1.48.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime-types/2.1.31:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
mime-db: 1.48.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/mime/1.6.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
|
||||||
}
|
|
||||||
engines: {node: '>=4'}
|
|
||||||
hasBin: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms/2.0.0:
|
|
||||||
resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ms/2.1.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
|
||||||
}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/negotiator/0.6.2:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/on-finished/2.3.0:
|
|
||||||
resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
ee-first: 1.1.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/parseurl/1.3.3:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/path-to-regexp/0.1.7:
|
|
||||||
resolution: {integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/proxy-addr/2.0.7:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dependencies:
|
|
||||||
forwarded: 0.2.0
|
|
||||||
ipaddr.js: 1.9.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/qs/6.7.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
|
||||||
}
|
|
||||||
engines: {node: '>=0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/range-parser/1.2.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/raw-body/2.4.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dependencies:
|
|
||||||
bytes: 3.1.0
|
|
||||||
http-errors: 1.7.2
|
|
||||||
iconv-lite: 0.4.24
|
|
||||||
unpipe: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/safe-buffer/5.1.2:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
|
||||||
}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/safer-buffer/2.1.2:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
|
||||||
}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/send/0.17.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
debug: 2.6.9
|
|
||||||
depd: 1.1.2
|
|
||||||
destroy: 1.0.4
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
etag: 1.8.1
|
|
||||||
fresh: 0.5.2
|
|
||||||
http-errors: 1.7.3
|
|
||||||
mime: 1.6.0
|
|
||||||
ms: 2.1.1
|
|
||||||
on-finished: 2.3.0
|
|
||||||
range-parser: 1.2.1
|
|
||||||
statuses: 1.5.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/serve-static/1.14.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.8.0'}
|
|
||||||
dependencies:
|
|
||||||
encodeurl: 1.0.2
|
|
||||||
escape-html: 1.0.3
|
|
||||||
parseurl: 1.3.3
|
|
||||||
send: 0.17.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/setprototypeof/1.1.1:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
|
|
||||||
}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/statuses/1.5.0:
|
|
||||||
resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/toidentifier/1.0.0:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
|
||||||
}
|
|
||||||
engines: {node: '>=0.6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/type-is/1.6.18:
|
|
||||||
resolution:
|
|
||||||
{
|
|
||||||
integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
|
|
||||||
}
|
|
||||||
engines: {node: '>= 0.6'}
|
|
||||||
dependencies:
|
|
||||||
media-typer: 0.3.0
|
|
||||||
mime-types: 2.1.31
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/unpipe/1.0.0:
|
|
||||||
resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/utils-merge/1.0.1:
|
|
||||||
resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=}
|
|
||||||
engines: {node: '>= 0.4.0'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/vary/1.1.2:
|
|
||||||
resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=}
|
|
||||||
engines: {node: '>= 0.8'}
|
|
||||||
dev: false
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "pnpm-fixture",
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "^1.3.8",
|
||||||
|
"array-flatten": "^3.0.0",
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-disposition": "^0.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+100
@@ -0,0 +1,100 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
.:
|
||||||
|
dependencies:
|
||||||
|
accepts:
|
||||||
|
specifier: ^1.3.8
|
||||||
|
version: 1.3.8
|
||||||
|
array-flatten:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
|
bytes:
|
||||||
|
specifier: ^3.1.2
|
||||||
|
version: 3.1.2
|
||||||
|
content-disposition:
|
||||||
|
specifier: ^0.5.4
|
||||||
|
version: 0.5.4
|
||||||
|
|
||||||
|
packages:
|
||||||
|
accepts@1.3.8:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
array-flatten@3.0.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes@3.1.2:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
|
content-disposition@0.5.4:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
negotiator@0.6.3:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||||
|
}
|
||||||
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
safe-buffer@5.2.1:
|
||||||
|
resolution:
|
||||||
|
{
|
||||||
|
integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshots:
|
||||||
|
accepts@1.3.8:
|
||||||
|
dependencies:
|
||||||
|
mime-types: 2.1.35
|
||||||
|
negotiator: 0.6.3
|
||||||
|
|
||||||
|
array-flatten@3.0.0: {}
|
||||||
|
|
||||||
|
bytes@3.1.2: {}
|
||||||
|
|
||||||
|
content-disposition@0.5.4:
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: 5.2.1
|
||||||
|
|
||||||
|
mime-db@1.52.0: {}
|
||||||
|
|
||||||
|
mime-types@2.1.35:
|
||||||
|
dependencies:
|
||||||
|
mime-db: 1.52.0
|
||||||
|
|
||||||
|
negotiator@0.6.3: {}
|
||||||
|
|
||||||
|
safe-buffer@5.2.1: {}
|
||||||
@@ -1,368 +0,0 @@
|
|||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
||||||
# yarn lockfile v1
|
|
||||||
|
|
||||||
|
|
||||||
accepts@~1.3.7:
|
|
||||||
version "1.3.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
|
|
||||||
integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
|
|
||||||
dependencies:
|
|
||||||
mime-types "~2.1.24"
|
|
||||||
negotiator "0.6.2"
|
|
||||||
|
|
||||||
array-flatten@1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
|
||||||
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
|
|
||||||
|
|
||||||
body-parser@1.19.0:
|
|
||||||
version "1.19.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
|
|
||||||
integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
|
|
||||||
dependencies:
|
|
||||||
bytes "3.1.0"
|
|
||||||
content-type "~1.0.4"
|
|
||||||
debug "2.6.9"
|
|
||||||
depd "~1.1.2"
|
|
||||||
http-errors "1.7.2"
|
|
||||||
iconv-lite "0.4.24"
|
|
||||||
on-finished "~2.3.0"
|
|
||||||
qs "6.7.0"
|
|
||||||
raw-body "2.4.0"
|
|
||||||
type-is "~1.6.17"
|
|
||||||
|
|
||||||
bytes@3.1.0:
|
|
||||||
version "3.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
|
|
||||||
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
|
|
||||||
|
|
||||||
content-disposition@0.5.3:
|
|
||||||
version "0.5.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
|
|
||||||
integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
|
|
||||||
dependencies:
|
|
||||||
safe-buffer "5.1.2"
|
|
||||||
|
|
||||||
content-type@~1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
|
||||||
|
|
||||||
cookie-signature@1.0.6:
|
|
||||||
version "1.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
|
||||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
|
||||||
|
|
||||||
cookie@0.4.0:
|
|
||||||
version "0.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
|
|
||||||
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
|
|
||||||
|
|
||||||
debug@2.6.9:
|
|
||||||
version "2.6.9"
|
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
|
||||||
dependencies:
|
|
||||||
ms "2.0.0"
|
|
||||||
|
|
||||||
depd@~1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
|
||||||
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
|
|
||||||
|
|
||||||
destroy@~1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
|
||||||
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
|
||||||
|
|
||||||
ee-first@1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
|
||||||
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
|
||||||
|
|
||||||
encodeurl@~1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
|
||||||
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
|
||||||
|
|
||||||
escape-html@~1.0.3:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
|
||||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
|
||||||
|
|
||||||
etag@~1.8.1:
|
|
||||||
version "1.8.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
|
||||||
|
|
||||||
express@^4.17.1:
|
|
||||||
version "4.17.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
|
|
||||||
integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
|
|
||||||
dependencies:
|
|
||||||
accepts "~1.3.7"
|
|
||||||
array-flatten "1.1.1"
|
|
||||||
body-parser "1.19.0"
|
|
||||||
content-disposition "0.5.3"
|
|
||||||
content-type "~1.0.4"
|
|
||||||
cookie "0.4.0"
|
|
||||||
cookie-signature "1.0.6"
|
|
||||||
debug "2.6.9"
|
|
||||||
depd "~1.1.2"
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
etag "~1.8.1"
|
|
||||||
finalhandler "~1.1.2"
|
|
||||||
fresh "0.5.2"
|
|
||||||
merge-descriptors "1.0.1"
|
|
||||||
methods "~1.1.2"
|
|
||||||
on-finished "~2.3.0"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
path-to-regexp "0.1.7"
|
|
||||||
proxy-addr "~2.0.5"
|
|
||||||
qs "6.7.0"
|
|
||||||
range-parser "~1.2.1"
|
|
||||||
safe-buffer "5.1.2"
|
|
||||||
send "0.17.1"
|
|
||||||
serve-static "1.14.1"
|
|
||||||
setprototypeof "1.1.1"
|
|
||||||
statuses "~1.5.0"
|
|
||||||
type-is "~1.6.18"
|
|
||||||
utils-merge "1.0.1"
|
|
||||||
vary "~1.1.2"
|
|
||||||
|
|
||||||
finalhandler@~1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
|
|
||||||
integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
|
|
||||||
dependencies:
|
|
||||||
debug "2.6.9"
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
on-finished "~2.3.0"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
statuses "~1.5.0"
|
|
||||||
unpipe "~1.0.0"
|
|
||||||
|
|
||||||
forwarded@~0.1.2:
|
|
||||||
version "0.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
|
||||||
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
|
|
||||||
|
|
||||||
fresh@0.5.2:
|
|
||||||
version "0.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
|
||||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
|
||||||
|
|
||||||
http-errors@1.7.2:
|
|
||||||
version "1.7.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
|
|
||||||
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
|
|
||||||
dependencies:
|
|
||||||
depd "~1.1.2"
|
|
||||||
inherits "2.0.3"
|
|
||||||
setprototypeof "1.1.1"
|
|
||||||
statuses ">= 1.5.0 < 2"
|
|
||||||
toidentifier "1.0.0"
|
|
||||||
|
|
||||||
http-errors@~1.7.2:
|
|
||||||
version "1.7.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
|
||||||
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
|
|
||||||
dependencies:
|
|
||||||
depd "~1.1.2"
|
|
||||||
inherits "2.0.4"
|
|
||||||
setprototypeof "1.1.1"
|
|
||||||
statuses ">= 1.5.0 < 2"
|
|
||||||
toidentifier "1.0.0"
|
|
||||||
|
|
||||||
iconv-lite@0.4.24:
|
|
||||||
version "0.4.24"
|
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
|
||||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
|
||||||
dependencies:
|
|
||||||
safer-buffer ">= 2.1.2 < 3"
|
|
||||||
|
|
||||||
inherits@2.0.3:
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
|
||||||
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
|
|
||||||
|
|
||||||
inherits@2.0.4:
|
|
||||||
version "2.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
|
||||||
|
|
||||||
ipaddr.js@1.9.1:
|
|
||||||
version "1.9.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
|
||||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
|
||||||
|
|
||||||
media-typer@0.3.0:
|
|
||||||
version "0.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
|
||||||
|
|
||||||
merge-descriptors@1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
|
||||||
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
|
||||||
|
|
||||||
methods@~1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
|
||||||
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
|
|
||||||
|
|
||||||
mime-db@1.47.0:
|
|
||||||
version "1.47.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
|
|
||||||
integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
|
|
||||||
|
|
||||||
mime-types@~2.1.24:
|
|
||||||
version "2.1.30"
|
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
|
|
||||||
integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
|
|
||||||
dependencies:
|
|
||||||
mime-db "1.47.0"
|
|
||||||
|
|
||||||
mime@1.6.0:
|
|
||||||
version "1.6.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
|
||||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
|
||||||
|
|
||||||
ms@2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
|
||||||
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
|
||||||
|
|
||||||
ms@2.1.1:
|
|
||||||
version "2.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
|
||||||
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
|
||||||
|
|
||||||
negotiator@0.6.2:
|
|
||||||
version "0.6.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
|
||||||
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
|
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
|
||||||
version "2.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
|
||||||
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
|
|
||||||
dependencies:
|
|
||||||
ee-first "1.1.1"
|
|
||||||
|
|
||||||
parseurl@~1.3.3:
|
|
||||||
version "1.3.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
|
||||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
|
||||||
|
|
||||||
path-to-regexp@0.1.7:
|
|
||||||
version "0.1.7"
|
|
||||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
|
||||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
|
||||||
|
|
||||||
proxy-addr@~2.0.5:
|
|
||||||
version "2.0.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
|
|
||||||
integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
|
|
||||||
dependencies:
|
|
||||||
forwarded "~0.1.2"
|
|
||||||
ipaddr.js "1.9.1"
|
|
||||||
|
|
||||||
qs@6.7.0:
|
|
||||||
version "6.7.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
|
||||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
|
||||||
|
|
||||||
range-parser@~1.2.1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
|
||||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
|
||||||
|
|
||||||
raw-body@2.4.0:
|
|
||||||
version "2.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
|
|
||||||
integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
|
|
||||||
dependencies:
|
|
||||||
bytes "3.1.0"
|
|
||||||
http-errors "1.7.2"
|
|
||||||
iconv-lite "0.4.24"
|
|
||||||
unpipe "1.0.0"
|
|
||||||
|
|
||||||
safe-buffer@5.1.2:
|
|
||||||
version "5.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
|
||||||
|
|
||||||
"safer-buffer@>= 2.1.2 < 3":
|
|
||||||
version "2.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
|
||||||
|
|
||||||
send@0.17.1:
|
|
||||||
version "0.17.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
|
||||||
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
|
|
||||||
dependencies:
|
|
||||||
debug "2.6.9"
|
|
||||||
depd "~1.1.2"
|
|
||||||
destroy "~1.0.4"
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
etag "~1.8.1"
|
|
||||||
fresh "0.5.2"
|
|
||||||
http-errors "~1.7.2"
|
|
||||||
mime "1.6.0"
|
|
||||||
ms "2.1.1"
|
|
||||||
on-finished "~2.3.0"
|
|
||||||
range-parser "~1.2.1"
|
|
||||||
statuses "~1.5.0"
|
|
||||||
|
|
||||||
serve-static@1.14.1:
|
|
||||||
version "1.14.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
|
||||||
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
|
|
||||||
dependencies:
|
|
||||||
encodeurl "~1.0.2"
|
|
||||||
escape-html "~1.0.3"
|
|
||||||
parseurl "~1.3.3"
|
|
||||||
send "0.17.1"
|
|
||||||
|
|
||||||
setprototypeof@1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
|
||||||
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
|
|
||||||
|
|
||||||
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
|
||||||
version "1.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
|
||||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
|
||||||
|
|
||||||
toidentifier@1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
|
||||||
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
|
||||||
|
|
||||||
type-is@~1.6.17, type-is@~1.6.18:
|
|
||||||
version "1.6.18"
|
|
||||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
|
||||||
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
|
|
||||||
dependencies:
|
|
||||||
media-typer "0.3.0"
|
|
||||||
mime-types "~2.1.24"
|
|
||||||
|
|
||||||
unpipe@1.0.0, unpipe@~1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
|
||||||
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
|
|
||||||
|
|
||||||
utils-merge@1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
|
||||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
|
||||||
|
|
||||||
vary@~1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
|
||||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "yarn-fixture",
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"accepts": "^1.3.8",
|
||||||
|
"array-flatten": "^3.0.0",
|
||||||
|
"bytes": "^3.1.2",
|
||||||
|
"content-disposition": "^0.5.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
accepts@^1.3.8:
|
||||||
|
version "1.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||||
|
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
|
||||||
|
dependencies:
|
||||||
|
mime-types "~2.1.34"
|
||||||
|
negotiator "0.6.3"
|
||||||
|
|
||||||
|
array-flatten@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541"
|
||||||
|
integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==
|
||||||
|
|
||||||
|
bytes@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
|
||||||
|
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||||
|
|
||||||
|
content-disposition@^0.5.4:
|
||||||
|
version "0.5.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
|
||||||
|
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "5.2.1"
|
||||||
|
|
||||||
|
mime-db@1.52.0:
|
||||||
|
version "1.52.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
|
mime-types@~2.1.34:
|
||||||
|
version "2.1.35"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
|
dependencies:
|
||||||
|
mime-db "1.52.0"
|
||||||
|
|
||||||
|
negotiator@0.6.3:
|
||||||
|
version "0.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
|
||||||
|
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||||
|
|
||||||
|
safe-buffer@5.2.1:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||||
|
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||||
+310
-68
@@ -1,42 +1,125 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as exec from '@actions/exec';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as cache from '@actions/cache';
|
it,
|
||||||
import * as io from '@actions/io';
|
expect,
|
||||||
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
|
|
||||||
import each from 'jest-each';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
import * as main from '../src/main';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import * as util from '../src/util';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real util before mocking so we can spread it
|
||||||
|
const realUtil = await import('../src/util.js');
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/util.js', () => ({
|
||||||
|
...realUtil,
|
||||||
|
getNodeVersionFromFile: jest.fn(realUtil.getNodeVersionFromFile)
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const util = await import('../src/util.js');
|
||||||
|
const {default: OfficialBuilds} =
|
||||||
|
await import('../src/distributions/official_builds/official_builds.js');
|
||||||
|
|
||||||
|
import each from 'jest-each';
|
||||||
|
|
||||||
describe('main tests', () => {
|
describe('main tests', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let infoSpy: jest.SpyInstance;
|
let infoSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let inSpy: jest.SpyInstance;
|
let saveStateSpy: jest.Mock;
|
||||||
let setOutputSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let startGroupSpy: jest.SpyInstance;
|
let setOutputSpy: jest.Mock;
|
||||||
let endGroupSpy: jest.SpyInstance;
|
let startGroupSpy: jest.Mock;
|
||||||
|
let endGroupSpy: jest.Mock;
|
||||||
|
|
||||||
let whichSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
|
|
||||||
let existsSpy: jest.SpyInstance;
|
let existsSpy: jest.Mock;
|
||||||
|
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
|
||||||
let getNodeVersionFromFileSpy: jest.SpyInstance;
|
let getNodeVersionFromFileSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
|
||||||
let setupNodeJsSpy: jest.SpyInstance;
|
let setupNodeJsSpy: jest.SpiedFunction<
|
||||||
|
typeof OfficialBuilds.prototype.setupNodeJs
|
||||||
|
>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
inputs = {};
|
inputs = {};
|
||||||
@@ -47,35 +130,34 @@ describe('main tests', () => {
|
|||||||
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
infoSpy = jest.spyOn(core, 'info');
|
infoSpy = core.info as jest.Mock;
|
||||||
infoSpy.mockImplementation(() => {});
|
infoSpy.mockImplementation(() => {});
|
||||||
setOutputSpy = jest.spyOn(core, 'setOutput');
|
setOutputSpy = core.setOutput as jest.Mock;
|
||||||
setOutputSpy.mockImplementation(() => {});
|
setOutputSpy.mockImplementation(() => {});
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
warningSpy.mockImplementation(() => {});
|
warningSpy.mockImplementation(() => {});
|
||||||
startGroupSpy = jest.spyOn(core, 'startGroup');
|
saveStateSpy = core.saveState as jest.Mock;
|
||||||
|
saveStateSpy.mockImplementation(() => {});
|
||||||
|
startGroupSpy = core.startGroup as jest.Mock;
|
||||||
startGroupSpy.mockImplementation(() => {});
|
startGroupSpy.mockImplementation(() => {});
|
||||||
endGroupSpy = jest.spyOn(core, 'endGroup');
|
endGroupSpy = core.endGroup as jest.Mock;
|
||||||
endGroupSpy.mockImplementation(() => {});
|
endGroupSpy.mockImplementation(() => {});
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
|
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
|
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
|
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
cnSpy.mockImplementation(line => {
|
cnSpy.mockImplementation(() => true);
|
||||||
// uncomment to debug
|
|
||||||
process.stderr.write('write:' + line + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
||||||
setupNodeJsSpy.mockImplementation(() => {});
|
setupNodeJsSpy.mockImplementation(async () => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -90,29 +172,37 @@ describe('main tests', () => {
|
|||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
describe('getNodeVersionFromFile', () => {
|
describe('getNodeVersionFromFile', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
(util.getNodeVersionFromFile as jest.Mock).mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
each`
|
each`
|
||||||
contents | expected
|
contents | expected
|
||||||
${'12'} | ${'12'}
|
${'12'} | ${'12'}
|
||||||
${'12.3'} | ${'12.3'}
|
${'12.3'} | ${'12.3'}
|
||||||
${'12.3.4'} | ${'12.3.4'}
|
${'12.3.4'} | ${'12.3.4'}
|
||||||
${'v12.3.4'} | ${'12.3.4'}
|
${'v12.3.4'} | ${'12.3.4'}
|
||||||
${'lts/erbium'} | ${'lts/erbium'}
|
${'lts/erbium'} | ${'lts/erbium'}
|
||||||
${'lts/*'} | ${'lts/*'}
|
${'lts/*'} | ${'lts/*'}
|
||||||
${'nodejs 12.3.4'} | ${'12.3.4'}
|
${'nodejs 12.3.4'} | ${'12.3.4'}
|
||||||
${'ruby 2.3.4\nnodejs 12.3.4\npython 3.4.5'} | ${'12.3.4'}
|
${'ruby 2.3.4\nnodejs 12.3.4\npython 3.4.5'} | ${'12.3.4'}
|
||||||
${''} | ${''}
|
${''} | ${''}
|
||||||
${'unknown format'} | ${'unknown format'}
|
${'unknown format'} | ${'unknown format'}
|
||||||
${' 14.1.0 '} | ${'14.1.0'}
|
${' 14.1.0 '} | ${'14.1.0'}
|
||||||
${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'}| ${'>=14.0.0 <=17.0.0'}
|
${'{}'} | ${null}
|
||||||
${'{"volta": {"extends": "./package.json"}}'}| ${'18.0.0'}
|
${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'} | ${'>=14.0.0 <=17.0.0'}
|
||||||
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
${'{"volta": {"extends": "./package.json"}}'} | ${'18.0.0'}
|
||||||
${'{}'} | ${null}
|
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
||||||
`.it('parses "$contents"', ({contents, expected}) => {
|
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
||||||
|
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
||||||
|
`.it('parses "$contents"', ({contents, expected}: any) => {
|
||||||
const existsSpy = jest.spyOn(fs, 'existsSync');
|
const existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
existsSpy.mockImplementation(() => true);
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
readFileSpy.mockImplementation(filePath => {
|
readFileSpy.mockImplementation((filePath: any) => {
|
||||||
if (
|
if (
|
||||||
typeof filePath === 'string' &&
|
typeof filePath === 'string' &&
|
||||||
path.basename(filePath) === 'package.json'
|
path.basename(filePath) === 'package.json'
|
||||||
@@ -134,8 +224,10 @@ describe('main tests', () => {
|
|||||||
[{node: '16.0.2', npm: '7.3.3', yarn: '2.22.11'}],
|
[{node: '16.0.2', npm: '7.3.3', yarn: '2.22.11'}],
|
||||||
[{node: '14.0.1', npm: '8.1.0', yarn: '3.2.1'}],
|
[{node: '14.0.1', npm: '8.1.0', yarn: '3.2.1'}],
|
||||||
[{node: '17.0.2', npm: '6.3.3', yarn: ''}]
|
[{node: '17.0.2', npm: '6.3.3', yarn: ''}]
|
||||||
])('Tools versions %p', async obj => {
|
])('Tools versions %p', async (obj: any) => {
|
||||||
getExecOutputSpy.mockImplementation(async command => {
|
(
|
||||||
|
getExecOutputSpy as jest.Mock<typeof exec.getExecOutput>
|
||||||
|
).mockImplementation(async (command: string) => {
|
||||||
if (Reflect.has(obj, command) && !obj[command]) {
|
if (Reflect.has(obj, command) && !obj[command]) {
|
||||||
return {
|
return {
|
||||||
stdout: '',
|
stdout: '',
|
||||||
@@ -147,14 +239,14 @@ describe('main tests', () => {
|
|||||||
return {stdout: obj[command], stderr: '', exitCode: 0};
|
return {stdout: obj[command], stderr: '', exitCode: 0};
|
||||||
});
|
});
|
||||||
|
|
||||||
whichSpy.mockImplementation(cmd => {
|
whichSpy.mockImplementation((cmd: any) => {
|
||||||
return `some/${cmd}/path`;
|
return `some/${cmd}/path`;
|
||||||
});
|
});
|
||||||
|
|
||||||
await util.printEnvDetailsAndSetOutput();
|
await util.printEnvDetailsAndSetOutput();
|
||||||
|
|
||||||
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
||||||
Object.getOwnPropertyNames(obj).forEach(name => {
|
Object.getOwnPropertyNames(obj).forEach((name: any) => {
|
||||||
if (!obj[name]) {
|
if (!obj[name]) {
|
||||||
expect(infoSpy).toHaveBeenCalledWith(
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
`[warning]${name} does not exist`
|
`[warning]${name} does not exist`
|
||||||
@@ -170,11 +262,16 @@ describe('main tests', () => {
|
|||||||
delete inputs['node-version'];
|
delete inputs['node-version'];
|
||||||
inputs['node-version-file'] = '.nvmrc';
|
inputs['node-version-file'] = '.nvmrc';
|
||||||
|
|
||||||
getNodeVersionFromFileSpy = jest.spyOn(util, 'getNodeVersionFromFile');
|
getNodeVersionFromFileSpy = util.getNodeVersionFromFile as jest.Mock;
|
||||||
|
getNodeVersionFromFileSpy.mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
getNodeVersionFromFileSpy.mockRestore();
|
getNodeVersionFromFileSpy.mockImplementation(
|
||||||
|
realUtil.getNodeVersionFromFile as any
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not read node-version-file if node-version is provided', async () => {
|
it('does not read node-version-file if node-version is provided', async () => {
|
||||||
@@ -233,8 +330,8 @@ describe('main tests', () => {
|
|||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(core.setFailed as jest.Mock).toHaveBeenCalledWith(
|
||||||
`::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
|
`The specified node version file at: ${versionFilePath} does not exist`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -244,7 +341,7 @@ describe('main tests', () => {
|
|||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@@ -264,7 +361,7 @@ describe('main tests', () => {
|
|||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
inputs['cache'] = 'npm';
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@@ -280,4 +377,149 @@ describe('main tests', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('cache feature tests', () => {
|
||||||
|
it('Should enable caching when packageManager is npm and cache input is not provided', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(() =>
|
||||||
|
JSON.stringify({
|
||||||
|
packageManager: 'npm@10.8.2'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
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: any) => inputs[name]);
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(() =>
|
||||||
|
JSON.stringify({
|
||||||
|
devEngines: {
|
||||||
|
packageManager: {name: 'npm'}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should enable caching when devEngines.packageManager is array and one entry has name "npm"', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
|
inSpy.mockImplementation((name: any) => 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 "pnpm@8.0.0" and cache input is not provided', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(() =>
|
||||||
|
JSON.stringify({
|
||||||
|
packageManager: 'pnpm@8.0.0'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not enable caching if devEngines.packageManager.name is "pnpm"', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
inSpy.mockImplementation((name: any) => 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 not enable caching if devEngines.packageManager is array without "npm"', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
inSpy.mockImplementation((name: any) => 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: any) => inputs[name]);
|
||||||
|
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: any) => 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: any) => inputs[name]);
|
||||||
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
await main.run();
|
||||||
|
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {MockGlobber} from './glob-mock';
|
import {describe, it, expect} from '@jest/globals';
|
||||||
|
import {MockGlobber} from './glob-mock.js';
|
||||||
|
|
||||||
describe('mocked globber tests', () => {
|
describe('mocked globber tests', () => {
|
||||||
it('globber should return generator', async () => {
|
it('globber should return generator', async () => {
|
||||||
|
|||||||
+240
-106
@@ -1,51 +1,160 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let getDistSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getDistSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let cpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let cpSpy: jest.Mock;
|
||||||
let parseNodeVersionSpy: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let parseNodeVersionSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@@ -54,8 +163,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['RUNNER_TEMP'] = '/runner_temp';
|
process.env['RUNNER_TEMP'] = '/runner_temp';
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@@ -66,30 +175,33 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
cpSpy = jest.spyOn(io, 'cp');
|
cpSpy = io.cp as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@@ -106,28 +218,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -202,7 +304,7 @@ describe('setup-node', () => {
|
|||||||
inputs['node-version'] = '16-nightly';
|
inputs['node-version'] = '16-nightly';
|
||||||
os['arch'] = 'x64';
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize(
|
const toolPath = path.normalize(
|
||||||
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
||||||
@@ -224,7 +326,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@@ -244,7 +346,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@@ -255,7 +357,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '13.13.1-nightly20200415947ddec091';
|
const versionSpec = '13.13.1-nightly20200415947ddec091';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -275,7 +376,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
expect(dlSpy).toHaveBeenCalled();
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
||||||
@@ -291,15 +392,14 @@ describe('setup-node', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
dlSpy.mockImplementation(async url => {
|
dlSpy.mockImplementation(async (url: any) => {
|
||||||
if (workingUrls.includes(url)) {
|
if (workingUrls.includes(url as string)) {
|
||||||
return '/some/temp/path';
|
return '/some/temp/path';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,10 +414,10 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
workingUrls.forEach(url => {
|
workingUrls.forEach((url: any) => {
|
||||||
expect(dlSpy).toHaveBeenCalledWith(url);
|
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
|
||||||
});
|
});
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${toolPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(toolPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
||||||
@@ -333,15 +433,14 @@ describe('setup-node', () => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
dlSpy.mockImplementation(async url => {
|
dlSpy.mockImplementation(async (url: any) => {
|
||||||
if (workingUrls.includes(url)) {
|
if (workingUrls.includes(url as string)) {
|
||||||
return '/some/temp/path';
|
return '/some/temp/path';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,12 +455,10 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
workingUrls.forEach(url => {
|
workingUrls.forEach((url: any) => {
|
||||||
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
||||||
});
|
});
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith('Unexpected HTTP response: 404');
|
||||||
`::error::Unexpected HTTP response: 404${osm.EOL}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@@ -375,8 +472,8 @@ describe('setup-node', () => {
|
|||||||
findAllVersionsSpy.mockImplementation(() => []);
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -389,7 +486,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '18.0.0-nightly202204180699150267';
|
const versionSpec = '18.0.0-nightly202204180699150267';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
@@ -400,7 +496,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@@ -424,10 +520,8 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
const expectedUrl = `https://nodejs.org/download/nightly/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
const expectedUrl = `https://nodejs.org/download/nightly/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
||||||
@@ -449,6 +543,52 @@ describe('setup-node', () => {
|
|||||||
}
|
}
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
|
it('acquires specified architecture of node from mirror', async () => {
|
||||||
|
for (const {arch, version, osSpec} of [
|
||||||
|
{
|
||||||
|
arch: 'x86',
|
||||||
|
version: '18.0.0-nightly202110204cb3e06ed8',
|
||||||
|
osSpec: 'win32'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arch: 'x86',
|
||||||
|
version: '20.0.0-nightly2022101987cdf7d412',
|
||||||
|
osSpec: 'win32'
|
||||||
|
}
|
||||||
|
]) {
|
||||||
|
os.platform = osSpec;
|
||||||
|
os.arch = arch;
|
||||||
|
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
|
||||||
|
const platform = {
|
||||||
|
linux: 'linux',
|
||||||
|
darwin: 'darwin',
|
||||||
|
win32: 'win'
|
||||||
|
}[os.platform];
|
||||||
|
inputs['node-version'] = version;
|
||||||
|
inputs['architecture'] = arch;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
inputs['mirror'] = 'https://my-mirror.org';
|
||||||
|
inputs['mirror-token'] = 'my-mirror-token';
|
||||||
|
|
||||||
|
const expectedUrl = `https://my-mirror.org/download/nightly/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${version} - ${arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
describe('nightly versions', () => {
|
describe('nightly versions', () => {
|
||||||
it.each([
|
it.each([
|
||||||
[
|
[
|
||||||
@@ -499,9 +639,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -531,9 +669,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(findAllVersionsSpy).toHaveBeenCalled();
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -599,9 +735,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,51 +1,163 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestManifest from './data/versions-manifest.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Pre-import real authutil before mocking
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
const {default: OfficialBuilds} =
|
||||||
|
await import('../src/distributions/official_builds/official_builds.js');
|
||||||
|
|
||||||
|
const {default: nodeTestManifest} = await import(
|
||||||
|
'./data/versions-manifest.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
import type {IToolRelease} from '@actions/tool-cache';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let build: OfficialBuilds;
|
let build: InstanceType<typeof OfficialBuilds>;
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let getManifestSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let getManifestSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let readFileSyncSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let execSpy: jest.SpyInstance;
|
let readFileSyncSpy: jest.Mock;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@@ -53,8 +165,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@@ -65,34 +177,61 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
getManifestSpy = tc.getManifestFromRepo as jest.Mock;
|
||||||
|
|
||||||
|
// Restore findFromManifest after clearMocks.
|
||||||
|
// In ESM, the real tc.findFromManifest uses os.platform() from its own
|
||||||
|
// module namespace, which jest.spyOn on our os import cannot override.
|
||||||
|
// So we provide a minimal reimplementation that uses the test's os var.
|
||||||
|
(tc.findFromManifest as jest.Mock).mockImplementation(
|
||||||
|
async (versionSpec: any, stable: any, manifest: any, archFilter: any) => {
|
||||||
|
const arch = archFilter || os['arch'] || 'x64';
|
||||||
|
const plat = os['platform'] || process.platform;
|
||||||
|
const semverModule = await import('semver');
|
||||||
|
for (const rel of manifest || []) {
|
||||||
|
if (
|
||||||
|
semverModule.default.satisfies(rel.version, versionSpec, {
|
||||||
|
includePrerelease: true
|
||||||
|
}) &&
|
||||||
|
rel.stable === stable
|
||||||
|
) {
|
||||||
|
const file = rel.files.find(
|
||||||
|
(f: any) => f.arch === arch && f.platform === plat
|
||||||
|
);
|
||||||
|
if (file) return {...rel, files: [file]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
// gets
|
// gets
|
||||||
getManifestSpy.mockImplementation(
|
getManifestSpy.mockImplementation(() => <IToolRelease[]>nodeTestManifest);
|
||||||
() => <tc.IToolRelease[]>nodeTestManifest
|
|
||||||
);
|
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@@ -107,29 +246,23 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
getExecOutputSpy.mockImplementation(async () => ({
|
||||||
|
stdout: 'v16.15.0',
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -156,7 +289,7 @@ describe('setup-node', () => {
|
|||||||
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
||||||
os.platform = platform;
|
os.platform = platform;
|
||||||
os.arch = 'x64';
|
os.arch = 'x64';
|
||||||
const versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
|
const versions: IToolRelease[] | null = await tc.getManifestFromRepo(
|
||||||
'actions',
|
'actions',
|
||||||
'node-versions',
|
'node-versions',
|
||||||
'mocktoken'
|
'mocktoken'
|
||||||
@@ -187,7 +320,7 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache with stable not supplied', async () => {
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@@ -199,14 +332,14 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache and adds it to the path', async () => {
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
inputs['node-version'] = '12';
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@@ -219,7 +352,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@@ -235,7 +368,6 @@ describe('setup-node', () => {
|
|||||||
const resolvedVersion = versionSpec;
|
const resolvedVersion = versionSpec;
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
const expectedUrl =
|
const expectedUrl =
|
||||||
@@ -248,9 +380,19 @@ describe('setup-node', () => {
|
|||||||
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
||||||
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
cacheSpy.mockImplementation(async () => toolPath);
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
whichSpy.mockImplementation(cmd => {
|
whichSpy.mockImplementation((cmd: any) => {
|
||||||
return `some/${cmd}/path`;
|
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();
|
await main.run();
|
||||||
|
|
||||||
@@ -279,7 +421,43 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to a version from node dist from mirror', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '11.15.0';
|
||||||
|
const mirror = 'https://my_mirror_url';
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
inputs['mirror'] = mirror;
|
||||||
|
inputs['mirror-token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/11.15.0/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(getManifestSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Not found in manifest. Falling back to download directly from ${mirror}`
|
||||||
|
);
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@@ -290,7 +468,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '11.15.0';
|
const versionSpec = '11.15.0';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -314,7 +491,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(dlSpy).toHaveBeenCalled();
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@@ -333,8 +510,8 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -348,7 +525,6 @@ describe('setup-node', () => {
|
|||||||
const resolvedVersion = versionSpec;
|
const resolvedVersion = versionSpec;
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
@@ -357,7 +533,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('reports when download failed but version exists', async () => {
|
it('reports when download failed but version exists', async () => {
|
||||||
@@ -368,7 +544,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '11.15.0';
|
const versionSpec = '11.15.0';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -411,7 +586,6 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
const expectedUrl =
|
const expectedUrl =
|
||||||
@@ -523,7 +697,6 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['check-latest'] = 'true';
|
inputs['check-latest'] = 'true';
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -553,7 +726,7 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fallback to dist if manifest is not available', async () => {
|
it('fallback to dist if manifest is not available', async () => {
|
||||||
@@ -565,7 +738,6 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['check-latest'] = 'true';
|
inputs['check-latest'] = 'true';
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -597,8 +769,8 @@ describe('setup-node', () => {
|
|||||||
expect(logSpy).toHaveBeenCalledWith(
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
`Attempting to download ${versionSpec}...`
|
`Attempting to download ${versionSpec}...`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
}, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('LTS version', () => {
|
describe('LTS version', () => {
|
||||||
@@ -639,9 +811,7 @@ describe('setup-node', () => {
|
|||||||
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -699,9 +869,7 @@ describe('setup-node', () => {
|
|||||||
);
|
);
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -721,8 +889,8 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to parse LTS alias for Node version 'lts/'${osm.EOL}`
|
`Unable to parse LTS alias for Node version 'lts/'`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -745,8 +913,8 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find LTS release 'unknown' for Node version 'lts/unknown'.${osm.EOL}`
|
`Unable to find LTS release 'unknown' for Node version 'lts/unknown'.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -770,10 +938,10 @@ describe('setup-node', () => {
|
|||||||
expect(dbgSpy).toHaveBeenCalledWith(
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
'Getting manifest from actions/node-versions@main'
|
'Getting manifest from actions/node-versions@main'
|
||||||
);
|
);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to download manifest${osm.EOL}`
|
`Failed to fetch a valid manifest after 3 attempts. Last error: Unable to download manifest`
|
||||||
);
|
);
|
||||||
});
|
}, 10000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('latest alias syntax', () => {
|
describe('latest alias syntax', () => {
|
||||||
@@ -795,10 +963,13 @@ describe('setup-node', () => {
|
|||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(logSpy).toHaveBeenCalledWith('Unable to download manifest');
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Failed to fetch a valid manifest after 3 attempts. Last error: Unable to download manifest'
|
||||||
|
);
|
||||||
|
|
||||||
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
|
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
|
||||||
}
|
},
|
||||||
|
10000
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -828,4 +999,131 @@ describe('setup-node', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('acquires specified architecture of node from mirror', async () => {
|
||||||
|
for (const {arch, version, osSpec} of [
|
||||||
|
{arch: 'x86', version: '12.16.2', osSpec: 'win32'},
|
||||||
|
{arch: 'x86', version: '14.0.0', osSpec: 'win32'}
|
||||||
|
]) {
|
||||||
|
os.platform = osSpec;
|
||||||
|
os.arch = arch;
|
||||||
|
const fileExtension = os.platform === 'win32' ? '7z' : 'tar.gz';
|
||||||
|
const platform = {
|
||||||
|
linux: 'linux',
|
||||||
|
darwin: 'darwin',
|
||||||
|
win32: 'win'
|
||||||
|
}[os.platform];
|
||||||
|
inputs['node-version'] = version;
|
||||||
|
inputs['architecture'] = arch;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
inputs['mirror'] = 'https://my_mirror_url';
|
||||||
|
inputs['mirror-token'] = 'faketoken';
|
||||||
|
|
||||||
|
const expectedUrl =
|
||||||
|
arch === 'x64'
|
||||||
|
? `https://github.com/actions/node-versions/releases/download/${version}/node-${version}-${platform}-${arch}.zip`
|
||||||
|
: `https://my_mirror_url/dist/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize(`/cache/node/${version}/${arch}`);
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${version} - ${arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
describe('manifest retry and validation', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
inputs['node-version'] = 'lts/erbium';
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('retries fetching the manifest and succeeds on a later attempt', async () => {
|
||||||
|
let calls = 0;
|
||||||
|
getManifestSpy.mockImplementation(() => {
|
||||||
|
calls++;
|
||||||
|
if (calls < 2) {
|
||||||
|
throw new Error('transient network failure');
|
||||||
|
}
|
||||||
|
return <tc.IToolRelease[]>nodeTestManifest;
|
||||||
|
});
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
getExecOutputSpy.mockImplementation(async () => ({
|
||||||
|
stdout: `v${path.basename(path.dirname(toolPath))}\n`,
|
||||||
|
stderr: '',
|
||||||
|
exitCode: 0
|
||||||
|
}));
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(calls).toBe(2);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Retrying to fetch the manifest...');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`Found LTS release '12.16.2' for Node version 'lts/erbium'`
|
||||||
|
);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
it('rejects an empty manifest as invalid and retries', async () => {
|
||||||
|
getManifestSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getManifestSpy).toHaveBeenCalledTimes(3);
|
||||||
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
|
`Failed to fetch a valid manifest after 3 attempts. Last error: The manifest fetched is empty, truncated, or does not contain any valid tool release entries.`
|
||||||
|
);
|
||||||
|
}, 10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
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,4 +1,5 @@
|
|||||||
import tscMatcher from '../.github/tsc.json';
|
import {describe, it, expect} from '@jest/globals';
|
||||||
|
import tscMatcher from '../.github/tsc.json' with {type: 'json'};
|
||||||
|
|
||||||
describe('problem matcher tests', () => {
|
describe('problem matcher tests', () => {
|
||||||
it('tsc: matches TypeScript "pretty" error message', () => {
|
it('tsc: matches TypeScript "pretty" error message', () => {
|
||||||
|
|||||||
+178
-87
@@ -1,46 +1,152 @@
|
|||||||
import * as core from '@actions/core';
|
import {
|
||||||
import * as io from '@actions/io';
|
jest,
|
||||||
import * as tc from '@actions/tool-cache';
|
describe,
|
||||||
import * as httpm from '@actions/http-client';
|
it,
|
||||||
import * as exec from '@actions/exec';
|
expect,
|
||||||
import * as cache from '@actions/cache';
|
beforeEach,
|
||||||
|
afterEach,
|
||||||
|
afterAll
|
||||||
|
} from '@jest/globals';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import cp from 'child_process';
|
import cp from 'child_process';
|
||||||
import osm from 'os';
|
import osm from 'os';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as main from '../src/main';
|
|
||||||
import * as auth from '../src/authutil';
|
|
||||||
import {INodeVersion} from '../src/distributions/base-models';
|
|
||||||
|
|
||||||
import nodeTestDist from './data/node-dist-index.json';
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
import nodeTestDistNightly from './data/node-nightly-index.json';
|
|
||||||
import nodeTestDistRc from './data/node-rc-index.json';
|
// Mock @actions modules before importing anything that depends on them
|
||||||
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
notice: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
setOutput: jest.fn(),
|
||||||
|
getInput: jest.fn(),
|
||||||
|
getBooleanInput: jest.fn(),
|
||||||
|
getMultilineInput: jest.fn(),
|
||||||
|
addPath: jest.fn(),
|
||||||
|
exportVariable: jest.fn(),
|
||||||
|
saveState: jest.fn(),
|
||||||
|
getState: jest.fn(),
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
isDebug: jest.fn(() => false),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn(),
|
||||||
|
group: jest.fn((_name: string, fn: () => Promise<unknown>) => fn()),
|
||||||
|
toPlatformPath: jest.fn((p: string) => p),
|
||||||
|
toWin32Path: jest.fn((p: string) => p),
|
||||||
|
toPosixPath: jest.fn((p: string) => p)
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
which: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
cp: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/tool-cache', () => ({
|
||||||
|
find: jest.fn(),
|
||||||
|
findAllVersions: jest.fn(),
|
||||||
|
downloadTool: jest.fn(),
|
||||||
|
extractTar: jest.fn(),
|
||||||
|
extractZip: jest.fn(),
|
||||||
|
extractXar: jest.fn(),
|
||||||
|
extract7z: jest.fn(),
|
||||||
|
cacheDir: jest.fn(),
|
||||||
|
cacheFile: jest.fn(),
|
||||||
|
getManifestFromRepo: jest.fn(),
|
||||||
|
findFromManifest: jest.fn(),
|
||||||
|
HTTPError: class HTTPError extends Error {
|
||||||
|
readonly httpStatusCode: number;
|
||||||
|
constructor(httpStatusCode?: number) {
|
||||||
|
super(`Unexpected HTTP response: ${httpStatusCode}`);
|
||||||
|
this.httpStatusCode = httpStatusCode ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const _mockGetJson = jest.fn();
|
||||||
|
jest.unstable_mockModule('@actions/http-client', () => ({
|
||||||
|
HttpClient: jest.fn().mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/exec', () => ({
|
||||||
|
exec: jest.fn(),
|
||||||
|
getExecOutput: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/cache', () => ({
|
||||||
|
saveCache: jest.fn(),
|
||||||
|
restoreCache: jest.fn(),
|
||||||
|
isFeatureAvailable: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
const realAuth = await import('../src/authutil.js');
|
||||||
|
jest.unstable_mockModule('../src/authutil.js', () => ({
|
||||||
|
...realAuth,
|
||||||
|
configAuthentication: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const core = await import('@actions/core');
|
||||||
|
const io = await import('@actions/io');
|
||||||
|
const tc = await import('@actions/tool-cache');
|
||||||
|
const httpm = await import('@actions/http-client');
|
||||||
|
const exec = await import('@actions/exec');
|
||||||
|
const cache = await import('@actions/cache');
|
||||||
|
const main = await import('../src/main.js');
|
||||||
|
const auth = await import('../src/authutil.js');
|
||||||
|
|
||||||
|
const {default: nodeTestDist} = await import('./data/node-dist-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeTestDistNightly} = await import(
|
||||||
|
'./data/node-nightly-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
const {default: nodeTestDistRc} = await import('./data/node-rc-index.json', {
|
||||||
|
with: {type: 'json'}
|
||||||
|
});
|
||||||
|
const {default: nodeV8CanaryTestDist} = await import(
|
||||||
|
'./data/v8-canary-dist-index.json',
|
||||||
|
{with: {type: 'json'}}
|
||||||
|
);
|
||||||
|
|
||||||
|
import type {INodeVersion} from '../src/distributions/base-models.js';
|
||||||
|
|
||||||
describe('setup-node', () => {
|
describe('setup-node', () => {
|
||||||
let inputs = {} as any;
|
let inputs = {} as any;
|
||||||
let os = {} as any;
|
let os = {} as any;
|
||||||
|
|
||||||
let inSpy: jest.SpyInstance;
|
let inSpy: jest.Mock;
|
||||||
let findSpy: jest.SpyInstance;
|
let findSpy: jest.Mock;
|
||||||
let findAllVersionsSpy: jest.SpyInstance;
|
let findAllVersionsSpy: jest.Mock;
|
||||||
let cnSpy: jest.SpyInstance;
|
let cnSpy: jest.SpiedFunction<typeof process.stdout.write>;
|
||||||
let logSpy: jest.SpyInstance;
|
let logSpy: jest.Mock;
|
||||||
let warningSpy: jest.SpyInstance;
|
let warningSpy: jest.Mock;
|
||||||
let platSpy: jest.SpyInstance;
|
let addPathSpy: jest.Mock;
|
||||||
let archSpy: jest.SpyInstance;
|
let setFailedSpy: jest.Mock;
|
||||||
let dlSpy: jest.SpyInstance;
|
let platSpy: jest.SpiedFunction<typeof osm.platform>;
|
||||||
let exSpy: jest.SpyInstance;
|
let archSpy: jest.SpiedFunction<typeof osm.arch>;
|
||||||
let cacheSpy: jest.SpyInstance;
|
let dlSpy: jest.Mock;
|
||||||
let dbgSpy: jest.SpyInstance;
|
let exSpy: jest.Mock;
|
||||||
let whichSpy: jest.SpyInstance;
|
let cacheSpy: jest.Mock;
|
||||||
let existsSpy: jest.SpyInstance;
|
let dbgSpy: jest.Mock;
|
||||||
let mkdirpSpy: jest.SpyInstance;
|
let whichSpy: jest.Mock;
|
||||||
let execSpy: jest.SpyInstance;
|
let existsSpy: jest.SpiedFunction<typeof fs.existsSync>;
|
||||||
let authSpy: jest.SpyInstance;
|
let mkdirpSpy: jest.Mock;
|
||||||
let isCacheActionAvailable: jest.SpyInstance;
|
let execSpy: jest.SpiedFunction<typeof cp.execSync>;
|
||||||
let getExecOutputSpy: jest.SpyInstance;
|
let authSpy: jest.Mock;
|
||||||
let getJsonSpy: jest.SpyInstance;
|
let isCacheActionAvailable: jest.Mock;
|
||||||
|
let getExecOutputSpy: jest.Mock;
|
||||||
|
let getJsonSpy: jest.Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// @actions/core
|
// @actions/core
|
||||||
@@ -48,8 +154,8 @@ describe('setup-node', () => {
|
|||||||
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_PATH'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
process.env['GITHUB_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
inputs = {};
|
inputs = {};
|
||||||
inSpy = jest.spyOn(core, 'getInput');
|
inSpy = core.getInput as jest.Mock;
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
// node
|
// node
|
||||||
os = {};
|
os = {};
|
||||||
@@ -60,30 +166,33 @@ describe('setup-node', () => {
|
|||||||
execSpy = jest.spyOn(cp, 'execSync');
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
findSpy = jest.spyOn(tc, 'find');
|
findSpy = tc.find as jest.Mock;
|
||||||
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
findAllVersionsSpy = tc.findAllVersions as jest.Mock;
|
||||||
dlSpy = jest.spyOn(tc, 'downloadTool');
|
dlSpy = tc.downloadTool as jest.Mock;
|
||||||
exSpy = jest.spyOn(tc, 'extractTar');
|
exSpy = tc.extractTar as jest.Mock;
|
||||||
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
cacheSpy = tc.cacheDir as jest.Mock;
|
||||||
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
|
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist') as jest.Mock;
|
||||||
|
|
||||||
// http-client
|
// http-client
|
||||||
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
getJsonSpy = _mockGetJson;
|
||||||
|
(httpm.HttpClient as jest.Mock).mockImplementation(() => ({
|
||||||
|
getJson: _mockGetJson
|
||||||
|
}));
|
||||||
|
|
||||||
// io
|
// io
|
||||||
whichSpy = jest.spyOn(io, 'which');
|
whichSpy = io.which as jest.Mock;
|
||||||
existsSpy = jest.spyOn(fs, 'existsSync');
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
mkdirpSpy = io.mkdirP as jest.Mock;
|
||||||
|
|
||||||
// @actions/tool-cache
|
// @actions/tool-cache
|
||||||
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
isCacheActionAvailable = cache.isFeatureAvailable as jest.Mock;
|
||||||
isCacheActionAvailable.mockImplementation(() => false);
|
isCacheActionAvailable.mockImplementation(() => false);
|
||||||
|
|
||||||
// disable authentication portion for installer tests
|
// disable authentication portion for installer tests
|
||||||
authSpy = jest.spyOn(auth, 'configAuthentication');
|
authSpy = auth.configAuthentication as jest.Mock;
|
||||||
authSpy.mockImplementation(() => {});
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
getJsonSpy.mockImplementation(url => {
|
getJsonSpy.mockImplementation((url: any) => {
|
||||||
let res: any;
|
let res: any;
|
||||||
if (url.includes('/rc')) {
|
if (url.includes('/rc')) {
|
||||||
res = <INodeVersion[]>nodeTestDistRc;
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
@@ -98,28 +207,18 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// writes
|
// writes
|
||||||
cnSpy = jest.spyOn(process.stdout, 'write');
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
logSpy = jest.spyOn(core, 'info');
|
logSpy = core.info as jest.Mock;
|
||||||
dbgSpy = jest.spyOn(core, 'debug');
|
dbgSpy = core.debug as jest.Mock;
|
||||||
warningSpy = jest.spyOn(core, 'warning');
|
warningSpy = core.warning as jest.Mock;
|
||||||
cnSpy.mockImplementation(line => {
|
addPathSpy = core.addPath as jest.Mock;
|
||||||
// uncomment to debug
|
setFailedSpy = core.setFailed as jest.Mock;
|
||||||
// process.stderr.write('write:' + line + '\n');
|
cnSpy.mockImplementation(() => true);
|
||||||
});
|
logSpy.mockImplementation(() => {});
|
||||||
logSpy.mockImplementation(line => {
|
dbgSpy.mockImplementation(() => {});
|
||||||
// uncomment to debug
|
warningSpy.mockImplementation(() => {});
|
||||||
// process.stderr.write('log:' + line + '\n');
|
|
||||||
});
|
|
||||||
dbgSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to see debug output
|
|
||||||
// process.stderr.write(msg + '\n');
|
|
||||||
});
|
|
||||||
warningSpy.mockImplementation(msg => {
|
|
||||||
// uncomment to debug
|
|
||||||
// process.stderr.write('log:' + msg + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
// @actions/exec
|
// @actions/exec
|
||||||
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
getExecOutputSpy = exec.getExecOutput as jest.Mock;
|
||||||
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +251,7 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache with stable not supplied', async () => {
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
inputs['node-version'] = '12.0.0-rc.1';
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
@@ -164,14 +263,14 @@ describe('setup-node', () => {
|
|||||||
it('finds version in cache and adds it to the path', async () => {
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
inputs['node-version'] = '12.0.0-rc.1';
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
inSpy.mockImplementation(name => inputs[name]);
|
inSpy.mockImplementation((name: any) => inputs[name]);
|
||||||
|
|
||||||
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
findSpy.mockImplementation(() => toolPath);
|
findSpy.mockImplementation(() => toolPath);
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
const expPath = path.join(toolPath, 'bin');
|
const expPath = path.join(toolPath, 'bin');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('handles unhandled find error and reports error', async () => {
|
it('handles unhandled find error and reports error', async () => {
|
||||||
@@ -184,7 +283,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to a version from node dist', async () => {
|
it('falls back to a version from node dist', async () => {
|
||||||
@@ -194,7 +293,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '13.0.0-rc.0';
|
const versionSpec = '13.0.0-rc.0';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
// ... but not in the local cache
|
// ... but not in the local cache
|
||||||
@@ -213,7 +311,7 @@ describe('setup-node', () => {
|
|||||||
expect(exSpy).toHaveBeenCalled();
|
expect(exSpy).toHaveBeenCalled();
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Done');
|
expect(logSpy).toHaveBeenCalledWith('Done');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
expect(addPathSpy).toHaveBeenCalledWith(expPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not find a version that does not exist', async () => {
|
it('does not find a version that does not exist', async () => {
|
||||||
@@ -226,8 +324,8 @@ describe('setup-node', () => {
|
|||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -239,7 +337,6 @@ describe('setup-node', () => {
|
|||||||
const versionSpec = '14.7.0-rc.1';
|
const versionSpec = '14.7.0-rc.1';
|
||||||
|
|
||||||
inputs['node-version'] = versionSpec;
|
inputs['node-version'] = versionSpec;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
findSpy.mockImplementation(() => '');
|
findSpy.mockImplementation(() => '');
|
||||||
@@ -249,7 +346,7 @@ describe('setup-node', () => {
|
|||||||
});
|
});
|
||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
expect(setFailedSpy).toHaveBeenCalledWith(errMsg);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('acquires specified architecture of node', async () => {
|
it('acquires specified architecture of node', async () => {
|
||||||
@@ -265,10 +362,8 @@ describe('setup-node', () => {
|
|||||||
darwin: 'darwin',
|
darwin: 'darwin',
|
||||||
win32: 'win'
|
win32: 'win'
|
||||||
}[os.platform];
|
}[os.platform];
|
||||||
|
|
||||||
inputs['node-version'] = version;
|
inputs['node-version'] = version;
|
||||||
inputs['architecture'] = arch;
|
inputs['architecture'] = arch;
|
||||||
inputs['always-auth'] = false;
|
|
||||||
inputs['token'] = 'faketoken';
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
const expectedUrl = `https://nodejs.org/download/rc/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
const expectedUrl = `https://nodejs.org/download/rc/v${version}/node-v${version}-${platform}-${arch}.${fileExtension}`;
|
||||||
@@ -337,9 +432,7 @@ describe('setup-node', () => {
|
|||||||
// assert
|
// assert
|
||||||
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -373,9 +466,7 @@ describe('setup-node', () => {
|
|||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(addPathSpy).toHaveBeenCalledWith(path.join(toolPath, 'bin'));
|
||||||
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -394,8 +485,8 @@ describe('setup-node', () => {
|
|||||||
await main.run();
|
await main.run();
|
||||||
|
|
||||||
// assert
|
// assert
|
||||||
expect(cnSpy).toHaveBeenCalledWith(
|
expect(setFailedSpy).toHaveBeenCalledWith(
|
||||||
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
`Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ fi
|
|||||||
|
|
||||||
node_version="$(node --version)"
|
node_version="$(node --version)"
|
||||||
echo "Found node version '$node_version'"
|
echo "Found node version '$node_version'"
|
||||||
if [ -z "$(echo $node_version | grep --fixed-strings v$1)" ]; then
|
|
||||||
echo "Unexpected version"
|
# Extract the major version from the node version (remove the 'v' prefix)
|
||||||
|
actual_major_version=$(echo $node_version | sed -E 's/^v([0-9]+)\..*/\1/')
|
||||||
|
expected_major_version=$(echo $1 | sed -E 's/^([0-9]+)\..*/\1/') # Extract major version from argument
|
||||||
|
|
||||||
|
if [ "$actual_major_version" != "$expected_major_version" ]; then
|
||||||
|
echo "Expected Node.js $expected_major_version.x.x but found $node_version"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+13
-5
@@ -2,9 +2,6 @@ name: 'Setup Node.js environment'
|
|||||||
description: 'Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH.'
|
description: 'Setup a Node.js environment by adding problem matchers and optionally downloading and adding it to the PATH.'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
always-auth:
|
|
||||||
description: 'Set always-auth in npmrc.'
|
|
||||||
default: 'false'
|
|
||||||
node-version:
|
node-version:
|
||||||
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0.'
|
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0.'
|
||||||
node-version-file:
|
node-version-file:
|
||||||
@@ -23,17 +20,28 @@ inputs:
|
|||||||
default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
|
default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
|
||||||
cache:
|
cache:
|
||||||
description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.'
|
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. 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:
|
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.'
|
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.'
|
||||||
|
mirror:
|
||||||
|
description: 'Used to specify an alternative mirror to download Node.js binaries from'
|
||||||
|
mirror-token:
|
||||||
|
description: 'The token used as Authorization header when fetching from the mirror'
|
||||||
# TODO: add input to control forcing to pull from cloud or dist.
|
# TODO: add input to control forcing to pull from cloud or dist.
|
||||||
# escape valve for someone having issues or needing the absolute latest which isn't cached yet
|
# escape valve for someone having issues or needing the absolute latest which isn't cached yet
|
||||||
outputs:
|
outputs:
|
||||||
cache-hit:
|
cache-hit:
|
||||||
description: 'A boolean value to indicate if a cache was hit.'
|
description: 'A boolean value to indicate if a cache was hit.'
|
||||||
|
cache-primary-key:
|
||||||
|
description: 'The key used to restore and save the cache.'
|
||||||
|
cache-matched-key:
|
||||||
|
description: 'The key of the cache that was restored. Empty when no cache is found.'
|
||||||
node-version:
|
node-version:
|
||||||
description: 'The installed node version.'
|
description: 'The installed node version.'
|
||||||
runs:
|
runs:
|
||||||
using: 'node20'
|
using: 'node24'
|
||||||
main: 'dist/setup/index.js'
|
main: 'dist/setup/index.js'
|
||||||
post: 'dist/cache-save/index.js'
|
post: 'dist/cache-save/index.js'
|
||||||
post-if: success()
|
post-if: success()
|
||||||
|
|||||||
Vendored
+83419
-74560
File diff suppressed because one or more lines are too long
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
Vendored
+90297
-85023
File diff suppressed because one or more lines are too long
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ Currently, `actions/setup-node` supports caching dependencies for Npm and Yarn p
|
|||||||
For the first iteration, we have decided to not support cases where `package-lock.json` / `yarn.lock` are located outside of repository root.
|
For the first iteration, we have decided to not support cases where `package-lock.json` / `yarn.lock` are located outside of repository root.
|
||||||
Current implementation searches the following file patterns in the repository root: `package-lock.json`, `yarn.lock` (in order of resolving priorities)
|
Current implementation searches the following file patterns in the repository root: `package-lock.json`, `yarn.lock` (in order of resolving priorities)
|
||||||
|
|
||||||
Obviously, it made build-in caching unusable for mono-repos and repos with complex structure.
|
Obviously, it made built-in caching unusable for mono-repos and repos with complex structure.
|
||||||
We would like to revisit this decision and add customization for dependencies lock file location.
|
We would like to revisit this decision and add customization for dependencies lock file location.
|
||||||
|
|
||||||
## Proposal
|
## Proposal
|
||||||
@@ -24,7 +24,7 @@ The second option looks more generic because it allows to:
|
|||||||
## Decision
|
## Decision
|
||||||
|
|
||||||
Add `cache-dependency-path` input that will accept path (relative to repository root) to dependencies lock file.
|
Add `cache-dependency-path` input that will accept path (relative to repository root) to dependencies lock file.
|
||||||
If provided path contains wildcards, the action will search all maching files and calculate common hash like `${{ hashFiles('**/package-lock.json') }}` YAML construction does.
|
If provided path contains wildcards, the action will search all matching files and calculate common hash like `${{ hashFiles('**/package-lock.json') }}` YAML construction does.
|
||||||
The hash of provided matched files will be used as a part of cache key.
|
The hash of provided matched files will be used as a part of cache key.
|
||||||
|
|
||||||
Yaml examples:
|
Yaml examples:
|
||||||
|
|||||||
+222
-75
@@ -1,6 +1,6 @@
|
|||||||
## Working with lockfiles
|
## Working with lockfiles
|
||||||
|
|
||||||
All supported package managers recommend that you **always** commit the lockfile, although implementations vary doing so generally provides the following benefits:
|
Most supported package managers recommend that you **always** commit the lockfile, although implementations vary doing so generally provides the following benefits:
|
||||||
|
|
||||||
- Enables faster installation for CI and production environments, due to being able to skip package resolution.
|
- Enables faster installation for CI and production environments, due to being able to skip package resolution.
|
||||||
- Describes a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
|
- Describes a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
|
||||||
@@ -35,6 +35,25 @@ Ensure that `pnpm-lock.yaml` is always committed, when on CI pass `--frozen-lock
|
|||||||
- [Working with Git - Lockfiles](https://pnpm.io/git#lockfiles)
|
- [Working with Git - Lockfiles](https://pnpm.io/git#lockfiles)
|
||||||
- [Documentation of `--frozen-lockfile` option](https://pnpm.io/cli/install#--frozen-lockfile)
|
- [Documentation of `--frozen-lockfile` option](https://pnpm.io/cli/install#--frozen-lockfile)
|
||||||
|
|
||||||
|
### Running without a lockfile
|
||||||
|
|
||||||
|
If you choose not to use a lockfile, you must ensure that **caching is disabled**. The `cache` feature relies on the lockfile to generate a unique key for the cache entry.
|
||||||
|
|
||||||
|
To run without a lockfile:
|
||||||
|
1. Do not set the `cache` input.
|
||||||
|
2. If your `package.json` contains a `packageManager` field set to npm (or devEngines.packageManager), automatic caching is enabled by default. Override this by setting `package-manager-cache: false`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- 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
|
||||||
|
- run: npm install
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
## Check latest version
|
## Check latest version
|
||||||
|
|
||||||
The `check-latest` flag defaults to `false`. When set to `false`, the action will first check the local cache for a semver match. If unable to find a specific version in the cache, the action will attempt to download a version of Node.js. It will pull LTS versions from [node-versions releases](https://github.com/actions/node-versions/releases) and on miss or failure will fall back to the previous behavior of downloading directly from [node dist](https://nodejs.org/dist/). Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific version of Node.js is always used.
|
The `check-latest` flag defaults to `false`. When set to `false`, the action will first check the local cache for a semver match. If unable to find a specific version in the cache, the action will attempt to download a version of Node.js. It will pull LTS versions from [node-versions releases](https://github.com/actions/node-versions/releases) and on miss or failure will fall back to the previous behavior of downloading directly from [node dist](https://nodejs.org/dist/). Use the default or set `check-latest` to `false` if you prefer stability and if you want to ensure a specific version of Node.js is always used.
|
||||||
@@ -45,11 +64,12 @@ If `check-latest` is set to `true`, the action first checks if the cached versio
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '16'
|
node-version: '24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -63,29 +83,40 @@ See [supported version syntax](https://github.com/actions/setup-node#supported-v
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
When using the `package.json` input, the action will look for `volta.node` first. If `volta.node` isn't defined, then it will look for `engines.node`.
|
When using the `package.json` input, the action will look in the following fields for a specified Node version:
|
||||||
|
1. It checks `volta.node` first.
|
||||||
|
2. Then it checks `devEngines.runtime` for an entry with `"name": "node"`.
|
||||||
|
3. Then it will look for `engines.node`.
|
||||||
|
4. Otherwise it tries to resolve the file defined by [`volta.extends`](https://docs.volta.sh/advanced/workspaces)
|
||||||
|
and look for `volta.node`, `devEngines.runtime`, or `engines.node` recursively.
|
||||||
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": "^22 || ^24"
|
||||||
|
},
|
||||||
|
"devEngines": {
|
||||||
|
"runtime": {
|
||||||
|
"name": "node",
|
||||||
|
"version": "^24.3"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "16.0.0"
|
"node": "24.11.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Otherwise, when [`volta.extends`](https://docs.volta.sh/advanced/workspaces) is defined, then it will resolve the corresponding file and look for `volta.node` or `engines.node` recursively.
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
You can use any of the [supported operating systems](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners), and the compatible `architecture` can be selected using `architecture`. Values are `x86`, `x64`, `arm64`, `armv6l`, `armv7l`, `ppc64le`, `s390x` (not all of the architectures are available on all platforms).
|
You can use any of the [supported operating systems](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners), and the compatible `architecture` can be selected using `architecture`. Values are `x86`, `x64`, `arm64`, `armv6l`, `armv7l`, `ppc64le`, `s390x` (not all of the architectures are available on all platforms).
|
||||||
@@ -97,11 +128,12 @@ jobs:
|
|||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '24'
|
||||||
architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
|
architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -118,10 +150,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
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
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -133,10 +166,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
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
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -149,10 +183,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: 'v20.1.1-v8-canary20221103f7e2421e91'
|
node-version: 'v24.0.0-v8-canary2025030537242e55ac'
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -169,10 +204,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
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
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -185,10 +221,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
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
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -201,10 +238,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '16.0.0-nightly20210420a0261d231c'
|
node-version: '24.0.0-nightly202505066102159fa1'
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -219,27 +257,29 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Node sample
|
name: Node sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '16.0.0-rc.1'
|
node-version: '24.0.0-rc.4'
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- 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
|
## 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.
|
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:**
|
**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
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '24'
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
- run: yarn install --frozen-lockfile # optional, --immutable
|
- run: yarn install --frozen-lockfile # optional, --immutable
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
@@ -255,13 +295,13 @@ steps:
|
|||||||
# NOTE: pnpm caching support requires pnpm version >= 6.10.0
|
# NOTE: pnpm caching support requires pnpm version >= 6.10.0
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: pnpm/action-setup@v2
|
- uses: pnpm/action-setup@v6
|
||||||
with:
|
with:
|
||||||
version: 6.32.9
|
version: 10
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '24'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
- run: pnpm test
|
- run: pnpm test
|
||||||
@@ -274,10 +314,10 @@ steps:
|
|||||||
**Using wildcard patterns to cache dependencies**
|
**Using wildcard patterns to cache dependencies**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '24'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: '**/package-lock.json'
|
cache-dependency-path: '**/package-lock.json'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
@@ -287,10 +327,10 @@ steps:
|
|||||||
**Using a list of file paths to cache dependencies**
|
**Using a list of file paths to cache dependencies**
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '24'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
cache-dependency-path: |
|
cache-dependency-path: |
|
||||||
server/app/package-lock.json
|
server/app/package-lock.json
|
||||||
@@ -299,7 +339,52 @@ steps:
|
|||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Multiple Operating Systems and Architectures
|
**Restore-only cache**
|
||||||
|
|
||||||
|
You can restore caches without saving new entries, which helps reduce cache writes and storage usage in read-only cache workflows.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
# - uses: pnpm/action-setup@v6
|
||||||
|
# with:
|
||||||
|
# version: 10
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
|
|
||||||
|
- name: Normalize runner architecture
|
||||||
|
shell: bash
|
||||||
|
run: echo "ARCH=$(echo '${{ runner.arch }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Output of cache path
|
||||||
|
id: cachepath
|
||||||
|
shell: bash
|
||||||
|
run: echo "path=$(npm config get cache)" >> $GITHUB_OUTPUT
|
||||||
|
# run: echo "path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
|
||||||
|
# For yarn workflow, output of yarn cache dir (v1) or yarn config get cacheFolder (v2+)
|
||||||
|
# run: echo "path=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Restore Node cache
|
||||||
|
uses: actions/cache/restore@v5
|
||||||
|
with:
|
||||||
|
path: ${{ steps.cachepath.outputs.path }}
|
||||||
|
key: node-cache-${{ runner.os }}-${{ env.ARCH }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
# key: node-cache-${{ runner.os }}-${{ env.ARCH }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
# key: node-cache-${{ runner.os }}-${{ env.ARCH }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
# - run: yarn install --frozen-lockfile # optional, --immutable
|
||||||
|
# - run: pnpm install
|
||||||
|
```
|
||||||
|
> **Note**: Uncomment the commands relevant to your project's package manager.
|
||||||
|
|
||||||
|
> For more details related to cache scenarios, please refer [actions/cache/restore](https://github.com/actions/cache/tree/main/restore#only-restore-cache).
|
||||||
|
|
||||||
|
## Multiple operating systems and architectures
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
@@ -312,24 +397,25 @@ jobs:
|
|||||||
- macos-latest
|
- macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
node_version:
|
node_version:
|
||||||
- 12
|
- 22
|
||||||
- 14
|
- 24
|
||||||
- 16
|
- 26
|
||||||
architecture:
|
architecture:
|
||||||
- x64
|
- x64
|
||||||
# an extra windows-x86 run:
|
# an extra windows-x86 run:
|
||||||
include:
|
include:
|
||||||
- os: windows-2016
|
- os: windows-latest
|
||||||
node_version: 12
|
node_version: 24
|
||||||
architecture: x86
|
architecture: x86
|
||||||
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
|
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node_version }}
|
node-version: ${{ matrix.node_version }}
|
||||||
architecture: ${{ matrix.architecture }}
|
architecture: ${{ matrix.architecture }}
|
||||||
|
package-manager-cache: false # Disable automatic npm caching if not required
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
@@ -337,18 +423,20 @@ jobs:
|
|||||||
## Publish to npmjs and GPR with npm
|
## Publish to npmjs and GPR with npm
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '24.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
- run: npm publish
|
- run: npm publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -357,18 +445,20 @@ steps:
|
|||||||
## Publish to npmjs and GPR with yarn
|
## Publish to npmjs and GPR with yarn
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '24.x'
|
||||||
registry-url: <registry url>
|
registry-url: <registry url>
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
- run: yarn install --frozen-lockfile
|
- run: yarn install --frozen-lockfile
|
||||||
- run: yarn publish
|
- run: yarn publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
- run: yarn publish
|
- run: yarn publish
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -377,11 +467,12 @@ steps:
|
|||||||
## Use private packages
|
## Use private packages
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '24.x'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
# Skip post-install scripts here, as a malicious
|
# Skip post-install scripts here, as a malicious
|
||||||
# script could steal NODE_AUTH_TOKEN.
|
# script could steal NODE_AUTH_TOKEN.
|
||||||
- run: npm ci --ignore-scripts
|
- run: npm ci --ignore-scripts
|
||||||
@@ -397,10 +488,11 @@ Below you can find a sample "Setup .yarnrc.yml" step, that is going to allow you
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '14.x'
|
node-version: '24.x'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
- name: Setup .yarnrc.yml
|
- name: Setup .yarnrc.yml
|
||||||
run: |
|
run: |
|
||||||
yarn config set npmScopes.my-org.npmRegistryServer "https://npm.pkg.github.com"
|
yarn config set npmScopes.my-org.npmRegistryServer "https://npm.pkg.github.com"
|
||||||
@@ -416,5 +508,60 @@ To access private GitHub Packages within the same organization, go to "Manage Ac
|
|||||||
|
|
||||||
Please refer to the [Ensuring workflow access to your package - Configuring a package's access control and visibility](https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility#ensuring-workflow-access-to-your-package) for more details.
|
Please refer to the [Ensuring workflow access to your package - Configuring a package's access control and visibility](https://docs.github.com/en/packages/learn-github-packages/configuring-a-packages-access-control-and-visibility#ensuring-workflow-access-to-your-package) for more details.
|
||||||
|
|
||||||
### always-auth input
|
## Publishing to npm with Trusted Publisher (OIDC)
|
||||||
The always-auth input sets `always-auth=true` in .npmrc file. With this option set [npm](https://docs.npmjs.com/cli/v6/using-npm/config#always-auth)/yarn sends the authentication credentials when making a request to the registries.
|
|
||||||
|
npm supports Trusted Publishers, enabling packages to be published from GitHub Actions using OpenID Connect (OIDC) instead of long-lived npm tokens. This improves security by replacing static credentials with short-lived tokens, reducing the risk of credential leakage and simplifying authentication in CI/CD workflows.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
Trusted publishing requires a compatible npm version:
|
||||||
|
|
||||||
|
* **npm ≥ 11.5.1 (required)**
|
||||||
|
* **Node.js 24 or newer (recommended)** — includes a compatible npm version by default
|
||||||
|
|
||||||
|
> If npm is below 11.5.1, publishing will fail even if OIDC permissions are correctly configured.
|
||||||
|
|
||||||
|
You must also configure a **Trusted Publisher** in npm for your package/scope that matches your GitHub repository and workflow (and optional environment, if used).
|
||||||
|
|
||||||
|
> **Note**: In publishing workflows, set `package-manager-cache: false` because setup-node enables npm caching automatically when `package.json` specifies npm via `packageManager` or `devEngines.packageManager` (see [Running without a lockfile](#running-without-a-lockfile)), and a poisoned cache may expose credentials (including OIDC tokens) to attacker-controlled code.
|
||||||
|
|
||||||
|
### Example workflow
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
package-manager-cache: false # Disable automatic npm dependency caching to reduce cache poisoning risk
|
||||||
|
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run build --if-present
|
||||||
|
- run: npm publish
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: If the Trusted Publisher configuration (GitHub owner/repo/workflow file, and optional environment) does not match the workflow run identity exactly, publishing may fail with **E404 Not Found** even if the package exists on npm.
|
||||||
|
|
||||||
|
For more details, see the [npm Trusted Publishers documentation](https://docs.npmjs.com/trusted-publishers) and the [GitHub Actions OpenID Connect (OIDC) overview](https://docs.github.com/en/actions/concepts/security/openid-connect).
|
||||||
|
|
||||||
|
## Use private mirror
|
||||||
|
|
||||||
|
It is possible to use a private mirror hosting Node.js binaries. This mirror must be a full mirror of the official Node.js distribution.
|
||||||
|
The mirror URL can be set using the `mirror` input.
|
||||||
|
It is possible to specify a token to authenticate with the mirror using the `mirror-token` input.
|
||||||
|
The token will be passed in the `Authorization` header.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- 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
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// This is a reusable configuration file copied from https://github.com/actions/reusable-workflows/tree/main/reusable-configurations. Please don't make changes to this file as it's the subject of an automatic update.
|
||||||
|
import js from '@eslint/js';
|
||||||
|
import tsParser from '@typescript-eslint/parser';
|
||||||
|
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||||
|
import jest from 'eslint-plugin-jest';
|
||||||
|
import n from 'eslint-plugin-n';
|
||||||
|
import prettier from 'eslint-config-prettier';
|
||||||
|
import globals from 'globals';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/*', '!src/**', '!__tests__/**']
|
||||||
|
},
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
files: ['**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2015
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': tsPlugin,
|
||||||
|
n
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...tsPlugin.configs.recommended.rules,
|
||||||
|
'@typescript-eslint/no-require-imports': 'error',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'ts-ignore': 'allow-with-description'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-console': 'error',
|
||||||
|
yoda: 'error',
|
||||||
|
'prefer-const': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
destructuring: 'all'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-control-regex': 'off',
|
||||||
|
'no-constant-condition': ['error', {checkLoops: false}],
|
||||||
|
'no-undef': 'off',
|
||||||
|
'no-useless-assignment': 'off',
|
||||||
|
'n/no-extraneous-import': 'error'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['**/*{test,spec}.ts'],
|
||||||
|
plugins: {jest},
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.jest
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...jest.configs['flat/recommended'].rules,
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'jest/no-standalone-expect': 'off',
|
||||||
|
'jest/no-conditional-expect': 'off',
|
||||||
|
'no-console': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
prettier
|
||||||
|
];
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
clearMocks: true,
|
|
||||||
moduleFileExtensions: ['js', 'ts'],
|
|
||||||
testEnvironment: 'node',
|
|
||||||
testMatch: ['**/*.test.ts'],
|
|
||||||
testRunner: 'jest-circus/runner',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.ts$': 'ts-jest'
|
|
||||||
},
|
|
||||||
verbose: true
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
roots: ['<rootDir>'],
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': [
|
||||||
|
'ts-jest',
|
||||||
|
{
|
||||||
|
useESM: true,
|
||||||
|
diagnostics: {
|
||||||
|
ignoreCodes: [151002]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
extensionsToTreatAsEsm: ['.ts'],
|
||||||
|
transformIgnorePatterns: ['node_modules/(?!(@actions)/)'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^(\\.{1,2}/.*)\\.js$': '$1'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
}
|
||||||
Generated
+3598
-2567
File diff suppressed because it is too large
Load Diff
+39
-32
@@ -1,16 +1,20 @@
|
|||||||
{
|
{
|
||||||
"name": "setup-node",
|
"name": "setup-node",
|
||||||
"version": "4.0.0",
|
"version": "7.0.0",
|
||||||
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "setup node action",
|
"description": "setup node action",
|
||||||
"main": "lib/setup-node.js",
|
"main": "lib/setup-node.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=24.0.0"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ncc build -o dist/setup src/setup-node.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
"build": "ncc build -o dist/setup src/setup-node.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
||||||
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
|
"format": "prettier --no-error-on-unmatched-pattern --write \"**/*.{ts,yml,yaml}\"",
|
||||||
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"",
|
"format-check": "prettier --no-error-on-unmatched-pattern --check \"**/*.{ts,yml,yaml}\"",
|
||||||
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"",
|
"lint": "eslint \"**/*.ts\"",
|
||||||
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix",
|
"lint:fix": "eslint \"**/*.ts\" --fix",
|
||||||
"test": "jest --coverage",
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --coverage",
|
||||||
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -25,33 +29,36 @@
|
|||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/cache": "^3.2.4",
|
"@actions/cache": "^6.2.0",
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^3.0.1",
|
||||||
"@actions/exec": "^1.1.0",
|
"@actions/exec": "^3.0.0",
|
||||||
"@actions/github": "^5.1.1",
|
"@actions/github": "^9.1.1",
|
||||||
"@actions/glob": "^0.4.0",
|
"@actions/glob": "^0.7.0",
|
||||||
"@actions/http-client": "^2.2.1",
|
"@actions/http-client": "^4.0.1",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/io": "^3.0.2",
|
||||||
"@actions/tool-cache": "^2.0.1",
|
"@actions/tool-cache": "^4.0.0",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.8.5"
|
||||||
"uuid": "^9.0.1"
|
},
|
||||||
|
"overrides": {
|
||||||
|
"brace-expansion": "5.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.12",
|
"@eslint/js": "^10.0.1",
|
||||||
"@types/node": "^20.11.25",
|
"@jest/globals": "^30.4.1",
|
||||||
"@types/semver": "^7.5.8",
|
"@types/node": "^24.1.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
"@types/semver": "^7.7.0",
|
||||||
"@typescript-eslint/parser": "^5.54.0",
|
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
||||||
"@vercel/ncc": "^0.38.0",
|
"@typescript-eslint/parser": "^8.62.0",
|
||||||
"eslint": "^8.57.0",
|
"@vercel/ncc": "^0.44.0",
|
||||||
"eslint-config-prettier": "^8.6.0",
|
"eslint": "^10.5.0",
|
||||||
"eslint-plugin-jest": "^27.9.0",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-jest": "^29.15.2",
|
||||||
"jest": "^29.7.0",
|
"eslint-plugin-n": "^18.1.0",
|
||||||
"jest-circus": "^29.7.0",
|
"globals": "^17.7.0",
|
||||||
"jest-each": "^29.7.0",
|
"jest": "^30.4.2",
|
||||||
"prettier": "^2.8.4",
|
"jest-each": "^30.4.1",
|
||||||
"ts-jest": "^29.1.2",
|
"prettier": "^3.8.4",
|
||||||
"typescript": "^5.4.2"
|
"ts-jest": "^29.4.11",
|
||||||
|
"typescript": "^6.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-14
@@ -4,7 +4,7 @@ import * as path from 'path';
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as github from '@actions/github';
|
import * as github from '@actions/github';
|
||||||
|
|
||||||
export function configAuthentication(registryUrl: string, alwaysAuth: string) {
|
export function configAuthentication(registryUrl: string) {
|
||||||
const npmrc: string = path.resolve(
|
const npmrc: string = path.resolve(
|
||||||
process.env['RUNNER_TEMP'] || process.cwd(),
|
process.env['RUNNER_TEMP'] || process.cwd(),
|
||||||
'.npmrc'
|
'.npmrc'
|
||||||
@@ -13,14 +13,10 @@ export function configAuthentication(registryUrl: string, alwaysAuth: string) {
|
|||||||
registryUrl += '/';
|
registryUrl += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
writeRegistryToFile(registryUrl, npmrc, alwaysAuth);
|
writeRegistryToFile(registryUrl, npmrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeRegistryToFile(
|
function writeRegistryToFile(registryUrl: string, fileLocation: string) {
|
||||||
registryUrl: string,
|
|
||||||
fileLocation: string,
|
|
||||||
alwaysAuth: string
|
|
||||||
) {
|
|
||||||
let scope: string = core.getInput('scope');
|
let scope: string = core.getInput('scope');
|
||||||
if (!scope && registryUrl.indexOf('npm.pkg.github.com') > -1) {
|
if (!scope && registryUrl.indexOf('npm.pkg.github.com') > -1) {
|
||||||
scope = github.context.repo.owner;
|
scope = github.context.repo.owner;
|
||||||
@@ -47,13 +43,11 @@ function writeRegistryToFile(
|
|||||||
const authString: string =
|
const authString: string =
|
||||||
registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}';
|
registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}';
|
||||||
const registryString = `${scope}registry=${registryUrl}`;
|
const registryString = `${scope}registry=${registryUrl}`;
|
||||||
const alwaysAuthString = `always-auth=${alwaysAuth}`;
|
newContents += `${authString}${os.EOL}${registryString}`;
|
||||||
newContents += `${authString}${os.EOL}${registryString}${os.EOL}${alwaysAuthString}`;
|
|
||||||
fs.writeFileSync(fileLocation, newContents);
|
fs.writeFileSync(fileLocation, newContents);
|
||||||
core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation);
|
core.exportVariable('NPM_CONFIG_USERCONFIG', fileLocation);
|
||||||
// Export empty node_auth_token if didn't exist so npm doesn't complain about not being able to find it
|
// Only export NODE_AUTH_TOKEN if explicitly provided by user
|
||||||
core.exportVariable(
|
if (Object.prototype.hasOwnProperty.call(process.env, 'NODE_AUTH_TOKEN')) {
|
||||||
'NODE_AUTH_TOKEN',
|
core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN);
|
||||||
process.env.NODE_AUTH_TOKEN || 'XXXXX-XXXXX-XXXXX-XXXXX'
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import * as core from '@actions/core';
|
|||||||
import * as glob from '@actions/glob';
|
import * as glob from '@actions/glob';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
import {
|
import {
|
||||||
getCacheDirectories,
|
getCacheDirectories,
|
||||||
getPackageManagerInfo,
|
getPackageManagerInfo,
|
||||||
repoHasYarnBerryManagedDependencies,
|
repoHasYarnBerryManagedDependencies,
|
||||||
PackageManagerInfo
|
PackageManagerInfo
|
||||||
} from './cache-utils';
|
} from './cache-utils.js';
|
||||||
|
|
||||||
export const restoreCache = async (
|
export const restoreCache = async (
|
||||||
packageManager: string,
|
packageManager: string,
|
||||||
@@ -21,6 +22,7 @@ export const restoreCache = async (
|
|||||||
throw new Error(`Caching for '${packageManager}' is not supported`);
|
throw new Error(`Caching for '${packageManager}' is not supported`);
|
||||||
}
|
}
|
||||||
const platform = process.env.RUNNER_OS;
|
const platform = process.env.RUNNER_OS;
|
||||||
|
const arch = os.arch();
|
||||||
|
|
||||||
const cachePaths = await getCacheDirectories(
|
const cachePaths = await getCacheDirectories(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
@@ -38,11 +40,12 @@ export const restoreCache = async (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyPrefix = `node-cache-${platform}-${packageManager}`;
|
const keyPrefix = `node-cache-${platform}-${arch}-${packageManager}`;
|
||||||
const primaryKey = `${keyPrefix}-${fileHash}`;
|
const primaryKey = `${keyPrefix}-${fileHash}`;
|
||||||
core.debug(`primary key is ${primaryKey}`);
|
core.debug(`primary key is ${primaryKey}`);
|
||||||
|
|
||||||
core.saveState(State.CachePrimaryKey, primaryKey);
|
core.saveState(State.CachePrimaryKey, primaryKey);
|
||||||
|
core.setOutput('cache-primary-key', primaryKey);
|
||||||
|
|
||||||
const isManagedByYarnBerry = await repoHasYarnBerryManagedDependencies(
|
const isManagedByYarnBerry = await repoHasYarnBerryManagedDependencies(
|
||||||
packageManagerInfo,
|
packageManagerInfo,
|
||||||
@@ -59,6 +62,8 @@ export const restoreCache = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
core.setOutput('cache-hit', Boolean(cacheKey));
|
core.setOutput('cache-hit', Boolean(cacheKey));
|
||||||
|
core.setOutput('cache-matched-key', cacheKey);
|
||||||
|
core.debug(`cache-matched-key is ${cacheKey}`);
|
||||||
|
|
||||||
if (!cacheKey) {
|
if (!cacheKey) {
|
||||||
core.info(`${packageManager} cache is not found`);
|
core.info(`${packageManager} cache is not found`);
|
||||||
|
|||||||
+8
-3
@@ -1,12 +1,13 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as cache from '@actions/cache';
|
import * as cache from '@actions/cache';
|
||||||
|
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
import {getPackageManagerInfo} from './cache-utils';
|
import {getPackageManagerInfo} from './cache-utils.js';
|
||||||
|
|
||||||
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
||||||
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
||||||
// throw an uncaught exception. Instead of failing this action, just warn.
|
// throw an uncaught exception. Instead of failing this action, just warn.
|
||||||
|
|
||||||
process.on('uncaughtException', e => {
|
process.on('uncaughtException', e => {
|
||||||
const warningPrefix = '[warning]';
|
const warningPrefix = '[warning]';
|
||||||
core.info(`${warningPrefix}${e.message}`);
|
core.info(`${warningPrefix}${e.message}`);
|
||||||
@@ -61,7 +62,11 @@ const cachePackages = async (packageManager: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cacheId = await cache.saveCache(cachePaths, primaryKey);
|
const cacheId = await cache.saveCache(cachePaths, primaryKey);
|
||||||
if (cacheId == -1) {
|
if (cacheId === -1) {
|
||||||
|
// saveCache returns -1 without throwing when the cache was not saved, e.g.
|
||||||
|
// a reserve collision or a read-only token (fork PR). @actions/cache has
|
||||||
|
// already logged the reason at the appropriate severity, so just trace it.
|
||||||
|
core.debug(`Cache was not saved for the key: ${primaryKey}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-9
@@ -4,7 +4,7 @@ import * as cache from '@actions/cache';
|
|||||||
import * as glob from '@actions/glob';
|
import * as glob from '@actions/glob';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import {unique} from './util';
|
import {unique} from './util.js';
|
||||||
|
|
||||||
export interface PackageManagerInfo {
|
export interface PackageManagerInfo {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -91,7 +91,7 @@ export const getCommandOutputNotEmpty = async (
|
|||||||
error: string,
|
error: string,
|
||||||
cwd?: string
|
cwd?: string
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const stdOut = getCommandOutput(toolCommand, cwd);
|
const stdOut = await getCommandOutput(toolCommand, cwd);
|
||||||
if (!stdOut) {
|
if (!stdOut) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
@@ -167,14 +167,12 @@ const getCacheDirectoriesFromCacheDependencyPath = async (
|
|||||||
packageManagerInfo: PackageManagerInfo,
|
packageManagerInfo: PackageManagerInfo,
|
||||||
cacheDependencyPath: string
|
cacheDependencyPath: string
|
||||||
): Promise<string[]> => {
|
): Promise<string[]> => {
|
||||||
const projectDirectories = await getProjectDirectoriesFromCacheDependencyPath(
|
const projectDirectories =
|
||||||
cacheDependencyPath
|
await getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath);
|
||||||
);
|
|
||||||
const cacheFoldersPaths = await Promise.all(
|
const cacheFoldersPaths = await Promise.all(
|
||||||
projectDirectories.map(async projectDirectory => {
|
projectDirectories.map(async projectDirectory => {
|
||||||
const cacheFolderPath = await packageManagerInfo.getCacheFolderPath(
|
const cacheFolderPath =
|
||||||
projectDirectory
|
await packageManagerInfo.getCacheFolderPath(projectDirectory);
|
||||||
);
|
|
||||||
core.debug(
|
core.debug(
|
||||||
`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`
|
`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`
|
||||||
);
|
);
|
||||||
@@ -295,7 +293,13 @@ export function isGhes(): boolean {
|
|||||||
const ghUrl = new URL(
|
const ghUrl = new URL(
|
||||||
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
|
||||||
);
|
);
|
||||||
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM';
|
|
||||||
|
const hostname = ghUrl.hostname.trimEnd().toUpperCase();
|
||||||
|
const isGitHubHost = hostname === 'GITHUB.COM';
|
||||||
|
const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM');
|
||||||
|
const isLocalHost = hostname.endsWith('.LOCALHOST');
|
||||||
|
|
||||||
|
return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCacheFeatureAvailable(): boolean {
|
export function isCacheFeatureAvailable(): boolean {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import * as tc from '@actions/tool-cache';
|
|||||||
|
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
|
||||||
import BaseDistribution from './base-distribution';
|
import BaseDistribution from './base-distribution.js';
|
||||||
import {NodeInputs} from './base-models';
|
import {NodeInputs} from './base-models.js';
|
||||||
|
|
||||||
export default abstract class BasePrereleaseNodejs extends BaseDistribution {
|
export default abstract class BasePrereleaseNodejs extends BaseDistribution {
|
||||||
protected abstract distribution: string;
|
protected abstract distribution: string;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import {v4 as uuidv4} from 'uuid';
|
|
||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import * as hc from '@actions/http-client';
|
import * as hc from '@actions/http-client';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
@@ -10,8 +9,8 @@ import * as assert from 'assert';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from './base-models';
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from './base-models.js';
|
||||||
|
|
||||||
export default abstract class BaseDistribution {
|
export default abstract class BaseDistribution {
|
||||||
protected httpClient: hc.HttpClient;
|
protected httpClient: hc.HttpClient;
|
||||||
@@ -24,12 +23,11 @@ export default abstract class BaseDistribution {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract getDistributionUrl(): string;
|
protected abstract getDistributionUrl(mirror: string): string;
|
||||||
|
|
||||||
public async setupNodeJs() {
|
public async setupNodeJs() {
|
||||||
let nodeJsVersions: INodeVersion[] | undefined;
|
|
||||||
if (this.nodeInfo.checkLatest) {
|
if (this.nodeInfo.checkLatest) {
|
||||||
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
const evaluatedVersion = await this.findVersionInDist(undefined);
|
||||||
this.nodeInfo.versionSpec = evaluatedVersion;
|
this.nodeInfo.versionSpec = evaluatedVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +35,7 @@ export default abstract class BaseDistribution {
|
|||||||
if (toolPath) {
|
if (toolPath) {
|
||||||
core.info(`Found in cache @ ${toolPath}`);
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
} else {
|
} else {
|
||||||
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
const evaluatedVersion = await this.findVersionInDist(undefined);
|
||||||
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
||||||
toolPath = await this.downloadNodejs(toolName);
|
toolPath = await this.downloadNodejs(toolName);
|
||||||
}
|
}
|
||||||
@@ -97,10 +95,19 @@ export default abstract class BaseDistribution {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async getNodeJsVersions(): Promise<INodeVersion[]> {
|
protected async getNodeJsVersions(): Promise<INodeVersion[]> {
|
||||||
const initialUrl = this.getDistributionUrl();
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
const dataUrl = `${initialUrl}/index.json`;
|
const dataUrl = `${initialUrl}/index.json`;
|
||||||
|
|
||||||
const response = await this.httpClient.getJson<INodeVersion[]>(dataUrl);
|
const headers = {};
|
||||||
|
|
||||||
|
if (this.nodeInfo.mirrorToken) {
|
||||||
|
headers['Authorization'] = this.nodeInfo.mirrorToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.httpClient.getJson<INodeVersion[]>(
|
||||||
|
dataUrl,
|
||||||
|
headers
|
||||||
|
);
|
||||||
return response.result || [];
|
return response.result || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +124,7 @@ export default abstract class BaseDistribution {
|
|||||||
? `${fileName}.zip`
|
? `${fileName}.zip`
|
||||||
: `${fileName}.7z`
|
: `${fileName}.7z`
|
||||||
: `${fileName}.tar.gz`;
|
: `${fileName}.tar.gz`;
|
||||||
const initialUrl = this.getDistributionUrl();
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
const url = `${initialUrl}/v${version}/${urlFileName}`;
|
const url = `${initialUrl}/v${version}/${urlFileName}`;
|
||||||
|
|
||||||
return <INodeVersionInfo>{
|
return <INodeVersionInfo>{
|
||||||
@@ -134,7 +141,11 @@ export default abstract class BaseDistribution {
|
|||||||
`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`
|
`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
downloadPath = await tc.downloadTool(info.downloadUrl);
|
downloadPath = await tc.downloadTool(
|
||||||
|
info.downloadUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
err instanceof tc.HTTPError &&
|
err instanceof tc.HTTPError &&
|
||||||
@@ -150,29 +161,31 @@ export default abstract class BaseDistribution {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolPath = await this.extractArchive(downloadPath, info);
|
const toolPath = await this.extractArchive(downloadPath, info, true);
|
||||||
core.info('Done');
|
core.info('Done');
|
||||||
|
|
||||||
return toolPath;
|
return toolPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected validRange(versionSpec: string) {
|
protected validRange(versionSpec: string): {
|
||||||
let options: semver.RangeOptions | undefined;
|
range: string;
|
||||||
|
options: semver.RangeOptions | undefined;
|
||||||
|
} {
|
||||||
const c = semver.clean(versionSpec) || '';
|
const c = semver.clean(versionSpec) || '';
|
||||||
const valid = semver.valid(c) ?? versionSpec;
|
const valid = semver.valid(c) ?? versionSpec;
|
||||||
|
|
||||||
return {range: valid, options};
|
return {range: valid, options: undefined};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async acquireWindowsNodeFromFallbackLocation(
|
protected async acquireWindowsNodeFromFallbackLocation(
|
||||||
version: string,
|
version: string,
|
||||||
arch: string = os.arch()
|
arch: string = os.arch()
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const initialUrl = this.getDistributionUrl();
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
const osArch: string = this.translateArchToDistUrl(arch);
|
const osArch: string = this.translateArchToDistUrl(arch);
|
||||||
|
|
||||||
// Create temporary folder to download to
|
// Create temporary folder to download to
|
||||||
const tempDownloadFolder = `temp_${uuidv4()}`;
|
const tempDownloadFolder = `temp_${crypto.randomUUID()}`;
|
||||||
const tempDirectory = process.env['RUNNER_TEMP'] || '';
|
const tempDirectory = process.env['RUNNER_TEMP'] || '';
|
||||||
assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
|
assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
|
||||||
const tempDir: string = path.join(tempDirectory, tempDownloadFolder);
|
const tempDir: string = path.join(tempDirectory, tempDownloadFolder);
|
||||||
@@ -185,18 +198,34 @@ export default abstract class BaseDistribution {
|
|||||||
|
|
||||||
core.info(`Downloading only node binary from ${exeUrl}`);
|
core.info(`Downloading only node binary from ${exeUrl}`);
|
||||||
|
|
||||||
const exePath = await tc.downloadTool(exeUrl);
|
const exePath = await tc.downloadTool(
|
||||||
|
exeUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
||||||
const libPath = await tc.downloadTool(libUrl);
|
const libPath = await tc.downloadTool(
|
||||||
|
libUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
||||||
exeUrl = `${initialUrl}/v${version}/node.exe`;
|
exeUrl = `${initialUrl}/v${version}/node.exe`;
|
||||||
libUrl = `${initialUrl}/v${version}/node.lib`;
|
libUrl = `${initialUrl}/v${version}/node.lib`;
|
||||||
|
|
||||||
const exePath = await tc.downloadTool(exeUrl);
|
const exePath = await tc.downloadTool(
|
||||||
|
exeUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
||||||
const libPath = await tc.downloadTool(libUrl);
|
const libPath = await tc.downloadTool(
|
||||||
|
libUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
||||||
} else {
|
} else {
|
||||||
throw err;
|
throw err;
|
||||||
@@ -210,7 +239,8 @@ export default abstract class BaseDistribution {
|
|||||||
|
|
||||||
protected async extractArchive(
|
protected async extractArchive(
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
info: INodeVersionInfo | null
|
info: INodeVersionInfo | null,
|
||||||
|
isOfficialArchive?: boolean
|
||||||
) {
|
) {
|
||||||
//
|
//
|
||||||
// Extract
|
// Extract
|
||||||
@@ -225,12 +255,17 @@ export default abstract class BaseDistribution {
|
|||||||
// on Windows runners without PowerShell Core.
|
// on Windows runners without PowerShell Core.
|
||||||
//
|
//
|
||||||
// For default PowerShell Windows it should contain extension type to unpack it.
|
// For default PowerShell Windows it should contain extension type to unpack it.
|
||||||
if (extension === '.zip') {
|
if (extension === '.zip' && isOfficialArchive) {
|
||||||
const renamedArchive = `${downloadPath}.zip`;
|
const renamedArchive = `${downloadPath}.zip`;
|
||||||
fs.renameSync(downloadPath, renamedArchive);
|
fs.renameSync(downloadPath, renamedArchive);
|
||||||
extPath = await tc.extractZip(renamedArchive);
|
extPath = await tc.extractZip(renamedArchive);
|
||||||
} else {
|
} else {
|
||||||
const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe');
|
const _7zPath = path.join(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'../..',
|
||||||
|
'externals',
|
||||||
|
'7zr.exe'
|
||||||
|
);
|
||||||
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
||||||
}
|
}
|
||||||
// 7z extracts to folder matching file name
|
// 7z extracts to folder matching file name
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ export interface NodeInputs {
|
|||||||
auth?: string;
|
auth?: string;
|
||||||
checkLatest: boolean;
|
checkLatest: boolean;
|
||||||
stable: boolean;
|
stable: boolean;
|
||||||
|
mirror: string;
|
||||||
|
mirrorToken: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface INodeVersionInfo {
|
export interface INodeVersionInfo {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import BaseDistribution from './base-distribution';
|
import BaseDistribution from './base-distribution.js';
|
||||||
import {NodeInputs} from './base-models';
|
import {NodeInputs} from './base-models.js';
|
||||||
import NightlyNodejs from './nightly/nightly_builds';
|
import NightlyNodejs from './nightly/nightly_builds.js';
|
||||||
import OfficialBuilds from './official_builds/official_builds';
|
import OfficialBuilds from './official_builds/official_builds.js';
|
||||||
import RcBuild from './rc/rc_builds';
|
import RcBuild from './rc/rc_builds.js';
|
||||||
import CanaryBuild from './v8-canary/canary_builds';
|
import CanaryBuild from './v8-canary/canary_builds.js';
|
||||||
|
|
||||||
enum Distributions {
|
enum Distributions {
|
||||||
DEFAULT = '',
|
DEFAULT = '',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
import BasePrereleaseNodejs from '../base-distribution-prerelease.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class NightlyNodejs extends BasePrereleaseNodejs {
|
export default class NightlyNodejs extends BasePrereleaseNodejs {
|
||||||
protected distribution = 'nightly';
|
protected distribution = 'nightly';
|
||||||
@@ -7,7 +7,8 @@ export default class NightlyNodejs extends BasePrereleaseNodejs {
|
|||||||
super(nodeInfo);
|
super(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getDistributionUrl(): string {
|
protected getDistributionUrl(mirror: string): string {
|
||||||
return 'https://nodejs.org/download/nightly';
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/nightly`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import * as tc from '@actions/tool-cache';
|
import * as tc from '@actions/tool-cache';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
|
||||||
import BaseDistribution from '../base-distribution';
|
import BaseDistribution from '../base-distribution.js';
|
||||||
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models';
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models.js';
|
||||||
|
|
||||||
interface INodeRelease extends tc.IToolRelease {
|
interface INodeRelease extends tc.IToolRelease {
|
||||||
lts?: string;
|
lts?: string;
|
||||||
@@ -62,7 +63,9 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
|
|
||||||
if (toolPath) {
|
if (toolPath) {
|
||||||
core.info(`Found in cache @ ${toolPath}`);
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
|
const installedDir = toolPath;
|
||||||
this.addToolPath(toolPath);
|
this.addToolPath(toolPath);
|
||||||
|
await this.verifyNodeVersion(installedDir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,15 +87,23 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
downloadPath = await tc.downloadTool(
|
downloadPath = await tc.downloadTool(
|
||||||
versionInfo.downloadUrl,
|
versionInfo.downloadUrl,
|
||||||
undefined,
|
undefined,
|
||||||
this.nodeInfo.auth
|
this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||||
|
? this.nodeInfo.mirrorToken
|
||||||
|
: this.nodeInfo.auth
|
||||||
);
|
);
|
||||||
|
|
||||||
if (downloadPath) {
|
if (downloadPath) {
|
||||||
toolPath = await this.extractArchive(downloadPath, versionInfo);
|
toolPath = await this.extractArchive(
|
||||||
|
downloadPath,
|
||||||
|
versionInfo,
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
core.info(
|
core.info(
|
||||||
'Not found in manifest. Falling back to download directly from Node'
|
`Not found in manifest. Falling back to download directly from ${
|
||||||
|
this.nodeInfo.mirror || 'Node'
|
||||||
|
}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -115,11 +126,13 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
toolPath = await this.downloadDirectlyFromNode();
|
toolPath = await this.downloadDirectlyFromNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const installedDir = toolPath;
|
||||||
if (this.osPlat != 'win32') {
|
if (this.osPlat != 'win32') {
|
||||||
toolPath = path.join(toolPath, 'bin');
|
toolPath = path.join(toolPath, 'bin');
|
||||||
}
|
}
|
||||||
|
|
||||||
core.addPath(toolPath);
|
core.addPath(toolPath);
|
||||||
|
await this.verifyNodeVersion(installedDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected addToolPath(toolPath: string) {
|
protected addToolPath(toolPath: string) {
|
||||||
@@ -172,17 +185,46 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getDistributionUrl(): string {
|
protected getDistributionUrl(mirror: string): string {
|
||||||
return `https://nodejs.org/dist`;
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/dist`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getManifest(): Promise<tc.IToolRelease[]> {
|
private async getManifest(): Promise<tc.IToolRelease[]> {
|
||||||
core.debug('Getting manifest from actions/node-versions@main');
|
let lastError: Error | undefined;
|
||||||
return tc.getManifestFromRepo(
|
const maxAttempts = 3;
|
||||||
'actions',
|
core.debug(`Getting manifest from actions/node-versions@main`);
|
||||||
'node-versions',
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
this.nodeInfo.auth,
|
try {
|
||||||
'main'
|
const manifest = await tc.getManifestFromRepo(
|
||||||
|
'actions',
|
||||||
|
'node-versions',
|
||||||
|
this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||||
|
? this.nodeInfo.mirrorToken
|
||||||
|
: this.nodeInfo.auth,
|
||||||
|
'main'
|
||||||
|
);
|
||||||
|
if (Array.isArray(manifest) && manifest.length > 0) {
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
lastError = new Error(
|
||||||
|
`The manifest fetched is empty, truncated, or does not contain any valid tool release entries.`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
lastError = err instanceof Error ? err : new Error(String(err));
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
); // Retry after a delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`Failed to fetch a valid manifest after ${maxAttempts} attempts. Last error: ${lastError?.message}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,8 +256,8 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
alias === '*'
|
alias === '*'
|
||||||
? numbered[numbered.length - 1]
|
? numbered[numbered.length - 1]
|
||||||
: n < 0
|
: n < 0
|
||||||
? numbered[numbered.length - 1 + n]
|
? numbered[numbered.length - 1 + n]
|
||||||
: aliases[alias];
|
: aliases[alias];
|
||||||
|
|
||||||
if (!release) {
|
if (!release) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@@ -287,4 +329,30 @@ export default class OfficialBuilds extends BaseDistribution {
|
|||||||
private isLatestSyntax(versionSpec): boolean {
|
private isLatestSyntax(versionSpec): boolean {
|
||||||
return ['current', 'latest', 'node'].includes(versionSpec);
|
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.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import BaseDistribution from '../base-distribution';
|
import BaseDistribution from '../base-distribution.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class RcBuild extends BaseDistribution {
|
export default class RcBuild extends BaseDistribution {
|
||||||
constructor(nodeInfo: NodeInputs) {
|
constructor(nodeInfo: NodeInputs) {
|
||||||
super(nodeInfo);
|
super(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDistributionUrl(): string {
|
getDistributionUrl(mirror: string): string {
|
||||||
return 'https://nodejs.org/download/rc';
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/rc`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
import BasePrereleaseNodejs from '../base-distribution-prerelease.js';
|
||||||
import {NodeInputs} from '../base-models';
|
import {NodeInputs} from '../base-models.js';
|
||||||
|
|
||||||
export default class CanaryBuild extends BasePrereleaseNodejs {
|
export default class CanaryBuild extends BasePrereleaseNodejs {
|
||||||
protected distribution = 'v8-canary';
|
protected distribution = 'v8-canary';
|
||||||
@@ -7,7 +7,8 @@ export default class CanaryBuild extends BasePrereleaseNodejs {
|
|||||||
super(nodeInfo);
|
super(nodeInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getDistributionUrl(): string {
|
protected getDistributionUrl(mirror: string): string {
|
||||||
return 'https://nodejs.org/download/v8-canary';
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/v8-canary`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-14
@@ -1,14 +1,16 @@
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
import fs from 'fs';
|
||||||
|
import {fileURLToPath} from 'url';
|
||||||
|
|
||||||
import * as auth from './authutil';
|
import * as auth from './authutil.js';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {restoreCache} from './cache-restore';
|
import {restoreCache} from './cache-restore.js';
|
||||||
import {isCacheFeatureAvailable} from './cache-utils';
|
import {isCacheFeatureAvailable} from './cache-utils.js';
|
||||||
import {getNodejsDistribution} from './distributions/installer-factory';
|
import {getNodejsDistribution} from './distributions/installer-factory.js';
|
||||||
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util';
|
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util.js';
|
||||||
import {State} from './constants';
|
import {State} from './constants.js';
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
try {
|
try {
|
||||||
@@ -20,6 +22,9 @@ export async function run() {
|
|||||||
|
|
||||||
let arch = core.getInput('architecture');
|
let arch = core.getInput('architecture');
|
||||||
const cache = core.getInput('cache');
|
const cache = core.getInput('cache');
|
||||||
|
const packagemanagercache =
|
||||||
|
(core.getInput('package-manager-cache') || 'true').toUpperCase() ===
|
||||||
|
'TRUE';
|
||||||
|
|
||||||
// if architecture supplied but node-version is not
|
// if architecture supplied but node-version is not
|
||||||
// if we don't throw a warning, the already installed x64 node will be used which is not probably what user meant.
|
// if we don't throw a warning, the already installed x64 node will be used which is not probably what user meant.
|
||||||
@@ -36,6 +41,8 @@ export async function run() {
|
|||||||
if (version) {
|
if (version) {
|
||||||
const token = core.getInput('token');
|
const token = core.getInput('token');
|
||||||
const auth = !token ? undefined : `token ${token}`;
|
const auth = !token ? undefined : `token ${token}`;
|
||||||
|
const mirror = core.getInput('mirror');
|
||||||
|
const mirrorToken = core.getInput('mirror-token');
|
||||||
const stable =
|
const stable =
|
||||||
(core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
|
(core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
|
||||||
const checkLatest =
|
const checkLatest =
|
||||||
@@ -45,7 +52,9 @@ export async function run() {
|
|||||||
checkLatest,
|
checkLatest,
|
||||||
auth,
|
auth,
|
||||||
stable,
|
stable,
|
||||||
arch
|
arch,
|
||||||
|
mirror,
|
||||||
|
mirrorToken
|
||||||
};
|
};
|
||||||
const nodeDistribution = getNodejsDistribution(nodejsInfo);
|
const nodeDistribution = getNodejsDistribution(nodejsInfo);
|
||||||
await nodeDistribution.setupNodeJs();
|
await nodeDistribution.setupNodeJs();
|
||||||
@@ -54,18 +63,36 @@ export async function run() {
|
|||||||
await printEnvDetailsAndSetOutput();
|
await printEnvDetailsAndSetOutput();
|
||||||
|
|
||||||
const registryUrl: string = core.getInput('registry-url');
|
const registryUrl: string = core.getInput('registry-url');
|
||||||
const alwaysAuth: string = core.getInput('always-auth');
|
|
||||||
if (registryUrl) {
|
if (registryUrl) {
|
||||||
auth.configAuthentication(registryUrl, alwaysAuth);
|
auth.configAuthentication(registryUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache && isCacheFeatureAvailable()) {
|
const cacheDependencyPath = core.getInput('cache-dependency-path');
|
||||||
core.saveState(State.CachePackageManager, cache);
|
|
||||||
const cacheDependencyPath = core.getInput('cache-dependency-path');
|
if (isCacheFeatureAvailable()) {
|
||||||
await restoreCache(cache, cacheDependencyPath);
|
// 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');
|
const matchersPath = path.join(
|
||||||
|
path.dirname(fileURLToPath(import.meta.url)),
|
||||||
|
'../..',
|
||||||
|
'.github'
|
||||||
|
);
|
||||||
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
||||||
core.info(
|
core.info(
|
||||||
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
||||||
@@ -113,3 +140,34 @@ function resolveVersionInput(): string {
|
|||||||
|
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNameFromPackageManagerField(): string | undefined {
|
||||||
|
const npmRegex = /^(\^)?npm(@.*)?$/; // matches "npm", "npm@...", "^npm@..."
|
||||||
|
try {
|
||||||
|
const packageJson = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(process.env.GITHUB_WORKSPACE!, 'package.json'),
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
import {run} from './main';
|
import {run} from './main.js';
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|||||||
+13
-1
@@ -26,6 +26,18 @@ export function getNodeVersionFromFile(versionFilePath: string): string | null {
|
|||||||
return manifest.volta.node;
|
return manifest.volta.node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// support devEngines from npm 11
|
||||||
|
if (manifest.devEngines?.runtime) {
|
||||||
|
// find an entry with name set to node and having set a version.
|
||||||
|
// the devEngines.runtime can either be an object or an array of objects
|
||||||
|
const nodeEntry = [manifest.devEngines.runtime]
|
||||||
|
.flat()
|
||||||
|
.find(({name, version}) => name?.toLowerCase() === 'node' && version);
|
||||||
|
if (nodeEntry) {
|
||||||
|
return nodeEntry.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (manifest.engines?.node) {
|
if (manifest.engines?.node) {
|
||||||
return manifest.engines.node;
|
return manifest.engines.node;
|
||||||
}
|
}
|
||||||
@@ -93,7 +105,7 @@ async function getToolVersion(tool: string, options: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return stdout.trim();
|
return stdout.trim();
|
||||||
} catch (err) {
|
} catch {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
"target": "ES2022", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
"module": "NodeNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
"outDir": "./lib", /* Redirect output structure to the directory. */
|
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
@@ -10,5 +10,5 @@
|
|||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
"resolveJsonModule": true, /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */
|
"resolveJsonModule": true, /* Allows importing modules with a '.json' extension, which is a common practice in node projects. */
|
||||||
},
|
},
|
||||||
"exclude": ["__tests__", "lib", "node_modules"]
|
"exclude": ["__tests__", "lib", "node_modules", "jest.config.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user