Compare commits
440 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
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 | |
|
|
eff380dfbc | |
|
|
c2ac33f2c6 | |
|
|
25b062c917 | |
|
|
60edb5dd54 | |
|
|
d86ebcd40b | |
|
|
b39b52d121 | |
|
|
7247617371 | |
|
|
f3ec4ca66f | |
|
|
ec97f37504 | |
|
|
5ef044f9d0 | |
|
|
c45882a6ea | |
|
|
ee36e8b5c0 | |
|
|
8f152de45c | |
|
|
23755b521f | |
|
|
54534a2a9b | |
|
|
1a4442cacd | |
|
|
6e9e44895f | |
|
|
e52912ef25 | |
|
|
ac16ae42d7 | |
|
|
5a8d9111e3 | |
|
|
9e956a555c | |
|
|
7da2a7eb0c | |
|
|
2a017f350d | |
|
|
72c43c2d8f | |
|
|
d3ace34546 | |
|
|
acbf0586b1 | |
|
|
f1744b62b7 | |
|
|
2651591c72 | |
|
|
5e21ff4d9b | |
|
|
bea5baf987 | |
|
|
d82f92a0eb | |
|
|
ca2d4e0cdd | |
|
|
c7a93deeac | |
|
|
34050076a5 | |
|
|
f8aa08ed8e | |
|
|
e2d34eacc8 | |
|
|
ef9c88b169 | |
|
|
ea800d4ebc | |
|
|
cb95c398f6 | |
|
|
69b2dd252e | |
|
|
e33196f742 | |
|
|
c6722d36aa | |
|
|
8170e22e8f | |
|
|
698d50532e | |
|
|
869f4dd0c7 | |
|
|
10efafcbcf | |
|
|
7d16907b89 | |
|
|
d0d39bda2f | |
|
|
15a2477e08 | |
|
|
7598dbcd6e | |
|
|
a9893b0cfb | |
|
|
5b32c9063c | |
|
|
d98fa11138 | |
|
|
9d255ef245 | |
|
|
e828f9b7f3 | |
|
|
a4fcaaf314 | |
|
|
10f5623502 | |
|
|
fcd18100cc | |
|
|
962678f22c | |
|
|
7c29869aec | |
|
|
ae9f0f7448 | |
|
|
3dbcda8bc2 | |
|
|
f38519bb96 | |
|
|
9227cda3f0 | |
|
|
64ed1c7eab | |
|
|
92a57f4a93 | |
|
|
99e61d697a | |
|
|
3e8819f8f2 | |
|
|
8cd2fb28b8 | |
|
|
c406543918 | |
|
|
92a07fe466 | |
|
|
217387cf3e | |
|
|
2db3663870 | |
|
|
bbe2ac79a1 | |
|
|
f5ab623822 | |
|
|
ca97bf7f80 | |
|
|
fe4d514f1a | |
|
|
8151ea11a4 | |
|
|
772ffdda26 | |
|
|
da188081b1 | |
|
|
377c6dae40 | |
|
|
b28830cbe2 | |
|
|
676975d9aa | |
|
|
d1b197b965 | |
|
|
069a4f8926 | |
|
|
e77eaaccd3 | |
|
|
a69d45adcd | |
|
|
3ae886ede4 | |
|
|
41acaa2e85 | |
|
|
2349c84f5c | |
|
|
6bc15ab23c | |
|
|
9b8fcdc725 | |
|
|
00e1b6691b | |
|
|
16352bb09b | |
|
|
788c6ccbd0 | |
|
|
8c91899e58 | |
|
|
c81d8ad96d | |
|
|
c96ab56c5b | |
|
|
969bd26639 | |
|
|
9f3a02bbd1 | |
|
|
4cffe5c52b | |
|
|
0efefb3c0b | |
|
|
1e6f2cd312 | |
|
|
30f0e7dc5a | |
|
|
c8f0d10585 | |
|
|
b4b18e5317 | |
|
|
9efe00a002 | |
|
|
35ba06beb7 | |
|
|
9fc76ff685 | |
|
|
dc62cc63a8 | |
|
|
d04c34e7d9 | |
|
|
32f78d9bfa | |
|
|
e954e15431 | |
|
|
792255d078 | |
|
|
089aa7ea91 | |
|
|
094c36e88e | |
|
|
5bbf7221ae | |
|
|
dbb54d08f2 | |
|
|
3a1b76e782 | |
|
|
94f88d78c4 | |
|
|
348e008008 | |
|
|
d79e93a91d | |
|
|
5d6bb1273a | |
|
|
0d3aa68dd3 | |
|
|
18090dee77 | |
|
|
bb59d50268 | |
|
|
5b579f1638 | |
|
|
9f20343a3a | |
|
|
dee2a9689c | |
|
|
57cec77d94 | |
|
|
5a01179c35 | |
|
|
2a814b57e1 | |
|
|
2fddd8803e | |
|
|
ad8542ca5e | |
|
|
3d11add771 | |
|
|
072a2e3b10 | |
|
|
28ad38fe06 | |
|
|
48de4c13f6 | |
|
|
aab7cc882a | |
|
|
5b949b50c3 | |
|
|
09ba51f18e | |
|
|
b3ca1ac971 | |
|
|
78faa555e1 | |
|
|
0f15a51ed6 | |
|
|
460f88643a | |
|
|
d8b0944270 | |
|
|
0ad06209dd | |
|
|
adeb189fd8 | |
|
|
0d7418813c | |
|
|
3ac35dad40 | |
|
|
4096f07b51 | |
|
|
1c48dc5a9e | |
|
|
d86a20eb78 | |
|
|
8211e009a7 | |
|
|
dbfbe9b6da | |
|
|
9aa86428fe | |
|
|
f40b60859d | |
|
|
a03d9f05e4 | |
|
|
1a4ff5493d | |
|
|
7d610f0c26 | |
|
|
bcb9f31327 | |
|
|
73ea510762 | |
|
|
eaead3517c | |
|
|
24301fe9fb | |
|
|
11c7f932ce | |
|
|
f1702f8f38 | |
|
|
1196f2ae1b | |
|
|
c22fc3c628 | |
|
|
cdcc53e14f | |
|
|
b287e177c9 | |
|
|
eeb10cff27 | |
|
|
dbb64ac1d1 | |
|
|
82496765f3 | |
|
|
ed1a46e9f2 | |
|
|
daff393d43 | |
|
|
b14573ddb9 | |
|
|
7569de03e7 | |
|
|
b20a2561b9 | |
|
|
ea3459bb45 | |
|
|
141334fcd1 | |
|
|
808c8f917f | |
|
|
fd1b409bc3 | |
|
|
9a03ebd9cc | |
|
|
45e544a71a | |
|
|
8c66f89aef | |
|
|
3601f2a33e | |
|
|
4d62fafc05 | |
|
|
17f8bd9264 | |
|
|
b067f78ed3 | |
|
|
0bd06765ef | |
|
|
25184c4485 | |
|
|
337fdf2194 | |
|
|
86bf502a33 | |
|
|
146c4d84a5 | |
|
|
56337c4255 | |
|
|
76d3665dc0 | |
|
|
5b52f097d3 | |
|
|
75716637dc | |
|
|
546fd45825 | |
|
|
bacd6b4b3a | |
|
|
bed538bd04 | |
|
|
f3e93d1b9d | |
|
|
ed960c9656 | |
|
|
77f43a4400 | |
|
|
9ced9a43a2 | |
|
|
3e90744edf | |
|
|
28df918a56 | |
|
|
f099707f6e | |
|
|
f658dc5bd7 | |
|
|
5e2e068714 | |
|
|
7a0f7a9962 | |
|
|
7b558676dd | |
|
|
1f8c6b94b2 | |
|
|
9a74eb4e64 | |
|
|
04c56d2f95 | |
|
|
d08cf22211 | |
|
|
360ab8b75b | |
|
|
fd4bd829f2 | |
|
|
a4b8ed2f4e | |
|
|
270253e841 | |
|
|
d1178716db | |
|
|
041bafb672 | |
|
|
996306e892 | |
|
|
85d4122530 | |
|
|
928244ce45 | |
|
|
25316bbc1f | |
|
|
76a4cff9ae | |
|
|
a869e7cc2d | |
|
|
a0cdab1110 | |
|
|
4d0182af5e | |
|
|
896ee80599 | |
|
|
d21ecc7c5b | |
|
|
d6e3b5539e | |
|
|
db716f881a | |
|
|
c24389f79e | |
|
|
004fb83876 | |
|
|
180b77fed2 | |
|
|
9f31dbbbc4 | |
|
|
aa759c6c94 | |
|
|
0ae03de2b7 | |
|
|
4bc87b8e10 | |
|
|
b96348a4e9 | |
|
|
3af302a4f2 | |
|
|
f452812b44 | |
|
|
f43d8a1f27 | |
|
|
e93556ca66 | |
|
|
0453e516eb | |
|
|
399982b368 | |
|
|
d278e78bdd | |
|
|
fdbc93ea6e | |
|
|
f24e78e0f6 | |
|
|
3c1fbef7d3 | |
|
|
5aa72c249a | |
|
|
d9aab2aba4 | |
|
|
162ddf7c40 | |
|
|
38d90ce44d | |
|
|
e1e1d4c853 | |
|
|
40f85c8c97 | |
|
|
e215578cb0 | |
|
|
9af89732f0 | |
|
|
cfe70f9fe8 | |
|
|
b259ba49fe | |
|
|
55e10498cf | |
|
|
bcb4cecf9b | |
|
|
72922790cb | |
|
|
48042628a6 | |
|
|
8624ddbdce | |
|
|
282049a5b1 | |
|
|
87a21fa69e | |
|
|
e79375d669 | |
|
|
57b9c8c424 | |
|
|
4bef829942 | |
|
|
9b447f0b81 | |
|
|
d36a3314a5 | |
|
|
c2dfe2df98 | |
|
|
f2b87bb2c2 | |
|
|
d9ce83b637 | |
|
|
56c3db9dda | |
|
|
92722aec8c | |
|
|
c62861c1e7 | |
|
|
36d2ce149e | |
|
|
bcdcde00c0 | |
|
|
0ec4841e4b | |
|
|
e761a7024e | |
|
|
c8c6442c74 | |
|
|
fa68745dc0 | |
|
|
c8889b24a3 | |
|
|
88d5bfde31 | |
|
|
1088324e41 | |
|
|
05f3e9f4fa | |
|
|
3d0d4edb10 | |
|
|
5929471019 | |
|
|
1bf30534fd | |
|
|
ae26aaf1b5 | |
|
|
5fddb2d510 | |
|
|
8c35c6c880 | |
|
|
24265e14d4 | |
|
|
5c355be170 | |
|
|
46071b5c7a | |
|
|
05f0551dbd | |
|
|
23bdee16bc | |
|
|
4257181919 | |
|
|
7f4d5225d8 | |
|
|
68fafecfa4 | |
|
|
c46424eee2 | |
|
|
1d846de8c0 | |
|
|
d06286dee6 | |
|
|
d8c1b596a6 | |
|
|
2bfa943ffb | |
|
|
6c8a6e529a | |
|
|
27082cecf3 | |
|
|
05e7d6cc23 | |
|
|
d6fea3c137 | |
|
|
4667755142 | |
|
|
2bbfc7698a | |
|
|
5984462aaa | |
|
|
aabe026707 | |
|
|
b7cfa22539 | |
|
|
70b9252472 | |
|
|
7d147e85af | |
|
|
66dfac5328 | |
|
|
312bbeecfd | |
|
|
26a6dcb73b | |
|
|
f4215cc0e0 | |
|
|
14e150d2b1 | |
|
|
5da2d61209 | |
|
|
a600caf7cc | |
|
|
8dde5bc004 | |
|
|
c6fd00ceb9 | |
|
|
2f532cef8f | |
|
|
c211e95e60 | |
|
|
99d584aa06 | |
|
|
2a5c060ce2 | |
|
|
2801f51f90 | |
|
|
0a161fe2f9 | |
|
|
63fffe4411 | |
|
|
a83bf51129 | |
|
|
922f2f9468 | |
|
|
7a3669aa8a | |
|
|
66f3e9f7ce | |
|
|
82271389a6 | |
|
|
2e40994c28 | |
|
|
c7e4d38541 | |
|
|
3eb4bc9d68 | |
|
|
bf47decdc2 | |
|
|
6afbfca452 | |
|
|
3e84c3523b | |
|
|
6668516d2a | |
|
|
f27ebaf08b | |
|
|
42d59fbc36 | |
|
|
2d8887128d | |
|
|
0635b31a61 | |
|
|
59e61b8951 | |
|
|
321b6ccb03 | |
|
|
1ae8f4b1fd | |
|
|
0e2f9cde8b | |
|
|
e434342e4e | |
|
|
7c6182c745 | |
|
|
a47b2f66c6 | |
|
|
89ea387bde | |
|
|
44c9c18728 | |
|
|
e715d9a456 | |
|
|
b1f2e78536 | |
|
|
55897e37f3 | |
|
|
46903d1fb1 | |
|
|
28505ad4d3 | |
|
|
2d53d29868 | |
|
|
1e163ded31 | |
|
|
ffde538781 | |
|
|
0cc027b656 | |
|
|
339aa41a8d | |
|
|
641e88986e | |
|
|
e99a7e62b2 | |
|
|
0dc69b3a71 | |
|
|
52eb8a7524 | |
|
|
83c9f7a7df | |
|
|
ada4b782b3 | |
|
|
6b7f6c9fa6 |
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Ignore list
|
||||||
|
/*
|
||||||
|
|
||||||
|
# Do not ignore these folders:
|
||||||
|
!__tests__/
|
||||||
|
!src/
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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,2 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
.licenses/** -diff linguist-generated=true
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a bug report
|
||||||
|
title: ''
|
||||||
|
labels: bug, needs triage
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--- Please direct any generic questions related to actions to our support community forum at https://github.community/c/code-to-cloud/github-actions/41 --->
|
||||||
|
<!--- Before opening up a new bug report, please make sure to check for similar existing issues -->
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Action version:**
|
||||||
|
Specify the action version
|
||||||
|
|
||||||
|
**Platform:**
|
||||||
|
- [ ] Ubuntu
|
||||||
|
- [ ] macOS
|
||||||
|
- [ ] Windows
|
||||||
|
|
||||||
|
**Runner type:**
|
||||||
|
- [ ] Hosted
|
||||||
|
- [ ] Self-hosted
|
||||||
|
|
||||||
|
**Tools version:**
|
||||||
|
<!--- Please specify versions of node and package manager (npm, yarn, pnpm and etc)-->
|
||||||
|
|
||||||
|
**Repro steps:**
|
||||||
|
A description with steps to reproduce the issue. If you have a public example or repo to share, please provide the link.
|
||||||
|
|
||||||
|
**Expected behavior:**
|
||||||
|
A description of what you expected to happen.
|
||||||
|
|
||||||
|
**Actual behavior:**
|
||||||
|
A description of what is actually happening.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature request, needs triage
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
<!--- Please direct any generic questions related to actions to our support community forum at https://github.community/c/code-to-cloud/github-actions/41 --->
|
||||||
|
<!--- Before opening up a new feature request, please make sure to check for similar existing issues and pull requests -->
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Describe your proposal.
|
||||||
|
|
||||||
|
**Justification:**
|
||||||
|
Justification or a use case for your proposal.
|
||||||
|
|
||||||
|
**Are you willing to submit a PR?**
|
||||||
|
<!--- We accept contributions! -->
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
**Description:**
|
||||||
|
Describe your changes.
|
||||||
|
|
||||||
|
**Related issue:**
|
||||||
|
Add link to the related issue.
|
||||||
|
|
||||||
|
**Check list:**
|
||||||
|
- [ ] Mark if documentation changes are required.
|
||||||
|
- [ ] Mark if tests were added or updated to cover the changes.
|
||||||
|
|
@ -4,14 +4,15 @@
|
||||||
"owner": "tsc",
|
"owner": "tsc",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(?:\\s+\\d+\\>)?([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\)\\s*:\\s+(error|warning|info)\\s+(\\w{1,2}\\d+)\\s*:\\s*(.*)$",
|
"regexp": "^([^\\s].*)[\\(:](\\d+)[,:](\\d+)(?:\\):\\s+|\\s+-\\s+)(error|warning|info)\\s+TS(\\d+)\\s*:\\s*(.*)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"location": 2,
|
"line": 2,
|
||||||
"severity": 3,
|
"column": 3,
|
||||||
"code": 4,
|
"severity": 4,
|
||||||
"message": 5
|
"code": 5,
|
||||||
|
"message": 6
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
name: Basic validation
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/*
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-basic-validation:
|
||||||
|
name: Basic validation
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/basic-validation.yml@main
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
name: Check dist
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-check-dist:
|
||||||
|
name: Check dist/
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/check-dist.yml@main
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
name: CodeQL analysis
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * 0'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-codeQL-analysis:
|
||||||
|
name: CodeQL analysis
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/codeql-analysis.yml@main
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
name: e2e-cache
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/*
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
node-npm-depencies-caching:
|
||||||
|
name: Test npm (Node ${{ matrix.node-version}}, ${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Clean global cache
|
||||||
|
run: npm cache clean --force
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'npm'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm install
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
node-pnpm-depencies-caching:
|
||||||
|
name: Test pnpm (Node ${{ matrix.node-version}}, ${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 6.10.0
|
||||||
|
- name: Generate pnpm file
|
||||||
|
run: pnpm install
|
||||||
|
- name: Remove dependencies
|
||||||
|
shell: pwsh
|
||||||
|
run: Remove-Item node_modules -Force -Recurse
|
||||||
|
- name: Clean global cache
|
||||||
|
run: rm -rf ~/.pnpm-store
|
||||||
|
shell: bash
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'pnpm'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pnpm install
|
||||||
|
- name: Verify node and pnpm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
node-yarn1-depencies-caching:
|
||||||
|
name: Test yarn 1 (Node ${{ matrix.node-version}}, ${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Yarn version
|
||||||
|
run: yarn --version
|
||||||
|
- name: Generate yarn file
|
||||||
|
run: yarn install --ignore-engines
|
||||||
|
- name: Remove dependencies
|
||||||
|
shell: pwsh
|
||||||
|
run: Remove-Item node_modules -Force -Recurse
|
||||||
|
- name: Clean global cache
|
||||||
|
run: yarn cache clean
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --ignore-engines
|
||||||
|
- name: Verify node and yarn
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
node-yarn3-depencies-caching:
|
||||||
|
name: Test yarn 3 (Node ${{ matrix.node-version}}, ${{ matrix.os }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
YARN_ENABLE_IMMUTABLE_INSTALLS: false
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Update yarn
|
||||||
|
run: yarn set version 3.6.4
|
||||||
|
- name: Yarn version
|
||||||
|
run: yarn --version
|
||||||
|
- name: Generate simple .yarnrc.yml
|
||||||
|
run: |
|
||||||
|
echo "nodeLinker: node-modules" >> .yarnrc.yml
|
||||||
|
- name: Generate yarn file
|
||||||
|
run: yarn install
|
||||||
|
- name: Remove dependencies
|
||||||
|
shell: pwsh
|
||||||
|
run: Remove-Item node_modules -Force -Recurse
|
||||||
|
- name: Clean global cache
|
||||||
|
run: yarn cache clean --all
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install
|
||||||
|
- name: Verify node and yarn
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
yarn-subprojects:
|
||||||
|
name: Test yarn subprojects
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: prepare sub-projects
|
||||||
|
run: __tests__/prepare-yarn-subprojects.sh yarn1
|
||||||
|
|
||||||
|
# expect
|
||||||
|
# - no errors
|
||||||
|
# - log
|
||||||
|
# ##[debug]Cache Paths:
|
||||||
|
# ##[debug]["sub2/.yarn/cache","sub3/.yarn/cache","../../../.cache/yarn/v6"]
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/*.lock
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
yarn-subprojects-berry-local:
|
||||||
|
name: Test yarn subprojects all locally managed
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: prepare sub-projects
|
||||||
|
run: __tests__/prepare-yarn-subprojects.sh keepcache keepcache
|
||||||
|
|
||||||
|
# expect
|
||||||
|
# - no errors
|
||||||
|
# - log
|
||||||
|
# ##[info]All dependencies are managed locally by yarn3, the previous cache can be used
|
||||||
|
# ##[debug]["node-cache-Linux-yarn-401024703386272f1a950c9f014cbb1bb79a7a5b6e1fb00e8b90d06734af41ee","node-cache-Linux-yarn"]
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
cache-dependency-path: |
|
||||||
|
sub2/*.lock
|
||||||
|
sub3/*.lock
|
||||||
|
|
||||||
|
yarn-subprojects-berry-global:
|
||||||
|
name: Test yarn subprojects some locally managed
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: prepare sub-projects
|
||||||
|
run: __tests__/prepare-yarn-subprojects.sh global
|
||||||
|
|
||||||
|
# expect
|
||||||
|
# - no errors
|
||||||
|
# - log must
|
||||||
|
# ##[debug]"/home/runner/work/setup-node-test/setup-node-test/sub2" dependencies are managed by yarn 3 locally
|
||||||
|
# ##[debug]"/home/runner/work/setup-node-test/setup-node-test/sub3" dependencies are not managed by yarn 3 locally
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
cache-dependency-path: |
|
||||||
|
sub2/*.lock
|
||||||
|
sub3/*.lock
|
||||||
|
|
||||||
|
yarn-subprojects-berry-git:
|
||||||
|
name: Test yarn subprojects managed by git
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: prepare sub-projects
|
||||||
|
run: /bin/bash __tests__/prepare-yarn-subprojects.sh keepcache
|
||||||
|
|
||||||
|
# expect
|
||||||
|
# - no errors
|
||||||
|
# - log
|
||||||
|
# [debug]"/home/runner/work/setup-node-test/setup-node-test/sub2" has .yarn/cache - dependencies are kept in the repository
|
||||||
|
# [debug]"/home/runner/work/setup-node-test/setup-node-test/sub3" has .yarn/cache - dependencies are kept in the repository
|
||||||
|
# [debug]["node-cache-Linux-yarn-401024703386272f1a950c9f014cbb1bb79a7a5b6e1fb00e8b90d06734af41ee"]
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: 'yarn'
|
||||||
|
cache-dependency-path: |
|
||||||
|
sub2/*.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-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- 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-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- 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
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: Licensed
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-licensed:
|
||||||
|
name: Licensed
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/licensed.yml@main
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
name: proxy
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/*
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-proxy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ubuntu:latest
|
||||||
|
options: --dns 127.0.0.1
|
||||||
|
services:
|
||||||
|
squid-proxy:
|
||||||
|
image: ubuntu/squid:latest
|
||||||
|
ports:
|
||||||
|
- 3128:3128
|
||||||
|
env:
|
||||||
|
https_proxy: http://squid-proxy:3128
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Clear tool cache
|
||||||
|
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||||
|
- name: Setup node 24
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: 24.x
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
|
test-bypass-proxy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
https_proxy: http://no-such-proxy:3128
|
||||||
|
no_proxy: api.github.com,github.com,nodejs.org,registry.npmjs.org,*.s3.amazonaws.com,s3.amazonaws.com
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Clear tool cache
|
||||||
|
run: rm -rf $RUNNER_TOOL_CACHE/*
|
||||||
|
- name: Setup node 24
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
- name: Verify node and npm
|
||||||
|
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@v6
|
||||||
|
- name: Publish
|
||||||
|
id: publish
|
||||||
|
uses: actions/publish-immutable-action@v0.0.4
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Release new action version
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [released]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
TAG_NAME:
|
||||||
|
description: 'Tag name that the major tag will point to'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_tag:
|
||||||
|
name: Update the major tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
|
||||||
|
environment:
|
||||||
|
name: releaseNewActionVersion
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Update the ${{ env.TAG_NAME }} tag
|
||||||
|
uses: actions/publish-action@v0.4.0
|
||||||
|
with:
|
||||||
|
source-tag: ${{ env.TAG_NAME }}
|
||||||
|
slack-webhook: ${{ secrets.SLACK_WEBHOOK }}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
name: Update configuration files
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * 0'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-update-configuration-files:
|
||||||
|
name: Update configuration files
|
||||||
|
uses: actions/reusable-workflows/.github/workflows/update-config-files.yml@main
|
||||||
|
|
@ -0,0 +1,292 @@
|
||||||
|
name: versions
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- releases/*
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
local-cache:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
lts-syntax:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest-large]
|
||||||
|
node-version: [lts/dubnium, lts/erbium, lts/fermium, lts/*, lts/-1]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
check-latest: true
|
||||||
|
- if: runner.os != 'Windows' && runner.os != 'macOS'
|
||||||
|
name: Verify node and npm
|
||||||
|
run: |
|
||||||
|
. "$NVM_DIR/nvm.sh"
|
||||||
|
[[ $(nvm version-remote "${{ matrix.node-version }}") =~ ^v([^.]+) ]]
|
||||||
|
__tests__/verify-node.sh "${BASH_REMATCH[1]}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
v8-canary-syntax:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version:
|
||||||
|
[
|
||||||
|
'20-v8-canary',
|
||||||
|
'20.0.0-v8-canary',
|
||||||
|
'20.0.0-v8-canary20221101e50e45c9f8'
|
||||||
|
]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: |
|
||||||
|
canaryVersion="${{ matrix.node-version }}"
|
||||||
|
majorVersion=$(echo $canaryVersion | cut -d- -f1)
|
||||||
|
__tests__/verify-node.sh "$majorVersion"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
nightly-syntax:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20-nightly, 25-nightly, 24.0.0-nightly]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: |
|
||||||
|
nightlyVersion="${{ matrix.node-version }}"
|
||||||
|
majorVersion=$(echo $nightlyVersion | cut -d- -f1)
|
||||||
|
__tests__/verify-node.sh "$majorVersion"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
rc-syntax:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20.0.0-rc.1, 22.14.0-rc.1, 24.0.0-rc.4]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: |
|
||||||
|
rcVersion="${{ matrix.node-version }}"
|
||||||
|
majorVersion=$(echo $rcVersion | cut -d- -f1)
|
||||||
|
__tests__/verify-node.sh "$majorVersion"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
manifest:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20.10.0, 22.0.0, 24.9.0]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
check-latest:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [20, 22, 24]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node and check latest
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
check-latest: true
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
version-file:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version-file:
|
||||||
|
[.nvmrc, .tool-versions, .tool-versions-node, package.json]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup node from node version file
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version-file: '__tests__/data/${{ matrix.node-version-file }}'
|
||||||
|
- name: Verify node
|
||||||
|
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@v6
|
||||||
|
- name: Setup node from node version file
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version-file: '__tests__/data/package-dev-engines.json'
|
||||||
|
- name: Verify node
|
||||||
|
run: __tests__/verify-node.sh 20
|
||||||
|
|
||||||
|
version-file-volta:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup node from node version file
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version-file: '__tests__/data/package-volta.json'
|
||||||
|
- name: Verify node
|
||||||
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
|
version-file-volta-extends:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup node from node version file
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version-file: '__tests__/data/package-volta-extends.json'
|
||||||
|
- name: Verify node
|
||||||
|
run: __tests__/verify-node.sh 24
|
||||||
|
|
||||||
|
node-dist:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [21, 23]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node from dist
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Verify node and npm
|
||||||
|
run: __tests__/verify-node.sh "${{ matrix.node-version }}"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
old-versions:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest-large]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
# test old versions which didn't have npm and layout different
|
||||||
|
- name: Setup node 0.12.18 from dist
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: 0.12.18
|
||||||
|
- name: Verify node
|
||||||
|
run: __tests__/verify-node.sh 0.12.18 SKIP_NPM
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
arch:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup node 20 x86 from dist
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
architecture: 'x86'
|
||||||
|
- name: Verify node
|
||||||
|
run: __tests__/verify-arch.sh "ia32"
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
node-latest-aliases:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-large]
|
||||||
|
node-version: [current, latest, node]
|
||||||
|
steps:
|
||||||
|
- name: Get node version
|
||||||
|
run: |
|
||||||
|
latestNodeVersion=$(curl https://nodejs.org/dist/index.json | jq -r '. [0].version')
|
||||||
|
echo "LATEST_NODE_VERSION=$latestNodeVersion" >> $GITHUB_OUTPUT
|
||||||
|
id: version
|
||||||
|
shell: bash
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- name: Retrieve version after install
|
||||||
|
run: |
|
||||||
|
updatedVersion=$(echo $(node --version))
|
||||||
|
echo "NODE_VERSION_UPDATED=$updatedVersion" >> $GITHUB_OUTPUT
|
||||||
|
id: updatedVersion
|
||||||
|
shell: bash
|
||||||
|
- name: Compare versions
|
||||||
|
if: ${{ steps.version.outputs.LATEST_NODE_VERSION != steps.updatedVersion.outputs.NODE_VERSION_UPDATED}}
|
||||||
|
run: |
|
||||||
|
echo "Latest node version failed to download."
|
||||||
|
exit 1
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
name: Main workflow
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- releases/*
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ${{ matrix.operating-system }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
operating-system: [ubuntu-latest, windows-latest]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Setup node 12
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 12.x
|
|
||||||
- run: npm ci
|
|
||||||
- run: npm run build
|
|
||||||
- run: npm run format-check
|
|
||||||
- run: npm test
|
|
||||||
- name: Verify no unstaged changes
|
|
||||||
if: runner.os != 'windows'
|
|
||||||
run: __tests__/verify-no-unstaged-changes.sh
|
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Clear tool cache
|
|
||||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
|
||||||
- name: Setup node 10
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
node-version: 10.x
|
|
||||||
- name: Verify node and npm
|
|
||||||
run: __tests__/verify-node.sh 10
|
|
||||||
|
|
||||||
test-proxy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: ubuntu:latest
|
|
||||||
options: --dns 127.0.0.1
|
|
||||||
services:
|
|
||||||
squid-proxy:
|
|
||||||
image: datadog/squid:latest
|
|
||||||
ports:
|
|
||||||
- 3128:3128
|
|
||||||
env:
|
|
||||||
https_proxy: http://squid-proxy:3128
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Clear tool cache
|
|
||||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
|
||||||
- name: Setup node 10
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
node-version: 10.x
|
|
||||||
- name: Verify node and npm
|
|
||||||
run: __tests__/verify-node.sh 10
|
|
||||||
|
|
||||||
test-bypass-proxy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
https_proxy: http://no-such-proxy:3128
|
|
||||||
no_proxy: github.com,nodejs.org,registry.npmjs.org
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Clear tool cache
|
|
||||||
run: rm -rf $RUNNER_TOOL_CACHE/*
|
|
||||||
- name: Setup node 10
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
node-version: 10.x
|
|
||||||
- name: Verify node and npm
|
|
||||||
run: __tests__/verify-node.sh 10
|
|
||||||
|
|
@ -2,6 +2,9 @@ node_modules/
|
||||||
lib/
|
lib/
|
||||||
__tests__/runner/*
|
__tests__/runner/*
|
||||||
|
|
||||||
|
validate/temp
|
||||||
|
validate/node
|
||||||
|
|
||||||
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
|
|
@ -90,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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
sources:
|
||||||
|
npm: true
|
||||||
|
|
||||||
|
allowed:
|
||||||
|
- apache-2.0
|
||||||
|
- 0bsd
|
||||||
|
- bsd-2-clause
|
||||||
|
- bsd-3-clause
|
||||||
|
- isc
|
||||||
|
- mit
|
||||||
|
- cc0-1.0
|
||||||
|
- unlicense
|
||||||
|
|
||||||
|
reviewed:
|
||||||
|
npm:
|
||||||
|
- "@actions/http-client"
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Ignore list
|
||||||
|
/*
|
||||||
|
|
||||||
|
# Do not ignore these folders:
|
||||||
|
!__tests__/
|
||||||
|
!.github/
|
||||||
|
!src/
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// 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'
|
||||||
|
};
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"printWidth": 80,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"useTabs": false,
|
|
||||||
"semi": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"bracketSpacing": false,
|
|
||||||
"arrowParens": "avoid",
|
|
||||||
"parser": "typescript"
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug Jest Tests on Nix",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--inspect-brk",
|
||||||
|
"${workspaceRoot}/node_modules/.bin/jest",
|
||||||
|
"--runInBand"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
|
"port": 9229
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
* @actions/setup-actions-team
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to make participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all project spaces, and it also applies when
|
||||||
|
an individual is representing the project or its community in public spaces.
|
||||||
|
Examples of representing a project or community include using an official
|
||||||
|
project e-mail address, posting via an official social media account, or acting
|
||||||
|
as an appointed representative at an online or offline event. Representation of
|
||||||
|
a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at opensource@github.com. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
310
README.md
310
README.md
|
|
@ -1,110 +1,274 @@
|
||||||
# setup-node
|
# setup-node
|
||||||
|
|
||||||
<p align="left">
|
[](https://github.com/actions/setup-node/actions/workflows/basic-validation.yml)
|
||||||
<a href="https://github.com/actions/setup-node"><img alt="GitHub Actions status" src="https://github.com/actions/setup-node/workflows/Main%20workflow/badge.svg"></a>
|
[](https://github.com/actions/setup-node/actions/workflows/versions.yml)
|
||||||
</p>
|
[](https://github.com/actions/setup-node/actions/workflows/e2e-cache.yml)
|
||||||
|
[](https://github.com/actions/setup-node/actions/workflows/proxy.yml)
|
||||||
|
|
||||||
This action sets by node environment for use in actions by:
|
This action provides the following functionality for GitHub Actions users:
|
||||||
|
|
||||||
- optionally downloading and caching a version of node - npm by version spec and add to PATH
|
- Optionally downloading and caching distribution of the requested Node.js version, and adding it to the PATH
|
||||||
- registering problem matchers for error output
|
- Optionally caching npm/yarn/pnpm dependencies
|
||||||
|
- Registering problem matchers for error output
|
||||||
|
- Configuring authentication for GPR or npm
|
||||||
|
|
||||||
# Usage
|
## 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/v5.0.0)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
See [action.yml](action.yml)
|
See [action.yml](action.yml)
|
||||||
|
|
||||||
Basic:
|
<!-- start usage -->
|
||||||
|
```yaml
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
# Version Spec of the version to use in SemVer notation.
|
||||||
|
# It also admits such aliases as lts/*, latest, nightly and canary builds
|
||||||
|
# Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node
|
||||||
|
node-version: ''
|
||||||
|
|
||||||
|
# File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions.
|
||||||
|
# If node-version and node-version-file are both provided the action will use version from node-version.
|
||||||
|
node-version-file: ''
|
||||||
|
|
||||||
|
# Set this option if you want the action to check for the latest available version
|
||||||
|
# that satisfies the version spec.
|
||||||
|
# It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen).
|
||||||
|
# Default: false
|
||||||
|
check-latest: false
|
||||||
|
|
||||||
|
# Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.
|
||||||
|
# Default: ''. The action use system architecture by default
|
||||||
|
architecture: ''
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# We recommend using a service account with the least permissions necessary. Also
|
||||||
|
# when generating a new PAT, select the least scopes necessary.
|
||||||
|
#
|
||||||
|
# [Learn more about creating and using encrypted secrets](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets)
|
||||||
|
#
|
||||||
|
# Default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
|
||||||
|
token: ''
|
||||||
|
|
||||||
|
# Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.
|
||||||
|
# Package manager should be pre-installed
|
||||||
|
# Default: ''
|
||||||
|
cache: ''
|
||||||
|
|
||||||
|
# Controls automatic caching for npm. By default, caching for npm is enabled if either the devEngines.packageManager field or the top-level packageManager field in package.json specifies npm and no explicit cache input is provided.
|
||||||
|
# To disable automatic caching for npm, set package-manager-cache to false.
|
||||||
|
# default: true
|
||||||
|
package-manager-cache: true
|
||||||
|
|
||||||
|
# Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc.
|
||||||
|
# It will generate hash from the target file for primary key. It works only If cache is specified.
|
||||||
|
# Supports wildcards or a list of file names for caching multiple dependencies.
|
||||||
|
# Default: ''
|
||||||
|
cache-dependency-path: ''
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# Default: ''
|
||||||
|
registry-url: ''
|
||||||
|
|
||||||
|
# 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/).
|
||||||
|
# Default: ''
|
||||||
|
scope: ''
|
||||||
|
|
||||||
|
# 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: ''
|
||||||
|
mirror: ''
|
||||||
|
|
||||||
|
# Optional mirror token.
|
||||||
|
# The token will be used as a bearer token in the Authorization header
|
||||||
|
# Default: ''
|
||||||
|
mirror-token: ''
|
||||||
|
```
|
||||||
|
<!-- end usage -->
|
||||||
|
|
||||||
|
**Basic:**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '10.x'
|
node-version: 24
|
||||||
- run: npm install
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
Matrix Testing:
|
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/).
|
||||||
|
|
||||||
|
For information regarding locally cached versions of Node.js on GitHub hosted runners, check out [GitHub Actions Runner Images](https://github.com/actions/runner-images).
|
||||||
|
|
||||||
|
### Supported version syntax
|
||||||
|
|
||||||
|
The `node-version` input supports the Semantic Versioning Specification, for more detailed examples please refer to [the semver package documentation](https://github.com/npm/node-semver).
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
- Major versions: `22`, `24`
|
||||||
|
- More specific versions: `20.19`, `22.17.1` , `24.8.0`
|
||||||
|
- NVM LTS syntax: `lts/iron`, `lts/jod`, `lts/*`, `lts/-n`
|
||||||
|
- Latest release: `*` or `latest`/`current`/`node`
|
||||||
|
|
||||||
|
**Note:** Like the other values, `*` will get the latest [locally-cached Node.js version](https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2204-Readme.md#nodejs), or the latest version from [actions/node-versions](https://github.com/actions/node-versions/blob/main/versions-manifest.json), depending on the [`check-latest`](docs/advanced-usage.md#check-latest-version) input.
|
||||||
|
|
||||||
|
`current`/`latest`/`node` always resolve to the latest [dist version](https://nodejs.org/dist/index.json).
|
||||||
|
That version is then downloaded from actions/node-versions if possible, or directly from Node.js if not.
|
||||||
|
Since it will not be cached always, there is possibility of hitting rate limit when downloading from dist
|
||||||
|
|
||||||
|
### Checking in lockfiles
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Note:** The action does not cache `node_modules`
|
||||||
|
|
||||||
|
See the examples of using cache for `yarn`/`pnpm` and `cache-dependency-path` input in the [Advanced usage](docs/advanced-usage.md#caching-packages-data) guide.
|
||||||
|
|
||||||
|
**Caching npm dependencies:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: 'npm'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Caching npm dependencies in monorepos:**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: subdir/package-lock.json
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
Caching for npm dependencies is automatically enabled when your `package.json` contains either `devEngines.packageManager` field or top-level `packageManager` field set to `npm`, and no explicit cache input is provided.
|
||||||
|
|
||||||
|
This behavior is controlled by the `package-manager-cache` input, which defaults to `true`. To turn off automatic caching, set `package-manager-cache` to `false`.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
package-manager-cache: false
|
||||||
|
- run: npm ci
|
||||||
|
```
|
||||||
|
> If your `package.json` file does not include a `packageManager` field set to `npm`, caching will be disabled unless you explicitly enable it. For workflows with elevated privileges or access to sensitive information, we recommend disabling automatic caching for npm by setting `package-manager-cache: false` when caching is not required for secure operation.
|
||||||
|
|
||||||
|
## Matrix Testing
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-16.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [ '10', '8' ]
|
node: [ 20, 22, 24 ]
|
||||||
name: Node ${{ matrix.node }} sample
|
name: Node ${{ matrix.node }} sample
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v6
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- run: npm install
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
Publish to npmjs and GPR with npm:
|
## Using `setup-node` on GHES
|
||||||
|
|
||||||
|
`setup-node` comes pre-installed on the appliance with GHES if Actions is enabled. When dynamically downloading Nodejs distributions, `setup-node` downloads distributions from [`actions/node-versions`](https://github.com/actions/node-versions) on github.com (outside of the appliance). These calls to `actions/node-versions` are made via unauthenticated requests, which are limited to [60 requests per hour per IP](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting). If more requests are made within the time frame, then you will start to see rate-limit errors during downloading that looks like: `##[error]API rate limit exceeded for...`. After that error the action will try to download versions directly from the official site, but it also can have rate limit so it's better to put token.
|
||||||
|
|
||||||
|
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
|
||||||
steps:
|
uses: actions/setup-node@v6
|
||||||
- uses: actions/checkout@v1
|
with:
|
||||||
- uses: actions/setup-node@v1
|
token: ${{ secrets.GH_DOTCOM_TOKEN }}
|
||||||
with:
|
node-version: 24
|
||||||
node-version: '10.x'
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
- run: npm install
|
|
||||||
- run: npm publish
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
|
||||||
- run: npm publish
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Publish to npmjs and GPR with yarn:
|
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.
|
||||||
|
|
||||||
|
## Advanced usage
|
||||||
|
|
||||||
|
- [Check latest version](docs/advanced-usage.md#check-latest-version)
|
||||||
|
- [Using a node version file](docs/advanced-usage.md#node-version-file)
|
||||||
|
- [Using different architectures](docs/advanced-usage.md#architecture)
|
||||||
|
- [Using v8 canary versions](docs/advanced-usage.md#v8-canary-versions)
|
||||||
|
- [Using nightly versions](docs/advanced-usage.md#nightly-versions)
|
||||||
|
- [Using rc versions](docs/advanced-usage.md#rc-versions)
|
||||||
|
- [Caching packages data](docs/advanced-usage.md#caching-packages-data)
|
||||||
|
- [Using multiple operating systems and architectures](docs/advanced-usage.md#multiple-operating-systems-and-architectures)
|
||||||
|
- [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)
|
||||||
|
- [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
|
```yaml
|
||||||
steps:
|
permissions:
|
||||||
- uses: actions/checkout@v1
|
contents: read # access to check out code and install dependencies
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: '10.x'
|
|
||||||
registry-url: <registry url>
|
|
||||||
- run: yarn install
|
|
||||||
- run: yarn publish
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
|
||||||
- run: yarn publish
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Use private packages:
|
## License
|
||||||
```yaml
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: '10.x'
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
# Skip post-install scripts here, as a malicious
|
|
||||||
# script could steal NODE_AUTH_TOKEN.
|
|
||||||
- run: npm install --ignore-scripts
|
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
||||||
# `npm rebuild` will run all those post-install scripts for us.
|
|
||||||
- run: npm rebuild && npm run prepare --if-present
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
The scripts and documentation in this project are released under the [MIT License](LICENSE)
|
The scripts and documentation in this project are released under the [MIT License](LICENSE)
|
||||||
|
|
||||||
# Contributions
|
## Contributions
|
||||||
|
|
||||||
Contributions are welcome! See [Contributor's Guide](docs/contributors.md)
|
Contributions are welcome! See [Contributor's Guide](docs/contributors.md)
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
:wave: Be nice. See [our code of conduct](CODE_OF_CONDUCT.md)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
Files located in data directory are used only for testing purposes.
|
||||||
|
|
||||||
|
|
||||||
|
## Here the list of files in the data directory
|
||||||
|
- `.nvmrc`, `.tools-versions` and `package.json` are used to test node-version-file logic
|
||||||
|
- `package-lock.json`, `pnpm-lock.yaml` and `yarn.lock` are used to test cache logic
|
||||||
|
- `versions-manifest.json` is used for unit testing to check downloading Node.js versions from the node-versions repository.
|
||||||
|
- `node-dist-index.json` is used for unit testing to check downloading Node.js versions from the official site. The file was constructed from https://nodejs.org/dist/index.json
|
||||||
|
- `node-rc-index.json` is used for unit testing to check downloading Node.js rc versions from the official site. The file was constructed from https://nodejs.org/download/rc/index.json
|
||||||
|
- `node-nightly-index.json` is used for unit testing to check downloading Node.js nightly builds from the official site. The file was constructed from https://nodejs.org/download/nightly/index.json
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`installer tests Appends trailing slash to registry 1`] = `
|
|
||||||
"//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}
|
|
||||||
registry=https://registry.npmjs.org/
|
|
||||||
always-auth=false"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`installer tests Automatically configures GPR scope 1`] = `
|
|
||||||
"npm.pkg.github.com/:_authToken=\${NODE_AUTH_TOKEN}
|
|
||||||
@ownername:registry=npm.pkg.github.com/
|
|
||||||
always-auth=false"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`installer tests Configures scoped npm registries 1`] = `
|
|
||||||
"//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}
|
|
||||||
@myscope:registry=https://registry.npmjs.org/
|
|
||||||
always-auth=false"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`installer tests Sets up npmrc for always-auth true 1`] = `
|
|
||||||
"//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}
|
|
||||||
registry=https://registry.npmjs.org/
|
|
||||||
always-auth=true"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`installer tests Sets up npmrc for npmjs 1`] = `
|
|
||||||
"//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}
|
|
||||||
registry=https://registry.npmjs.org/
|
|
||||||
always-auth=false"
|
|
||||||
`;
|
|
||||||
|
|
@ -1,68 +1,221 @@
|
||||||
import io = require('@actions/io');
|
import os from 'os';
|
||||||
import fs = require('fs');
|
import fs from 'fs';
|
||||||
import path = require('path');
|
import * as path from 'path';
|
||||||
|
import * as core from '@actions/core';
|
||||||
const tempDir = path.join(
|
import * as io from '@actions/io';
|
||||||
__dirname,
|
|
||||||
'runner',
|
|
||||||
path.join(
|
|
||||||
Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substring(7)
|
|
||||||
),
|
|
||||||
'temp'
|
|
||||||
);
|
|
||||||
|
|
||||||
const rcFile = path.join(tempDir, '.npmrc');
|
|
||||||
|
|
||||||
process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo';
|
|
||||||
process.env['RUNNER_TEMP'] = tempDir;
|
|
||||||
import * as auth from '../src/authutil';
|
import * as auth from '../src/authutil';
|
||||||
|
import * as cacheUtils from '../src/cache-utils';
|
||||||
|
|
||||||
|
let rcFile: string;
|
||||||
|
|
||||||
|
describe('authutil tests', () => {
|
||||||
|
const _runnerDir = path.join(__dirname, 'runner');
|
||||||
|
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let logSpy: jest.SpyInstance;
|
||||||
|
let dbgSpy: jest.SpyInstance;
|
||||||
|
|
||||||
describe('installer tests', () => {
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
const randPath = path.join(Math.random().toString(36).substring(7));
|
||||||
|
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||||
|
process.env['GITHUB_ENV'] = ''; // Stub out Environment file functionality so we can verify it writes to standard out (toolkit is backwards compatible)
|
||||||
|
const tempDir = path.join(_runnerDir, randPath, 'temp');
|
||||||
await io.rmRF(tempDir);
|
await io.rmRF(tempDir);
|
||||||
await io.mkdirP(tempDir);
|
await io.mkdirP(tempDir);
|
||||||
|
process.env['GITHUB_REPOSITORY'] = 'OwnerName/repo';
|
||||||
|
process.env['RUNNER_TEMP'] = tempDir;
|
||||||
|
rcFile = path.join(tempDir, '.npmrc');
|
||||||
}, 100000);
|
}, 100000);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
if (fs.existsSync(rcFile)) {
|
await io.rmRF(rcFile);
|
||||||
fs.unlinkSync(rcFile);
|
// if (fs.existsSync(rcFile)) {
|
||||||
}
|
// fs.unlinkSync(rcFile);
|
||||||
|
// }
|
||||||
process.env['INPUT_SCOPE'] = '';
|
process.env['INPUT_SCOPE'] = '';
|
||||||
});
|
|
||||||
|
// writes
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
logSpy = jest.spyOn(console, 'log');
|
||||||
|
dbgSpy = jest.spyOn(core, 'debug');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
function dbg(message: string) {
|
||||||
|
process.stderr.write('dbg::' + message + '::\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
if (_runnerDir) {
|
||||||
|
await io.rmRF(_runnerDir);
|
||||||
|
}
|
||||||
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
function readRcFile(rcFile: string) {
|
||||||
|
const rc = {};
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
for (const line of contents.split(os.EOL)) {
|
||||||
|
const parts = line.split('=');
|
||||||
|
if (parts.length == 2) {
|
||||||
|
rc[parts[0].trim()] = parts[1].trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
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.existsSync(rcFile)).toBe(true);
|
|
||||||
expect(fs.readFileSync(rcFile, {encoding: 'utf8'})).toMatchSnapshot();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
||||||
});
|
});
|
||||||
|
|
||||||
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.existsSync(rcFile)).toBe(true);
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
expect(fs.readFileSync(rcFile, {encoding: 'utf8'})).toMatchSnapshot();
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['registry']).toBe('https://registry.npmjs.org/');
|
||||||
});
|
});
|
||||||
|
|
||||||
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.existsSync(rcFile)).toBe(true);
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
expect(fs.readFileSync(rcFile, {encoding: 'utf8'})).toMatchSnapshot();
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['@myscope:registry']).toBe('https://registry.npmjs.org/');
|
||||||
});
|
});
|
||||||
|
|
||||||
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.existsSync(rcFile)).toBe(true);
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
expect(fs.readFileSync(rcFile, {encoding: 'utf8'})).toMatchSnapshot();
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sets up npmrc for always-auth true', async () => {
|
it('is already set the NODE_AUTH_TOKEN export it', async () => {
|
||||||
await auth.configAuthentication('https://registry.npmjs.org/', 'true');
|
process.env.NODE_AUTH_TOKEN = 'foobar';
|
||||||
expect(fs.existsSync(rcFile)).toBe(true);
|
await auth.configAuthentication('npm.pkg.github.com');
|
||||||
expect(fs.readFileSync(rcFile, {encoding: 'utf8'})).toMatchSnapshot();
|
expect(fs.statSync(rcFile)).toBeDefined();
|
||||||
|
const rc = readRcFile(rcFile);
|
||||||
|
expect(rc['@ownername:registry']).toBe('npm.pkg.github.com/');
|
||||||
|
expect(process.env.NODE_AUTH_TOKEN).toEqual('foobar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not export NODE_AUTH_TOKEN if not set in environment', async () => {
|
||||||
|
const exportSpy = jest.spyOn(core, 'exportVariable');
|
||||||
|
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 = jest.spyOn(core, 'exportVariable');
|
||||||
|
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 () => {
|
||||||
|
fs.writeFileSync(rcFile, 'registry=NNN');
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}registry=https://registry.npmjs.org/`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configAuthentication should overwrite only non-scoped', async () => {
|
||||||
|
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`@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 () => {
|
||||||
|
fs.writeFileSync(rcFile, '@myscope:registry=NNN');
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`@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 () => {
|
||||||
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
|
fs.writeFileSync(rcFile, `@myscope:registry=NNN`);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configAuthentication should overwrite only scoped', async () => {
|
||||||
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
|
fs.writeFileSync(rcFile, `registry=NNN${os.EOL}@myscope:registry=MMM`);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`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 () => {
|
||||||
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
|
fs.writeFileSync(rcFile, `registry=MMM`);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configAuthentication should overwrite only one scoped', async () => {
|
||||||
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
|
fs.writeFileSync(
|
||||||
|
rcFile,
|
||||||
|
`@otherscope:registry=NNN${os.EOL}@myscope:registry=MMM`
|
||||||
|
);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`@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 () => {
|
||||||
|
process.env['INPUT_SCOPE'] = 'myscope';
|
||||||
|
fs.writeFileSync(rcFile, `@otherscope:registry=MMM`);
|
||||||
|
await auth.configAuthentication('https://registry.npmjs.org/');
|
||||||
|
const contents = fs.readFileSync(rcFile, {encoding: 'utf8'});
|
||||||
|
expect(contents).toBe(
|
||||||
|
`@otherscope:registry=MMM${os.EOL}//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}${os.EOL}@myscope:registry=https://registry.npmjs.org/`
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as glob from '@actions/glob';
|
||||||
|
import osm from 'os';
|
||||||
|
|
||||||
|
import * as utils from '../src/cache-utils';
|
||||||
|
import {restoreCache} from '../src/cache-restore';
|
||||||
|
|
||||||
|
describe('cache-restore', () => {
|
||||||
|
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) {
|
||||||
|
process.env.RUNNER_OS = 'Linux';
|
||||||
|
}
|
||||||
|
const platform = process.env.RUNNER_OS;
|
||||||
|
const arch = 'arm64';
|
||||||
|
const commonPath = '/some/random/path';
|
||||||
|
const npmCachePath = `${commonPath}/npm`;
|
||||||
|
const pnpmCachePath = `${commonPath}/pnpm`;
|
||||||
|
const yarn1CachePath = `${commonPath}/yarn1`;
|
||||||
|
const yarn2CachePath = `${commonPath}/yarn2`;
|
||||||
|
const yarnFileHash =
|
||||||
|
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
|
||||||
|
const npmFileHash =
|
||||||
|
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
|
||||||
|
const pnpmFileHash =
|
||||||
|
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
|
||||||
|
const cachesObject: Record<string, string> = {
|
||||||
|
[npmCachePath]: npmFileHash,
|
||||||
|
[pnpmCachePath]: pnpmFileHash,
|
||||||
|
[yarn1CachePath]: yarnFileHash,
|
||||||
|
[yarn2CachePath]: yarnFileHash
|
||||||
|
};
|
||||||
|
|
||||||
|
function findCacheFolder(command: string) {
|
||||||
|
switch (command) {
|
||||||
|
case 'npm config get cache':
|
||||||
|
return npmCachePath;
|
||||||
|
case 'pnpm store path --silent':
|
||||||
|
return pnpmCachePath;
|
||||||
|
case 'yarn cache dir':
|
||||||
|
return yarn1CachePath;
|
||||||
|
case 'yarn config get cacheFolder':
|
||||||
|
return yarn2CachePath;
|
||||||
|
default:
|
||||||
|
return 'packge/not/found';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let saveStateSpy: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let setOutputSpy: jest.SpyInstance;
|
||||||
|
let getCommandOutputSpy: jest.SpyInstance;
|
||||||
|
let restoreCacheSpy: jest.SpyInstance;
|
||||||
|
let hashFilesSpy: jest.SpyInstance;
|
||||||
|
let archSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// core
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||||
|
setOutputSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||||
|
saveStateSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
// glob
|
||||||
|
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
||||||
|
hashFilesSpy.mockImplementation((pattern: string) => {
|
||||||
|
if (pattern.includes('package-lock.json')) {
|
||||||
|
return npmFileHash;
|
||||||
|
} else if (pattern.includes('pnpm-lock.yaml')) {
|
||||||
|
return pnpmFileHash;
|
||||||
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
|
return yarnFileHash;
|
||||||
|
} 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
|
||||||
|
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||||
|
|
||||||
|
// os
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => arch);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Validate provided package manager', () => {
|
||||||
|
it.each([['npm7'], ['npm6'], ['pnpm6'], ['yarn1'], ['yarn2'], ['random']])(
|
||||||
|
'Throw an error because %s is not supported',
|
||||||
|
async packageManager => {
|
||||||
|
await expect(restoreCache(packageManager, '')).rejects.toThrow(
|
||||||
|
`Caching for '${packageManager}' is not supported`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Restore dependencies', () => {
|
||||||
|
it.each([
|
||||||
|
['yarn', '2.1.2', yarnFileHash],
|
||||||
|
['yarn', '1.2.3', yarnFileHash],
|
||||||
|
['npm', '', npmFileHash],
|
||||||
|
['pnpm', '', pnpmFileHash]
|
||||||
|
] as const)(
|
||||||
|
'restored dependencies for %s',
|
||||||
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
|
// Set workspace to the appropriate fixture folder
|
||||||
|
setWorkspaceFor(packageManager);
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) => {
|
||||||
|
if (command.includes('version')) {
|
||||||
|
return toolVersion;
|
||||||
|
} else {
|
||||||
|
return findCacheFolder(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await restoreCache(packageManager, '');
|
||||||
|
expect(hashFilesSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache restored from key: node-cache-${platform}-${arch}-${packageManager}-${fileHash}`
|
||||||
|
);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`${packageManager} cache is not found`
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', true);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Dependencies changed', () => {
|
||||||
|
it.each([
|
||||||
|
['yarn', '2.1.2', yarnFileHash],
|
||||||
|
['yarn', '1.2.3', yarnFileHash],
|
||||||
|
['npm', '', npmFileHash],
|
||||||
|
['pnpm', '', pnpmFileHash]
|
||||||
|
] as const)(
|
||||||
|
'dependencies are changed %s',
|
||||||
|
async (packageManager, toolVersion, fileHash) => {
|
||||||
|
// Set workspace to the appropriate fixture folder
|
||||||
|
setWorkspaceFor(packageManager);
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) => {
|
||||||
|
if (command.includes('version')) {
|
||||||
|
return toolVersion;
|
||||||
|
} else {
|
||||||
|
return findCacheFolder(command);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
restoreCacheSpy.mockImplementationOnce(() => undefined);
|
||||||
|
await restoreCache(packageManager, '');
|
||||||
|
expect(hashFilesSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`${packageManager} cache is not found`
|
||||||
|
);
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('cache-hit', false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalGithubWorkspace === undefined) {
|
||||||
|
delete process.env['GITHUB_WORKSPACE'];
|
||||||
|
} else {
|
||||||
|
process.env['GITHUB_WORKSPACE'] = originalGithubWorkspace;
|
||||||
|
}
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,392 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import * as glob from '@actions/glob';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import * as utils from '../src/cache-utils';
|
||||||
|
import {run} from '../src/cache-save';
|
||||||
|
import {State} from '../src/constants';
|
||||||
|
|
||||||
|
describe('run', () => {
|
||||||
|
const yarnFileHash =
|
||||||
|
'b8a0bae5243251f7c07dd52d1f78ff78281dfefaded700a176261b6b54fa245b';
|
||||||
|
const npmFileHash =
|
||||||
|
'abf7c9b306a3149dcfba4673e2362755503bcceaab46f0e4e6fee0ade493e20c';
|
||||||
|
const pnpmFileHash =
|
||||||
|
'26309058093e84713f38869c50cf1cee9b08155ede874ec1b44ce3fca8c68c70';
|
||||||
|
const commonPath = '/some/random/path';
|
||||||
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
|
|
||||||
|
const inputs = {} as any;
|
||||||
|
|
||||||
|
let getInputSpy: jest.SpyInstance;
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let setFailedSpy: jest.SpyInstance;
|
||||||
|
let getStateSpy: jest.SpyInstance;
|
||||||
|
let saveCacheSpy: jest.SpyInstance;
|
||||||
|
let getCommandOutputSpy: jest.SpyInstance;
|
||||||
|
let hashFilesSpy: jest.SpyInstance;
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getInputSpy = jest.spyOn(core, 'getInput');
|
||||||
|
getInputSpy.mockImplementation((name: string) => inputs[name]);
|
||||||
|
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
setFailedSpy = jest.spyOn(core, 'setFailed');
|
||||||
|
setFailedSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
getStateSpy = jest.spyOn(core, 'getState');
|
||||||
|
|
||||||
|
// cache
|
||||||
|
saveCacheSpy = jest.spyOn(cache, 'saveCache');
|
||||||
|
saveCacheSpy.mockImplementation(() => undefined);
|
||||||
|
|
||||||
|
// glob
|
||||||
|
hashFilesSpy = jest.spyOn(glob, 'hashFiles');
|
||||||
|
hashFilesSpy.mockImplementation((pattern: string) => {
|
||||||
|
if (pattern.includes('package-lock.json')) {
|
||||||
|
return npmFileHash;
|
||||||
|
} else if (pattern.includes('yarn.lock')) {
|
||||||
|
return yarnFileHash;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
|
// utils
|
||||||
|
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
existsSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Package manager validation', () => {
|
||||||
|
it('Package manager is not provided, skip caching', async () => {
|
||||||
|
inputs['cache'] = '';
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
expect(infoSpy).not.toHaveBeenCalled();
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(debugSpy).toHaveBeenLastCalledWith(
|
||||||
|
"Caching for '' is not supported"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Package manager is not valid, skip caching', async () => {
|
||||||
|
inputs['cache'] = 'yarn3';
|
||||||
|
getStateSpy.mockImplementation(key =>
|
||||||
|
key === State.CachePackageManager ? inputs['cache'] : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
expect(infoSpy).not.toHaveBeenCalled();
|
||||||
|
expect(saveCacheSpy).not.toHaveBeenCalled();
|
||||||
|
expect(debugSpy).toHaveBeenLastCalledWith(
|
||||||
|
"Caching for 'yarn3' is not supported"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Validate unchanged cache is not saved', () => {
|
||||||
|
it('should not save cache for yarn1', async () => {
|
||||||
|
inputs['cache'] = 'yarn';
|
||||||
|
getStateSpy.mockImplementation(key =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not save cache for yarn2', async () => {
|
||||||
|
inputs['cache'] = 'yarn';
|
||||||
|
getStateSpy.mockImplementation(key =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not save cache for npm', async () => {
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
getStateSpy.mockImplementation(key =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
getCommandOutputSpy.mockImplementationOnce(() => `${commonPath}/npm`);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not save cache for pnpm', async () => {
|
||||||
|
inputs['cache'] = 'pnpm';
|
||||||
|
getStateSpy.mockImplementation(key =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CachePrimaryKey || key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('action saves the cache', () => {
|
||||||
|
it('saves cache from yarn 1', async () => {
|
||||||
|
inputs['cache'] = 'yarn';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenLastCalledWith(
|
||||||
|
`Cache saved with the key: ${npmFileHash}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves cache from yarn 2', async () => {
|
||||||
|
inputs['cache'] = 'yarn';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${yarnFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenLastCalledWith(
|
||||||
|
`Cache saved with the key: ${npmFileHash}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves cache from npm', async () => {
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenLastCalledWith(
|
||||||
|
`Cache saved with the key: ${yarnFileHash}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves cache from pnpm', async () => {
|
||||||
|
inputs['cache'] = 'pnpm';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? pnpmFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${pnpmFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenLastCalledWith(
|
||||||
|
`Cache saved with the key: ${npmFileHash}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('save with -1 cacheId , should not fail workflow', async () => {
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
saveCacheSpy.mockImplementation(() => {
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).not.toHaveBeenLastCalledWith(
|
||||||
|
`Cache saved with the key: ${yarnFileHash}`
|
||||||
|
);
|
||||||
|
expect(setFailedSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves with error from toolkit, should fail workflow', async () => {
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
getStateSpy.mockImplementation((key: string) =>
|
||||||
|
key === State.CachePackageManager
|
||||||
|
? inputs['cache']
|
||||||
|
: key === State.CacheMatchedKey
|
||||||
|
? npmFileHash
|
||||||
|
: key === State.CachePrimaryKey
|
||||||
|
? yarnFileHash
|
||||||
|
: key === State.CachePaths
|
||||||
|
? '["/foo/bar"]'
|
||||||
|
: 'not expected'
|
||||||
|
);
|
||||||
|
saveCacheSpy.mockImplementation(() => {
|
||||||
|
throw new cache.ValidationError('Validation failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
await run();
|
||||||
|
|
||||||
|
expect(getInputSpy).not.toHaveBeenCalled();
|
||||||
|
expect(getStateSpy).toHaveBeenCalledTimes(4);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(debugSpy).toHaveBeenCalledTimes(0);
|
||||||
|
expect(infoSpy).not.toHaveBeenCalledWith(
|
||||||
|
`Cache hit occurred on the primary key ${npmFileHash}, not saving cache.`
|
||||||
|
);
|
||||||
|
expect(saveCacheSpy).toHaveBeenCalled();
|
||||||
|
expect(setFailedSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,401 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import path from 'path';
|
||||||
|
import * as utils from '../src/cache-utils';
|
||||||
|
import {
|
||||||
|
PackageManagerInfo,
|
||||||
|
isCacheFeatureAvailable,
|
||||||
|
supportedPackageManagers,
|
||||||
|
isGhes,
|
||||||
|
resetProjectDirectoriesMemoized
|
||||||
|
} from '../src/cache-utils';
|
||||||
|
import fs from 'fs';
|
||||||
|
import * as cacheUtils from '../src/cache-utils';
|
||||||
|
import * as glob from '@actions/glob';
|
||||||
|
import {Globber} from '@actions/glob';
|
||||||
|
import {MockGlobber} from './mock/glob-mock';
|
||||||
|
|
||||||
|
describe('cache-utils', () => {
|
||||||
|
const versionYarn1 = '1.2.3';
|
||||||
|
|
||||||
|
let debugSpy: jest.SpyInstance;
|
||||||
|
let getCommandOutputSpy: jest.SpyInstance;
|
||||||
|
let isFeatureAvailable: jest.SpyInstance;
|
||||||
|
let info: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let fsRealPathSyncSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
console.log('::stop-commands::stoptoken');
|
||||||
|
process.env['GITHUB_WORKSPACE'] = path.join(__dirname, 'data');
|
||||||
|
debugSpy = jest.spyOn(core, 'debug');
|
||||||
|
debugSpy.mockImplementation(msg => {});
|
||||||
|
|
||||||
|
info = jest.spyOn(core, 'info');
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
|
||||||
|
isFeatureAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
|
||||||
|
getCommandOutputSpy = jest.spyOn(utils, 'getCommandOutput');
|
||||||
|
|
||||||
|
fsRealPathSyncSpy = jest.spyOn(fs, 'realpathSync');
|
||||||
|
fsRealPathSyncSpy.mockImplementation(dirName => {
|
||||||
|
return dirName;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::');
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
describe('getPackageManagerInfo', () => {
|
||||||
|
it.each<[string, PackageManagerInfo | null]>([
|
||||||
|
['npm', utils.supportedPackageManagers.npm],
|
||||||
|
['pnpm', utils.supportedPackageManagers.pnpm],
|
||||||
|
['yarn', utils.supportedPackageManagers.yarn],
|
||||||
|
['yarn1', null],
|
||||||
|
['yarn2', null],
|
||||||
|
['npm7', null]
|
||||||
|
])('getPackageManagerInfo for %s is %o', async (packageManager, result) => {
|
||||||
|
getCommandOutputSpy.mockImplementationOnce(() => versionYarn1);
|
||||||
|
await expect(utils.getPackageManagerInfo(packageManager)).resolves.toBe(
|
||||||
|
result
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isCacheFeatureAvailable for GHES is false', () => {
|
||||||
|
isFeatureAvailable.mockImplementation(() => false);
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://www.test.com';
|
||||||
|
|
||||||
|
expect(isCacheFeatureAvailable()).toBeFalsy();
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isCacheFeatureAvailable for GHES has an interhal error', () => {
|
||||||
|
isFeatureAvailable.mockImplementation(() => false);
|
||||||
|
process.env['GITHUB_SERVER_URL'] = '';
|
||||||
|
isCacheFeatureAvailable();
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
'The runner was not able to contact the cache service. Caching will be skipped'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isCacheFeatureAvailable for GHES is available', () => {
|
||||||
|
isFeatureAvailable.mockImplementation(() => true);
|
||||||
|
|
||||||
|
expect(isCacheFeatureAvailable()).toStrictEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env['GITHUB_SERVER_URL'] = '';
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getCacheDirectoriesPaths', () => {
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
let lstatSpy: jest.SpyInstance;
|
||||||
|
let globCreateSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
|
lstatSpy = jest.spyOn(fs, 'lstatSync');
|
||||||
|
lstatSpy.mockImplementation(arg => ({
|
||||||
|
isDirectory: () => true
|
||||||
|
}));
|
||||||
|
|
||||||
|
globCreateSpy = jest.spyOn(glob, 'create');
|
||||||
|
|
||||||
|
globCreateSpy.mockImplementation(
|
||||||
|
(pattern: string): Promise<Globber> =>
|
||||||
|
MockGlobber.create(['/foo', '/bar'])
|
||||||
|
);
|
||||||
|
|
||||||
|
resetProjectDirectoriesMemoized();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
existsSpy.mockRestore();
|
||||||
|
lstatSpy.mockRestore();
|
||||||
|
globCreateSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[supportedPackageManagers.npm, ''],
|
||||||
|
[supportedPackageManagers.npm, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.npm, '/**/file.lock'],
|
||||||
|
[supportedPackageManagers.pnpm, ''],
|
||||||
|
[supportedPackageManagers.pnpm, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.pnpm, '/**/file.lock']
|
||||||
|
])(
|
||||||
|
'getCacheDirectoriesPaths should return one dir for non yarn',
|
||||||
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
|
getCommandOutputSpy.mockImplementation(() => 'foo');
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
packageManagerInfo,
|
||||||
|
cacheDependency
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual(['foo']);
|
||||||
|
// to do not call for a version
|
||||||
|
// call once for get cache folder
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(1);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('getCacheDirectoriesPaths should return one dir for yarn without cacheDependency', async () => {
|
||||||
|
getCommandOutputSpy.mockImplementation(() => 'foo');
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
''
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual(['foo']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[supportedPackageManagers.npm, ''],
|
||||||
|
[supportedPackageManagers.npm, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.npm, '/**/file.lock'],
|
||||||
|
[supportedPackageManagers.pnpm, ''],
|
||||||
|
[supportedPackageManagers.pnpm, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.pnpm, '/**/file.lock'],
|
||||||
|
[supportedPackageManagers.yarn, ''],
|
||||||
|
[supportedPackageManagers.yarn, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.yarn, '/**/file.lock']
|
||||||
|
])(
|
||||||
|
'getCacheDirectoriesPaths should throw for getCommandOutput returning empty',
|
||||||
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||||
|
// return empty string to indicate getCacheFolderPath failed
|
||||||
|
// --version still works
|
||||||
|
command.includes('version') ? '1.' : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
cacheUtils.getCacheDirectories(packageManagerInfo, cacheDependency)
|
||||||
|
).rejects.toThrow(); //'Could not get cache folder path for /dir');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[supportedPackageManagers.yarn, '/dir/file.lock'],
|
||||||
|
[supportedPackageManagers.yarn, '/**/file.lock']
|
||||||
|
])(
|
||||||
|
'getCacheDirectoriesPaths should nothrow in case of having not directories',
|
||||||
|
async (packageManagerInfo, cacheDependency) => {
|
||||||
|
lstatSpy.mockImplementation(arg => ({
|
||||||
|
isDirectory: () => false
|
||||||
|
}));
|
||||||
|
|
||||||
|
await cacheUtils.getCacheDirectories(
|
||||||
|
packageManagerInfo,
|
||||||
|
cacheDependency
|
||||||
|
);
|
||||||
|
expect(warningSpy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
`No existing directories found containing cache-dependency-path="${cacheDependency}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
|
'getCacheDirectoriesPaths yarn v%s should return one dir without cacheDependency',
|
||||||
|
async version => {
|
||||||
|
getCommandOutputSpy.mockImplementationOnce(() => version);
|
||||||
|
getCommandOutputSpy.mockImplementationOnce(() => `foo${version}`);
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
''
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual([`foo${version}`]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency',
|
||||||
|
async version => {
|
||||||
|
let dirNo = 1;
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||||
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
|
);
|
||||||
|
globCreateSpy.mockImplementation(
|
||||||
|
(pattern: string): Promise<Globber> =>
|
||||||
|
MockGlobber.create(['/tmp/dir1/file', '/tmp/dir2/file'])
|
||||||
|
);
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
'/tmp/**/file'
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_2`]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
|
'getCacheDirectoriesPaths yarn v%s should return 2 dirs with globbed cacheDependency expanding to duplicates',
|
||||||
|
async version => {
|
||||||
|
let dirNo = 1;
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||||
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
|
);
|
||||||
|
globCreateSpy.mockImplementation(
|
||||||
|
(pattern: string): Promise<Globber> =>
|
||||||
|
MockGlobber.create([
|
||||||
|
'/tmp/dir1/file',
|
||||||
|
'/tmp/dir2/file',
|
||||||
|
'/tmp/dir1/file'
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
'/tmp/**/file'
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_2`]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
|
'getCacheDirectoriesPaths yarn v%s should return 2 uniq dirs despite duplicate cache directories',
|
||||||
|
async version => {
|
||||||
|
let dirNo = 1;
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||||
|
command.includes('version')
|
||||||
|
? version
|
||||||
|
: `file_${version}_${dirNo++ % 2}`
|
||||||
|
);
|
||||||
|
globCreateSpy.mockImplementation(
|
||||||
|
(pattern: string): Promise<Globber> =>
|
||||||
|
MockGlobber.create([
|
||||||
|
'/tmp/dir1/file',
|
||||||
|
'/tmp/dir2/file',
|
||||||
|
'/tmp/dir3/file'
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
'/tmp/**/file'
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual([`file_${version}_1`, `file_${version}_0`]);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledTimes(6);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'yarn --version',
|
||||||
|
'/tmp/dir1'
|
||||||
|
);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'yarn --version',
|
||||||
|
'/tmp/dir2'
|
||||||
|
);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'yarn --version',
|
||||||
|
'/tmp/dir3'
|
||||||
|
);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
version.startsWith('1.')
|
||||||
|
? 'yarn cache dir'
|
||||||
|
: 'yarn config get cacheFolder',
|
||||||
|
'/tmp/dir1'
|
||||||
|
);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
version.startsWith('1.')
|
||||||
|
? 'yarn cache dir'
|
||||||
|
: 'yarn config get cacheFolder',
|
||||||
|
'/tmp/dir2'
|
||||||
|
);
|
||||||
|
expect(getCommandOutputSpy).toHaveBeenCalledWith(
|
||||||
|
version.startsWith('1.')
|
||||||
|
? 'yarn cache dir'
|
||||||
|
: 'yarn config get cacheFolder',
|
||||||
|
'/tmp/dir3'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(['1.1.1', '2.2.2'])(
|
||||||
|
'getCacheDirectoriesPaths yarn v%s should return 4 dirs with multiple globs',
|
||||||
|
async version => {
|
||||||
|
// simulate wrong indents
|
||||||
|
const cacheDependencyPath = `/tmp/dir1/file
|
||||||
|
/tmp/dir2/file
|
||||||
|
/tmp/**/file
|
||||||
|
`;
|
||||||
|
globCreateSpy.mockImplementation(
|
||||||
|
(pattern: string): Promise<Globber> =>
|
||||||
|
MockGlobber.create([
|
||||||
|
'/tmp/dir1/file',
|
||||||
|
'/tmp/dir2/file',
|
||||||
|
'/tmp/dir3/file',
|
||||||
|
'/tmp/dir4/file'
|
||||||
|
])
|
||||||
|
);
|
||||||
|
let dirNo = 1;
|
||||||
|
getCommandOutputSpy.mockImplementation((command: string) =>
|
||||||
|
command.includes('version') ? version : `file_${version}_${dirNo++}`
|
||||||
|
);
|
||||||
|
const dirs = await cacheUtils.getCacheDirectories(
|
||||||
|
supportedPackageManagers.yarn,
|
||||||
|
cacheDependencyPath
|
||||||
|
);
|
||||||
|
expect(dirs).toEqual([
|
||||||
|
`file_${version}_1`,
|
||||||
|
`file_${version}_2`,
|
||||||
|
`file_${version}_3`,
|
||||||
|
`file_${version}_4`
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,591 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import fs from 'fs';
|
||||||
|
import cp from 'child_process';
|
||||||
|
import osm from 'os';
|
||||||
|
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';
|
||||||
|
import nodeTestDist from './data/node-dist-index.json';
|
||||||
|
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||||
|
import nodeTestDistRc from './data/node-rc-index.json';
|
||||||
|
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||||
|
|
||||||
|
describe('setup-node', () => {
|
||||||
|
let inputs = {} as any;
|
||||||
|
let os = {} as any;
|
||||||
|
|
||||||
|
let inSpy: jest.SpyInstance;
|
||||||
|
let findSpy: jest.SpyInstance;
|
||||||
|
let findAllVersionsSpy: jest.SpyInstance;
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let logSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let getManifestSpy: jest.SpyInstance;
|
||||||
|
let getDistSpy: jest.SpyInstance;
|
||||||
|
let platSpy: jest.SpyInstance;
|
||||||
|
let archSpy: jest.SpyInstance;
|
||||||
|
let dlSpy: jest.SpyInstance;
|
||||||
|
let exSpy: jest.SpyInstance;
|
||||||
|
let cacheSpy: jest.SpyInstance;
|
||||||
|
let dbgSpy: jest.SpyInstance;
|
||||||
|
let whichSpy: jest.SpyInstance;
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
let readFileSyncSpy: jest.SpyInstance;
|
||||||
|
let mkdirpSpy: jest.SpyInstance;
|
||||||
|
let execSpy: jest.SpyInstance;
|
||||||
|
let authSpy: jest.SpyInstance;
|
||||||
|
let parseNodeVersionSpy: jest.SpyInstance;
|
||||||
|
let isCacheActionAvailable: jest.SpyInstance;
|
||||||
|
let getExecOutputSpy: jest.SpyInstance;
|
||||||
|
let getJsonSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// @actions/core
|
||||||
|
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||||
|
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
|
||||||
|
inputs = {};
|
||||||
|
inSpy = jest.spyOn(core, 'getInput');
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
// node
|
||||||
|
os = {};
|
||||||
|
platSpy = jest.spyOn(osm, 'platform');
|
||||||
|
platSpy.mockImplementation(() => os['platform']);
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
findSpy = jest.spyOn(tc, 'find');
|
||||||
|
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||||
|
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||||
|
exSpy = jest.spyOn(tc, 'extractTar');
|
||||||
|
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||||
|
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||||
|
|
||||||
|
// http-client
|
||||||
|
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||||
|
|
||||||
|
// io
|
||||||
|
whichSpy = jest.spyOn(io, 'which');
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
|
||||||
|
// disable authentication portion for installer tests
|
||||||
|
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||||
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
// gets
|
||||||
|
getManifestSpy.mockImplementation(
|
||||||
|
() => <tc.IToolRelease[]>nodeTestManifest
|
||||||
|
);
|
||||||
|
|
||||||
|
getJsonSpy.mockImplementation(url => {
|
||||||
|
let res: any;
|
||||||
|
if (url.includes('/rc')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
|
} else if (url.includes('/nightly')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistNightly;
|
||||||
|
} else if (url.includes('/v8-canary')) {
|
||||||
|
res = <INodeVersion[]>nodeV8CanaryTestDist;
|
||||||
|
} else {
|
||||||
|
res = <INodeVersion[]>nodeTestDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result: res};
|
||||||
|
});
|
||||||
|
|
||||||
|
// writes
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
logSpy = jest.spyOn(core, 'info');
|
||||||
|
dbgSpy = jest.spyOn(core, 'debug');
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
warningSpy.mockImplementation(msg => {
|
||||||
|
// uncomment to debug
|
||||||
|
// process.stderr.write('log:' + msg + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
// @actions/exec
|
||||||
|
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Found in cache tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
it('finds version in cache with stable true', async () => {
|
||||||
|
inputs['node-version'] = '20-v8-canary';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
inputs.stable = 'true';
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(findSpy).toHaveBeenCalledWith(
|
||||||
|
'node',
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'x64'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
|
inputs['node-version'] = '20-v8-canary';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/20.0.0-v8-canary20221103f7e2421e91/x64'
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles unhandled find error and reports error', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
const errMsg = 'unhandled error message';
|
||||||
|
inputs['node-version'] = '20.0.0-v8-canary20221103f7e2421e91';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||||
|
});
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Manifest tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
it('falls back to a version from node dist', 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';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/11.11.0/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Not found in manifest. Falling back to download directly from Node'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not find a version that does not exist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '23.0.0-v8-canary20221103f7e2421e91';
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports a failed download', async () => {
|
||||||
|
const errMsg = 'unhandled download message';
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is in the manifest
|
||||||
|
const versionSpec = '19.0.0-v8-canary';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
dlSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('acquires specified architecture of node', async () => {
|
||||||
|
for (const {arch, version, osSpec} of [
|
||||||
|
{
|
||||||
|
arch: 'x86',
|
||||||
|
version: '20.0.0-v8-canary20221022e83bcb6c41',
|
||||||
|
osSpec: 'win32'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
arch: 'x86',
|
||||||
|
version: '20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
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';
|
||||||
|
|
||||||
|
const expectedUrl = `https://nodejs.org/download/v8-canary/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', () => {
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'20.0.0-v8-canary',
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'https://nodejs.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',
|
||||||
|
'https://nodejs.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',
|
||||||
|
'https://nodejs.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',
|
||||||
|
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'19.0.0-v8-canary202210187d6960f23f',
|
||||||
|
'19.0.0-v8-canary202210187d6960f23f',
|
||||||
|
'https://nodejs.org/download/v8-canary/v19.0.0-v8-canary202210187d6960f23f/node-v19.0.0-v8-canary202210187d6960f23f-linux-x64.tar.gz'
|
||||||
|
]
|
||||||
|
])(
|
||||||
|
'finds the versions in the index.json and installs it',
|
||||||
|
async (input, expectedVersion, expectedUrl) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91'
|
||||||
|
],
|
||||||
|
['20.0.0-v8-canary', '20.0.0-v8-canary20221103f7e2421e91'],
|
||||||
|
['20-v8-canary', '20.0.0-v8-canary20221103f7e2421e91']
|
||||||
|
])(
|
||||||
|
'finds the %s version in the hostedToolcache',
|
||||||
|
async (input, expectedVersion) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
findSpy.mockReturnValue(toolPath);
|
||||||
|
findAllVersionsSpy.mockReturnValue([
|
||||||
|
'20.0.0-v8-canary20221103f7e2421e91',
|
||||||
|
'20.0.0-v8-canary20221030fefe1c0879',
|
||||||
|
'19.0.0-v8-canary202210172ec229fc56',
|
||||||
|
'20.0.0-v8-canary2022102310ff1e5a8d'
|
||||||
|
]);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::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://nodejs.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://nodejs.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://nodejs.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://nodejs.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';
|
||||||
|
|
||||||
|
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(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::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(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setup-node v8 canary tests', () => {
|
||||||
|
it('v8 canary setup node flow with cached', async () => {
|
||||||
|
const versionSpec = 'v20-v8-canary';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionExpected = 'v20.0.0-v8-canary20221103f7e2421e91';
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [versionExpected]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize(`/cache/node/${versionExpected}/x64`);
|
||||||
|
findSpy.mockImplementation(version => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${toolPath}${path.sep}bin${osm.EOL}`
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(dlSpy).not.toHaveBeenCalled();
|
||||||
|
expect(exSpy).not.toHaveBeenCalled();
|
||||||
|
expect(cacheSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
v24
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
nodejs 24.0.0
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
node 24.0.0
|
||||||
|
|
@ -0,0 +1,770 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "v14.1.0",
|
||||||
|
"date": "2020-04-29",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "8.1.307.31",
|
||||||
|
"uv": "1.37.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1g",
|
||||||
|
"modules": "83",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v14.0.0",
|
||||||
|
"date": "2020-04-21",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "8.1.307.30",
|
||||||
|
"uv": "1.37.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1f",
|
||||||
|
"modules": "83",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v13.14.0",
|
||||||
|
"date": "2020-04-28",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "7.9.317.25",
|
||||||
|
"uv": "1.37.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1g",
|
||||||
|
"modules": "79",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v13.13.0",
|
||||||
|
"date": "2020-04-14",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "7.9.317.25",
|
||||||
|
"uv": "1.35.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1f",
|
||||||
|
"modules": "79",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v12.16.3",
|
||||||
|
"date": "2020-04-28",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "7.8.279.23",
|
||||||
|
"uv": "1.34.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1g",
|
||||||
|
"modules": "72",
|
||||||
|
"lts": "Erbium",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v12.16.2",
|
||||||
|
"date": "2020-04-08",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "7.8.279.23",
|
||||||
|
"uv": "1.34.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1e",
|
||||||
|
"modules": "72",
|
||||||
|
"lts": "Erbium",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v12.1.0",
|
||||||
|
"date": "2019-04-29",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.9.0",
|
||||||
|
"v8": "7.4.288.21",
|
||||||
|
"uv": "1.28.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1b",
|
||||||
|
"modules": "72",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v11.15.0",
|
||||||
|
"date": "2019-04-30",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.7.0",
|
||||||
|
"v8": "7.0.276.38",
|
||||||
|
"uv": "1.27.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1b",
|
||||||
|
"modules": "67",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v10.20.1",
|
||||||
|
"date": "2020-04-10",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "6.8.275.32",
|
||||||
|
"uv": "1.34.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1e",
|
||||||
|
"modules": "64",
|
||||||
|
"lts": "Dubnium",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v10.20.0",
|
||||||
|
"date": "2020-03-24",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.14.4",
|
||||||
|
"v8": "6.8.275.32",
|
||||||
|
"uv": "1.34.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.1.1e",
|
||||||
|
"modules": "64",
|
||||||
|
"lts": "Dubnium",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v9.11.2",
|
||||||
|
"date": "2018-06-12",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "5.6.0",
|
||||||
|
"v8": "6.2.414.46",
|
||||||
|
"uv": "1.19.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2o",
|
||||||
|
"modules": "59",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v9.11.1",
|
||||||
|
"date": "2018-04-05",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "5.6.0",
|
||||||
|
"v8": "6.2.414.46",
|
||||||
|
"uv": "1.19.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2o",
|
||||||
|
"modules": "59",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v8.17.0",
|
||||||
|
"date": "2019-12-17",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.13.4",
|
||||||
|
"v8": "6.2.414.78",
|
||||||
|
"uv": "1.23.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2s",
|
||||||
|
"modules": "57",
|
||||||
|
"lts": "Carbon",
|
||||||
|
"security": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v8.16.2",
|
||||||
|
"date": "2019-10-08",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "6.4.1",
|
||||||
|
"v8": "6.2.414.78",
|
||||||
|
"uv": "1.23.2",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2s",
|
||||||
|
"modules": "57",
|
||||||
|
"lts": "Carbon",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v7.10.1",
|
||||||
|
"date": "2017-07-11",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "4.2.0",
|
||||||
|
"v8": "5.5.372.43",
|
||||||
|
"uv": "1.11.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2k",
|
||||||
|
"modules": "51",
|
||||||
|
"lts": false,
|
||||||
|
"security": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v7.10.0",
|
||||||
|
"date": "2017-05-02",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "4.2.0",
|
||||||
|
"v8": "5.5.372.43",
|
||||||
|
"uv": "1.11.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2k",
|
||||||
|
"modules": "51",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v6.17.1",
|
||||||
|
"date": "2019-04-03",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "3.10.10",
|
||||||
|
"v8": "5.1.281.111",
|
||||||
|
"uv": "1.16.1",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2r",
|
||||||
|
"modules": "48",
|
||||||
|
"lts": "Boron",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v6.17.0",
|
||||||
|
"date": "2019-02-28",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "3.10.10",
|
||||||
|
"v8": "5.1.281.111",
|
||||||
|
"uv": "1.16.1",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2r",
|
||||||
|
"modules": "48",
|
||||||
|
"lts": "Boron",
|
||||||
|
"security": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v5.12.0",
|
||||||
|
"date": "2016-06-23",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi"
|
||||||
|
],
|
||||||
|
"npm": "3.8.6",
|
||||||
|
"v8": "4.6.85.32",
|
||||||
|
"uv": "1.8.0",
|
||||||
|
"zlib": "1.2.8",
|
||||||
|
"openssl": "1.0.2h",
|
||||||
|
"modules": "47",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v4.9.1",
|
||||||
|
"date": "2018-03-29",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "2.15.11",
|
||||||
|
"v8": "4.5.103.53",
|
||||||
|
"uv": "1.9.1",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2o",
|
||||||
|
"modules": "46",
|
||||||
|
"lts": "Argon",
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v4.9.0",
|
||||||
|
"date": "2018-03-28",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv6l",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "2.15.11",
|
||||||
|
"v8": "4.5.103.53",
|
||||||
|
"uv": "1.9.1",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "1.0.2o",
|
||||||
|
"modules": "46",
|
||||||
|
"lts": "Argon",
|
||||||
|
"security": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v0.12.18",
|
||||||
|
"date": "2017-02-22",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"osx-x86-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi"
|
||||||
|
],
|
||||||
|
"npm": "2.15.11",
|
||||||
|
"v8": "3.28.71.20",
|
||||||
|
"uv": "1.6.1",
|
||||||
|
"zlib": "1.2.8",
|
||||||
|
"openssl": "1.0.1u",
|
||||||
|
"modules": "14",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v0.12.17",
|
||||||
|
"date": "2016-10-18",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-x64",
|
||||||
|
"linux-x86",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"osx-x86-tar",
|
||||||
|
"src",
|
||||||
|
"sunos-x64",
|
||||||
|
"sunos-x86",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi"
|
||||||
|
],
|
||||||
|
"npm": "2.15.1",
|
||||||
|
"v8": "3.28.71.19",
|
||||||
|
"uv": "1.6.1",
|
||||||
|
"zlib": "1.2.8",
|
||||||
|
"openssl": "1.0.1u",
|
||||||
|
"modules": "14",
|
||||||
|
"lts": false,
|
||||||
|
"security": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
[
|
||||||
|
{"version":"v20.0.0-nightly2022101987cdf7d412","date":"2022-10-19","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.19.2","v8":"10.7.193.16","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.5+quic","modules":"111","lts":false,"security":false},
|
||||||
|
{"version":"v19.0.0-nightly202210182672219b78","date":"2022-10-18","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.19.2","v8":"10.7.193.13","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.5+quic","modules":"111","lts":false,"security":false},
|
||||||
|
|
||||||
|
|
||||||
|
{"version":"v19.0.0-nightly202204201fe5d56403","date":"2022-04-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip"],"npm":"8.7.0","v8":"10.1.124.8","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.2+quic","modules":"108","lts":false,"security":false},
|
||||||
|
{"version":"v18.0.0-nightly20220419bde889bd4e","date":"2022-04-19","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip"],"npm":"8.7.0","v8":"10.1.124.8","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.2+quic","modules":"108","lts":false,"security":false},
|
||||||
|
{"version":"v18.0.0-nightly202204180699150267","date":"2022-04-18","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip"],"npm":"8.7.0","v8":"10.1.124.8","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.2+quic","modules":"108","lts":false,"security":false},
|
||||||
|
|
||||||
|
{"version":"v18.0.0-nightly202110204cb3e06ed8","date":"2021-10-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.1.0","v8":"9.5.172.21","uv":"1.42.0","zlib":"1.2.11","openssl":"3.0.0+quic","modules":"102","lts":false,"security":false},
|
||||||
|
{"version":"v17.5.0-nightly20220209e43808936a","date":"2022-02-09","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.4.1","v8":"9.6.180.15","uv":"1.43.0","zlib":"1.2.11","openssl":"3.0.1+quic","modules":"102","lts":false,"security":false},
|
||||||
|
{"version":"v17.0.0-nightly202110193f11666dc7","date":"2021-10-19","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.1.0","v8":"9.5.172.21","uv":"1.42.0","zlib":"1.2.11","openssl":"3.0.0+quic","modules":"102","lts":false,"security":false},
|
||||||
|
{"version":"v17.0.0-nightly20211018c0a70203de","date":"2021-10-18","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"8.0.0","v8":"9.5.172.21","uv":"1.42.0","zlib":"1.2.11","openssl":"3.0.0+quic","modules":"102","lts":false,"security":false},
|
||||||
|
|
||||||
|
{"version":"v16.0.0-nightly20210420a0261d231c","date":"2021-04-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.10.0","v8":"9.0.257.17","uv":"1.41.0","zlib":"1.2.11","openssl":"1.1.1k+quic","modules":"93","lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-nightly20210417bc31dc0e0f","date":"2021-04-17","files":["aix-ppc64","headers","linux-arm64","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.10.0","v8":"9.0.257.17","uv":"1.41.0","zlib":"1.2.11","openssl":"1.1.1k+quic","modules":"93","lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-nightly20210416d3162da8dd","date":"2021-04-16","files":["aix-ppc64","headers","linux-arm64","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.9.0","v8":"9.0.257.17","uv":"1.41.0","zlib":"1.2.11","openssl":"1.1.1k+quic","modules":"93","lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-nightly20210415c3a5e15ebe","date":"2021-04-15","files":["aix-ppc64","headers","linux-arm64","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.9.0","v8":"9.0.257.17","uv":"1.41.0","zlib":"1.2.11","openssl":"1.1.1k+quic","modules":"93","lts":false,"security":false},
|
||||||
|
|
||||||
|
|
||||||
|
{"version":"v15.0.0-nightly2020102011f1ad939f","date":"2020-10-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.0.2","v8":"8.6.395.16","uv":"1.40.0","zlib":"1.2.11","openssl":"1.1.1g","modules":"88","lts":false,"security":false},
|
||||||
|
{"version":"v15.0.0-nightly20201019c55f661551","date":"2020-10-19","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"7.0.2","v8":"8.6.395.16","uv":"1.40.0","zlib":"1.2.11","openssl":"1.1.1g","modules":"88","lts":false,"security":false},
|
||||||
|
{"version":"v14.0.0-nightly20200421c3554307c6","date":"2020-04-21","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.14.4","v8":"8.1.307.30","uv":"1.37.0","zlib":"1.2.11","openssl":"1.1.1f","modules":"83","lts":false,"security":false},
|
||||||
|
{"version":"v14.0.0-nightly202004204af0598134","date":"2020-04-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.14.4","v8":"8.1.307.26","uv":"1.37.0","zlib":"1.2.11","openssl":"1.1.1f","modules":"83","lts":false,"security":false},
|
||||||
|
|
||||||
|
{"version":"v13.13.1-nightly20200415947ddec091","date":"2020-04-15","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.14.4","v8":"7.9.317.25","uv":"1.35.0","zlib":"1.2.11","openssl":"1.1.1f","modules":"79","lts":false,"security":false},
|
||||||
|
{"version":"v13.11.1-nightly2020032628e298f219","date":"2020-03-26","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.14.3","v8":"7.9.317.25","uv":"1.35.0","zlib":"1.2.11","openssl":"1.1.1e","modules":"79","lts":false,"security":false},
|
||||||
|
|
||||||
|
{"version":"v13.10.2-nightly202003056122620832","date":"2020-03-05","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.13.7","v8":"7.9.317.25","uv":"1.34.2","zlib":"1.2.11","openssl":"1.1.1d","modules":"79","lts":false,"security":false},
|
||||||
|
{"version":"v13.9.1-nightly202003041bca7b6c70","date":"2020-03-04","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.13.7","v8":"7.9.317.25","uv":"1.34.2","zlib":"1.2.11","openssl":"1.1.1d","modules":"79","lts":false,"security":false},
|
||||||
|
{"version":"v13.0.0-nightly201908175e3b4d6ed9","date":"2019-08-17","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.10.2","v8":"7.6.303.28","uv":"1.31.0","zlib":"1.2.11","openssl":"1.1.1c","modules":"77","lts":false,"security":true},
|
||||||
|
{"version":"v13.0.0-nightly2019081671b5ce5885","date":"2019-08-16","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.10.2","v8":"7.6.303.28","uv":"1.31.0","zlib":"1.2.11","openssl":"1.1.1c","modules":"77","lts":false,"security":true},
|
||||||
|
{"version":"v13.0.0-nightly2019072962a809fa54","date":"2019-07-29","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"npm":"6.10.0","v8":"7.5.288.22","uv":"1.30.1","zlib":"1.2.11","openssl":"1.1.1c","modules":"74","lts":false,"security":false}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
[
|
||||||
|
{"version":"v19.0.0-rc.2","date":"2022-10-14","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v19.0.0-rc.1","date":"2022-10-04","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v17.0.0-rc.1","date":"2021-10-05","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v17.0.0-rc.0","date":"2021-09-21","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v16.17.0-rc.1","date":"2022-08-06","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-arm64-tar","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-rc.2","date":"2021-04-07","files":["headers","linux-arm64","linux-ppc64le","linux-x64","osx-x64-pkg","osx-x64-tar","src"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-rc.1","date":"2021-03-30","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v16.0.0-rc.0","date":"2021-03-19","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.19.0-rc.0","date":"2022-01-25","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.18.0-rc.0","date":"2021-09-08","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.17.4-rc.0","date":"2021-07-20","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.17.1-rc.0","date":"2021-06-11","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.16.0-rc.0","date":"2021-02-22","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.15.5-rc.1","date":"2021-02-08","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.15.5-rc.0","date":"2021-01-27","files":["headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.15.2-rc.0","date":"2020-12-14","files":["headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v14.7.0-rc.1","date":"2020-07-29","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.11.0-rc.1","date":"2020-03-11","files":["headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.11.0-rc.0","date":"2020-03-10","files":["headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-x64","osx-x64-pkg","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.10.1-rc.0","date":"2020-03-04","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.4.0-rc.0","date":"2019-12-13","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.0.1-rc.0","date":"2019-10-23","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.0.0-rc.3","date":"2019-10-21","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.0.0-rc.2","date":"2019-10-15","files":["headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.0.0-rc.1","date":"2019-10-01","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false},
|
||||||
|
{"version":"v13.0.0-rc.0","date":"2019-09-25","files":["aix-ppc64","headers","linux-arm64","linux-armv7l","linux-ppc64le","linux-s390x","linux-x64","osx-x64-pkg","osx-x64-tar","src","sunos-x64","win-x64-7z","win-x64-exe","win-x64-msi","win-x64-zip","win-x86-7z","win-x86-exe","win-x86-msi","win-x86-zip"],"v8":"","uv":"","zlib":null,"openssl":null,"modules":null,"lts":false,"security":false}
|
||||||
|
]
|
||||||
|
|
@ -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": "^20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"volta": {
|
||||||
|
"extends": "./package-volta.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.0.0"
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "24.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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: {}
|
||||||
|
|
@ -0,0 +1,537 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221103f7e2421e91",
|
||||||
|
"date": "2022-11-03",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.138.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary202211026bf85d0fb4",
|
||||||
|
"date": "2022-11-02",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.130.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221101e50e45c9f8",
|
||||||
|
"date": "2022-11-01",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.129.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary202210311b1e675ad0",
|
||||||
|
"date": "2022-10-31",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.125.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221030fefe1c0879",
|
||||||
|
"date": "2022-10-30",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.125.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary202210293881e51ba2",
|
||||||
|
"date": "2022-10-29",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.122.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary202210286fe49d2a49",
|
||||||
|
"date": "2022-10-28",
|
||||||
|
"files": [
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.112.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221027c470b3108c",
|
||||||
|
"date": "2022-10-27",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.101.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221026c24f7d1e4a",
|
||||||
|
"date": "2022-10-26",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.88.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221025b063237e20",
|
||||||
|
"date": "2022-10-25",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.73.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary2022102454996f930f",
|
||||||
|
"date": "2022-10-24",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.61.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary2022102310ff1e5a8d",
|
||||||
|
"date": "2022-10-23",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.61.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221022e83bcb6c41",
|
||||||
|
"date": "2022-10-22",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip",
|
||||||
|
"win-x86-7z",
|
||||||
|
"win-x86-exe",
|
||||||
|
"win-x86-msi",
|
||||||
|
"win-x86-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.60.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221021f6d5f347fa",
|
||||||
|
"date": "2022-10-21",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.48.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221020f78c149307",
|
||||||
|
"date": "2022-10-20",
|
||||||
|
"files": [
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.38.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v20.0.0-v8-canary20221019d52c76f76e",
|
||||||
|
"date": "2022-10-19",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-pkg",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.27.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v19.0.0-v8-canary202210187d6960f23f",
|
||||||
|
"date": "2022-10-18",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.12.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "v19.0.0-v8-canary202210172ec229fc56",
|
||||||
|
"date": "2022-10-17",
|
||||||
|
"files": [
|
||||||
|
"aix-ppc64",
|
||||||
|
"headers",
|
||||||
|
"linux-arm64",
|
||||||
|
"linux-armv7l",
|
||||||
|
"linux-ppc64le",
|
||||||
|
"linux-s390x",
|
||||||
|
"linux-x64",
|
||||||
|
"osx-arm64-tar",
|
||||||
|
"osx-x64-tar",
|
||||||
|
"src",
|
||||||
|
"win-x64-7z",
|
||||||
|
"win-x64-exe",
|
||||||
|
"win-x64-msi",
|
||||||
|
"win-x64-zip"
|
||||||
|
],
|
||||||
|
"npm": "8.19.2",
|
||||||
|
"v8": "10.9.6.0",
|
||||||
|
"uv": "1.43.0",
|
||||||
|
"zlib": "1.2.11",
|
||||||
|
"openssl": "3.0.5+quic",
|
||||||
|
"modules": "112",
|
||||||
|
"lts": false,
|
||||||
|
"security": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"version": "14.0.0",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Fermium",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/14.0.0-20200507.99",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-14.0.0-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-14.0.0-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-14.0.0-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "13.13.0",
|
||||||
|
"stable": true,
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/13.13.0-20200507.97",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-13.13.0-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200507.97/node-13.13.0-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-13.13.0-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200507.97/node-13.13.0-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-13.13.0-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/13.13.0-20200507.97/node-13.13.0-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "12.16.2",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Erbium",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/12.16.2-20200507.95",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-12.16.2-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-12.16.2-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-12.16.2-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "12.0.0",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Erbium",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/12.0.0-20200507.71",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-12.0.0-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.0.0-20200507.71/node-12.0.0-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-12.0.0-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.0.0-20200507.71/node-12.0.0-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-12.0.0-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/12.0.0-20200507.71/node-12.0.0-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "10.20.1",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Dubnium",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/10.20.1-20200507.70",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-10.20.1-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200507.70/node-10.20.1-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-10.20.1-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200507.70/node-10.20.1-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-10.20.1-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/10.20.1-20200507.70/node-10.20.1-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "8.17.0",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Carbon",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/8.17.0-20200507.37",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-8.17.0-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200507.37/node-8.17.0-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-8.17.0-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200507.37/node-8.17.0-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-8.17.0-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/8.17.0-20200507.37/node-8.17.0-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "6.17.1",
|
||||||
|
"stable": true,
|
||||||
|
"lts": "Boron",
|
||||||
|
"release_url": "https://github.com/actions/node-versions/releases/tag/6.17.1-20200529.2",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"filename": "node-6.17.1-darwin-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "darwin",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200529.2/node-6.17.1-darwin-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-6.17.1-linux-x64.tar.gz",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "linux",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200529.2/node-6.17.1-linux-x64.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename": "node-6.17.1-win32-x64.7z",
|
||||||
|
"arch": "x64",
|
||||||
|
"platform": "win32",
|
||||||
|
"download_url": "https://github.com/actions/node-versions/releases/download/6.17.1-20200529.2/node-6.17.1-win32-x64.7z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -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==
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
import io = require('@actions/io');
|
|
||||||
import fs = require('fs');
|
|
||||||
import os = require('os');
|
|
||||||
import path = require('path');
|
|
||||||
|
|
||||||
const toolDir = path.join(
|
|
||||||
__dirname,
|
|
||||||
'runner',
|
|
||||||
path.join(
|
|
||||||
Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substring(7)
|
|
||||||
),
|
|
||||||
'tools'
|
|
||||||
);
|
|
||||||
const tempDir = path.join(
|
|
||||||
__dirname,
|
|
||||||
'runner',
|
|
||||||
path.join(
|
|
||||||
Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substring(7)
|
|
||||||
),
|
|
||||||
'temp'
|
|
||||||
);
|
|
||||||
|
|
||||||
process.env['RUNNER_TOOL_CACHE'] = toolDir;
|
|
||||||
process.env['RUNNER_TEMP'] = tempDir;
|
|
||||||
import * as installer from '../src/installer';
|
|
||||||
|
|
||||||
const IS_WINDOWS = process.platform === 'win32';
|
|
||||||
|
|
||||||
describe('installer tests', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await io.rmRF(toolDir);
|
|
||||||
await io.rmRF(tempDir);
|
|
||||||
}, 100000);
|
|
||||||
|
|
||||||
it('Acquires version of node if no matching version is installed', async () => {
|
|
||||||
await installer.getNode('10.16.0');
|
|
||||||
const nodeDir = path.join(toolDir, 'node', '10.16.0', os.arch());
|
|
||||||
|
|
||||||
expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true);
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true);
|
|
||||||
} else {
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'bin', 'node'))).toBe(true);
|
|
||||||
}
|
|
||||||
}, 100000);
|
|
||||||
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
it('Falls back to backup location if first one doesnt contain correct version', async () => {
|
|
||||||
await installer.getNode('5.10.1');
|
|
||||||
const nodeDir = path.join(toolDir, 'node', '5.10.1', os.arch());
|
|
||||||
|
|
||||||
expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true);
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true);
|
|
||||||
}, 100000);
|
|
||||||
|
|
||||||
it('Falls back to third location if second one doesnt contain correct version', async () => {
|
|
||||||
await installer.getNode('0.12.18');
|
|
||||||
const nodeDir = path.join(toolDir, 'node', '0.12.18', os.arch());
|
|
||||||
|
|
||||||
expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true);
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true);
|
|
||||||
}, 100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('Throws if no location contains correct node version', async () => {
|
|
||||||
let thrown = false;
|
|
||||||
try {
|
|
||||||
await installer.getNode('1000');
|
|
||||||
} catch {
|
|
||||||
thrown = true;
|
|
||||||
}
|
|
||||||
expect(thrown).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Acquires version of node with long paths', async () => {
|
|
||||||
const toolpath = await installer.getNode('8.8.1');
|
|
||||||
const nodeDir = path.join(toolDir, 'node', '8.8.1', os.arch());
|
|
||||||
|
|
||||||
expect(fs.existsSync(`${nodeDir}.complete`)).toBe(true);
|
|
||||||
if (IS_WINDOWS) {
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'node.exe'))).toBe(true);
|
|
||||||
} else {
|
|
||||||
expect(fs.existsSync(path.join(nodeDir, 'bin', 'node'))).toBe(true);
|
|
||||||
}
|
|
||||||
}, 100000);
|
|
||||||
|
|
||||||
it('Uses version of node installed in cache', async () => {
|
|
||||||
const nodeDir: string = path.join(toolDir, 'node', '250.0.0', os.arch());
|
|
||||||
await io.mkdirP(nodeDir);
|
|
||||||
fs.writeFileSync(`${nodeDir}.complete`, 'hello');
|
|
||||||
// This will throw if it doesn't find it in the cache (because no such version exists)
|
|
||||||
await installer.getNode('250.0.0');
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Doesnt use version of node that was only partially installed in cache', async () => {
|
|
||||||
const nodeDir: string = path.join(toolDir, 'node', '251.0.0', os.arch());
|
|
||||||
await io.mkdirP(nodeDir);
|
|
||||||
let thrown = false;
|
|
||||||
try {
|
|
||||||
// This will throw if it doesn't find it in the cache (because no such version exists)
|
|
||||||
await installer.getNode('251.0.0');
|
|
||||||
} catch {
|
|
||||||
thrown = true;
|
|
||||||
}
|
|
||||||
expect(thrown).toBe(true);
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Resolves semantic versions of node installed in cache', async () => {
|
|
||||||
const nodeDir: string = path.join(toolDir, 'node', '252.0.0', os.arch());
|
|
||||||
await io.mkdirP(nodeDir);
|
|
||||||
fs.writeFileSync(`${nodeDir}.complete`, 'hello');
|
|
||||||
// These will throw if it doesn't find it in the cache (because no such version exists)
|
|
||||||
await installer.getNode('252.0.0');
|
|
||||||
await installer.getNode('252');
|
|
||||||
await installer.getNode('252.0');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,433 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import osm from 'os';
|
||||||
|
|
||||||
|
import each from 'jest-each';
|
||||||
|
|
||||||
|
import * as main from '../src/main';
|
||||||
|
import * as util from '../src/util';
|
||||||
|
import OfficialBuilds from '../src/distributions/official_builds/official_builds';
|
||||||
|
|
||||||
|
describe('main tests', () => {
|
||||||
|
let inputs = {} as any;
|
||||||
|
let os = {} as any;
|
||||||
|
|
||||||
|
let infoSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let saveStateSpy: jest.SpyInstance;
|
||||||
|
let inSpy: jest.SpyInstance;
|
||||||
|
let setOutputSpy: jest.SpyInstance;
|
||||||
|
let startGroupSpy: jest.SpyInstance;
|
||||||
|
let endGroupSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
let whichSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
let getExecOutputSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
let getNodeVersionFromFileSpy: jest.SpyInstance;
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let findSpy: jest.SpyInstance;
|
||||||
|
let isCacheActionAvailable: jest.SpyInstance;
|
||||||
|
|
||||||
|
let setupNodeJsSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
inputs = {};
|
||||||
|
|
||||||
|
// node
|
||||||
|
os = {};
|
||||||
|
console.log('::stop-commands::stoptoken');
|
||||||
|
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_OUTPUT'] = ''; // Stub out ENV file functionality so we can verify it writes to standard out
|
||||||
|
infoSpy = jest.spyOn(core, 'info');
|
||||||
|
infoSpy.mockImplementation(() => {});
|
||||||
|
setOutputSpy = jest.spyOn(core, 'setOutput');
|
||||||
|
setOutputSpy.mockImplementation(() => {});
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
warningSpy.mockImplementation(() => {});
|
||||||
|
saveStateSpy = jest.spyOn(core, 'saveState');
|
||||||
|
saveStateSpy.mockImplementation(() => {});
|
||||||
|
startGroupSpy = jest.spyOn(core, 'startGroup');
|
||||||
|
startGroupSpy.mockImplementation(() => {});
|
||||||
|
endGroupSpy = jest.spyOn(core, 'endGroup');
|
||||||
|
endGroupSpy.mockImplementation(() => {});
|
||||||
|
inSpy = jest.spyOn(core, 'getInput');
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
whichSpy = jest.spyOn(io, 'which');
|
||||||
|
|
||||||
|
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
|
||||||
|
findSpy = jest.spyOn(tc, 'find');
|
||||||
|
|
||||||
|
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// uncomment to debug
|
||||||
|
process.stderr.write('write:' + line + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
setupNodeJsSpy = jest.spyOn(OfficialBuilds.prototype, 'setupNodeJs');
|
||||||
|
setupNodeJsSpy.mockImplementation(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::');
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
describe('getNodeVersionFromFile', () => {
|
||||||
|
each`
|
||||||
|
contents | expected
|
||||||
|
${'12'} | ${'12'}
|
||||||
|
${'12.3'} | ${'12.3'}
|
||||||
|
${'12.3.4'} | ${'12.3.4'}
|
||||||
|
${'v12.3.4'} | ${'12.3.4'}
|
||||||
|
${'lts/erbium'} | ${'lts/erbium'}
|
||||||
|
${'lts/*'} | ${'lts/*'}
|
||||||
|
${'nodejs 12.3.4'} | ${'12.3.4'}
|
||||||
|
${'ruby 2.3.4\nnodejs 12.3.4\npython 3.4.5'} | ${'12.3.4'}
|
||||||
|
${''} | ${''}
|
||||||
|
${'unknown format'} | ${'unknown format'}
|
||||||
|
${' 14.1.0 '} | ${'14.1.0'}
|
||||||
|
${'{}'} | ${null}
|
||||||
|
${'{"volta": {"node": ">=14.0.0 <=17.0.0"}}'} | ${'>=14.0.0 <=17.0.0'}
|
||||||
|
${'{"volta": {"extends": "./package.json"}}'} | ${'18.0.0'}
|
||||||
|
${'{"engines": {"node": "17.0.0"}}'} | ${'17.0.0'}
|
||||||
|
${'{"devEngines": {"runtime": {"name": "node", "version": "22.0.0"}}}'} | ${'22.0.0'}
|
||||||
|
${'{"devEngines": {"runtime": [{"name": "bun"}, {"name": "node", "version": "22.0.0"}]}}'} | ${'22.0.0'}
|
||||||
|
`.it('parses "$contents"', ({contents, expected}) => {
|
||||||
|
const existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
existsSpy.mockImplementation(() => true);
|
||||||
|
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(filePath => {
|
||||||
|
if (
|
||||||
|
typeof filePath === 'string' &&
|
||||||
|
path.basename(filePath) === 'package.json'
|
||||||
|
) {
|
||||||
|
// Special case for volta.extends
|
||||||
|
return '{"volta": {"node": "18.0.0"}}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return contents;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(util.getNodeVersionFromFile('file')).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('printEnvDetailsAndSetOutput', () => {
|
||||||
|
it.each([
|
||||||
|
[{node: '12.0.2', npm: '6.3.3', yarn: '1.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: '17.0.2', npm: '6.3.3', yarn: ''}]
|
||||||
|
])('Tools versions %p', async obj => {
|
||||||
|
getExecOutputSpy.mockImplementation(async command => {
|
||||||
|
if (Reflect.has(obj, command) && !obj[command]) {
|
||||||
|
return {
|
||||||
|
stdout: '',
|
||||||
|
stderr: `${command} does not exist`,
|
||||||
|
exitCode: 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {stdout: obj[command], stderr: '', exitCode: 0};
|
||||||
|
});
|
||||||
|
|
||||||
|
whichSpy.mockImplementation(cmd => {
|
||||||
|
return `some/${cmd}/path`;
|
||||||
|
});
|
||||||
|
|
||||||
|
await util.printEnvDetailsAndSetOutput();
|
||||||
|
|
||||||
|
expect(setOutputSpy).toHaveBeenCalledWith('node-version', obj['node']);
|
||||||
|
Object.getOwnPropertyNames(obj).forEach(name => {
|
||||||
|
if (!obj[name]) {
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`[warning]${name} does not exist`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(`${name}: ${obj[name]}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('node-version-file flag', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
delete inputs['node-version'];
|
||||||
|
inputs['node-version-file'] = '.nvmrc';
|
||||||
|
|
||||||
|
getNodeVersionFromFileSpy = jest.spyOn(util, 'getNodeVersionFromFile');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
getNodeVersionFromFileSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not read node-version-file if node-version is provided', async () => {
|
||||||
|
// Arrange
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(inputs['node-version']).toBeDefined();
|
||||||
|
expect(inputs['node-version-file']).toBeDefined();
|
||||||
|
expect(getNodeVersionFromFileSpy).not.toHaveBeenCalled();
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
'Both node-version and node-version-file inputs are specified, only node-version will be used'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not read node-version-file if node-version-file is not provided', async () => {
|
||||||
|
// Arrange
|
||||||
|
delete inputs['node-version-file'];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(getNodeVersionFromFileSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reads node-version-file', async () => {
|
||||||
|
// Arrange
|
||||||
|
const expectedVersionSpec = '14';
|
||||||
|
getNodeVersionFromFileSpy.mockImplementation(() => expectedVersionSpec);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||||
|
expect(infoSpy).toHaveBeenCalledWith(
|
||||||
|
`Resolved ${inputs['node-version-file']} as ${expectedVersionSpec}`
|
||||||
|
);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
it('should throw an error if node-version-file is not accessible', async () => {
|
||||||
|
// Arrange
|
||||||
|
inputs['node-version-file'] = 'non-existing-file';
|
||||||
|
const versionFilePath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'data',
|
||||||
|
inputs['node-version-file']
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(getNodeVersionFromFileSpy).toHaveBeenCalled();
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::The specified node version file at: ${versionFilePath} does not exist${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('cache on GHES', () => {
|
||||||
|
it('Should throw an error, because cache is not supported', async () => {
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
|
||||||
|
// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
process.env['GITHUB_SERVER_URL'] = 'https://www.test.com';
|
||||||
|
isCacheActionAvailable.mockImplementation(() => false);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
`Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should throw an internal error', async () => {
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
|
||||||
|
// expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
process.env['GITHUB_SERVER_URL'] = '';
|
||||||
|
isCacheActionAvailable.mockImplementation(() => false);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
'The runner was not able to contact the cache service. Caching will be skipped'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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 => 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 => 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 => 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 => 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 => 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 => inputs[name]);
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(() =>
|
||||||
|
JSON.stringify({
|
||||||
|
devEngines: {
|
||||||
|
packageManager: [{name: 'pnpm'}, {name: 'yarn'}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should not enable caching if packageManager field is missing in package.json and cache input is not provided', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
const readFileSpy = jest.spyOn(fs, 'readFileSync');
|
||||||
|
readFileSpy.mockImplementation(() =>
|
||||||
|
JSON.stringify({
|
||||||
|
// packageManager field is not present
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should skip caching when package-manager-cache is false', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'false';
|
||||||
|
inputs['cache'] = '';
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
await main.run();
|
||||||
|
expect(saveStateSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should enable caching with cache input explicitly provided', async () => {
|
||||||
|
inputs['package-manager-cache'] = 'true';
|
||||||
|
inputs['cache'] = 'npm';
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
isCacheActionAvailable.mockImplementation(() => true);
|
||||||
|
await main.run();
|
||||||
|
expect(saveStateSpy).toHaveBeenCalledWith(expect.anything(), 'npm');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import {MockGlobber} from './glob-mock';
|
||||||
|
|
||||||
|
describe('mocked globber tests', () => {
|
||||||
|
it('globber should return generator', async () => {
|
||||||
|
const globber = new MockGlobber(['aaa', 'bbb', 'ccc']);
|
||||||
|
const generator = globber.globGenerator();
|
||||||
|
const result: string[] = [];
|
||||||
|
for await (const itemPath of generator) {
|
||||||
|
result.push(itemPath);
|
||||||
|
}
|
||||||
|
expect(result).toEqual(['aaa', 'bbb', 'ccc']);
|
||||||
|
});
|
||||||
|
it('globber should return glob', async () => {
|
||||||
|
const globber = new MockGlobber(['aaa', 'bbb', 'ccc']);
|
||||||
|
const result: string[] = await globber.glob();
|
||||||
|
expect(result).toEqual(['aaa', 'bbb', 'ccc']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import {Globber} from '@actions/glob';
|
||||||
|
|
||||||
|
export class MockGlobber implements Globber {
|
||||||
|
private readonly expected: string[];
|
||||||
|
constructor(expected: string[]) {
|
||||||
|
this.expected = expected;
|
||||||
|
}
|
||||||
|
getSearchPaths(): string[] {
|
||||||
|
return this.expected.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
async glob(): Promise<string[]> {
|
||||||
|
const result: string[] = [];
|
||||||
|
for await (const itemPath of this.globGenerator()) {
|
||||||
|
result.push(itemPath);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async *globGenerator(): AsyncGenerator<string, void> {
|
||||||
|
for (const e of this.expected) {
|
||||||
|
yield e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async create(expected: string[]): Promise<MockGlobber> {
|
||||||
|
return new MockGlobber(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,650 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import fs from 'fs';
|
||||||
|
import cp from 'child_process';
|
||||||
|
import osm from 'os';
|
||||||
|
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';
|
||||||
|
import nodeTestDist from './data/node-dist-index.json';
|
||||||
|
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||||
|
import nodeTestDistRc from './data/node-rc-index.json';
|
||||||
|
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||||
|
|
||||||
|
describe('setup-node', () => {
|
||||||
|
let inputs = {} as any;
|
||||||
|
let os = {} as any;
|
||||||
|
|
||||||
|
let inSpy: jest.SpyInstance;
|
||||||
|
let findSpy: jest.SpyInstance;
|
||||||
|
let findAllVersionsSpy: jest.SpyInstance;
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let logSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let getManifestSpy: jest.SpyInstance;
|
||||||
|
let getDistSpy: jest.SpyInstance;
|
||||||
|
let platSpy: jest.SpyInstance;
|
||||||
|
let archSpy: jest.SpyInstance;
|
||||||
|
let dlSpy: jest.SpyInstance;
|
||||||
|
let exSpy: jest.SpyInstance;
|
||||||
|
let cacheSpy: jest.SpyInstance;
|
||||||
|
let dbgSpy: jest.SpyInstance;
|
||||||
|
let whichSpy: jest.SpyInstance;
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
let mkdirpSpy: jest.SpyInstance;
|
||||||
|
let cpSpy: jest.SpyInstance;
|
||||||
|
let execSpy: jest.SpyInstance;
|
||||||
|
let authSpy: jest.SpyInstance;
|
||||||
|
let parseNodeVersionSpy: jest.SpyInstance;
|
||||||
|
let isCacheActionAvailable: jest.SpyInstance;
|
||||||
|
let getExecOutputSpy: jest.SpyInstance;
|
||||||
|
let getJsonSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// @actions/core
|
||||||
|
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||||
|
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['RUNNER_TEMP'] = '/runner_temp';
|
||||||
|
inputs = {};
|
||||||
|
inSpy = jest.spyOn(core, 'getInput');
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
// node
|
||||||
|
os = {};
|
||||||
|
platSpy = jest.spyOn(osm, 'platform');
|
||||||
|
platSpy.mockImplementation(() => os['platform']);
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
findSpy = jest.spyOn(tc, 'find');
|
||||||
|
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||||
|
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||||
|
exSpy = jest.spyOn(tc, 'extractTar');
|
||||||
|
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||||
|
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||||
|
|
||||||
|
// http-client
|
||||||
|
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||||
|
|
||||||
|
// io
|
||||||
|
whichSpy = jest.spyOn(io, 'which');
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||||
|
cpSpy = jest.spyOn(io, 'cp');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
|
||||||
|
// disable authentication portion for installer tests
|
||||||
|
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||||
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
getJsonSpy.mockImplementation(url => {
|
||||||
|
let res: any;
|
||||||
|
if (url.includes('/rc')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
|
} else if (url.includes('/nightly')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistNightly;
|
||||||
|
} else if (url.includes('/v8-canary')) {
|
||||||
|
res = <INodeVersion[]>nodeV8CanaryTestDist;
|
||||||
|
} else {
|
||||||
|
res = <INodeVersion[]>nodeTestDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result: res};
|
||||||
|
});
|
||||||
|
|
||||||
|
// writes
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
logSpy = jest.spyOn(core, 'info');
|
||||||
|
dbgSpy = jest.spyOn(core, 'debug');
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
warningSpy.mockImplementation(msg => {
|
||||||
|
// uncomment to debug
|
||||||
|
// process.stderr.write('log:' + msg + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
// @actions/exec
|
||||||
|
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Found in cache tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
it('finds version in cache with stable true', async () => {
|
||||||
|
inputs['node-version'] = '16-nightly';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
inputs.stable = 'true';
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'12.0.1',
|
||||||
|
'16.0.0-nightly20210415c3a5e15ebe',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'16.1.3'
|
||||||
|
]);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(findSpy).toHaveBeenCalledWith(
|
||||||
|
'node',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'x64'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache with stable false', async () => {
|
||||||
|
inputs['node-version'] = '16.0.0-nightly20210415c3a5e15ebe';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
inputs.stable = 'false';
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/16.0.0-nightly20210415c3a5e15ebe/x64'
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'12.0.1',
|
||||||
|
'16.0.0-nightly20210415c3a5e15ebe',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'16.1.3'
|
||||||
|
]);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(findSpy).toHaveBeenCalledWith(
|
||||||
|
'node',
|
||||||
|
'16.0.0-nightly20210415c3a5e15ebe',
|
||||||
|
'x64'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
|
inputs['node-version'] = '16-nightly';
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/16.0.0-nightly20210417bc31dc0e0f/x64'
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'12.0.1',
|
||||||
|
'16.0.0-nightly20210415c3a5e15ebe',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'16.1.3'
|
||||||
|
]);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(findSpy).toHaveBeenCalledWith(
|
||||||
|
'node',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'x64'
|
||||||
|
);
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles unhandled find error and reports error', async () => {
|
||||||
|
const errMsg = 'unhandled error message';
|
||||||
|
inputs['node-version'] = '16.0.0-nightly20210417bc31dc0e0f';
|
||||||
|
|
||||||
|
findAllVersionsSpy.mockImplementation(() => [
|
||||||
|
'12.0.1',
|
||||||
|
'16.0.0-nightly20210415c3a5e15ebe',
|
||||||
|
'16.0.0-nightly20210417bc31dc0e0f',
|
||||||
|
'16.1.3'
|
||||||
|
]);
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to a version from node dist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '13.13.1-nightly20200415947ddec091';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
|
||||||
|
);
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('windows: falls back to exe version if not in manifest and not in node dist', async () => {
|
||||||
|
os.platform = 'win32';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '13.13.1-nightly20200415947ddec091';
|
||||||
|
|
||||||
|
const workingUrls = [
|
||||||
|
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.exe`,
|
||||||
|
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.lib`
|
||||||
|
];
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async url => {
|
||||||
|
if (workingUrls.includes(url)) {
|
||||||
|
return '/some/temp/path';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new tc.HTTPError(404);
|
||||||
|
});
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
|
||||||
|
);
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
mkdirpSpy.mockImplementation(async () => {});
|
||||||
|
cpSpy.mockImplementation(async () => {});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
workingUrls.forEach(url => {
|
||||||
|
expect(dlSpy).toHaveBeenCalledWith(url, undefined, undefined);
|
||||||
|
});
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${toolPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('linux: does not fall back to exe version if not in manifest and not in node dist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '13.13.1-nightly20200415947ddec091';
|
||||||
|
|
||||||
|
const workingUrls = [
|
||||||
|
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.exe`,
|
||||||
|
`https://nodejs.org/download/nightly/v${versionSpec}/win-x64/node.lib`
|
||||||
|
];
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async url => {
|
||||||
|
if (workingUrls.includes(url)) {
|
||||||
|
return '/some/temp/path';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new tc.HTTPError(404);
|
||||||
|
});
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
'/cache/node/13.13.1-nightly20200415947ddec091/x64'
|
||||||
|
);
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
mkdirpSpy.mockImplementation(async () => {});
|
||||||
|
cpSpy.mockImplementation(async () => {});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
workingUrls.forEach(url => {
|
||||||
|
expect(dlSpy).not.toHaveBeenCalledWith(url);
|
||||||
|
});
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unexpected HTTP response: 404${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not find a version that does not exist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '10.13.1-nightly20200415947ddec091';
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports a failed download', async () => {
|
||||||
|
const errMsg = 'unhandled download message';
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is in the manifest
|
||||||
|
const versionSpec = '18.0.0-nightly202204180699150267';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('acquires specified architecture of node', 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';
|
||||||
|
|
||||||
|
const expectedUrl = `https://nodejs.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);
|
||||||
|
|
||||||
|
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', () => {
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'17.5.0-nightly',
|
||||||
|
'17.5.0-nightly20220209e43808936a',
|
||||||
|
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'17-nightly',
|
||||||
|
'17.5.0-nightly20220209e43808936a',
|
||||||
|
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'18.0.0-nightly',
|
||||||
|
'18.0.0-nightly20220419bde889bd4e',
|
||||||
|
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'18-nightly',
|
||||||
|
'18.0.0-nightly20220419bde889bd4e',
|
||||||
|
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'20.0.0-nightly',
|
||||||
|
'20.0.0-nightly2022101987cdf7d412',
|
||||||
|
'https://nodejs.org/download/nightly/v20.0.0-nightly2022101987cdf7d412/node-v20.0.0-nightly2022101987cdf7d412-linux-x64.tar.gz'
|
||||||
|
]
|
||||||
|
])(
|
||||||
|
'finds the versions in the index.json and installs it',
|
||||||
|
async (input, expectedVersion, expectedUrl) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['17.5.0-nightly', '17.5.0-nightly20220209e43808936a'],
|
||||||
|
['17-nightly', '17.5.0-nightly20220209e43808936a'],
|
||||||
|
['20.0.0-nightly', '20.0.0-nightly2022101987cdf7d412']
|
||||||
|
])(
|
||||||
|
'finds the %s version in the hostedToolcache',
|
||||||
|
async (input, expectedVersion) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
findSpy.mockReturnValue(toolPath);
|
||||||
|
findAllVersionsSpy.mockReturnValue([
|
||||||
|
'17.5.0-nightly20220209e43808936a',
|
||||||
|
'17.5.0-nightly20220209e43808935a',
|
||||||
|
'20.0.0-nightly2022101987cdf7d412',
|
||||||
|
'20.0.0-nightly2022101987cdf7d411'
|
||||||
|
]);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(findAllVersionsSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'17.5.0-nightly',
|
||||||
|
'17.5.0-nightly20220209e43808936a',
|
||||||
|
'17.0.0-nightly202110193f11666dc7',
|
||||||
|
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'17-nightly',
|
||||||
|
'17.5.0-nightly20220209e43808936a',
|
||||||
|
'17.0.0-nightly202110193f11666dc7',
|
||||||
|
'https://nodejs.org/download/nightly/v17.5.0-nightly20220209e43808936a/node-v17.5.0-nightly20220209e43808936a-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'18.0.0-nightly',
|
||||||
|
'18.0.0-nightly20220419bde889bd4e',
|
||||||
|
'18.0.0-nightly202204180699150267',
|
||||||
|
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'18-nightly',
|
||||||
|
'18.0.0-nightly20220419bde889bd4e',
|
||||||
|
'18.0.0-nightly202204180699150267',
|
||||||
|
'https://nodejs.org/download/nightly/v18.0.0-nightly20220419bde889bd4e/node-v18.0.0-nightly20220419bde889bd4e-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'20.0.0-nightly',
|
||||||
|
'20.0.0-nightly2022101987cdf7d412',
|
||||||
|
'20.0.0-nightly2022101987cdf7d411',
|
||||||
|
'https://nodejs.org/download/nightly/v20.0.0-nightly2022101987cdf7d412/node-v20.0.0-nightly2022101987cdf7d412-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';
|
||||||
|
|
||||||
|
findSpy.mockReturnValue(foundToolPath);
|
||||||
|
findAllVersionsSpy.mockReturnValue([
|
||||||
|
'17.0.0-nightly202110193f11666dc7',
|
||||||
|
'18.0.0-nightly202204180699150267',
|
||||||
|
'20.0.0-nightly2022101987cdf7d411'
|
||||||
|
]);
|
||||||
|
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(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,901 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import fs from 'fs';
|
||||||
|
import cp from 'child_process';
|
||||||
|
import osm from 'os';
|
||||||
|
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';
|
||||||
|
import nodeTestDist from './data/node-dist-index.json';
|
||||||
|
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||||
|
import nodeTestDistRc from './data/node-rc-index.json';
|
||||||
|
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||||
|
|
||||||
|
describe('setup-node', () => {
|
||||||
|
let build: OfficialBuilds;
|
||||||
|
let inputs = {} as any;
|
||||||
|
let os = {} as any;
|
||||||
|
|
||||||
|
let inSpy: jest.SpyInstance;
|
||||||
|
let findSpy: jest.SpyInstance;
|
||||||
|
let findAllVersionsSpy: jest.SpyInstance;
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let logSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let getManifestSpy: jest.SpyInstance;
|
||||||
|
let platSpy: jest.SpyInstance;
|
||||||
|
let archSpy: jest.SpyInstance;
|
||||||
|
let dlSpy: jest.SpyInstance;
|
||||||
|
let exSpy: jest.SpyInstance;
|
||||||
|
let cacheSpy: jest.SpyInstance;
|
||||||
|
let dbgSpy: jest.SpyInstance;
|
||||||
|
let whichSpy: jest.SpyInstance;
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
let readFileSyncSpy: jest.SpyInstance;
|
||||||
|
let mkdirpSpy: jest.SpyInstance;
|
||||||
|
let execSpy: jest.SpyInstance;
|
||||||
|
let authSpy: jest.SpyInstance;
|
||||||
|
let isCacheActionAvailable: jest.SpyInstance;
|
||||||
|
let getExecOutputSpy: jest.SpyInstance;
|
||||||
|
let getJsonSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// @actions/core
|
||||||
|
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||||
|
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
|
||||||
|
inputs = {};
|
||||||
|
inSpy = jest.spyOn(core, 'getInput');
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
// node
|
||||||
|
os = {};
|
||||||
|
platSpy = jest.spyOn(osm, 'platform');
|
||||||
|
platSpy.mockImplementation(() => os['platform']);
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
findSpy = jest.spyOn(tc, 'find');
|
||||||
|
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||||
|
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||||
|
exSpy = jest.spyOn(tc, 'extractTar');
|
||||||
|
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||||
|
getManifestSpy = jest.spyOn(tc, 'getManifestFromRepo');
|
||||||
|
|
||||||
|
// http-client
|
||||||
|
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||||
|
|
||||||
|
// io
|
||||||
|
whichSpy = jest.spyOn(io, 'which');
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
|
||||||
|
// disable authentication portion for installer tests
|
||||||
|
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||||
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
// gets
|
||||||
|
getManifestSpy.mockImplementation(
|
||||||
|
() => <tc.IToolRelease[]>nodeTestManifest
|
||||||
|
);
|
||||||
|
|
||||||
|
getJsonSpy.mockImplementation(url => {
|
||||||
|
let res: any;
|
||||||
|
if (url.includes('/rc')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
|
} else if (url.includes('/nightly')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistNightly;
|
||||||
|
} else {
|
||||||
|
res = <INodeVersion[]>nodeTestDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result: res};
|
||||||
|
});
|
||||||
|
|
||||||
|
// writes
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
logSpy = jest.spyOn(core, 'info');
|
||||||
|
dbgSpy = jest.spyOn(core, 'debug');
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
warningSpy.mockImplementation(msg => {
|
||||||
|
// uncomment to debug
|
||||||
|
// process.stderr.write('log:' + msg + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
// @actions/exec
|
||||||
|
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Manifest find tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
it.each([
|
||||||
|
['12.16.2', 'darwin', '12.16.2', 'Erbium'],
|
||||||
|
['12', 'linux', '12.16.2', 'Erbium'],
|
||||||
|
['10', 'win32', '10.20.1', 'Dubnium'],
|
||||||
|
['*', 'linux', '14.0.0', 'Fermium']
|
||||||
|
])(
|
||||||
|
'can find %s from manifest on %s',
|
||||||
|
async (versionSpec, platform, expectedVersion, expectedLts) => {
|
||||||
|
os.platform = platform;
|
||||||
|
os.arch = 'x64';
|
||||||
|
const versions: tc.IToolRelease[] | null = await tc.getManifestFromRepo(
|
||||||
|
'actions',
|
||||||
|
'node-versions',
|
||||||
|
'mocktoken'
|
||||||
|
);
|
||||||
|
expect(versions).toBeDefined();
|
||||||
|
const match = await tc.findFromManifest(versionSpec, true, versions);
|
||||||
|
expect(match).toBeDefined();
|
||||||
|
expect(match?.version).toBe(expectedVersion);
|
||||||
|
expect((match as any).lts).toBe(expectedLts);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Found in cache tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
it('finds version in cache with stable true', async () => {
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs.stable = 'true';
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles unhandled find error and reports error', async () => {
|
||||||
|
const errMsg = 'unhandled error message';
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||||
|
});
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Manifest tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
it('downloads a version from a manifest match', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is in the manifest
|
||||||
|
const versionSpec = '12.16.2';
|
||||||
|
const resolvedVersion = versionSpec;
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
const expectedUrl =
|
||||||
|
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
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);
|
||||||
|
whichSpy.mockImplementation(cmd => {
|
||||||
|
return `some/${cmd}/path`;
|
||||||
|
});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'node',
|
||||||
|
['--version'],
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'npm',
|
||||||
|
['--version'],
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
expect(getExecOutputSpy).toHaveBeenCalledWith(
|
||||||
|
'yarn',
|
||||||
|
['--version'],
|
||||||
|
expect.anything()
|
||||||
|
);
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${resolvedVersion} - ${os.arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
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(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to a version from node dist', 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';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['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 Node'
|
||||||
|
);
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not find a version that does not exist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '9.99.9';
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Not found in manifest. Falling back to download directly from Node'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports a failed download', async () => {
|
||||||
|
const errMsg = 'unhandled download message';
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is in the manifest
|
||||||
|
const versionSpec = '12.16.2';
|
||||||
|
const resolvedVersion = versionSpec;
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
dlSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports when download failed but version exists', 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';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementationOnce(async () => {
|
||||||
|
throw new tc.HTTPError(404);
|
||||||
|
});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(getManifestSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Not found in manifest. Falling back to download directly from Node'
|
||||||
|
);
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(warningSpy).toHaveBeenCalledWith(
|
||||||
|
`Node version ${versionSpec} for platform ${os.platform} and architecture ${os.arch} was found but failed to download. ` +
|
||||||
|
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
|
||||||
|
'To resolve this issue you may either fall back to the older version or try again later.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('acquires specified architecture of node', 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';
|
||||||
|
|
||||||
|
const expectedUrl =
|
||||||
|
arch === 'x64'
|
||||||
|
? `https://github.com/actions/node-versions/releases/download/${version}/node-${version}-${platform}-${arch}.zip`
|
||||||
|
: `https://nodejs.org/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('check-latest flag', () => {
|
||||||
|
it('use local version and dont check manifest if check-latest is not specified', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs['check-latest'] = 'false';
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.1/x64');
|
||||||
|
findSpy.mockReturnValue(toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
expect(logSpy).not.toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve the latest version from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).not.toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check latest version and resolve it from local cache', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.16.2/x64');
|
||||||
|
findSpy.mockReturnValue(toolPath);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve the latest version from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('check latest version and install it from manifest', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
inputs['node-version'] = '12';
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
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);
|
||||||
|
const expectedUrl =
|
||||||
|
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz';
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve the latest version from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith("Resolved as '12.16.2'");
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring 12.16.2 - ${os.arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fallback to dist if version if not found in manifest', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '11';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/11.11.0/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve the latest version from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Failed to resolve version ${versionSpec} from manifest`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fallback to dist if manifest is not available', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
// a version which is not in the manifest but is in node dist
|
||||||
|
const versionSpec = '12';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['check-latest'] = 'true';
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
getManifestSpy.mockImplementation(() => {
|
||||||
|
throw new Error('Unable to download manifest');
|
||||||
|
});
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/12.11.0/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve the latest version from manifest...'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Unable to resolve version from manifest...'
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Failed to resolve version ${versionSpec} from manifest`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${versionSpec}...`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('LTS version', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
inputs.stable = 'true';
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['erbium', '12.16.2'],
|
||||||
|
['*', '14.0.0'],
|
||||||
|
['-1', '12.16.2']
|
||||||
|
])(
|
||||||
|
'find latest LTS version and resolve it from local cache (lts/%s)',
|
||||||
|
async (lts, expectedVersion) => {
|
||||||
|
// arrange
|
||||||
|
inputs['node-version'] = `lts/${lts}`;
|
||||||
|
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
findSpy.mockReturnValue(toolPath);
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve LTS alias from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`LTS alias '${lts}' for Node version 'lts/${lts}'`
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'erbium',
|
||||||
|
'12.16.2',
|
||||||
|
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'*',
|
||||||
|
'14.0.0',
|
||||||
|
'https://github.com/actions/node-versions/releases/download/14.0.0-20200507.99/node-14.0.0-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'-1',
|
||||||
|
'12.16.2',
|
||||||
|
'https://github.com/actions/node-versions/releases/download/12.16.2-20200507.95/node-12.16.2-linux-x64.tar.gz'
|
||||||
|
]
|
||||||
|
])(
|
||||||
|
'find latest LTS version and install it from manifest (lts/%s)',
|
||||||
|
async (lts, expectedVersion, expectedUrl) => {
|
||||||
|
// arrange
|
||||||
|
inputs['node-version'] = `lts/${lts}`;
|
||||||
|
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
const expectedMajor = expectedVersion.split('.')[0];
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve LTS alias from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).not.toHaveBeenCalledWith('No manifest cached');
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`LTS alias '${lts}' for Node version 'lts/${lts}'`
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`Found LTS release '${expectedVersion}' for Node version 'lts/${lts}'`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Attempting to download ${expectedMajor}...`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
`Acquiring ${expectedVersion} - ${os.arch} from ${expectedUrl}`
|
||||||
|
);
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('fail with unable to parse LTS alias (lts/)', async () => {
|
||||||
|
// arrange
|
||||||
|
inputs['node-version'] = 'lts/';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve LTS alias from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to parse LTS alias for Node version 'lts/'${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fail to find LTS version (lts/unknown)', async () => {
|
||||||
|
// arrange
|
||||||
|
inputs['node-version'] = 'lts/unknown';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve LTS alias from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
`LTS alias 'unknown' for Node version 'lts/unknown'`
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find LTS release 'unknown' for Node version 'lts/unknown'.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fail if manifest is not available', async () => {
|
||||||
|
// arrange
|
||||||
|
inputs['node-version'] = 'lts/erbium';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
getManifestSpy.mockImplementation(() => {
|
||||||
|
throw new Error('Unable to download manifest');
|
||||||
|
});
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(
|
||||||
|
'Attempt to resolve LTS alias from manifest...'
|
||||||
|
);
|
||||||
|
expect(dbgSpy).toHaveBeenCalledWith(
|
||||||
|
'Getting manifest from actions/node-versions@main'
|
||||||
|
);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to download manifest${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('latest alias syntax', () => {
|
||||||
|
it.each(['latest', 'current', 'node'])(
|
||||||
|
'download the %s version if alias is provided',
|
||||||
|
async inputVersion => {
|
||||||
|
// Arrange
|
||||||
|
inputs['node-version'] = inputVersion;
|
||||||
|
|
||||||
|
os.platform = 'darwin';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
getManifestSpy.mockImplementation(() => {
|
||||||
|
throw new Error('Unable to download manifest');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Unable to download manifest');
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('latest alias syntax from cache', () => {
|
||||||
|
it.each(['latest', 'current', 'node'])(
|
||||||
|
'download the %s version if alias is provided',
|
||||||
|
async inputVersion => {
|
||||||
|
// Arrange
|
||||||
|
inputs['node-version'] = inputVersion;
|
||||||
|
const expectedVersion = nodeTestDist[0];
|
||||||
|
|
||||||
|
os.platform = 'darwin';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const toolPath = path.normalize(
|
||||||
|
`/cache/node/${expectedVersion.version}/x64`
|
||||||
|
);
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('getting latest node version...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
export YARN_ENABLE_IMMUTABLE_INSTALLS=false
|
||||||
|
rm package.json
|
||||||
|
rm package-lock.json
|
||||||
|
echo "create yarn2 project in the sub2"
|
||||||
|
mkdir sub2
|
||||||
|
cd sub2
|
||||||
|
cat <<EOT >package.json
|
||||||
|
{
|
||||||
|
"name": "subproject",
|
||||||
|
"dependencies": {
|
||||||
|
"random": "^3.0.6",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
yarn set version 2.4.3
|
||||||
|
yarn install
|
||||||
|
|
||||||
|
echo "create yarn3 project in the sub3"
|
||||||
|
cd ..
|
||||||
|
mkdir sub3
|
||||||
|
cd sub3
|
||||||
|
cat <<EOT >package.json
|
||||||
|
{
|
||||||
|
"name": "subproject",
|
||||||
|
"dependencies": {
|
||||||
|
"random": "^3.0.6",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
yarn set version 3.5.1
|
||||||
|
yarn install
|
||||||
|
if [ x$1 = 'xglobal' ];then
|
||||||
|
echo enableGlobalCache
|
||||||
|
echo 'enableGlobalCache: true' >> .yarnrc.yml
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
if [ x$1 != 'xkeepcache' -a x$2 != 'xkeepcache' ]; then
|
||||||
|
rm -rf sub2/.yarn/cache
|
||||||
|
rm -rf sub3/.yarn/cache
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x$1 = 'xyarn1' ];then
|
||||||
|
echo "create yarn1 project in the root"
|
||||||
|
cat <<EOT >package.json
|
||||||
|
{
|
||||||
|
"name": "subproject",
|
||||||
|
"dependencies": {
|
||||||
|
"random": "^3.0.6",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
yarn set version 1.22.19
|
||||||
|
yarn install
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import tscMatcher from '../.github/tsc.json';
|
||||||
|
|
||||||
|
describe('problem matcher tests', () => {
|
||||||
|
it('tsc: matches TypeScript "pretty" error message', () => {
|
||||||
|
const [
|
||||||
|
{
|
||||||
|
pattern: [{regexp}]
|
||||||
|
}
|
||||||
|
] = tscMatcher.problemMatcher;
|
||||||
|
const exampleErrorMessage =
|
||||||
|
"lib/index.js:23:42 - error TS2345: Argument of type 'A' is not assignable to parameter of type 'B'.";
|
||||||
|
|
||||||
|
const match = exampleErrorMessage.match(new RegExp(regexp));
|
||||||
|
expect(match).not.toBeNull();
|
||||||
|
expect(match![1]).toEqual('lib/index.js');
|
||||||
|
expect(match![2]).toEqual('23');
|
||||||
|
expect(match![3]).toEqual('42');
|
||||||
|
expect(match![4]).toEqual('error');
|
||||||
|
expect(match![5]).toEqual('2345');
|
||||||
|
expect(match![6]).toEqual(
|
||||||
|
"Argument of type 'A' is not assignable to parameter of type 'B'."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tsc: matches TypeScript error message from log file', () => {
|
||||||
|
const [
|
||||||
|
{
|
||||||
|
pattern: [{regexp}]
|
||||||
|
}
|
||||||
|
] = tscMatcher.problemMatcher;
|
||||||
|
const exampleErrorMessage =
|
||||||
|
"lib/index.js(23,42): error TS2345: Argument of type 'A' is not assignable to parameter of type 'B'.";
|
||||||
|
|
||||||
|
const match = exampleErrorMessage.match(new RegExp(regexp));
|
||||||
|
expect(match).not.toBeNull();
|
||||||
|
expect(match![1]).toEqual('lib/index.js');
|
||||||
|
expect(match![2]).toEqual('23');
|
||||||
|
expect(match![3]).toEqual('42');
|
||||||
|
expect(match![4]).toEqual('error');
|
||||||
|
expect(match![5]).toEqual('2345');
|
||||||
|
expect(match![6]).toEqual(
|
||||||
|
"Argument of type 'A' is not assignable to parameter of type 'B'."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,399 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as httpm from '@actions/http-client';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import fs from 'fs';
|
||||||
|
import cp from 'child_process';
|
||||||
|
import osm from 'os';
|
||||||
|
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';
|
||||||
|
import nodeTestDistNightly from './data/node-nightly-index.json';
|
||||||
|
import nodeTestDistRc from './data/node-rc-index.json';
|
||||||
|
import nodeV8CanaryTestDist from './data/v8-canary-dist-index.json';
|
||||||
|
|
||||||
|
describe('setup-node', () => {
|
||||||
|
let inputs = {} as any;
|
||||||
|
let os = {} as any;
|
||||||
|
|
||||||
|
let inSpy: jest.SpyInstance;
|
||||||
|
let findSpy: jest.SpyInstance;
|
||||||
|
let findAllVersionsSpy: jest.SpyInstance;
|
||||||
|
let cnSpy: jest.SpyInstance;
|
||||||
|
let logSpy: jest.SpyInstance;
|
||||||
|
let warningSpy: jest.SpyInstance;
|
||||||
|
let platSpy: jest.SpyInstance;
|
||||||
|
let archSpy: jest.SpyInstance;
|
||||||
|
let dlSpy: jest.SpyInstance;
|
||||||
|
let exSpy: jest.SpyInstance;
|
||||||
|
let cacheSpy: jest.SpyInstance;
|
||||||
|
let dbgSpy: jest.SpyInstance;
|
||||||
|
let whichSpy: jest.SpyInstance;
|
||||||
|
let existsSpy: jest.SpyInstance;
|
||||||
|
let mkdirpSpy: jest.SpyInstance;
|
||||||
|
let execSpy: jest.SpyInstance;
|
||||||
|
let authSpy: jest.SpyInstance;
|
||||||
|
let isCacheActionAvailable: jest.SpyInstance;
|
||||||
|
let getExecOutputSpy: jest.SpyInstance;
|
||||||
|
let getJsonSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// @actions/core
|
||||||
|
console.log('::stop-commands::stoptoken'); // Disable executing of runner commands when running tests in actions
|
||||||
|
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
|
||||||
|
inputs = {};
|
||||||
|
inSpy = jest.spyOn(core, 'getInput');
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
// node
|
||||||
|
os = {};
|
||||||
|
platSpy = jest.spyOn(osm, 'platform');
|
||||||
|
platSpy.mockImplementation(() => os['platform']);
|
||||||
|
archSpy = jest.spyOn(osm, 'arch');
|
||||||
|
archSpy.mockImplementation(() => os['arch']);
|
||||||
|
execSpy = jest.spyOn(cp, 'execSync');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
findSpy = jest.spyOn(tc, 'find');
|
||||||
|
findAllVersionsSpy = jest.spyOn(tc, 'findAllVersions');
|
||||||
|
dlSpy = jest.spyOn(tc, 'downloadTool');
|
||||||
|
exSpy = jest.spyOn(tc, 'extractTar');
|
||||||
|
cacheSpy = jest.spyOn(tc, 'cacheDir');
|
||||||
|
// getDistSpy = jest.spyOn(im, 'getVersionsFromDist');
|
||||||
|
|
||||||
|
// http-client
|
||||||
|
getJsonSpy = jest.spyOn(httpm.HttpClient.prototype, 'getJson');
|
||||||
|
|
||||||
|
// io
|
||||||
|
whichSpy = jest.spyOn(io, 'which');
|
||||||
|
existsSpy = jest.spyOn(fs, 'existsSync');
|
||||||
|
mkdirpSpy = jest.spyOn(io, 'mkdirP');
|
||||||
|
|
||||||
|
// @actions/tool-cache
|
||||||
|
isCacheActionAvailable = jest.spyOn(cache, 'isFeatureAvailable');
|
||||||
|
isCacheActionAvailable.mockImplementation(() => false);
|
||||||
|
|
||||||
|
// disable authentication portion for installer tests
|
||||||
|
authSpy = jest.spyOn(auth, 'configAuthentication');
|
||||||
|
authSpy.mockImplementation(() => {});
|
||||||
|
|
||||||
|
getJsonSpy.mockImplementation(url => {
|
||||||
|
let res: any;
|
||||||
|
if (url.includes('/rc')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistRc;
|
||||||
|
} else if (url.includes('/nightly')) {
|
||||||
|
res = <INodeVersion[]>nodeTestDistNightly;
|
||||||
|
} else {
|
||||||
|
res = <INodeVersion[]>nodeTestDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result: res};
|
||||||
|
});
|
||||||
|
|
||||||
|
// writes
|
||||||
|
cnSpy = jest.spyOn(process.stdout, 'write');
|
||||||
|
logSpy = jest.spyOn(core, 'info');
|
||||||
|
dbgSpy = jest.spyOn(core, 'debug');
|
||||||
|
warningSpy = jest.spyOn(core, 'warning');
|
||||||
|
cnSpy.mockImplementation(line => {
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
warningSpy.mockImplementation(msg => {
|
||||||
|
// uncomment to debug
|
||||||
|
// process.stderr.write('log:' + msg + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
// @actions/exec
|
||||||
|
getExecOutputSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
getExecOutputSpy.mockImplementation(() => 'v16.15.0-rc.1');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.resetAllMocks();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
//jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
console.log('::stoptoken::'); // Re-enable executing of runner commands when running tests in actions
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
}, 100000);
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// Found in cache tests
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
it('finds version in cache with stable true', async () => {
|
||||||
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
inputs.stable = 'true';
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache with stable not supplied', async () => {
|
||||||
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finds version in cache and adds it to the path', async () => {
|
||||||
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
|
inSpy.mockImplementation(name => inputs[name]);
|
||||||
|
|
||||||
|
const toolPath = path.normalize('/cache/node/12.0.0-rc.1/x64');
|
||||||
|
findSpy.mockImplementation(() => toolPath);
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles unhandled find error and reports error', async () => {
|
||||||
|
const errMsg = 'unhandled error message';
|
||||||
|
inputs['node-version'] = '12.0.0-rc.1';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith('::error::' + errMsg + osm.EOL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to a version from node dist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '13.0.0-rc.0';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
// ... but not in the local cache
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
const toolPath = path.normalize('/cache/node/13.0.0-rc.0/x64');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
const expPath = path.join(toolPath, 'bin');
|
||||||
|
|
||||||
|
expect(dlSpy).toHaveBeenCalled();
|
||||||
|
expect(exSpy).toHaveBeenCalled();
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Done');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::add-path::${expPath}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not find a version that does not exist', async () => {
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '9.99.9-rc.1';
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reports a failed download', async () => {
|
||||||
|
const errMsg = 'unhandled download message';
|
||||||
|
os.platform = 'linux';
|
||||||
|
os.arch = 'x64';
|
||||||
|
|
||||||
|
const versionSpec = '14.7.0-rc.1';
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
inputs['token'] = 'faketoken';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
dlSpy.mockImplementation(() => {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
});
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(`::error::${errMsg}${osm.EOL}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('acquires specified architecture of node', async () => {
|
||||||
|
for (const {arch, version, osSpec} of [
|
||||||
|
{arch: 'x86', version: '13.4.0-rc.0', osSpec: 'win32'},
|
||||||
|
{arch: 'x86', version: '14.15.5-rc.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';
|
||||||
|
|
||||||
|
const expectedUrl = `https://nodejs.org/download/rc/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('rc versions', () => {
|
||||||
|
it.each([
|
||||||
|
[
|
||||||
|
'13.10.1-rc.0',
|
||||||
|
'13.10.1-rc.0',
|
||||||
|
'https://nodejs.org/download/rc/v13.10.1-rc.0/node-v13.10.1-rc.0-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'14.15.5-rc.1',
|
||||||
|
'14.15.5-rc.1',
|
||||||
|
'https://nodejs.org/download/rc/v14.15.5-rc.1/node-v14.15.5-rc.1-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'16.17.0-rc.1',
|
||||||
|
'16.17.0-rc.1',
|
||||||
|
'https://nodejs.org/download/rc/v16.17.0-rc.1/node-v16.17.0-rc.1-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'17.0.0-rc.1',
|
||||||
|
'17.0.0-rc.1',
|
||||||
|
'https://nodejs.org/download/rc/v17.0.0-rc.1/node-v17.0.0-rc.1-linux-x64.tar.gz'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'19.0.0-rc.2',
|
||||||
|
'19.0.0-rc.2',
|
||||||
|
'https://nodejs.org/download/rc/v19.0.0-rc.2/node-v19.0.0-rc.2-linux-x64.tar.gz'
|
||||||
|
]
|
||||||
|
])(
|
||||||
|
'finds the versions in the index.json and installs it',
|
||||||
|
async (input, expectedVersion, expectedUrl) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
cacheSpy.mockImplementation(async () => toolPath);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Extracting ...');
|
||||||
|
expect(logSpy).toHaveBeenCalledWith('Adding to the cache ...');
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['13.10.1-rc.0', '13.10.1-rc.0'],
|
||||||
|
['14.15.5-rc.1', '14.15.5-rc.1'],
|
||||||
|
['16.17.0-rc.1', '16.17.0-rc.1'],
|
||||||
|
['17.0.0-rc.1', '17.0.0-rc.1']
|
||||||
|
])(
|
||||||
|
'finds the %s version in the hostedToolcache',
|
||||||
|
async (input, expectedVersion) => {
|
||||||
|
const toolPath = path.normalize(`/cache/node/${expectedVersion}/x64`);
|
||||||
|
findSpy.mockImplementation((_, version) =>
|
||||||
|
path.normalize(`/cache/node/${version}/x64`)
|
||||||
|
);
|
||||||
|
findAllVersionsSpy.mockReturnValue([
|
||||||
|
'2.2.2-rc.2',
|
||||||
|
'1.1.1-rc.1',
|
||||||
|
'99.1.1',
|
||||||
|
expectedVersion,
|
||||||
|
'88.1.1',
|
||||||
|
'3.3.3-rc.3'
|
||||||
|
]);
|
||||||
|
|
||||||
|
inputs['node-version'] = input;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(logSpy).toHaveBeenCalledWith(`Found in cache @ ${toolPath}`);
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::add-path::${path.join(toolPath, 'bin')}${osm.EOL}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it('throws an error if version is not found', async () => {
|
||||||
|
const versionSpec = '19.0.0-rc.3';
|
||||||
|
|
||||||
|
findSpy.mockImplementation(() => '');
|
||||||
|
findAllVersionsSpy.mockImplementation(() => []);
|
||||||
|
dlSpy.mockImplementation(async () => '/some/temp/path');
|
||||||
|
exSpy.mockImplementation(async () => '/some/other/temp/path');
|
||||||
|
|
||||||
|
inputs['node-version'] = versionSpec;
|
||||||
|
os['arch'] = 'x64';
|
||||||
|
os['platform'] = 'linux';
|
||||||
|
// act
|
||||||
|
await main.run();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(cnSpy).toHaveBeenCalledWith(
|
||||||
|
`::error::Unable to find Node version '${versionSpec}' for platform ${os.platform} and architecture ${os.arch}.${osm.EOL}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
architecture="$(node -e 'console.log(process.arch)')"
|
||||||
|
if [ -z "$(echo $architecture | grep --fixed-strings $1)" ]; then
|
||||||
|
echo "Unexpected architecture"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Skip testing architecture"
|
||||||
|
fi
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
if [[ "$(git status --porcelain)" != "" ]]; then
|
|
||||||
echo ----------------------------------------
|
|
||||||
echo git status
|
|
||||||
echo ----------------------------------------
|
|
||||||
git status
|
|
||||||
echo ----------------------------------------
|
|
||||||
echo git diff
|
|
||||||
echo ----------------------------------------
|
|
||||||
git diff
|
|
||||||
echo ----------------------------------------
|
|
||||||
echo Troubleshooting
|
|
||||||
echo ----------------------------------------
|
|
||||||
echo "::error::Unstaged changes detected. Locally try running: git clean -ffdx && npm ci && npm run pre-checkin"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
@ -7,13 +7,22 @@ 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 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
|
||||||
|
|
||||||
echo "Testing npm install"
|
if [ -z "$2" ]; then
|
||||||
mkdir -p test-npm-install
|
echo "Testing npm install"
|
||||||
cd test-npm-install
|
mkdir -p test-npm-install
|
||||||
npm init -y || exit 1
|
cd test-npm-install
|
||||||
npm install @actions/core || exit 1
|
npm init -y || exit 1
|
||||||
|
npm install @actions/core || exit 1
|
||||||
|
else
|
||||||
|
echo "Skip testing npm"
|
||||||
|
fi
|
||||||
|
|
|
||||||
50
action.yml
50
action.yml
|
|
@ -1,21 +1,43 @@
|
||||||
name: 'Setup Node.js environment'
|
name: 'Setup Node.js environment'
|
||||||
description: 'Setup a Node.js environment and add it to the PATH, additionally providing proxy support'
|
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: 10.x, 10.15.1, >=10.15.0'
|
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0.'
|
||||||
default: '10.x'
|
node-version-file:
|
||||||
|
description: 'File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions.'
|
||||||
|
architecture:
|
||||||
|
description: 'Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.'
|
||||||
|
check-latest:
|
||||||
|
description: 'Set this option if you want the action to check for the latest available version that satisfies the version spec.'
|
||||||
|
default: false
|
||||||
registry-url:
|
registry-url:
|
||||||
description: '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'
|
description: '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.'
|
||||||
scope:
|
scope:
|
||||||
description: 'Optional scope for authenticating against scoped registries'
|
description: '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/).'
|
||||||
# Deprecated option, do not use. Will not be supported after October 1, 2019
|
token:
|
||||||
version:
|
description: Used to pull node distributions from node-versions. 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 on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting.
|
||||||
description: 'Deprecated. Use node-version instead. Will not be supported after October 1, 2019'
|
default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
|
||||||
deprecationMessage: 'The version property will not be supported after October 1, 2019. Use node-version instead'
|
cache:
|
||||||
|
description: 'Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.'
|
||||||
|
package-manager-cache:
|
||||||
|
description: 'Set to false to disable automatic caching. By default, caching is enabled when either devEngines.packageManager or the top-level packageManager field in package.json specifies npm as the package manager.'
|
||||||
|
default: true
|
||||||
|
cache-dependency-path:
|
||||||
|
description: 'Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc. Supports wildcards or a list of file names for caching multiple dependencies.'
|
||||||
|
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.
|
||||||
|
# escape valve for someone having issues or needing the absolute latest which isn't cached yet
|
||||||
|
outputs:
|
||||||
|
cache-hit:
|
||||||
|
description: 'A boolean value to indicate if a cache was hit.'
|
||||||
|
node-version:
|
||||||
|
description: 'The installed node version.'
|
||||||
runs:
|
runs:
|
||||||
using: 'node12'
|
using: 'node24'
|
||||||
main: 'dist/index.js'
|
main: 'dist/setup/index.js'
|
||||||
|
post: 'dist/cache-save/index.js'
|
||||||
|
post-if: success()
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,59 @@
|
||||||
|
# 0. Caching dependencies
|
||||||
|
Date: 2021-05-21
|
||||||
|
|
||||||
|
Status: Accepted
|
||||||
|
|
||||||
|
# Context
|
||||||
|
`actions/setup-node` is the 2nd most popular action in GitHub Actions. A lot of customers use it in conjunction with [actions/cache](https://github.com/actions/cache) to speed up dependencies installation.
|
||||||
|
See more examples on proper usage in [actions/cache documentation](https://github.com/actions/cache/blob/main/examples.md#node---npm).
|
||||||
|
|
||||||
|
# Goals & Anti-Goals
|
||||||
|
Integration of caching functionality into `actions/setup-node` action will bring the following benefits for action users:
|
||||||
|
- Decrease the entry threshold for using the cache for Node.js dependencies and simplify initial configuration
|
||||||
|
- Simplify YAML pipelines because no need additional steps to enable caching
|
||||||
|
- More users will use cache for Node.js so more customers will have fast builds!
|
||||||
|
|
||||||
|
We will add support for NPM and Yarn dependencies caching.
|
||||||
|
As the first stage, we won't support custom locations for `package-lock.json`, `yarn.lock` files and action will work only when files are located in repository root.
|
||||||
|
|
||||||
|
We don't pursue the goal to provide wide customization of caching in scope of `actions/setup-node` action. The purpose of this integration is covering ~90% of basic use-cases. If user needs flexible customization, we should advice them to use `actions/cache` directly.
|
||||||
|
|
||||||
|
# Decision
|
||||||
|
- Add `cache` input parameter to `actions/setup-node`. For now, input will accept the following values:
|
||||||
|
- `npm` - enable caching for npm dependencies
|
||||||
|
- `yarn` - enable caching for yarn dependencies
|
||||||
|
- `''` - disable caching (default value)
|
||||||
|
- Cache feature will be disabled by default to make sure that we don't break existing customers. We will consider enabling cache by default in next major release (`v3`)
|
||||||
|
- Action will try to search `package-lock.json` or `yarn.lock` (npm 7.x supports `yarn.lock` files) files in the repository root and throw error if no one is found
|
||||||
|
- The hash of found file will be used as cache key (the same approach like [actions/cache](https://github.com/actions/cache/blob/main/examples.md#node---npm) recommends)
|
||||||
|
- The following key cache will be used `${{ runner.os }}-npm-${{ hashFiles('<package-lock-path>') }}`
|
||||||
|
- Action will cache global cache:
|
||||||
|
- Npm (retrieved via `npm config get cache`)
|
||||||
|
- Yarn 1 (retrieved via `yarn cache dir`)
|
||||||
|
- Yarn 2 (retrieved via `yarn config get cacheFolder`)
|
||||||
|
|
||||||
|
# Example of real use-cases
|
||||||
|
Npm package manager:
|
||||||
|
```yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '14'
|
||||||
|
cache: npm
|
||||||
|
```
|
||||||
|
|
||||||
|
Yarn package manager:
|
||||||
|
```yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '14'
|
||||||
|
cache: yarn
|
||||||
|
```
|
||||||
|
|
||||||
|
# Release process
|
||||||
|
|
||||||
|
As soon as functionality is implemented, we will release minor update of action. No need to bump major version since there are no breaking changes for existing users.
|
||||||
|
After that, we will update [starter-workflows](https://github.com/actions/starter-workflows/blob/main/ci/node.js.yml) and [GitHub Action documentation](https://docs.github.com/en/actions/guides/building-and-testing-nodejs#example-caching-dependencies).
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
# 0. Support caching dependencies for mono repos
|
||||||
|
Date: 2021-07-13
|
||||||
|
|
||||||
|
Status: Proposed
|
||||||
|
|
||||||
|
## Context
|
||||||
|
Currently, `actions/setup-node` supports caching dependencies for Npm and Yarn package managers.
|
||||||
|
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)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Proposal
|
||||||
|
We have the following options:
|
||||||
|
1. Allow to specify directory where `package-lock.json` or `yarn.lock` are located
|
||||||
|
2. Allow to specify path to the dependencies lock file (including directory path and filename)
|
||||||
|
|
||||||
|
The second option looks more generic because it allows to:
|
||||||
|
- specify multiple dependencies files using file patterns like `**/package-lock.json` ([one of recommended approaches in actions/cache](https://github.com/actions/cache/blob/main/examples.md#macos-and-ubuntu))
|
||||||
|
- specify custom dependencies files like `src/npm-shrinkwrap.json`
|
||||||
|
- change default resolving priority if both `yarn.lock` and `package-lock.json` exist in repository
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
Yaml examples:
|
||||||
|
```yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
cache: npm
|
||||||
|
cache-dependency-path: 'sub-project/package-lock.json'
|
||||||
|
```
|
||||||
|
```yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
cache: yarn
|
||||||
|
cache-dependency-path: 'sub-project/**/yarn.lock'
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,545 @@
|
||||||
|
## Working with lockfiles
|
||||||
|
|
||||||
|
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.
|
||||||
|
- Describes a single representation of a dependency tree such that teammates, deployments, and continuous integration are guaranteed to install exactly the same dependencies.
|
||||||
|
- Provides a facility for users to "time-travel" to previous states of `node_modules` without having to commit the directory itself.
|
||||||
|
- Facilitates greater visibility of tree changes through readable source control diffs.
|
||||||
|
|
||||||
|
In order to get the most out of using your lockfile on continuous integration follow the conventions outlined below for your respective package manager.
|
||||||
|
|
||||||
|
### NPM
|
||||||
|
|
||||||
|
Ensure that `package-lock.json` is always committed, use `npm ci` instead of `npm install` when installing packages.
|
||||||
|
|
||||||
|
**See also:**
|
||||||
|
- [Documentation of `package-lock.json`](https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json)
|
||||||
|
- [Documentation of `npm ci`](https://docs.npmjs.com/cli/v8/commands/npm-ci)
|
||||||
|
|
||||||
|
### Yarn
|
||||||
|
|
||||||
|
To ensure that `yarn.lock` is always committed, use `yarn install --immutable` when installing packages.
|
||||||
|
|
||||||
|
**See also:**
|
||||||
|
- [Documentation of `yarn.lock`](https://classic.yarnpkg.com/en/docs/yarn-lock)
|
||||||
|
- [Documentation of `--frozen-lockfile` option](https://classic.yarnpkg.com/en/docs/cli/install#toc-yarn-install-frozen-lockfile)
|
||||||
|
- [QA - Should lockfiles be committed to the repository?](https://yarnpkg.com/getting-started/qa/#should-lockfiles-be-committed-to-the-repository)
|
||||||
|
- [Documentation of `yarn install`](https://yarnpkg.com/cli/install)
|
||||||
|
|
||||||
|
### PNPM
|
||||||
|
|
||||||
|
Ensure that `pnpm-lock.yaml` is always committed, when on CI pass `--frozen-lockfile` to `pnpm install` when installing packages.
|
||||||
|
|
||||||
|
**See also:**
|
||||||
|
- [Working with Git - Lockfiles](https://pnpm.io/git#lockfiles)
|
||||||
|
- [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@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
If `check-latest` is set to `true`, the action first checks if the cached version is the latest one. If the locally cached version is not the most up-to-date, a version of Node.js will then be downloaded. Set `check-latest` to `true` it you want the most up-to-date version of Node.js to always be used.
|
||||||
|
|
||||||
|
> Setting `check-latest` to `true` has performance implications as downloading versions of Node is slower than using cached versions.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
check-latest: true
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Node version file
|
||||||
|
|
||||||
|
The `node-version-file` input accepts a path to a file containing the version of Node.js to be used by a project, for example `.nvmrc`, `.node-version`, `.tool-versions`, or `package.json`. If both the `node-version` and the `node-version-file` inputs are provided then the `node-version` input is used.
|
||||||
|
See [supported version syntax](https://github.com/actions/setup-node#supported-version-syntax).
|
||||||
|
|
||||||
|
> The action will search for the node version file relative to the repository root.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version-file: '.nvmrc'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
"engines": {
|
||||||
|
"node": "^22 || ^24"
|
||||||
|
},
|
||||||
|
"devEngines": {
|
||||||
|
"runtime": {
|
||||||
|
"name": "node",
|
||||||
|
"version": "^24.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"volta": {
|
||||||
|
"node": "24.11.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
When using `architecture`, `node-version` must be provided as well.
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: windows-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
architecture: 'x64' # optional, x64 or x86. If not specified, x64 will be used by default
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## V8 Canary versions
|
||||||
|
|
||||||
|
You can specify a nightly version to download it from https://nodejs.org/download/v8-canary.
|
||||||
|
|
||||||
|
### Install v8 canary build for specific node version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.0.0-v8-canary' # it will install the latest v8 canary release for node 24.0.0
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
### Install v8 canary build for major node version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24-v8-canary' # it will install the latest v8 canary release for node 24
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install the exact v8 canary version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: 'v24.0.0-v8-canary2025030537242e55ac'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nightly versions
|
||||||
|
|
||||||
|
You can specify a nightly version to download it from https://nodejs.org/download/nightly.
|
||||||
|
|
||||||
|
### Install the nightly build for a major version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24-nightly' # it will install the latest nightly release for node 24
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install the nightly build for a specific version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.0.0-nightly' # it will install the latest nightly release for node 24.0.0
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install an exact nightly version
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.0.0-nightly202505066102159fa1'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## RC versions
|
||||||
|
|
||||||
|
You can use specify a rc version to download it from https://nodejs.org/download/rc.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Node sample
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.0.0-rc.4'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** Unlike nightly versions, which support version range specifiers, you must specify the exact version for a release candidate: `24.0.0-rc.4`.
|
||||||
|
|
||||||
|
## Caching packages data
|
||||||
|
The action follows [actions/cache](https://github.com/actions/cache/blob/main/examples.md#node---npm) guidelines, and caches global cache on the machine instead of `node_modules`, so cache can be reused between different Node.js versions.
|
||||||
|
|
||||||
|
**Caching yarn dependencies:**
|
||||||
|
Yarn caching handles both Yarn Classic (v1) and Yarn Berry (v2, v3, v4+).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: 'yarn'
|
||||||
|
- run: yarn install --frozen-lockfile # optional, --immutable
|
||||||
|
- run: yarn test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Caching pnpm (v6.10+) dependencies:**
|
||||||
|
```yaml
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
# NOTE: pnpm caching support requires pnpm version >= 6.10.0
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 10
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: 'pnpm'
|
||||||
|
- run: pnpm install
|
||||||
|
- run: pnpm test
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Note**: By default `--frozen-lockfile` option is passed starting from pnpm `6.10.x`. It will be automatically added if you run it on [CI](https://pnpm.io/cli/install#--frozen-lockfile).
|
||||||
|
> If the `pnpm-lock.yaml` file changes then pass `--frozen-lockfile` option.
|
||||||
|
|
||||||
|
|
||||||
|
**Using wildcard patterns to cache dependencies**
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: '**/package-lock.json'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Using a list of file paths to cache dependencies**
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: |
|
||||||
|
server/app/package-lock.json
|
||||||
|
frontend/app/package-lock.json
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**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@v6
|
||||||
|
# - uses: pnpm/action-setup@v6
|
||||||
|
# with:
|
||||||
|
# version: 10
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
|
||||||
|
- 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
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
- windows-latest
|
||||||
|
node_version:
|
||||||
|
- 20
|
||||||
|
- 22
|
||||||
|
- 24
|
||||||
|
architecture:
|
||||||
|
- x64
|
||||||
|
# an extra windows-x86 run:
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
node_version: 24
|
||||||
|
architecture: x86
|
||||||
|
name: Node ${{ matrix.node_version }} - ${{ matrix.architecture }} on ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Setup node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node_version }}
|
||||||
|
architecture: ${{ matrix.architecture }}
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
```
|
||||||
|
|
||||||
|
## Publish to npmjs and GPR with npm
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm publish
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
|
- run: npm publish
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Publish to npmjs and GPR with yarn
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
registry-url: <registry url>
|
||||||
|
- run: yarn install --frozen-lockfile
|
||||||
|
- run: yarn publish
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.YARN_TOKEN }}
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
registry-url: 'https://npm.pkg.github.com'
|
||||||
|
- run: yarn publish
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use private packages
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
# Skip post-install scripts here, as a malicious
|
||||||
|
# script could steal NODE_AUTH_TOKEN.
|
||||||
|
- run: npm ci --ignore-scripts
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
# `npm rebuild` will run all those post-install scripts for us.
|
||||||
|
- run: npm rebuild && npm run prepare --if-present
|
||||||
|
```
|
||||||
|
### Yarn2 configuration
|
||||||
|
Yarn2 ignores both .npmrc and .yarnrc files created by the action, so before installing dependencies from the private repo it is necessary either to create or to modify existing yarnrc.yml file with `yarn config set` commands.
|
||||||
|
|
||||||
|
Below you can find a sample "Setup .yarnrc.yml" step, that is going to allow you to configure a private GitHub registry for 'my-org' organisation.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
- name: Setup .yarnrc.yml
|
||||||
|
run: |
|
||||||
|
yarn config set npmScopes.my-org.npmRegistryServer "https://npm.pkg.github.com"
|
||||||
|
yarn config set npmScopes.my-org.npmAlwaysAuth true
|
||||||
|
yarn config set npmScopes.my-org.npmAuthToken $NPM_AUTH_TOKEN
|
||||||
|
env:
|
||||||
|
NPM_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: yarn install --immutable
|
||||||
|
```
|
||||||
|
|
||||||
|
To access private GitHub Packages within the same organization, go to "Manage Actions access" in Package settings and set the repositories you want to access.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Publishing to npm with Trusted Publisher (OIDC)
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
### Example workflow
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: '24'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- 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@v6
|
||||||
|
with:
|
||||||
|
node-version: '24.x'
|
||||||
|
mirror: 'https://nodejs.org/dist'
|
||||||
|
mirror-token: 'your-mirror-token'
|
||||||
|
```
|
||||||
|
|
@ -1,22 +1,116 @@
|
||||||
# Contributors
|
# Contributors
|
||||||
|
|
||||||
### Checkin
|
Thank you for contributing!
|
||||||
|
|
||||||
- Do checkin source (src)
|
We have prepared a short guide so that the process of making your contribution is as simple and clear as possible. Please check it out before you contribute!
|
||||||
- Do checkin build output (lib)
|
|
||||||
- Do checkin runtime node_modules
|
|
||||||
- Do not checkin devDependency node_modules (husky can help see below)
|
|
||||||
|
|
||||||
### devDependencies
|
## How can I contribute...
|
||||||
|
|
||||||
In order to handle correctly checking in node_modules without devDependencies, we run [Husky](https://github.com/typicode/husky) before each commit.
|
* [Contribute Documentation:green_book:](#contribute-documentation)
|
||||||
This step ensures that formatting and checkin rules are followed and that devDependencies are excluded. To make sure Husky runs correctly, please use the following workflow:
|
|
||||||
|
|
||||||
```
|
* [Contribute Code :computer:](#contribute-code)
|
||||||
npm install # installs all devDependencies including Husky
|
|
||||||
git add abc.ext # Add the files you've changed. This should include files in src, lib, and node_modules (see above)
|
|
||||||
git commit -m "Informative commit message" # Commit. This will run Husky
|
|
||||||
```
|
|
||||||
|
|
||||||
During the commit step, Husky will take care of formatting all files with [Prettier](https://github.com/prettier/prettier) as well as pruning out devDependencies using `npm prune --production`.
|
* [Provide Support on Issues:pencil:](#provide-support-on-issues)
|
||||||
It will also make sure these changes are appropriately included in your commit (no further work is needed)
|
|
||||||
|
* [Review Pull Requests:mag:](#review-pull-requests)
|
||||||
|
|
||||||
|
## Contribute documentation
|
||||||
|
|
||||||
|
Documentation is a super important, critical part of this project. Docs are how we keep track of what we're doing, how, and why. It's how we stay on the same page about our policies and how we tell others everything they need to be able to use this project or contribute to it.
|
||||||
|
|
||||||
|
Documentation contributions of any size are welcome! Feel free to contribute even if you're just rewording a sentence to be more clear, or fixing a spelling mistake!
|
||||||
|
|
||||||
|
**How to contribute:**
|
||||||
|
|
||||||
|
Pull requests are the easiest way to contribute changes to git repos at GitHub. They are the preferred contribution method, as they offer a convenient way of commenting and amending the proposed changes.
|
||||||
|
|
||||||
|
- Please check that no one else has already created a pull request with these or similar changes
|
||||||
|
- Use a "feature branch" for your changes. That separates the changes in the pull request from your other changes and makes it easy to edit/amend commits in the pull request
|
||||||
|
- Make sure your changes are formatted properly and consistently with the rest of the documentation
|
||||||
|
- Re-read what you wrote, and run a spellchecker on it to make sure you didn't miss anything
|
||||||
|
- If your pull request is connected to an open issue, please, leave a link to this issue in the `Related issue:` section
|
||||||
|
- If you later need to add new commits to the pull request, you can simply commit the changes to the local branch and then push them. The pull request gets automatically updated
|
||||||
|
|
||||||
|
**Once you've filed the pull request:**
|
||||||
|
|
||||||
|
- Maintainers will review your pull request
|
||||||
|
- If a maintainer requests changes, first of all, try to think about this request critically and only after that implement and request another review
|
||||||
|
- If your PR gets accepted, it will soon be merged into the main branch. But your contribution will take effect only after the release of a new version of the action and updating the major tag
|
||||||
|
> Sometimes maintainers reject pull requests and that's ok! Usually, along with rejection, we supply the reason for it. Nonetheless, we still really appreciate you taking the time to do it, and we don't take that lightly :heart:
|
||||||
|
|
||||||
|
## Contribute code
|
||||||
|
|
||||||
|
We like code commits a lot! They're super handy, and they keep the project going and doing the work it needs to do to be useful to others.
|
||||||
|
|
||||||
|
Code contributions of just about any size are acceptable!
|
||||||
|
|
||||||
|
The main difference between code contributions and documentation contributions is that contributing code requires the inclusion of relevant tests for the code being added or changed. Contributions without accompanying tests will be held off until a test is added unless the maintainers consider the specific tests to be either impossible or way too much of a burden for such a contribution.
|
||||||
|
|
||||||
|
**How to contribute:**
|
||||||
|
|
||||||
|
Pull requests are the easiest way to contribute changes to git repos at GitHub. They are the preferred contribution method, as they offer a convenient way of commenting and amending the proposed changes.
|
||||||
|
|
||||||
|
- Please check that no one else has already created a pull request with these or similar changes
|
||||||
|
- Use a "feature branch" for your changes. That separates the changes in the pull request from your other changes and makes it easy to edit/amend commits in the pull request
|
||||||
|
- **Run `pre-checkin` script to format, lint, build and test changes**
|
||||||
|
- Make sure your changes are well formatted and that all tests are passing
|
||||||
|
- If your pull request is connected to an open issue, please, leave a link to this issue in the `Related issue:` section
|
||||||
|
- If you later need to add new commits to the pull request, you can simply commit the changes to the local branch and then push them. The pull request gets automatically updated
|
||||||
|
|
||||||
|
**Learn more about how to work with the repository:**
|
||||||
|
|
||||||
|
- To implement new features or fix bugs, you need to make changes to the `.ts` files, which are located in the `src` folder
|
||||||
|
- To comply with the code style, **you need to run the `format` script**
|
||||||
|
- To lint the code, **you need to run the `lint:fix` script**
|
||||||
|
- To transpile source code to `javascript` we use [NCC](https://github.com/vercel/ncc). **It is very important to run the `build` script after making changes**, otherwise your changes will not get into the final `javascript` build
|
||||||
|
- You can also start formatting, building code, and testing with a single `pre-checkin` command
|
||||||
|
|
||||||
|
**Learn more about how to implement tests:**
|
||||||
|
|
||||||
|
Adding or changing tests is an integral part of making a change to the code.
|
||||||
|
Unit tests are in the `__tests__` folder, and end-to-end tests are in the `workflows` folder, particularly in the [versions.yml](https://github.com/actions/setup-node/blob/main/.github/workflows/versions.yml) and [e2e-cache.yml](https://github.com/actions/setup-node/blob/main/.github/workflows/e2e-cache.yml) files.
|
||||||
|
|
||||||
|
- The contributor can add various types of tests (like unit tests or end-to-end tests), which, in his opinion, will be necessary and sufficient for testing new or changed functionality
|
||||||
|
- Tests should cover a successful execution, as well as some edge cases and possible errors
|
||||||
|
- As already mentioned, pull requests without tests will be considered more carefully by maintainers. If you are sure that in this situation the tests are not needed or cannot be implemented with a commensurate effort - please add this clarification message to your pull request
|
||||||
|
|
||||||
|
**Once you've filed the pull request:**
|
||||||
|
|
||||||
|
- CI will start automatically with some checks. Wait until the end of the execution and make sure that all checks passed successfully. If some checks fail, you can open them one by one, try to find the reason for failing and make changes to your code to resolve the problem
|
||||||
|
- Maintainers will review your pull request
|
||||||
|
- If a maintainer requests changes, first of all, try to think about his request critically and only after that implement and request another review
|
||||||
|
- If your PR gets accepted, it will soon be merged into the main branch. But your contribution will take effect only after the release of a new version of the action and updating the major tag
|
||||||
|
> Sometimes maintainers reject pull requests and that's ok! Usually, along with rejection, we supply the reason for it. Nonetheless, we still really appreciate you taking the time to do it, and we don't take that lightly :heart:
|
||||||
|
|
||||||
|
## Provide support on issues
|
||||||
|
|
||||||
|
Helping out other users with their questions is an awesome way of contributing to any community. It's not uncommon for most of the issues on open source projects to be support-related questions by users trying to understand something they ran into or find their way around a known bug.
|
||||||
|
|
||||||
|
**To help other folks out with their questions:**
|
||||||
|
|
||||||
|
- Go to the [issue tracker](https://github.com/actions/setup-node/issues)
|
||||||
|
- Read through the list until you find something that you're familiar enough with to answer to
|
||||||
|
- Respond to the issue with whatever details are needed to clarify the question, or get more details about what's going on
|
||||||
|
- Once the discussion wraps up and things are clarified, ask the original issue filer (or a maintainer) to close it for you
|
||||||
|
|
||||||
|
*Some notes on picking up support issues:*
|
||||||
|
|
||||||
|
- Avoid responding to issues you don't know you can answer accurately
|
||||||
|
- Try to refer to past issues with accepted answers as much as possible. Link to them from your replies
|
||||||
|
- Be kind and patient with users. Often, folks who have run into confusing things might be upset or impatient. This is natural. If you feel uncomfortable in conversation with them, it's better to stay away or withdraw from the issue.
|
||||||
|
|
||||||
|
> If some user is violating our code of conduct [standards](https://github.com/actions/setup-node/blob/main/CODE_OF_CONDUCT.md#our-standards), refer to the [Enforcement](https://github.com/actions/setup-node/blob/main/CODE_OF_CONDUCT.md#enforcement) section of the Code of Conduct to resolve the conflict
|
||||||
|
|
||||||
|
|
||||||
|
## Review pull requests
|
||||||
|
|
||||||
|
|
||||||
|
Another great way to contribute is to review pull request. Please, be extra kind: people who submit code/doc contributions are putting themselves in a pretty vulnerable position, and have put time and care into what they've done (even if that's not obvious to you!) Please, always respond with respect, and be understanding, but don't feel like you need to sacrifice your standards for their sake, either.
|
||||||
|
|
||||||
|
**How to review:**
|
||||||
|
|
||||||
|
- Go to the [pull requests](https://github.com/actions/setup-node/pulls)
|
||||||
|
- Make sure you're familiar with the code or documentation is updated, unless it's a minor change (spellchecking, minor formatting, etc.)
|
||||||
|
- Review changes using the GitHub functionality. You can ask a clarifying question, point out an error or suggest an alternative.
|
||||||
|
> Note: You may ask for minor changes - "nitpicks", but consider whether they are real blockers to merging or not
|
||||||
|
- Submit your review, which may include comments, an approval, or a changes request
|
||||||
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
|
|
@ -1,15 +1,20 @@
|
||||||
{
|
{
|
||||||
"name": "setup-node",
|
"name": "setup-node",
|
||||||
"version": "1.0.0",
|
"version": "6.4.0",
|
||||||
"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": "tsc && ncc build",
|
"build": "ncc build -o dist/setup src/setup-node.ts && ncc build -o dist/cache-save src/cache-save.ts",
|
||||||
"format": "prettier --write **/*.ts",
|
"format": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --write \"**/*.{ts,yml,yaml}\"",
|
||||||
"format-check": "prettier --check **/*.ts",
|
"format-check": "prettier --no-error-on-unmatched-pattern --config ./.prettierrc.js --check \"**/*.{ts,yml,yaml}\"",
|
||||||
"test": "jest",
|
"lint": "eslint --config ./.eslintrc.js \"**/*.ts\"",
|
||||||
"pre-checkin": "npm run format && npm run build && npm test"
|
"lint:fix": "eslint --config ./.eslintrc.js \"**/*.ts\" --fix",
|
||||||
|
"test": "jest --coverage",
|
||||||
|
"pre-checkin": "npm run format && npm run lint:fix && npm run build && npm test"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
@ -23,22 +28,32 @@
|
||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.2.2",
|
"@actions/cache": "^5.0.5",
|
||||||
"@actions/github": "^1.1.0",
|
"@actions/core": "^2.0.3",
|
||||||
"@actions/http-client": "^1.0.6",
|
"@actions/exec": "^2.0.0",
|
||||||
"@actions/io": "^1.0.2",
|
"@actions/github": "^6.0.1",
|
||||||
"@actions/tool-cache": "^1.3.1",
|
"@actions/glob": "^0.5.1",
|
||||||
"semver": "^6.1.1"
|
"@actions/http-client": "^3.0.2",
|
||||||
|
"@actions/io": "^2.0.0",
|
||||||
|
"@actions/tool-cache": "^3.0.1",
|
||||||
|
"semver": "^7.6.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^24.0.13",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/node": "^12.0.4",
|
"@types/node": "^24.1.0",
|
||||||
"@types/semver": "^6.0.0",
|
"@types/semver": "^7.5.8",
|
||||||
"@zeit/ncc": "^0.21.0",
|
"@typescript-eslint/eslint-plugin": "^5.54.0",
|
||||||
"jest": "^24.8.0",
|
"@typescript-eslint/parser": "^5.54.0",
|
||||||
"jest-circus": "^24.7.1",
|
"@vercel/ncc": "^0.38.3",
|
||||||
"prettier": "^1.17.1",
|
"eslint": "^8.57.0",
|
||||||
"ts-jest": "^24.0.2",
|
"eslint-config-prettier": "^8.6.0",
|
||||||
"typescript": "^3.5.1"
|
"eslint-plugin-jest": "^27.9.0",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"jest-circus": "^29.7.0",
|
||||||
|
"jest-each": "^29.7.0",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
|
"ts-jest": "^29.4.1",
|
||||||
|
"typescript": "^5.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -29,16 +25,16 @@ function writeRegistryToFile(
|
||||||
scope = '@' + scope;
|
scope = '@' + scope;
|
||||||
}
|
}
|
||||||
if (scope) {
|
if (scope) {
|
||||||
scope = scope.toLowerCase();
|
scope = scope.toLowerCase() + ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
core.debug(`Setting auth in ${fileLocation}`);
|
core.debug(`Setting auth in ${fileLocation}`);
|
||||||
let newContents: string = '';
|
let newContents = '';
|
||||||
if (fs.existsSync(fileLocation)) {
|
if (fs.existsSync(fileLocation)) {
|
||||||
const curContents: string = fs.readFileSync(fileLocation, 'utf8');
|
const curContents: string = fs.readFileSync(fileLocation, 'utf8');
|
||||||
curContents.split(os.EOL).forEach((line: string) => {
|
curContents.split(os.EOL).forEach((line: string) => {
|
||||||
// Add current contents unless they are setting the registry
|
// Add current contents unless they are setting the registry
|
||||||
if (!line.toLowerCase().startsWith('registry')) {
|
if (!line.toLowerCase().startsWith(`${scope}registry`)) {
|
||||||
newContents += line + os.EOL;
|
newContents += line + os.EOL;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -46,13 +42,12 @@ function writeRegistryToFile(
|
||||||
// Remove http: or https: from front of registry.
|
// Remove http: or https: from front of registry.
|
||||||
const authString: string =
|
const authString: string =
|
||||||
registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}';
|
registryUrl.replace(/(^\w+:|^)/, '') + ':_authToken=${NODE_AUTH_TOKEN}';
|
||||||
const registryString: string = scope
|
const registryString = `${scope}registry=${registryUrl}`;
|
||||||
? `${scope}:registry=${registryUrl}`
|
newContents += `${authString}${os.EOL}${registryString}`;
|
||||||
: `registry=${registryUrl}`;
|
|
||||||
const alwaysAuthString: string = `always-auth=${alwaysAuth}`;
|
|
||||||
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 so npm doesn't complain about not being able to find it
|
// Only export NODE_AUTH_TOKEN if explicitly provided by user
|
||||||
core.exportVariable('NODE_AUTH_TOKEN', 'XXXXX-XXXXX-XXXXX-XXXXX');
|
if (Object.prototype.hasOwnProperty.call(process.env, 'NODE_AUTH_TOKEN')) {
|
||||||
|
core.exportVariable('NODE_AUTH_TOKEN', process.env.NODE_AUTH_TOKEN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as glob from '@actions/glob';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
import {State} from './constants';
|
||||||
|
import {
|
||||||
|
getCacheDirectories,
|
||||||
|
getPackageManagerInfo,
|
||||||
|
repoHasYarnBerryManagedDependencies,
|
||||||
|
PackageManagerInfo
|
||||||
|
} from './cache-utils';
|
||||||
|
|
||||||
|
export const restoreCache = async (
|
||||||
|
packageManager: string,
|
||||||
|
cacheDependencyPath: string
|
||||||
|
) => {
|
||||||
|
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
||||||
|
if (!packageManagerInfo) {
|
||||||
|
throw new Error(`Caching for '${packageManager}' is not supported`);
|
||||||
|
}
|
||||||
|
const platform = process.env.RUNNER_OS;
|
||||||
|
const arch = os.arch();
|
||||||
|
|
||||||
|
const cachePaths = await getCacheDirectories(
|
||||||
|
packageManagerInfo,
|
||||||
|
cacheDependencyPath
|
||||||
|
);
|
||||||
|
core.saveState(State.CachePaths, cachePaths);
|
||||||
|
const lockFilePath = cacheDependencyPath
|
||||||
|
? cacheDependencyPath
|
||||||
|
: findLockFile(packageManagerInfo);
|
||||||
|
const fileHash = await glob.hashFiles(lockFilePath);
|
||||||
|
|
||||||
|
if (!fileHash) {
|
||||||
|
throw new Error(
|
||||||
|
'Some specified paths were not resolved, unable to cache dependencies.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyPrefix = `node-cache-${platform}-${arch}-${packageManager}`;
|
||||||
|
const primaryKey = `${keyPrefix}-${fileHash}`;
|
||||||
|
core.debug(`primary key is ${primaryKey}`);
|
||||||
|
|
||||||
|
core.saveState(State.CachePrimaryKey, primaryKey);
|
||||||
|
|
||||||
|
const isManagedByYarnBerry = await repoHasYarnBerryManagedDependencies(
|
||||||
|
packageManagerInfo,
|
||||||
|
cacheDependencyPath
|
||||||
|
);
|
||||||
|
let cacheKey: string | undefined;
|
||||||
|
if (isManagedByYarnBerry) {
|
||||||
|
core.info(
|
||||||
|
'All dependencies are managed locally by yarn3, the previous cache can be used'
|
||||||
|
);
|
||||||
|
cacheKey = await cache.restoreCache(cachePaths, primaryKey, [keyPrefix]);
|
||||||
|
} else {
|
||||||
|
cacheKey = await cache.restoreCache(cachePaths, primaryKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.setOutput('cache-hit', Boolean(cacheKey));
|
||||||
|
|
||||||
|
if (!cacheKey) {
|
||||||
|
core.info(`${packageManager} cache is not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.saveState(State.CacheMatchedKey, cacheKey);
|
||||||
|
core.info(`Cache restored from key: ${cacheKey}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findLockFile = (packageManager: PackageManagerInfo) => {
|
||||||
|
const lockFiles = packageManager.lockFilePatterns;
|
||||||
|
const workspace = process.env.GITHUB_WORKSPACE!;
|
||||||
|
|
||||||
|
const rootContent = fs.readdirSync(workspace);
|
||||||
|
|
||||||
|
const lockFile = lockFiles.find(item => rootContent.includes(item));
|
||||||
|
if (!lockFile) {
|
||||||
|
throw new Error(
|
||||||
|
`Dependencies lock file is not found in ${workspace}. Supported file patterns: ${lockFiles.toString()}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.join(workspace, lockFile);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
|
||||||
|
import {State} from './constants';
|
||||||
|
import {getPackageManagerInfo} from './cache-utils';
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// throw an uncaught exception. Instead of failing this action, just warn.
|
||||||
|
|
||||||
|
process.on('uncaughtException', e => {
|
||||||
|
const warningPrefix = '[warning]';
|
||||||
|
core.info(`${warningPrefix}${e.message}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Added early exit to resolve issue with slow post action step:
|
||||||
|
export async function run(earlyExit?: boolean) {
|
||||||
|
try {
|
||||||
|
const cacheLock = core.getState(State.CachePackageManager);
|
||||||
|
|
||||||
|
if (cacheLock) {
|
||||||
|
await cachePackages(cacheLock);
|
||||||
|
|
||||||
|
if (earlyExit) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.debug(`Caching for '${cacheLock}' is not supported`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed((error as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachePackages = async (packageManager: string) => {
|
||||||
|
const state = core.getState(State.CacheMatchedKey);
|
||||||
|
const primaryKey = core.getState(State.CachePrimaryKey);
|
||||||
|
const cachePaths = JSON.parse(
|
||||||
|
core.getState(State.CachePaths) || '[]'
|
||||||
|
) as string[];
|
||||||
|
|
||||||
|
const packageManagerInfo = await getPackageManagerInfo(packageManager);
|
||||||
|
if (!packageManagerInfo) {
|
||||||
|
core.debug(`Caching for '${packageManager}' is not supported`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cachePaths.length) {
|
||||||
|
// TODO: core.getInput has a bug - it can return undefined despite its definition (tests only?)
|
||||||
|
// export declare function getInput(name: string, options?: InputOptions): string;
|
||||||
|
const cacheDependencyPath = core.getInput('cache-dependency-path') || '';
|
||||||
|
throw new Error(
|
||||||
|
`Cache folder paths are not retrieved for ${packageManager} with cache-dependency-path = ${cacheDependencyPath}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primaryKey === state) {
|
||||||
|
core.info(
|
||||||
|
`Cache hit occurred on the primary key ${primaryKey}, not saving cache.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheId = await cache.saveCache(cachePaths, primaryKey);
|
||||||
|
if (cacheId == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Cache saved with the key: ${primaryKey}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
run(true);
|
||||||
|
|
@ -0,0 +1,320 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as cache from '@actions/cache';
|
||||||
|
import * as glob from '@actions/glob';
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import {unique} from './util';
|
||||||
|
|
||||||
|
export interface PackageManagerInfo {
|
||||||
|
name: string;
|
||||||
|
lockFilePatterns: Array<string>;
|
||||||
|
getCacheFolderPath: (projectDir?: string) => Promise<string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SupportedPackageManagers {
|
||||||
|
npm: PackageManagerInfo;
|
||||||
|
pnpm: PackageManagerInfo;
|
||||||
|
yarn: PackageManagerInfo;
|
||||||
|
}
|
||||||
|
export const supportedPackageManagers: SupportedPackageManagers = {
|
||||||
|
npm: {
|
||||||
|
name: 'npm',
|
||||||
|
lockFilePatterns: ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock'],
|
||||||
|
getCacheFolderPath: () =>
|
||||||
|
getCommandOutputNotEmpty(
|
||||||
|
'npm config get cache',
|
||||||
|
'Could not get npm cache folder path'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
pnpm: {
|
||||||
|
name: 'pnpm',
|
||||||
|
lockFilePatterns: ['pnpm-lock.yaml'],
|
||||||
|
getCacheFolderPath: () =>
|
||||||
|
getCommandOutputNotEmpty(
|
||||||
|
'pnpm store path --silent',
|
||||||
|
'Could not get pnpm cache folder path'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
yarn: {
|
||||||
|
name: 'yarn',
|
||||||
|
lockFilePatterns: ['yarn.lock'],
|
||||||
|
getCacheFolderPath: async projectDir => {
|
||||||
|
const yarnVersion = await getCommandOutputNotEmpty(
|
||||||
|
`yarn --version`,
|
||||||
|
'Could not retrieve version of yarn',
|
||||||
|
projectDir
|
||||||
|
);
|
||||||
|
|
||||||
|
core.debug(
|
||||||
|
`Consumed yarn version is ${yarnVersion} (working dir: "${
|
||||||
|
projectDir || ''
|
||||||
|
}")`
|
||||||
|
);
|
||||||
|
|
||||||
|
const stdOut = yarnVersion.startsWith('1.')
|
||||||
|
? await getCommandOutput('yarn cache dir', projectDir)
|
||||||
|
: await getCommandOutput('yarn config get cacheFolder', projectDir);
|
||||||
|
|
||||||
|
if (!stdOut) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not get yarn cache folder path for ${projectDir}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return stdOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCommandOutput = async (
|
||||||
|
toolCommand: string,
|
||||||
|
cwd?: string
|
||||||
|
): Promise<string> => {
|
||||||
|
let {stdout, stderr, exitCode} = await exec.getExecOutput(
|
||||||
|
toolCommand,
|
||||||
|
undefined,
|
||||||
|
{ignoreReturnCode: true, ...(cwd && {cwd})}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (exitCode) {
|
||||||
|
stderr = !stderr.trim()
|
||||||
|
? `The '${toolCommand}' command failed with exit code: ${exitCode}`
|
||||||
|
: stderr;
|
||||||
|
throw new Error(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCommandOutputNotEmpty = async (
|
||||||
|
toolCommand: string,
|
||||||
|
error: string,
|
||||||
|
cwd?: string
|
||||||
|
): Promise<string> => {
|
||||||
|
const stdOut = getCommandOutput(toolCommand, cwd);
|
||||||
|
if (!stdOut) {
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
return stdOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getPackageManagerInfo = async (packageManager: string) => {
|
||||||
|
if (packageManager === 'npm') {
|
||||||
|
return supportedPackageManagers.npm;
|
||||||
|
} else if (packageManager === 'pnpm') {
|
||||||
|
return supportedPackageManagers.pnpm;
|
||||||
|
} else if (packageManager === 'yarn') {
|
||||||
|
return supportedPackageManagers.yarn;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getProjectDirectoriesFromCacheDependencyPath is called twice during `restoreCache`
|
||||||
|
* - first through `getCacheDirectories`
|
||||||
|
* - second from `repoHasYarn3ManagedCache`
|
||||||
|
*
|
||||||
|
* it contains expensive IO operation and thus should be memoized
|
||||||
|
*/
|
||||||
|
|
||||||
|
let projectDirectoriesMemoized: string[] | null = null;
|
||||||
|
/**
|
||||||
|
* unit test must reset memoized variables
|
||||||
|
*/
|
||||||
|
export const resetProjectDirectoriesMemoized = () =>
|
||||||
|
(projectDirectoriesMemoized = null);
|
||||||
|
/**
|
||||||
|
* Expands (converts) the string input `cache-dependency-path` to list of directories that
|
||||||
|
* may be project roots
|
||||||
|
* @param cacheDependencyPath - either a single string or multiline string with possible glob patterns
|
||||||
|
* expected to be the result of `core.getInput('cache-dependency-path')`
|
||||||
|
* @return list of directories and possible
|
||||||
|
*/
|
||||||
|
const getProjectDirectoriesFromCacheDependencyPath = async (
|
||||||
|
cacheDependencyPath: string
|
||||||
|
): Promise<string[]> => {
|
||||||
|
if (projectDirectoriesMemoized !== null) {
|
||||||
|
return projectDirectoriesMemoized;
|
||||||
|
}
|
||||||
|
|
||||||
|
const globber = await glob.create(cacheDependencyPath);
|
||||||
|
const cacheDependenciesPaths = await globber.glob();
|
||||||
|
|
||||||
|
const existingDirectories: string[] = cacheDependenciesPaths
|
||||||
|
.map(path.dirname)
|
||||||
|
.filter(unique())
|
||||||
|
.map(dirName => fs.realpathSync(dirName))
|
||||||
|
.filter(directory => fs.lstatSync(directory).isDirectory());
|
||||||
|
|
||||||
|
if (!existingDirectories.length)
|
||||||
|
core.warning(
|
||||||
|
`No existing directories found containing cache-dependency-path="${cacheDependencyPath}"`
|
||||||
|
);
|
||||||
|
|
||||||
|
projectDirectoriesMemoized = existingDirectories;
|
||||||
|
return existingDirectories;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the cache directories configured for the repo if cache-dependency-path is not empty
|
||||||
|
* @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM
|
||||||
|
* @param cacheDependencyPath - either a single string or multiline string with possible glob patterns
|
||||||
|
* expected to be the result of `core.getInput('cache-dependency-path')`
|
||||||
|
* @return list of files on which the cache depends
|
||||||
|
*/
|
||||||
|
const getCacheDirectoriesFromCacheDependencyPath = async (
|
||||||
|
packageManagerInfo: PackageManagerInfo,
|
||||||
|
cacheDependencyPath: string
|
||||||
|
): Promise<string[]> => {
|
||||||
|
const projectDirectories =
|
||||||
|
await getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath);
|
||||||
|
const cacheFoldersPaths = await Promise.all(
|
||||||
|
projectDirectories.map(async projectDirectory => {
|
||||||
|
const cacheFolderPath =
|
||||||
|
await packageManagerInfo.getCacheFolderPath(projectDirectory);
|
||||||
|
core.debug(
|
||||||
|
`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the directory "${projectDirectory}"`
|
||||||
|
);
|
||||||
|
return cacheFolderPath;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// uniq in order to do not cache the same directories twice
|
||||||
|
return cacheFoldersPaths.filter(unique());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the cache directories configured for the repo ignoring cache-dependency-path
|
||||||
|
* @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM
|
||||||
|
* @return list of files on which the cache depends
|
||||||
|
*/
|
||||||
|
const getCacheDirectoriesForRootProject = async (
|
||||||
|
packageManagerInfo: PackageManagerInfo
|
||||||
|
): Promise<string[]> => {
|
||||||
|
const cacheFolderPath = await packageManagerInfo.getCacheFolderPath();
|
||||||
|
core.debug(
|
||||||
|
`${packageManagerInfo.name}'s cache folder "${cacheFolderPath}" configured for the root directory`
|
||||||
|
);
|
||||||
|
return [cacheFolderPath];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to find the cache directories configured for the repo
|
||||||
|
* currently it handles only the case of PM=yarn && cacheDependencyPath is not empty
|
||||||
|
* @param packageManagerInfo - an object having getCacheFolderPath method specific to given PM
|
||||||
|
* @param cacheDependencyPath - either a single string or multiline string with possible glob patterns
|
||||||
|
* expected to be the result of `core.getInput('cache-dependency-path')`
|
||||||
|
* @return list of files on which the cache depends
|
||||||
|
*/
|
||||||
|
export const getCacheDirectories = async (
|
||||||
|
packageManagerInfo: PackageManagerInfo,
|
||||||
|
cacheDependencyPath: string
|
||||||
|
): Promise<string[]> => {
|
||||||
|
// For yarn, if cacheDependencyPath is set, ask information about cache folders in each project
|
||||||
|
// folder satisfied by cacheDependencyPath https://github.com/actions/setup-node/issues/488
|
||||||
|
if (packageManagerInfo.name === 'yarn' && cacheDependencyPath) {
|
||||||
|
return getCacheDirectoriesFromCacheDependencyPath(
|
||||||
|
packageManagerInfo,
|
||||||
|
cacheDependencyPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return getCacheDirectoriesForRootProject(packageManagerInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to check if the directory is a yarn project configured to manage
|
||||||
|
* obsolete dependencies in the local cache
|
||||||
|
* @param directory - a path to the folder
|
||||||
|
* @return - true if the directory's project is yarn managed
|
||||||
|
* - if there's .yarn/cache folder do not mess with the dependencies kept in the repo, return false
|
||||||
|
* - global cache is not managed by yarn @see https://yarnpkg.com/features/offline-cache, return false
|
||||||
|
* - if local cache is not explicitly enabled (not yarn3), return false
|
||||||
|
* - return true otherwise
|
||||||
|
*/
|
||||||
|
const projectHasYarnBerryManagedDependencies = async (
|
||||||
|
directory: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const workDir = directory || process.env.GITHUB_WORKSPACE || '.';
|
||||||
|
core.debug(`check if "${workDir}" has locally managed yarn3 dependencies`);
|
||||||
|
|
||||||
|
// if .yarn/cache directory exists the cache is managed by version control system
|
||||||
|
const yarnCacheFile = path.join(workDir, '.yarn', 'cache');
|
||||||
|
if (
|
||||||
|
fs.existsSync(yarnCacheFile) &&
|
||||||
|
fs.lstatSync(yarnCacheFile).isDirectory()
|
||||||
|
) {
|
||||||
|
core.debug(
|
||||||
|
`"${workDir}" has .yarn/cache - dependencies are kept in the repository`
|
||||||
|
);
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: yarn1 returns 'undefined' with return code = 0
|
||||||
|
const enableGlobalCache = await getCommandOutput(
|
||||||
|
'yarn config get enableGlobalCache',
|
||||||
|
workDir
|
||||||
|
);
|
||||||
|
// only local cache is not managed by yarn
|
||||||
|
const managed = enableGlobalCache.includes('false');
|
||||||
|
if (managed) {
|
||||||
|
core.debug(`"${workDir}" dependencies are managed by yarn 3 locally`);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
core.debug(`"${workDir}" dependencies are not managed by yarn 3 locally`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function to report the repo contains Yarn managed projects
|
||||||
|
* @param packageManagerInfo - used to make sure current package manager is yarn
|
||||||
|
* @param cacheDependencyPath - either a single string or multiline string with possible glob patterns
|
||||||
|
* expected to be the result of `core.getInput('cache-dependency-path')`
|
||||||
|
* @return - true if all project directories configured to be Yarn managed
|
||||||
|
*/
|
||||||
|
export const repoHasYarnBerryManagedDependencies = async (
|
||||||
|
packageManagerInfo: PackageManagerInfo,
|
||||||
|
cacheDependencyPath: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
if (packageManagerInfo.name !== 'yarn') return false;
|
||||||
|
|
||||||
|
const yarnDirs = cacheDependencyPath
|
||||||
|
? await getProjectDirectoriesFromCacheDependencyPath(cacheDependencyPath)
|
||||||
|
: [''];
|
||||||
|
|
||||||
|
const isManagedList = await Promise.all(
|
||||||
|
yarnDirs.map(projectHasYarnBerryManagedDependencies)
|
||||||
|
);
|
||||||
|
|
||||||
|
return isManagedList.every(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isGhes(): boolean {
|
||||||
|
const ghUrl = new URL(
|
||||||
|
process.env['GITHUB_SERVER_URL'] || 'https://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 {
|
||||||
|
if (cache.isFeatureAvailable()) return true;
|
||||||
|
|
||||||
|
if (isGhes()) {
|
||||||
|
core.warning(
|
||||||
|
'Cache action is only supported on GHES version >= 3.5. If you are on version >=3.5 Please check with GHES admin if Actions cache service is enabled or not.'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
core.warning(
|
||||||
|
'The runner was not able to contact the cache service. Caching will be skipped'
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
export enum LockType {
|
||||||
|
Npm = 'npm',
|
||||||
|
Pnpm = 'pnpm',
|
||||||
|
Yarn = 'yarn'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum State {
|
||||||
|
CachePackageManager = 'SETUP_NODE_CACHE_PACKAGE_MANAGER',
|
||||||
|
CachePrimaryKey = 'CACHE_KEY',
|
||||||
|
CacheMatchedKey = 'CACHE_RESULT',
|
||||||
|
CachePaths = 'CACHE_PATHS'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Outputs {
|
||||||
|
CacheHit = 'cache-hit'
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
|
||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
|
import BaseDistribution from './base-distribution';
|
||||||
|
import {NodeInputs} from './base-models';
|
||||||
|
|
||||||
|
export default abstract class BasePrereleaseNodejs extends BaseDistribution {
|
||||||
|
protected abstract distribution: string;
|
||||||
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
super(nodeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected findVersionInHostedToolCacheDirectory(): string {
|
||||||
|
let toolPath = '';
|
||||||
|
const localVersionPaths = tc
|
||||||
|
.findAllVersions('node', this.nodeInfo.arch)
|
||||||
|
.filter(i => {
|
||||||
|
const prerelease = semver.prerelease(i, {});
|
||||||
|
if (!prerelease) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prerelease[0].toString().includes(this.distribution);
|
||||||
|
});
|
||||||
|
localVersionPaths.sort(semver.rcompare);
|
||||||
|
const localVersion = this.evaluateVersions(localVersionPaths);
|
||||||
|
if (localVersion) {
|
||||||
|
toolPath = tc.find('node', localVersion, this.nodeInfo.arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected validRange(versionSpec: string) {
|
||||||
|
let range: string;
|
||||||
|
const [raw, prerelease] = this.splitVersionSpec(versionSpec);
|
||||||
|
const isValidVersion = semver.valid(raw);
|
||||||
|
const rawVersion = (isValidVersion ? raw : semver.coerce(raw))!;
|
||||||
|
|
||||||
|
if (prerelease !== this.distribution) {
|
||||||
|
range = versionSpec;
|
||||||
|
} else {
|
||||||
|
range = `${semver.validRange(`^${rawVersion}-${this.distribution}`)}-0`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {range, options: {includePrerelease: !isValidVersion}};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected splitVersionSpec(versionSpec: string) {
|
||||||
|
return versionSpec.split(/-(.*)/s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,344 @@
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import * as hc from '@actions/http-client';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
|
||||||
|
import semver from 'semver';
|
||||||
|
import * as assert from 'assert';
|
||||||
|
|
||||||
|
import * as path from 'path';
|
||||||
|
import os from 'os';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from './base-models';
|
||||||
|
|
||||||
|
export default abstract class BaseDistribution {
|
||||||
|
protected httpClient: hc.HttpClient;
|
||||||
|
protected osPlat = os.platform();
|
||||||
|
|
||||||
|
constructor(protected nodeInfo: NodeInputs) {
|
||||||
|
this.httpClient = new hc.HttpClient('setup-node', [], {
|
||||||
|
allowRetries: true,
|
||||||
|
maxRetries: 3
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getDistributionUrl(mirror: string): string;
|
||||||
|
|
||||||
|
public async setupNodeJs() {
|
||||||
|
let nodeJsVersions: INodeVersion[] | undefined;
|
||||||
|
if (this.nodeInfo.checkLatest) {
|
||||||
|
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
||||||
|
this.nodeInfo.versionSpec = evaluatedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
let toolPath = this.findVersionInHostedToolCacheDirectory();
|
||||||
|
if (toolPath) {
|
||||||
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
|
} else {
|
||||||
|
const evaluatedVersion = await this.findVersionInDist(nodeJsVersions);
|
||||||
|
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
||||||
|
toolPath = await this.downloadNodejs(toolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.osPlat != 'win32') {
|
||||||
|
toolPath = path.join(toolPath, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
|
core.addPath(toolPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async findVersionInDist(nodeJsVersions?: INodeVersion[]) {
|
||||||
|
if (!nodeJsVersions) {
|
||||||
|
nodeJsVersions = await this.getNodeJsVersions();
|
||||||
|
}
|
||||||
|
const versions = this.filterVersions(nodeJsVersions);
|
||||||
|
const evaluatedVersion = this.evaluateVersions(versions);
|
||||||
|
if (!evaluatedVersion) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to find Node version '${this.nodeInfo.versionSpec}' for platform ${this.osPlat} and architecture ${this.nodeInfo.arch}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return evaluatedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected evaluateVersions(versions: string[]): string {
|
||||||
|
let version = '';
|
||||||
|
|
||||||
|
const {range, options} = this.validRange(this.nodeInfo.versionSpec);
|
||||||
|
|
||||||
|
core.debug(`evaluating ${versions.length} versions`);
|
||||||
|
|
||||||
|
for (const potential of versions) {
|
||||||
|
const satisfied: boolean = semver.satisfies(potential, range, options);
|
||||||
|
if (satisfied) {
|
||||||
|
version = potential;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
core.debug(`matched: ${version}`);
|
||||||
|
} else {
|
||||||
|
core.debug('match not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected findVersionInHostedToolCacheDirectory() {
|
||||||
|
return tc.find(
|
||||||
|
'node',
|
||||||
|
this.nodeInfo.versionSpec,
|
||||||
|
this.translateArchToDistUrl(this.nodeInfo.arch)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getNodeJsVersions(): Promise<INodeVersion[]> {
|
||||||
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
|
const dataUrl = `${initialUrl}/index.json`;
|
||||||
|
|
||||||
|
const headers = {};
|
||||||
|
|
||||||
|
if (this.nodeInfo.mirrorToken) {
|
||||||
|
headers['Authorization'] = this.nodeInfo.mirrorToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await this.httpClient.getJson<INodeVersion[]>(
|
||||||
|
dataUrl,
|
||||||
|
headers
|
||||||
|
);
|
||||||
|
return response.result || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getNodejsDistInfo(version: string) {
|
||||||
|
const osArch: string = this.translateArchToDistUrl(this.nodeInfo.arch);
|
||||||
|
version = semver.clean(version) || '';
|
||||||
|
const fileName: string =
|
||||||
|
this.osPlat == 'win32'
|
||||||
|
? `node-v${version}-win-${osArch}`
|
||||||
|
: `node-v${version}-${this.osPlat}-${osArch}`;
|
||||||
|
const urlFileName: string =
|
||||||
|
this.osPlat == 'win32'
|
||||||
|
? this.nodeInfo.arch === 'arm64'
|
||||||
|
? `${fileName}.zip`
|
||||||
|
: `${fileName}.7z`
|
||||||
|
: `${fileName}.tar.gz`;
|
||||||
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
|
const url = `${initialUrl}/v${version}/${urlFileName}`;
|
||||||
|
|
||||||
|
return <INodeVersionInfo>{
|
||||||
|
downloadUrl: url,
|
||||||
|
resolvedVersion: version,
|
||||||
|
arch: osArch,
|
||||||
|
fileName: fileName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async downloadNodejs(info: INodeVersionInfo) {
|
||||||
|
let downloadPath = '';
|
||||||
|
core.info(
|
||||||
|
`Acquiring ${info.resolvedVersion} - ${info.arch} from ${info.downloadUrl}`
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
downloadPath = await tc.downloadTool(
|
||||||
|
info.downloadUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
err instanceof tc.HTTPError &&
|
||||||
|
err.httpStatusCode == 404 &&
|
||||||
|
this.osPlat == 'win32'
|
||||||
|
) {
|
||||||
|
return await this.acquireWindowsNodeFromFallbackLocation(
|
||||||
|
info.resolvedVersion,
|
||||||
|
info.arch
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolPath = await this.extractArchive(downloadPath, info, true);
|
||||||
|
core.info('Done');
|
||||||
|
|
||||||
|
return toolPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected validRange(versionSpec: string) {
|
||||||
|
let options: semver.RangeOptions | undefined;
|
||||||
|
const c = semver.clean(versionSpec) || '';
|
||||||
|
const valid = semver.valid(c) ?? versionSpec;
|
||||||
|
|
||||||
|
return {range: valid, options};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async acquireWindowsNodeFromFallbackLocation(
|
||||||
|
version: string,
|
||||||
|
arch: string = os.arch()
|
||||||
|
): Promise<string> {
|
||||||
|
const initialUrl = this.getDistributionUrl(this.nodeInfo.mirror);
|
||||||
|
const osArch: string = this.translateArchToDistUrl(arch);
|
||||||
|
|
||||||
|
// Create temporary folder to download to
|
||||||
|
const tempDownloadFolder = `temp_${crypto.randomUUID()}`;
|
||||||
|
const tempDirectory = process.env['RUNNER_TEMP'] || '';
|
||||||
|
assert.ok(tempDirectory, 'Expected RUNNER_TEMP to be defined');
|
||||||
|
const tempDir: string = path.join(tempDirectory, tempDownloadFolder);
|
||||||
|
await io.mkdirP(tempDir);
|
||||||
|
let exeUrl: string;
|
||||||
|
let libUrl: string;
|
||||||
|
try {
|
||||||
|
exeUrl = `${initialUrl}/v${version}/win-${osArch}/node.exe`;
|
||||||
|
libUrl = `${initialUrl}/v${version}/win-${osArch}/node.lib`;
|
||||||
|
|
||||||
|
core.info(`Downloading only node binary from ${exeUrl}`);
|
||||||
|
|
||||||
|
const exePath = await tc.downloadTool(
|
||||||
|
exeUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
|
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
||||||
|
const libPath = await tc.downloadTool(
|
||||||
|
libUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
|
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
||||||
|
exeUrl = `${initialUrl}/v${version}/node.exe`;
|
||||||
|
libUrl = `${initialUrl}/v${version}/node.lib`;
|
||||||
|
|
||||||
|
const exePath = await tc.downloadTool(
|
||||||
|
exeUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
|
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
||||||
|
const libPath = await tc.downloadTool(
|
||||||
|
libUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirrorToken
|
||||||
|
);
|
||||||
|
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolPath = await tc.cacheDir(tempDir, 'node', version, arch);
|
||||||
|
|
||||||
|
return toolPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async extractArchive(
|
||||||
|
downloadPath: string,
|
||||||
|
info: INodeVersionInfo | null,
|
||||||
|
isOfficialArchive?: boolean
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
// Extract
|
||||||
|
//
|
||||||
|
core.info('Extracting ...');
|
||||||
|
let extPath: string;
|
||||||
|
info = info || ({} as INodeVersionInfo); // satisfy compiler, never null when reaches here
|
||||||
|
if (this.osPlat == 'win32') {
|
||||||
|
const extension = this.nodeInfo.arch === 'arm64' ? '.zip' : '.7z';
|
||||||
|
// Rename archive to add extension because after downloading
|
||||||
|
// archive does not contain extension type and it leads to some issues
|
||||||
|
// on Windows runners without PowerShell Core.
|
||||||
|
//
|
||||||
|
// For default PowerShell Windows it should contain extension type to unpack it.
|
||||||
|
if (extension === '.zip' && isOfficialArchive) {
|
||||||
|
const renamedArchive = `${downloadPath}.zip`;
|
||||||
|
fs.renameSync(downloadPath, renamedArchive);
|
||||||
|
extPath = await tc.extractZip(renamedArchive);
|
||||||
|
} else {
|
||||||
|
const _7zPath = path.join(__dirname, '../..', 'externals', '7zr.exe');
|
||||||
|
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
||||||
|
}
|
||||||
|
// 7z extracts to folder matching file name
|
||||||
|
const nestedPath = path.join(
|
||||||
|
extPath,
|
||||||
|
path.basename(info.fileName, extension)
|
||||||
|
);
|
||||||
|
if (fs.existsSync(nestedPath)) {
|
||||||
|
extPath = nestedPath;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
extPath = await tc.extractTar(downloadPath, undefined, [
|
||||||
|
'xz',
|
||||||
|
'--strip',
|
||||||
|
'1'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
|
||||||
|
//
|
||||||
|
core.info('Adding to the cache ...');
|
||||||
|
const toolPath = await tc.cacheDir(
|
||||||
|
extPath,
|
||||||
|
'node',
|
||||||
|
info.resolvedVersion,
|
||||||
|
info.arch
|
||||||
|
);
|
||||||
|
|
||||||
|
return toolPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDistFileName(): string {
|
||||||
|
const osArch: string = this.translateArchToDistUrl(this.nodeInfo.arch);
|
||||||
|
|
||||||
|
// node offers a json list of versions
|
||||||
|
let dataFileName: string;
|
||||||
|
switch (this.osPlat) {
|
||||||
|
case 'linux':
|
||||||
|
dataFileName = `linux-${osArch}`;
|
||||||
|
break;
|
||||||
|
case 'darwin':
|
||||||
|
dataFileName = `osx-${osArch}-tar`;
|
||||||
|
break;
|
||||||
|
case 'win32':
|
||||||
|
if (this.nodeInfo.arch === 'arm64') {
|
||||||
|
dataFileName = `win-${osArch}-zip`;
|
||||||
|
} else {
|
||||||
|
dataFileName = `win-${osArch}-exe`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected OS '${this.osPlat}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected filterVersions(nodeJsVersions: INodeVersion[]) {
|
||||||
|
const versions: string[] = [];
|
||||||
|
|
||||||
|
const dataFileName = this.getDistFileName();
|
||||||
|
|
||||||
|
nodeJsVersions.forEach((nodeVersion: INodeVersion) => {
|
||||||
|
// ensure this version supports your os and platform
|
||||||
|
if (nodeVersion.files.indexOf(dataFileName) >= 0) {
|
||||||
|
versions.push(nodeVersion.version);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return versions.sort(semver.rcompare);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected translateArchToDistUrl(arch: string): string {
|
||||||
|
switch (arch) {
|
||||||
|
case 'arm':
|
||||||
|
return 'armv7l';
|
||||||
|
default:
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
export interface NodeInputs {
|
||||||
|
versionSpec: string;
|
||||||
|
arch: string;
|
||||||
|
auth?: string;
|
||||||
|
checkLatest: boolean;
|
||||||
|
stable: boolean;
|
||||||
|
mirror: string;
|
||||||
|
mirrorToken: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface INodeVersionInfo {
|
||||||
|
downloadUrl: string;
|
||||||
|
resolvedVersion: string;
|
||||||
|
arch: string;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface INodeVersion {
|
||||||
|
version: string;
|
||||||
|
files: string[];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import BaseDistribution from './base-distribution';
|
||||||
|
import {NodeInputs} from './base-models';
|
||||||
|
import NightlyNodejs from './nightly/nightly_builds';
|
||||||
|
import OfficialBuilds from './official_builds/official_builds';
|
||||||
|
import RcBuild from './rc/rc_builds';
|
||||||
|
import CanaryBuild from './v8-canary/canary_builds';
|
||||||
|
|
||||||
|
enum Distributions {
|
||||||
|
DEFAULT = '',
|
||||||
|
CANARY = 'v8-canary',
|
||||||
|
NIGHTLY = 'nightly',
|
||||||
|
RC = 'rc'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNodejsDistribution(
|
||||||
|
installerOptions: NodeInputs
|
||||||
|
): BaseDistribution {
|
||||||
|
const versionSpec = installerOptions.versionSpec;
|
||||||
|
let distribution: BaseDistribution;
|
||||||
|
if (versionSpec.includes(Distributions.NIGHTLY)) {
|
||||||
|
distribution = new NightlyNodejs(installerOptions);
|
||||||
|
} else if (versionSpec.includes(Distributions.CANARY)) {
|
||||||
|
distribution = new CanaryBuild(installerOptions);
|
||||||
|
} else if (versionSpec.includes(Distributions.RC)) {
|
||||||
|
distribution = new RcBuild(installerOptions);
|
||||||
|
} else {
|
||||||
|
distribution = new OfficialBuilds(installerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return distribution;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
||||||
|
import {NodeInputs} from '../base-models';
|
||||||
|
|
||||||
|
export default class NightlyNodejs extends BasePrereleaseNodejs {
|
||||||
|
protected distribution = 'nightly';
|
||||||
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
super(nodeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDistributionUrl(mirror: string): string {
|
||||||
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/nightly`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,301 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as tc from '@actions/tool-cache';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import BaseDistribution from '../base-distribution';
|
||||||
|
import {NodeInputs, INodeVersion, INodeVersionInfo} from '../base-models';
|
||||||
|
|
||||||
|
interface INodeRelease extends tc.IToolRelease {
|
||||||
|
lts?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class OfficialBuilds extends BaseDistribution {
|
||||||
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
super(nodeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setupNodeJs() {
|
||||||
|
let manifest: tc.IToolRelease[] | undefined;
|
||||||
|
let nodeJsVersions: INodeVersion[] | undefined;
|
||||||
|
const osArch = this.translateArchToDistUrl(this.nodeInfo.arch);
|
||||||
|
|
||||||
|
if (this.isLtsAlias(this.nodeInfo.versionSpec)) {
|
||||||
|
core.info('Attempt to resolve LTS alias from manifest...');
|
||||||
|
|
||||||
|
// No try-catch since it's not possible to resolve LTS alias without manifest
|
||||||
|
manifest = await this.getManifest();
|
||||||
|
|
||||||
|
this.nodeInfo.versionSpec = this.resolveLtsAliasFromManifest(
|
||||||
|
this.nodeInfo.versionSpec,
|
||||||
|
this.nodeInfo.stable,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isLatestSyntax(this.nodeInfo.versionSpec)) {
|
||||||
|
nodeJsVersions = await this.getNodeJsVersions();
|
||||||
|
const versions = this.filterVersions(nodeJsVersions);
|
||||||
|
this.nodeInfo.versionSpec = this.evaluateVersions(versions);
|
||||||
|
|
||||||
|
core.info('getting latest node version...');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.nodeInfo.checkLatest) {
|
||||||
|
core.info('Attempt to resolve the latest version from manifest...');
|
||||||
|
const resolvedVersion = await this.resolveVersionFromManifest(
|
||||||
|
this.nodeInfo.versionSpec,
|
||||||
|
this.nodeInfo.stable,
|
||||||
|
osArch,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
if (resolvedVersion) {
|
||||||
|
this.nodeInfo.versionSpec = resolvedVersion;
|
||||||
|
core.info(`Resolved as '${resolvedVersion}'`);
|
||||||
|
} else {
|
||||||
|
core.info(
|
||||||
|
`Failed to resolve version ${this.nodeInfo.versionSpec} from manifest`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toolPath = this.findVersionInHostedToolCacheDirectory();
|
||||||
|
|
||||||
|
if (toolPath) {
|
||||||
|
core.info(`Found in cache @ ${toolPath}`);
|
||||||
|
this.addToolPath(toolPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let downloadPath = '';
|
||||||
|
try {
|
||||||
|
core.info(`Attempting to download ${this.nodeInfo.versionSpec}...`);
|
||||||
|
|
||||||
|
const versionInfo = await this.getInfoFromManifest(
|
||||||
|
this.nodeInfo.versionSpec,
|
||||||
|
this.nodeInfo.stable,
|
||||||
|
osArch,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
|
||||||
|
if (versionInfo) {
|
||||||
|
core.info(
|
||||||
|
`Acquiring ${versionInfo.resolvedVersion} - ${versionInfo.arch} from ${versionInfo.downloadUrl}`
|
||||||
|
);
|
||||||
|
downloadPath = await tc.downloadTool(
|
||||||
|
versionInfo.downloadUrl,
|
||||||
|
undefined,
|
||||||
|
this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||||
|
? this.nodeInfo.mirrorToken
|
||||||
|
: this.nodeInfo.auth
|
||||||
|
);
|
||||||
|
|
||||||
|
if (downloadPath) {
|
||||||
|
toolPath = await this.extractArchive(
|
||||||
|
downloadPath,
|
||||||
|
versionInfo,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(
|
||||||
|
`Not found in manifest. Falling back to download directly from ${
|
||||||
|
this.nodeInfo.mirror || 'Node'
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// Rate limit?
|
||||||
|
if (
|
||||||
|
err instanceof tc.HTTPError &&
|
||||||
|
(err.httpStatusCode === 403 || err.httpStatusCode === 429)
|
||||||
|
) {
|
||||||
|
core.info(
|
||||||
|
`Received HTTP status code ${err.httpStatusCode}. This usually indicates the rate limit has been exceeded`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
core.info((err as Error).message);
|
||||||
|
}
|
||||||
|
core.debug((err as Error).stack ?? 'empty stack');
|
||||||
|
core.info('Falling back to download directly from Node');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toolPath) {
|
||||||
|
toolPath = await this.downloadDirectlyFromNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.osPlat != 'win32') {
|
||||||
|
toolPath = path.join(toolPath, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
|
core.addPath(toolPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected addToolPath(toolPath: string) {
|
||||||
|
if (this.osPlat != 'win32') {
|
||||||
|
toolPath = path.join(toolPath, 'bin');
|
||||||
|
}
|
||||||
|
|
||||||
|
core.addPath(toolPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async downloadDirectlyFromNode() {
|
||||||
|
const nodeJsVersions = await this.getNodeJsVersions();
|
||||||
|
const versions = this.filterVersions(nodeJsVersions);
|
||||||
|
const evaluatedVersion = this.evaluateVersions(versions);
|
||||||
|
|
||||||
|
if (!evaluatedVersion) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to find Node version '${this.nodeInfo.versionSpec}' for platform ${this.osPlat} and architecture ${this.nodeInfo.arch}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toolName = this.getNodejsDistInfo(evaluatedVersion);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const toolPath = await this.downloadNodejs(toolName);
|
||||||
|
return toolPath;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof tc.HTTPError && error.httpStatusCode === 404) {
|
||||||
|
core.warning(
|
||||||
|
`Node version ${this.nodeInfo.versionSpec} for platform ${this.osPlat} and architecture ${this.nodeInfo.arch} was found but failed to download. ` +
|
||||||
|
'This usually happens when downloadable binaries are not fully updated at https://nodejs.org/. ' +
|
||||||
|
'To resolve this issue you may either fall back to the older version or try again later.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected evaluateVersions(versions: string[]): string {
|
||||||
|
let version = '';
|
||||||
|
|
||||||
|
if (this.isLatestSyntax(this.nodeInfo.versionSpec)) {
|
||||||
|
core.info(`getting latest node version...`);
|
||||||
|
return versions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
version = super.evaluateVersions(versions);
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDistributionUrl(mirror: string): string {
|
||||||
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/dist`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getManifest(): Promise<tc.IToolRelease[]> {
|
||||||
|
core.debug('Getting manifest from actions/node-versions@main');
|
||||||
|
return tc.getManifestFromRepo(
|
||||||
|
'actions',
|
||||||
|
'node-versions',
|
||||||
|
this.nodeInfo.mirror && this.nodeInfo.mirrorToken
|
||||||
|
? this.nodeInfo.mirrorToken
|
||||||
|
: this.nodeInfo.auth,
|
||||||
|
'main'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private resolveLtsAliasFromManifest(
|
||||||
|
versionSpec: string,
|
||||||
|
stable: boolean,
|
||||||
|
manifest: INodeRelease[]
|
||||||
|
): string {
|
||||||
|
const alias = versionSpec.split('lts/')[1]?.toLowerCase();
|
||||||
|
|
||||||
|
if (!alias) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to parse LTS alias for Node version '${versionSpec}'`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(`LTS alias '${alias}' for Node version '${versionSpec}'`);
|
||||||
|
|
||||||
|
// Supported formats are `lts/<alias>`, `lts/*`, and `lts/-n`. Where asterisk means highest possible LTS and -n means the nth-highest.
|
||||||
|
const n = Number(alias);
|
||||||
|
const aliases = Object.fromEntries(
|
||||||
|
manifest
|
||||||
|
.filter(x => x.lts && x.stable === stable)
|
||||||
|
.map(x => [x.lts!.toLowerCase(), x])
|
||||||
|
.reverse()
|
||||||
|
);
|
||||||
|
const numbered = Object.values(aliases);
|
||||||
|
const release =
|
||||||
|
alias === '*'
|
||||||
|
? numbered[numbered.length - 1]
|
||||||
|
: n < 0
|
||||||
|
? numbered[numbered.length - 1 + n]
|
||||||
|
: aliases[alias];
|
||||||
|
|
||||||
|
if (!release) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to find LTS release '${alias}' for Node version '${versionSpec}'.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.debug(
|
||||||
|
`Found LTS release '${release.version}' for Node version '${versionSpec}'`
|
||||||
|
);
|
||||||
|
|
||||||
|
return release.version.split('.')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resolveVersionFromManifest(
|
||||||
|
versionSpec: string,
|
||||||
|
stable: boolean,
|
||||||
|
osArch: string,
|
||||||
|
manifest: tc.IToolRelease[] | undefined
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const info = await this.getInfoFromManifest(
|
||||||
|
versionSpec,
|
||||||
|
stable,
|
||||||
|
osArch,
|
||||||
|
manifest
|
||||||
|
);
|
||||||
|
return info?.resolvedVersion;
|
||||||
|
} catch (err) {
|
||||||
|
core.info('Unable to resolve version from manifest...');
|
||||||
|
core.debug((err as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getInfoFromManifest(
|
||||||
|
versionSpec: string,
|
||||||
|
stable: boolean,
|
||||||
|
osArch: string,
|
||||||
|
manifest: tc.IToolRelease[] | undefined
|
||||||
|
): Promise<INodeVersionInfo | null> {
|
||||||
|
let info: INodeVersionInfo | null = null;
|
||||||
|
if (!manifest) {
|
||||||
|
core.debug('No manifest cached');
|
||||||
|
manifest = await this.getManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
const rel = await tc.findFromManifest(
|
||||||
|
versionSpec,
|
||||||
|
stable,
|
||||||
|
manifest,
|
||||||
|
osArch
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rel && rel.files.length > 0) {
|
||||||
|
info = <INodeVersionInfo>{};
|
||||||
|
info.resolvedVersion = rel.version;
|
||||||
|
info.arch = rel.files[0].arch;
|
||||||
|
info.downloadUrl = rel.files[0].download_url;
|
||||||
|
info.fileName = rel.files[0].filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLtsAlias(versionSpec: string): boolean {
|
||||||
|
return versionSpec.startsWith('lts/');
|
||||||
|
}
|
||||||
|
|
||||||
|
private isLatestSyntax(versionSpec): boolean {
|
||||||
|
return ['current', 'latest', 'node'].includes(versionSpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import BaseDistribution from '../base-distribution';
|
||||||
|
import {NodeInputs} from '../base-models';
|
||||||
|
|
||||||
|
export default class RcBuild extends BaseDistribution {
|
||||||
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
super(nodeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDistributionUrl(mirror: string): string {
|
||||||
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/rc`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import BasePrereleaseNodejs from '../base-distribution-prerelease';
|
||||||
|
import {NodeInputs} from '../base-models';
|
||||||
|
|
||||||
|
export default class CanaryBuild extends BasePrereleaseNodejs {
|
||||||
|
protected distribution = 'v8-canary';
|
||||||
|
constructor(nodeInfo: NodeInputs) {
|
||||||
|
super(nodeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDistributionUrl(mirror: string): string {
|
||||||
|
const url = mirror || 'https://nodejs.org';
|
||||||
|
return `${url}/download/v8-canary`;
|
||||||
|
}
|
||||||
|
}
|
||||||
251
src/installer.ts
251
src/installer.ts
|
|
@ -1,251 +0,0 @@
|
||||||
// Load tempDirectory before it gets wiped by tool-cache
|
|
||||||
let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || '';
|
|
||||||
import * as assert from 'assert';
|
|
||||||
import * as core from '@actions/core';
|
|
||||||
import * as hc from '@actions/http-client';
|
|
||||||
import * as io from '@actions/io';
|
|
||||||
import * as tc from '@actions/tool-cache';
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as semver from 'semver';
|
|
||||||
|
|
||||||
let osPlat: string = os.platform();
|
|
||||||
let osArch: string = translateArchToDistUrl(os.arch());
|
|
||||||
|
|
||||||
if (!tempDirectory) {
|
|
||||||
let baseLocation;
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
// On windows use the USERPROFILE env variable
|
|
||||||
baseLocation = process.env['USERPROFILE'] || 'C:\\';
|
|
||||||
} else {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
baseLocation = '/Users';
|
|
||||||
} else {
|
|
||||||
baseLocation = '/home';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tempDirectory = path.join(baseLocation, 'actions', 'temp');
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Node versions interface
|
|
||||||
// see https://nodejs.org/dist/index.json
|
|
||||||
//
|
|
||||||
interface INodeVersion {
|
|
||||||
version: string;
|
|
||||||
files: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getNode(versionSpec: string) {
|
|
||||||
// check cache
|
|
||||||
let toolPath: string;
|
|
||||||
toolPath = tc.find('node', versionSpec);
|
|
||||||
|
|
||||||
// If not found in cache, download
|
|
||||||
if (!toolPath) {
|
|
||||||
let version: string;
|
|
||||||
const c = semver.clean(versionSpec) || '';
|
|
||||||
// If explicit version
|
|
||||||
if (semver.valid(c) != null) {
|
|
||||||
// version to download
|
|
||||||
version = versionSpec;
|
|
||||||
} else {
|
|
||||||
// query nodejs.org for a matching version
|
|
||||||
version = await queryLatestMatch(versionSpec);
|
|
||||||
if (!version) {
|
|
||||||
throw new Error(
|
|
||||||
`Unable to find Node version '${versionSpec}' for platform ${osPlat} and architecture ${osArch}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check cache
|
|
||||||
toolPath = tc.find('node', version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!toolPath) {
|
|
||||||
// download, extract, cache
|
|
||||||
toolPath = await acquireNode(version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// a tool installer initimately knows details about the layout of that tool
|
|
||||||
// for example, node binary is in the bin folder after the extract on Mac/Linux.
|
|
||||||
// layouts could change by version, by platform etc... but that's the tool installers job
|
|
||||||
//
|
|
||||||
if (osPlat != 'win32') {
|
|
||||||
toolPath = path.join(toolPath, 'bin');
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// prepend the tools path. instructs the agent to prepend for future tasks
|
|
||||||
core.addPath(toolPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function queryLatestMatch(versionSpec: string): Promise<string> {
|
|
||||||
// node offers a json list of versions
|
|
||||||
let dataFileName: string;
|
|
||||||
switch (osPlat) {
|
|
||||||
case 'linux':
|
|
||||||
dataFileName = `linux-${osArch}`;
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
dataFileName = `osx-${osArch}-tar`;
|
|
||||||
break;
|
|
||||||
case 'win32':
|
|
||||||
dataFileName = `win-${osArch}-exe`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unexpected OS '${osPlat}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let versions: string[] = [];
|
|
||||||
let dataUrl = 'https://nodejs.org/dist/index.json';
|
|
||||||
let httpClient = new hc.HttpClient('setup-node', [], {
|
|
||||||
allowRetries: true,
|
|
||||||
maxRetries: 3
|
|
||||||
});
|
|
||||||
let response = await httpClient.getJson<INodeVersion[]>(dataUrl);
|
|
||||||
let nodeVersions = response.result || [];
|
|
||||||
nodeVersions.forEach((nodeVersion: INodeVersion) => {
|
|
||||||
// ensure this version supports your os and platform
|
|
||||||
if (nodeVersion.files.indexOf(dataFileName) >= 0) {
|
|
||||||
versions.push(nodeVersion.version);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// get the latest version that matches the version spec
|
|
||||||
let version: string = evaluateVersions(versions, versionSpec);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO - should we just export this from @actions/tool-cache? Lifted directly from there
|
|
||||||
function evaluateVersions(versions: string[], versionSpec: string): string {
|
|
||||||
let version = '';
|
|
||||||
core.debug(`evaluating ${versions.length} versions`);
|
|
||||||
versions = versions.sort((a, b) => {
|
|
||||||
if (semver.gt(a, b)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
});
|
|
||||||
for (let i = versions.length - 1; i >= 0; i--) {
|
|
||||||
const potential: string = versions[i];
|
|
||||||
const satisfied: boolean = semver.satisfies(potential, versionSpec);
|
|
||||||
if (satisfied) {
|
|
||||||
version = potential;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version) {
|
|
||||||
core.debug(`matched: ${version}`);
|
|
||||||
} else {
|
|
||||||
core.debug('match not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function acquireNode(version: string): Promise<string> {
|
|
||||||
//
|
|
||||||
// Download - a tool installer intimately knows how to get the tool (and construct urls)
|
|
||||||
//
|
|
||||||
version = semver.clean(version) || '';
|
|
||||||
let fileName: string =
|
|
||||||
osPlat == 'win32'
|
|
||||||
? `node-v${version}-win-${osArch}`
|
|
||||||
: `node-v${version}-${osPlat}-${osArch}`;
|
|
||||||
let urlFileName: string =
|
|
||||||
osPlat == 'win32' ? `${fileName}.7z` : `${fileName}.tar.gz`;
|
|
||||||
let downloadUrl = `https://nodejs.org/dist/v${version}/${urlFileName}`;
|
|
||||||
|
|
||||||
let downloadPath: string;
|
|
||||||
|
|
||||||
try {
|
|
||||||
downloadPath = await tc.downloadTool(downloadUrl);
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
|
||||||
return await acquireNodeFromFallbackLocation(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Extract
|
|
||||||
//
|
|
||||||
let extPath: string;
|
|
||||||
if (osPlat == 'win32') {
|
|
||||||
let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
|
|
||||||
extPath = await tc.extract7z(downloadPath, undefined, _7zPath);
|
|
||||||
} else {
|
|
||||||
extPath = await tc.extractTar(downloadPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
|
|
||||||
//
|
|
||||||
let toolRoot = path.join(extPath, fileName);
|
|
||||||
return await tc.cacheDir(toolRoot, 'node', version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For non LTS versions of Node, the files we need (for Windows) are sometimes located
|
|
||||||
// in a different folder than they normally are for other versions.
|
|
||||||
// Normally the format is similar to: https://nodejs.org/dist/v5.10.1/node-v5.10.1-win-x64.7z
|
|
||||||
// In this case, there will be two files located at:
|
|
||||||
// /dist/v5.10.1/win-x64/node.exe
|
|
||||||
// /dist/v5.10.1/win-x64/node.lib
|
|
||||||
// If this is not the structure, there may also be two files located at:
|
|
||||||
// /dist/v0.12.18/node.exe
|
|
||||||
// /dist/v0.12.18/node.lib
|
|
||||||
// This method attempts to download and cache the resources from these alternative locations.
|
|
||||||
// Note also that the files are normally zipped but in this case they are just an exe
|
|
||||||
// and lib file in a folder, not zipped.
|
|
||||||
async function acquireNodeFromFallbackLocation(
|
|
||||||
version: string
|
|
||||||
): Promise<string> {
|
|
||||||
// Create temporary folder to download in to
|
|
||||||
let tempDownloadFolder: string =
|
|
||||||
'temp_' + Math.floor(Math.random() * 2000000000);
|
|
||||||
let tempDir: string = path.join(tempDirectory, tempDownloadFolder);
|
|
||||||
await io.mkdirP(tempDir);
|
|
||||||
let exeUrl: string;
|
|
||||||
let libUrl: string;
|
|
||||||
try {
|
|
||||||
exeUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.exe`;
|
|
||||||
libUrl = `https://nodejs.org/dist/v${version}/win-${osArch}/node.lib`;
|
|
||||||
|
|
||||||
const exePath = await tc.downloadTool(exeUrl);
|
|
||||||
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
|
||||||
const libPath = await tc.downloadTool(libUrl);
|
|
||||||
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof tc.HTTPError && err.httpStatusCode == 404) {
|
|
||||||
exeUrl = `https://nodejs.org/dist/v${version}/node.exe`;
|
|
||||||
libUrl = `https://nodejs.org/dist/v${version}/node.lib`;
|
|
||||||
|
|
||||||
const exePath = await tc.downloadTool(exeUrl);
|
|
||||||
await io.cp(exePath, path.join(tempDir, 'node.exe'));
|
|
||||||
const libPath = await tc.downloadTool(libUrl);
|
|
||||||
await io.cp(libPath, path.join(tempDir, 'node.lib'));
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return await tc.cacheDir(tempDir, 'node', version);
|
|
||||||
}
|
|
||||||
|
|
||||||
// os.arch does not always match the relative download url, e.g.
|
|
||||||
// os.arch == 'arm' != node-v12.13.1-linux-armv7l.tar.gz
|
|
||||||
// All other currently supported architectures match, e.g.:
|
|
||||||
// os.arch = arm64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-arm64.tar.gz
|
|
||||||
// os.arch = x64 => https://nodejs.org/dist/v{VERSION}/node-v{VERSION}-{OS}-x64.tar.gz
|
|
||||||
function translateArchToDistUrl(arch: string): string {
|
|
||||||
switch (arch) {
|
|
||||||
case 'arm':
|
|
||||||
return 'armv7l';
|
|
||||||
default:
|
|
||||||
return arch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
import os from 'os';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
import * as auth from './authutil';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {restoreCache} from './cache-restore';
|
||||||
|
import {isCacheFeatureAvailable} from './cache-utils';
|
||||||
|
import {getNodejsDistribution} from './distributions/installer-factory';
|
||||||
|
import {getNodeVersionFromFile, printEnvDetailsAndSetOutput} from './util';
|
||||||
|
import {State} from './constants';
|
||||||
|
|
||||||
|
export async function run() {
|
||||||
|
try {
|
||||||
|
//
|
||||||
|
// Version is optional. If supplied, install / use from the tool cache
|
||||||
|
// If not supplied then task is still used to setup proxy, auth, etc...
|
||||||
|
//
|
||||||
|
const version = resolveVersionInput();
|
||||||
|
|
||||||
|
let arch = core.getInput('architecture');
|
||||||
|
const cache = core.getInput('cache');
|
||||||
|
const packagemanagercache =
|
||||||
|
(core.getInput('package-manager-cache') || 'true').toUpperCase() ===
|
||||||
|
'TRUE';
|
||||||
|
|
||||||
|
// 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 (arch && !version) {
|
||||||
|
core.warning(
|
||||||
|
'`architecture` is provided but `node-version` is missing. In this configuration, the version/architecture of Node will not be changed. To fix this, provide `architecture` in combination with `node-version`'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arch) {
|
||||||
|
arch = os.arch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
const token = core.getInput('token');
|
||||||
|
const auth = !token ? undefined : `token ${token}`;
|
||||||
|
const mirror = core.getInput('mirror');
|
||||||
|
const mirrorToken = core.getInput('mirror-token');
|
||||||
|
const stable =
|
||||||
|
(core.getInput('stable') || 'true').toUpperCase() === 'TRUE';
|
||||||
|
const checkLatest =
|
||||||
|
(core.getInput('check-latest') || 'false').toUpperCase() === 'TRUE';
|
||||||
|
const nodejsInfo = {
|
||||||
|
versionSpec: version,
|
||||||
|
checkLatest,
|
||||||
|
auth,
|
||||||
|
stable,
|
||||||
|
arch,
|
||||||
|
mirror,
|
||||||
|
mirrorToken
|
||||||
|
};
|
||||||
|
const nodeDistribution = getNodejsDistribution(nodejsInfo);
|
||||||
|
await nodeDistribution.setupNodeJs();
|
||||||
|
}
|
||||||
|
|
||||||
|
await printEnvDetailsAndSetOutput();
|
||||||
|
|
||||||
|
const registryUrl: string = core.getInput('registry-url');
|
||||||
|
if (registryUrl) {
|
||||||
|
auth.configAuthentication(registryUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheDependencyPath = core.getInput('cache-dependency-path');
|
||||||
|
|
||||||
|
if (isCacheFeatureAvailable()) {
|
||||||
|
// if the cache input is provided, use it for caching.
|
||||||
|
if (cache) {
|
||||||
|
core.saveState(State.CachePackageManager, cache);
|
||||||
|
await restoreCache(cache, cacheDependencyPath);
|
||||||
|
// package manager npm is detected from package.json, enable auto-caching for npm.
|
||||||
|
} else if (packagemanagercache) {
|
||||||
|
const resolvedPackageManager = getNameFromPackageManagerField();
|
||||||
|
if (resolvedPackageManager) {
|
||||||
|
core.info(
|
||||||
|
"Detected npm as the package manager from package.json's packageManager field. " +
|
||||||
|
'Auto caching has been enabled for npm. If you want to disable it, set package-manager-cache input to false'
|
||||||
|
);
|
||||||
|
core.saveState(State.CachePackageManager, resolvedPackageManager);
|
||||||
|
await restoreCache(resolvedPackageManager, cacheDependencyPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchersPath = path.join(__dirname, '../..', '.github');
|
||||||
|
core.info(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
||||||
|
core.info(
|
||||||
|
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
||||||
|
);
|
||||||
|
core.info(
|
||||||
|
`##[add-matcher]${path.join(matchersPath, 'eslint-compact.json')}`
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
core.setFailed((err as Error).message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveVersionInput(): string {
|
||||||
|
let version = core.getInput('node-version');
|
||||||
|
const versionFileInput = core.getInput('node-version-file');
|
||||||
|
|
||||||
|
if (version && versionFileInput) {
|
||||||
|
core.warning(
|
||||||
|
'Both node-version and node-version-file inputs are specified, only node-version will be used'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionFileInput) {
|
||||||
|
const versionFilePath = path.join(
|
||||||
|
process.env.GITHUB_WORKSPACE!,
|
||||||
|
versionFileInput
|
||||||
|
);
|
||||||
|
|
||||||
|
const parsedVersion = getNodeVersionFromFile(versionFilePath);
|
||||||
|
|
||||||
|
if (parsedVersion) {
|
||||||
|
version = parsedVersion;
|
||||||
|
} else {
|
||||||
|
core.warning(
|
||||||
|
`Could not determine node version from ${versionFilePath}. Falling back`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Resolved ${versionFileInput} as ${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,39 +1,3 @@
|
||||||
import * as core from '@actions/core';
|
import {run} from './main';
|
||||||
import * as installer from './installer';
|
|
||||||
import * as auth from './authutil';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
try {
|
|
||||||
//
|
|
||||||
// Version is optional. If supplied, install / use from the tool cache
|
|
||||||
// If not supplied then task is still used to setup proxy, auth, etc...
|
|
||||||
//
|
|
||||||
let version = core.getInput('version');
|
|
||||||
if (!version) {
|
|
||||||
version = core.getInput('node-version');
|
|
||||||
}
|
|
||||||
if (version) {
|
|
||||||
await installer.getNode(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
const registryUrl: string = core.getInput('registry-url');
|
|
||||||
const alwaysAuth: string = core.getInput('always-auth');
|
|
||||||
if (registryUrl) {
|
|
||||||
auth.configAuthentication(registryUrl, alwaysAuth);
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchersPath = path.join(__dirname, '..', '.github');
|
|
||||||
console.log(`##[add-matcher]${path.join(matchersPath, 'tsc.json')}`);
|
|
||||||
console.log(
|
|
||||||
`##[add-matcher]${path.join(matchersPath, 'eslint-stylish.json')}`
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`##[add-matcher]${path.join(matchersPath, 'eslint-compact.json')}`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
core.setFailed(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
run();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,120 @@
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export function getNodeVersionFromFile(versionFilePath: string): string | null {
|
||||||
|
if (!fs.existsSync(versionFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`The specified node version file at: ${versionFilePath} does not exist`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = fs.readFileSync(versionFilePath, 'utf8');
|
||||||
|
|
||||||
|
// Try parsing the file as an NPM `package.json` file.
|
||||||
|
try {
|
||||||
|
const manifest = JSON.parse(contents);
|
||||||
|
|
||||||
|
// Presume package.json file.
|
||||||
|
if (typeof manifest === 'object' && !!manifest) {
|
||||||
|
// Support Volta.
|
||||||
|
// See https://docs.volta.sh/guide/understanding#managing-your-project
|
||||||
|
if (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) {
|
||||||
|
return manifest.engines.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support Volta workspaces.
|
||||||
|
// See https://docs.volta.sh/advanced/workspaces
|
||||||
|
if (manifest.volta?.extends) {
|
||||||
|
const extendedFilePath = path.resolve(
|
||||||
|
path.dirname(versionFilePath),
|
||||||
|
manifest.volta.extends
|
||||||
|
);
|
||||||
|
core.info('Resolving node version from ' + extendedFilePath);
|
||||||
|
return getNodeVersionFromFile(extendedFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contents are an object, we parsed JSON
|
||||||
|
// this can happen if node-version-file is a package.json
|
||||||
|
// yet contains no volta.node or engines.node
|
||||||
|
//
|
||||||
|
// If node-version file is _not_ JSON, control flow
|
||||||
|
// will not have reached these lines.
|
||||||
|
//
|
||||||
|
// And because we've reached here, we know the contents
|
||||||
|
// *are* JSON, so no further string parsing makes sense.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
core.info('Node version file is not JSON file');
|
||||||
|
}
|
||||||
|
|
||||||
|
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
|
||||||
|
return found?.groups?.version ?? contents.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function printEnvDetailsAndSetOutput() {
|
||||||
|
core.startGroup('Environment details');
|
||||||
|
const promises = ['node', 'npm', 'yarn'].map(async tool => {
|
||||||
|
const pathTool = await io.which(tool, false);
|
||||||
|
const output = pathTool ? await getToolVersion(tool, ['--version']) : '';
|
||||||
|
|
||||||
|
return {tool, output};
|
||||||
|
});
|
||||||
|
|
||||||
|
const tools = await Promise.all(promises);
|
||||||
|
tools.forEach(({tool, output}) => {
|
||||||
|
if (tool === 'node') {
|
||||||
|
core.setOutput(`${tool}-version`, output);
|
||||||
|
}
|
||||||
|
core.info(`${tool}: ${output}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
core.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getToolVersion(tool: string, options: string[]) {
|
||||||
|
try {
|
||||||
|
const {stdout, stderr, exitCode} = await exec.getExecOutput(tool, options, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exitCode > 0) {
|
||||||
|
core.info(`[warning]${stderr}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return stdout.trim();
|
||||||
|
} catch (err) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const unique = () => {
|
||||||
|
const encountered = new Set();
|
||||||
|
return (value: unknown): boolean => {
|
||||||
|
if (encountered.has(value)) return false;
|
||||||
|
encountered.add(value);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
{
|
{
|
||||||
"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": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
"lib": [
|
|
||||||
"es6"
|
|
||||||
],
|
|
||||||
"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,
|
||||||
"strict": true, /* Enable all strict type-checking options. */
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
"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. */
|
||||||
},
|
},
|
||||||
"exclude": ["__tests__", "lib", "node_modules"]
|
"exclude": ["__tests__", "lib", "node_modules"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue