Compare commits

..

104 Commits

Author SHA1 Message Date
Tatyana Kostromskaya d4c8041748 Update RELEASES.md 2023-10-10 11:25:17 +00:00
Tatyana Kostromskaya 93e2fd004d Update github package to latest 2023-10-10 11:20:11 +00:00
Tatyana Kostromskaya 3d097f5e2f fix linter 2023-10-10 10:45:06 +00:00
Tatyana Kostromskaya 8e0ade0a6a define type for function 2023-10-09 15:44:21 +00:00
Tatyana Kostromskaya daf9b96fc8 Update octokit package 2023-10-09 12:42:17 +00:00
Tatyana Kostromskaya 797f48fcfa Update release notes for http-client@2.2.0 (#1549) 2023-10-06 16:03:00 +02:00
Tatyana Kostromskaya c8d1588732 Merge pull request #1547 from actions/takost/update-http-client
Add function to return proxy agent dispatcher for compatibility with latest `octokit` packages
2023-10-06 14:47:16 +02:00
Tatyana Kostromskaya 13e0ce9cf7 resolve comments 2023-10-06 12:39:29 +00:00
Tatyana Kostromskaya eae1b66cb0 fix audit 2023-10-05 16:41:02 +02:00
Tatyana Kostromskaya 129f884271 fix format 2023-10-05 16:34:31 +02:00
Tatyana Kostromskaya 0faced6a0b Add function to return proxy agent dispatcher for compatibility with latest octokit 2023-10-05 16:20:26 +02:00
Patrick Ellis 0d63834474 Merge pull request #1541 from actions/pje/upgrade-codeql-actions-to-v2
Upgrade codeql actions to v2
2023-09-27 16:14:48 -04:00
Patrick Ellis 8f032d304a Upgrade codeql actions to v2
Currently we're using v1, and there have been some important changes since then.

In particular, the latest version, v2.14.6, contains an important security patch:

> The CodeQL CLI no longer supports the `SEMMLE_JAVA_ARGS` environment variable. All previous versions of the CodeQL CLI perform command substitution on the `SEMMLE_JAVA_ARGS` value (for example, replacing `'$(echo foo)'` with `'foo'`) when starting a new Java virtual machine, which, depending on the execution environment, may have security implications. Users are advised to check their environments for possible `SEMMLE_JAVA_ARGS` misuse.

See the [codeql-cli-binaries release notes](https://github.com/github/codeql-cli-binaries/releases/tag/v2.14.4) for full details.
2023-09-27 15:18:59 -04:00
Tatyana Kostromskaya 28b09e224f Merge pull request #1526 from actions/takost/upd-dependencies
Update dependencies to latest
2023-09-27 12:37:10 +02:00
Tatyana Kostromskaya 111c95866e fix test + update semver 2023-09-26 11:10:18 +00:00
Tatyana Kostromskaya ddc9c52eb6 revert octokit changes 2023-09-26 11:05:37 +00:00
Tatyana Kostromskaya 6d37c6eb2b try to fix tests 2023-09-15 15:04:21 +00:00
Tatyana Kostromskaya 6477ef1460 tests 2023-09-15 13:54:28 +00:00
Tatyana Kostromskaya 2e5b10e3bd fix tests 2023-09-15 13:45:26 +00:00
Tatyana Kostromskaya 8c1e6a00f0 try to fix test 2023-09-15 13:28:29 +00:00
Tatyana Kostromskaya b2d5fa216f update github package 2023-09-14 14:32:08 +00:00
Luke Tomlinson c5c786523e @actions/core v1.10.1 (#1529) 2023-09-11 10:45:23 -04:00
Sean Burgess 63c648f3c2 Fix error message reference (#1511) 2023-09-11 10:25:17 -04:00
Tatyana Kostromskaya ce31408ff5 Update dependencies 2023-09-08 14:29:27 +00:00
Tatyana Kostromskaya e26febd988 Merge pull request #1508 from actions/takost/update-workflows-to-node20
Update workflows to node20
2023-09-01 11:55:53 +02:00
Tatyana Kostromskaya b051b4bada . 2023-08-29 14:56:32 +00:00
Tatyana Kostromskaya a08d666c78 . 2023-08-29 14:23:58 +00:00
Tatyana Kostromskaya 83bb7cdeef . 2023-08-29 14:09:20 +00:00
Tatyana Kostromskaya b552972717 revert 2023-08-29 11:19:06 +00:00
Tatyana Kostromskaya e3b0601228 test 2023-08-29 10:43:51 +00:00
Tatyana Kostromskaya 0956e634df test 2023-08-29 10:27:11 +00:00
Tatyana Kostromskaya c171cf52fb upd 2023-08-28 17:09:50 +02:00
Tatyana Kostromskaya 2f1b34f165 test tests 2023-08-28 16:59:29 +02:00
Tatyana Kostromskaya b61854c5ca update workflows to node20 2023-08-28 16:40:06 +02:00
Bethany 3d652d3133 Merge pull request #1505 from actions/bethanyj28/upload-tests
Add tests for `upload-artifact.ts`
2023-08-24 09:29:01 -04:00
Rob Herley c3df0928e2 Merge pull request #1502 from actions/robherley/download-artifact
[Artifacts] Support streaming download of artifact archive from blob storage
2023-08-24 09:27:27 -04:00
Rob Herley 9d756b2bc9 linter 2023-08-24 09:16:35 -04:00
Rob Herley 67c3b7a45c add tests for download artifact 2023-08-23 23:18:03 -04:00
Bethany 3963c722d8 merge download changes and lint 2023-08-23 14:02:50 -07:00
Bethany 3b44a4cc23 prettier 2023-08-23 13:55:26 -07:00
Bethany 03a876f0a7 add tests for upload 2023-08-23 13:54:31 -07:00
Bethany 62f943c0cc Merge pull request #1503 from actions/bethanyj28/download-artifact
Get a single artifact by name and download to `GITHUB_WORKSPACE`
2023-08-23 14:11:06 -04:00
Bethany 291200d54f include get artifact changes 2023-08-23 10:40:25 -07:00
Bethany 06e751600e move constants to retry-options 2023-08-23 10:36:33 -07:00
Bethany 4b6a4d80e1 use inline eslint disable 2023-08-23 10:12:06 -07:00
Bethany b2da9aa12c use string interpolation 2023-08-23 07:35:23 -07:00
Bethany 88f749f686 lint 2023-08-23 07:28:17 -07:00
Bethany b4f8e602b2 remove folder option in favor of path 2023-08-23 07:21:01 -07:00
Bethany ced07aa89c Use options to specify download folder 2023-08-23 06:47:51 -07:00
Bethany 6adf053d36 prettier 2023-08-22 11:47:14 -07:00
Bethany 671bf1ebd5 use GITHUB_WORKSPACE as default download dir 2023-08-22 11:44:38 -07:00
Bethany dd26bb1149 use require 2023-08-22 11:33:00 -07:00
Bethany 81a802e7e0 lint 2023-08-22 10:06:40 -07:00
Bethany 4214a1ff24 update dependencies and prettier 2023-08-22 09:57:14 -07:00
Bethany 0555a5f458 add get-artifact logic 2023-08-22 09:17:43 -07:00
Rob Herley 3aaff6685b cleanup 2023-08-21 17:47:17 -04:00
Rob Herley 9b383229c1 add download apis to stream zip from blob storage 2023-08-21 21:23:54 +00:00
Konrad Pabjan 7b617c260d [Artifacts] @actions/artifact list artifact functionality + download interface setup (#1495)
* actions/artifact preparation for download-artifact v4

* Test matrix strategy

* Fix needs dependency

* Improve list artifact test

* Fix typo

* Fix variables

* Cleanup download-all interfaces

* Fix tsc error

* Simplify to just name instead of artifactName

* Simplify to id instead of ArtifactId

* PR cleanup
2023-08-17 14:40:33 -04:00
Konrad Pabjan 20afb1a9fc [Artifacts] Add tests for E2E artifact upload (#1497)
* Add tests for E2E artifact upload

* Trigger Build

* Extra debug logs

* Debug dumping GitHub Context

* More logging

* Minor cleanup

* Trigger Build

* Unique artifact name

* Fix typo

* Fix

* Try using github-script

* Potential fix

* Cleanup

* More cleanup
2023-08-17 12:32:55 -04:00
Konrad Pabjan c9dab8c79d [Artifacts] Save md5 hash for each artifact upload (#1494)
* Hash artifact upload using md5

* Add imports

* Small tweaks

* PR feedback

* PR Feedback
2023-08-15 13:39:57 -04:00
Konrad Pabjan 45c49b09df [Artifacts] zip creation + blob storage upload functionality (#1488)
* Artifact zip creation + blob storage upload functionality

* Fix lint

* PR feedback
2023-08-10 15:28:41 -04:00
Bethany ab78839e86 Merge pull request #1487 from actions/bethanyj28/add-artifact-api-logic
Utilize client to create and finalize artifact
2023-08-10 10:19:24 -04:00
Bethany f03b6d639f update import 2023-08-09 17:50:46 -07:00
Bethany 58858b5078 don't use non-null assertions 2023-08-09 17:48:53 -07:00
Bethany 188abfc20b implement feedback 2023-08-09 17:42:14 -07:00
Bethany 2f42c127c7 update tests 2023-08-09 13:20:06 -07:00
Bethany 4dda3ab8a0 move getExpiration to upload-artifact 2023-08-09 13:12:30 -07:00
Bethany 4b219f79f3 Add tests for backend id fetch 2023-08-09 12:29:43 -07:00
Bethany 08d6314f7c prettier 2023-08-09 12:09:17 -07:00
Bethany b851b70474 catch errors at the root, remove unneccessary disabled rule 2023-08-09 12:08:43 -07:00
Bethany e8fb71c4bb lint 2023-08-09 11:34:18 -07:00
Bethany 73ad88882e utilize client, fetch IDs 2023-08-09 11:26:33 -07:00
Bethany 92695f58da Merge pull request #1486 from actions/bethanyj28/add-twirp-client
Add twirp client
2023-08-09 10:46:24 -04:00
Bethany 760f3fd3d1 Update packages/artifact/src/internal/shared/config.ts
Co-authored-by: Konrad Pabjan <konradpabjan@github.com>
2023-08-09 10:30:50 -04:00
Bethany c6117995d3 Update packages/artifact/src/internal/shared/config.ts
Co-authored-by: Konrad Pabjan <konradpabjan@github.com>
2023-08-09 10:30:44 -04:00
Bethany 24da3e2d1c lint 2023-08-09 07:10:43 -07:00
Bethany deda97d5e6 don't add extra line breaks 2023-08-09 07:02:06 -07:00
Bethany cfad1451e9 Update generated files to not use bigint 2023-08-09 07:00:27 -07:00
Bethany c0684c5add prettier 2023-08-08 13:19:43 -07:00
Bethany ad9b955fe9 remove unused package 2023-08-08 12:51:54 -07:00
Bethany 1718e0d97c revert target to es6 2023-08-08 12:49:45 -07:00
Bethany e85cd96d85 tests and fix bug for retry 2023-08-08 12:49:05 -07:00
Bethany bc24adbfd6 Merge pull request #1481 from actions/bethanyj28/add-twirp-definitions
[Artifacts] Add artifact Twirp API definitions
2023-08-08 10:36:34 -04:00
Bethany af1621025d wip 2023-08-07 16:26:07 -07:00
Bethany 6552cb9722 wip 2023-08-07 14:24:58 -07:00
Chad Kimes f74ff155bd Add option for concurrent cache downloads with timeout (#1484)
* Add option for concurrent cache downloads with timeout

* Add release notes

* Fix lint
2023-08-07 13:25:56 -04:00
Bethany a66e49ec8a Merge branch 'bethanyj28/add-twirp-definitions' into bethanyj28/add-twirp-client 2023-08-07 09:10:06 -07:00
Bethany d4c2fa4c68 add generated to eslintignore 2023-08-07 09:01:14 -07:00
Bethany 8c9ab93da7 Merge remote-tracking branch 'origin' into bethanyj28/add-twirp-definitions 2023-08-07 08:56:08 -07:00
Bethany 3773ef22b1 prettier and add generated files to prettierignore 2023-08-07 08:55:42 -07:00
Bethany 80e4680ac8 Merge branch 'bethanyj28/add-twirp-definitions' into bethanyj28/add-twirp-client 2023-08-07 08:48:20 -07:00
Bethany c608703ecf revert root tsconfig.json 2023-08-07 08:48:15 -07:00
Bethany 66ac937f2f target es2020 2023-08-07 08:45:28 -07:00
Bethany efcab31d38 pass in http client to constructor 2023-08-07 08:43:39 -07:00
Bethany 4c6d88f93a Start writing tests 2023-08-04 13:00:58 -07:00
Chad Kimes 19e0016878 actions/http-client 2.1.1 release (#1483) 2023-08-04 15:00:50 -04:00
Chad Kimes 2820b17d9d Add readBodyBuffer method to HttpClientResponse (#1475)
* Add readBodyBuffer method to HttpClientResponse

* Implement method in other package tests

* Make method optional to satisfy the test process
2023-08-04 14:35:26 -04:00
Bethany 8a5343d54a add twirp client 2023-08-04 09:23:14 -07:00
Bethany 3ebee1e8b4 package-lock.json conflict 2023-08-04 07:12:36 -07:00
Konrad Pabjan 7da3ac6eda [Artifacts] Name validation + zip specification creation (#1482)
* Artifact name validation + zip specification creation

* Fix linting issues

* Grammar fix

* Update test description
2023-08-04 09:53:42 -04:00
Vallie Joseph 2461056696 Audit Fix (#1480)
* fixing audit failures

* replacing lerna bootstrap with npm command

* audit fix for cache and tool-cache

* updating tunnel

* upgrading core packages

* re-adding tunnel as prod dep

* updating dependencies

* updating exec deps

* updating exec io package

* .

* Revert

* updating packages

* adding core as dep

* updating learna config

* updating lerna commands

* Removing audit failing packages in cache + tool-cache

* updating contribution bootstrap description

* updating libraries

* prettier lint

* hiding stricter rules

* updating prettier command

* Removing unknown flag

* Adding eslint prettier

* ignoring sym links

* updating ignore path

* updating prettier rules

* changing prettier + github ver

* updating ts and ignores

* Revert ts

* Adding unknown ignores

* downgrading lerna

* .

* adding nx

* Adding lint auto lint rules

* updating eslint ignore for glob packages

* Adding subdirs to ignore

* adding flag for ignore pattern in linter

* Expanding ignore regex

* Adding ignore rules

* adding another ignore pattern to tsconfig eslint

* adding ignore pattern to eslintrc

* syncing package-json

* updating traverse

* .

* test adding core and http client to base package

* running npm ci

* adding tsconfig paths

* adding base URL

* Adding explicit path to core and http-client

* editing tsc call

* updating artifact packages

* force build

* updating lock file version

* updating lock file version

* upgrading node version

* Adding babel traverse back

* fixing build issue

* fixing typescript ver

* updating package json

* Adding ignore for artifact test

* adding ignore to flags

* unlink after test completes

* cleanup

* merge + package edit
2023-08-03 16:36:11 -04:00
Bethany 3749c51d21 npm install 2023-08-03 12:59:51 -07:00
Bethany 769c896931 add artifact api twirp definitions 2023-08-03 12:43:45 -07:00
Konrad Pabjan c4f5ce2665 [Artifacts] Prepare for v2.0.0 of @actions/artifact (#1479)
* Prepare for v2.0.0 of @actions/artifact

* Run prettier

* temporary disable unused vars
2023-08-03 13:34:41 -04:00
105 changed files with 10732 additions and 25704 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
node_modules/
packages/*/node_modules/
packages/*/lib/
packages/glob/__tests__/_temp
packages/glob/__tests__/_temp
packages/*/src/generated/*/
+45 -8
View File
@@ -1,6 +1,13 @@
{
"plugins": ["jest", "@typescript-eslint"],
"extends": ["plugin:github/recommended"],
"plugins": [
"jest",
"@typescript-eslint",
"prettier"
],
"extends": [
"plugin:github/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
@@ -8,14 +15,33 @@
"project": "./tsconfig.eslint.json"
},
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"eslint-comments/no-use": "off",
"no-constant-condition": ["error", { "checkLoops": false }],
"github/no-then": "off",
"import/no-namespace": "off",
"no-shadow": "off",
"no-unused-vars": "off",
"i18n-text/no-en": "off",
"filenames/match-regex": "off",
"import/no-commonjs": "off",
"import/named": "off",
"no-sequences": "off",
"import/no-unresolved": "off",
"no-undef": "off",
"no-only-tests/no-only-tests": "off",
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
"accessibility": "no-public"
}
],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/await-thenable": "error",
@@ -23,8 +49,16 @@
"camelcase": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"@typescript-eslint/explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"@typescript-eslint/func-call-spacing": [
"error",
"never"
],
"@typescript-eslint/naming-convention": [
"error",
{
@@ -56,15 +90,18 @@
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"semi": "off",
"@typescript-eslint/semi": ["error", "never"],
"@typescript-eslint/semi": [
"error",
"never"
],
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/unbound-method": "error"
},
"ignorePatterns": "packages/glob/__tests__/_temp/**/",
"env": {
"node": true,
"es6": true,
"jest/globals": true
}
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ Note that before a PR will be accepted, you must ensure:
### Useful Scripts
- `npm run bootstrap` This runs `lerna bootstrap` which will install dependencies in this repository's packages and cross-link packages where necessary.
- `npm run bootstrap` This runs `lerna exec -- npm install` which will install dependencies in this repository's packages and cross-link packages where necessary.
- `npm run build` This compiles TypeScript code in each package (this is especially important if one package relies on changes in another when you're running tests). This is just an alias for `lerna run tsc`.
- `npm run format` This checks that formatting has been applied with Prettier.
- `npm test` This runs all Jest tests in all packages in this repository.
+82 -42
View File
@@ -1,3 +1,5 @@
# Temporarily disabled while v2.0.0 of @actions/artifact is under development
name: artifact-unit-tests
on:
push:
@@ -24,15 +26,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 16.x
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 16.x
# In order to upload & download artifacts from a shell script, certain env variables need to be set that are only available in the
# node context. This runs a local action that gets and sets the necessary env variables that are needed
- name: Set env variables
uses: ./packages/artifact/__tests__/ci-test-action/
node-version: 20.x
# Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible
# without these to just compile the artifacts package
@@ -48,48 +45,91 @@ jobs:
- name: Set artifact file contents
shell: bash
run: |
echo "non-gzip-artifact-content=hello" >> $GITHUB_ENV
echo "gzip-artifact-content=Some large amount of text that has a compression ratio that is greater than 100%. If greater than 100%, gzip is used to upload the file" >> $GITHUB_ENV
echo "empty-artifact-content=_EMPTY_" >> $GITHUB_ENV
echo "file1=hello from file 1" >> $GITHUB_ENV
echo "file2=hello from file 2" >> $GITHUB_ENV
- name: Create files that will be uploaded
run: |
mkdir artifact-path
echo '${{ env.non-gzip-artifact-content }}' > artifact-path/world.txt
echo '${{ env.gzip-artifact-content }}' > artifact-path/gzip.txt
touch artifact-path/empty.txt
echo '${{ env.file1 }}' > artifact-path/first.txt
echo '${{ env.file2 }}' > artifact-path/second.txt
# We're using node -e to call the functions directly available in the @actions/artifact package
- name: Upload artifacts using uploadArtifact()
run: |
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-1',['artifact-path/world.txt'], process.argv[1]))" "${{ github.workspace }}"
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-2',['artifact-path/gzip.txt'], process.argv[1]))" "${{ github.workspace }}"
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().uploadArtifact('my-artifact-3',['artifact-path/empty.txt'], process.argv[1]))" "${{ github.workspace }}"
- name: Upload Artifacts using actions/github-script@v6
uses: actions/github-script@v6
with:
script: |
const artifact = require('./packages/artifact/lib/artifact')
- name: Download artifacts using downloadArtifact()
run: |
mkdir artifact-1-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-1','artifact-1-directory'))"
mkdir artifact-2-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-2','artifact-2-directory'))"
mkdir artifact-3-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadArtifact('my-artifact-3','artifact-3-directory'))"
const artifactName = 'my-artifact-${{ matrix.runs-on }}'
console.log('artifactName: ' + artifactName)
- name: Verify downloadArtifact()
shell: bash
run: |
packages/artifact/__tests__/test-artifact-file.sh "artifact-1-directory/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "artifact-2-directory/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "artifact-3-directory/artifact-path/empty.txt" "${{ env.empty-artifact-content }}"
const fileContents = ['artifact-path/first.txt','artifact-path/second.txt']
- name: Download artifacts using downloadAllArtifacts()
run: |
mkdir multi-artifact-directory
node -e "Promise.resolve(require('./packages/artifact/lib/artifact-client').create().downloadAllArtifacts('multi-artifact-directory'))"
const uploadResult = await artifact.create().uploadArtifact(artifactName, fileContents, './')
console.log(uploadResult)
- name: Verify downloadAllArtifacts()
shell: bash
const success = uploadResult.success
const size = uploadResult.size
const id = uploadResult.id
if (!success) {
throw new Error('Failed to upload artifact')
} else {
console.log(`Successfully uploaded artifact ${id}`)
}
verify:
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
# Need root node_modules because certain npm packages like jest are configured for the entire repository and it won't be possible
# without these to just compile the artifacts package
- name: Install root npm packages
run: npm ci
- name: Compile artifact package
run: |
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-1/artifact-path/world.txt" "${{ env.non-gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-2/artifact-path/gzip.txt" "${{ env.gzip-artifact-content }}"
packages/artifact/__tests__/test-artifact-file.sh "multi-artifact-directory/my-artifact-3/artifact-path/empty.txt" "${{ env.empty-artifact-content }}"
npm ci
npm run tsc
working-directory: packages/artifact
- name: List artifacts using actions/github-script@v6
uses: actions/github-script@v6
with:
script: |
const artifact = require('./packages/artifact/lib/artifact')
const workflowRunId = process.env.GITHUB_RUN_ID
const repository = process.env.GITHUB_REPOSITORY
const repositoryOwner = repository.split('/')[0]
const repositoryName = repository.split('/')[1]
const listResult = await artifact.create().listArtifacts(workflowRunId, repositoryOwner, repositoryName, '${{ secrets.GITHUB_TOKEN }}')
console.log(listResult)
const artifacts = listResult.artifacts
if (artifacts.length !== 3) {
throw new Error('Expected 3 artifacts but only found ' + artifacts.length + ' artifacts')
}
const artifactNames = artifacts.map(artifact => artifact.name)
if (!artifactNames.includes('my-artifact-ubuntu-latest')){
throw new Error("Expected artifact list to contain an artifact named my-artifact-ubuntu-latest but it's missing")
}
if (!artifactNames.includes('my-artifact-windows-latest')){
throw new Error("Expected artifact list to contain an artifact named my-artifact-windows-latest but it's missing")
}
if (!artifactNames.includes('my-artifact-macos-latest')){
throw new Error("Expected artifact list to contain an artifact named my-artifact-macos-latest but it's missing")
}
console.log('Successfully listed artifacts that were uploaded')
+2 -2
View File
@@ -20,10 +20,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 16.x
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.x
- name: npm install
run: npm install
+2 -2
View File
@@ -24,10 +24,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 16.x
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.x
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
# node context. This runs a local action that gets and sets the necessary env variables that are needed
+2 -2
View File
@@ -23,10 +23,10 @@ jobs:
run: |
rm "C:\Program Files\Git\usr\bin\tar.exe"
- name: Set Node.js 12.x
- name: Set Node.js 20.x
uses: actions/setup-node@v1
with:
node-version: 12.x
node-version: 20.x
# In order to save & restore cache from a shell script, certain env variables need to be set that are only available in the
# node context. This runs a local action that gets and sets the necessary env variables that are needed
+3 -3
View File
@@ -24,14 +24,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: javascript
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2
+2 -2
View File
@@ -18,10 +18,10 @@ jobs:
- name: verify package exists
run: ls packages/${{ github.event.inputs.package }}
- name: Set Node.js 16.x
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.x
- name: npm install
run: npm install
+3 -3
View File
@@ -25,10 +25,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Set Node.js 16.x
- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 16.x
node-version: 20.x
- name: npm install
run: npm install
@@ -40,7 +40,7 @@ jobs:
run: npm run build
- name: npm test
run: npm test -- --runInBand
run: npm test -- --runInBand --forceExit
env:
GITHUB_TOKEN: ${{ github.token }}
+3 -1
View File
@@ -1,3 +1,5 @@
node_modules/
packages/*/node_modules/
packages/*/lib/
packages/*/lib/
packages/glob/__tests__/_temp/**/
packages/*/src/generated/*/
+2 -1
View File
@@ -7,5 +7,6 @@
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid",
"parser": "typescript"
"parser": "typescript",
"endOfLine": "auto"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"packages": [
"packages/*"
"packages/**/*"
],
"version": "independent"
}
}
+24
View File
@@ -0,0 +1,24 @@
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": []
}
}
},
"affected": {
"defaultBase": "master"
},
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"namedInputs": {
"default": [
"{projectRoot}/**/*",
"sharedGlobals"
],
"sharedGlobals": [],
"production": [
"default"
]
}
}
+6061 -22924
View File
File diff suppressed because it is too large Load Diff
+17 -13
View File
@@ -3,8 +3,10 @@
"private": true,
"scripts": {
"audit-all": "lerna run audit-moderate",
"bootstrap": "lerna bootstrap",
"bootstrap": "lerna exec -- npm install",
"build": "lerna run tsc",
"clean": "lerna clean",
"repair": "lerna repair",
"check-all": "concurrently \"npm:format-check\" \"npm:lint\" \"npm:test\" \"npm:build -- -- --noEmit\"",
"format": "prettier --write packages/**/*.ts",
"format-check": "prettier --check packages/**/*.ts",
@@ -14,19 +16,21 @@
"test": "jest --testTimeout 10000"
},
"devDependencies": {
"@types/jest": "^27.0.2",
"@types/node": "^16.18.1",
"@types/jest": "^29.5.4",
"@types/node": "^20.5.7",
"@types/signale": "^1.4.1",
"@typescript-eslint/parser": "^4.0.0",
"concurrently": "^6.1.0",
"eslint": "^7.23.0",
"eslint-plugin-github": "^4.1.3",
"eslint-plugin-jest": "^22.21.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.9.0",
"eslint-plugin-github": "^4.9.2",
"eslint-plugin-jest": "^27.2.3",
"eslint-plugin-prettier": "^5.0.0",
"flow-bin": "^0.115.0",
"jest": "^27.2.5",
"lerna": "^5.4.0",
"prettier": "^1.19.1",
"ts-jest": "^27.0.5",
"typescript": "^3.9.9"
"jest": "^29.6.4",
"lerna": "^7.1.4",
"nx": "16.6.0",
"prettier": "^3.0.0",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
}
}
}
+5 -87
View File
@@ -2,94 +2,12 @@
## Usage
You can use this package to interact with the actions artifacts.
- [Upload an Artifact](#Upload-an-Artifact)
- [Download a Single Artifact](#Download-a-Single-Artifact)
- [Download All Artifacts](#Download-all-Artifacts)
- [Additional Documentation](#Additional-Documentation)
- [Contributions](#Contributions)
You can use this package to interact with the Actions artifacts.
Relative paths and absolute paths are both allowed. Relative paths are rooted against the current working directory.
This most recently published version of this package (`1.1.1`) can be found [here](https://github.com/actions/toolkit/tree/@actions/artifact@1.1.1/packages/artifact)
## Upload an Artifact
## 🚧 Under construction 🚧
Method Name: `uploadArtifact`
This package is currently undergoing a major overhaul in preparation for `v4` versions of `upload-artifact` and `download-artifact` (these Actions will use a new `2.0.0` version of `@actions/artifact` that will soon be released). The upcoming version of `@actions/artifact` will take advantage of a major re-architecture with entirely new APIs.
#### Inputs
- `name`
- The name of the artifact that is being uploaded
- Required
- `files`
- A list of file paths that describe what should be uploaded as part of the artifact
- If a path is provided that does not exist, an error will be thrown
- Can be absolute or relative. Internally everything is normalized and resolved
- Required
- `rootDirectory`
- A file path that denotes the root directory of the files being uploaded. This path is used to strip the paths provided in `files` to control how they are uploaded and structured
- If a file specified in `files` is not in the `rootDirectory`, an error will be thrown
- Required
- `options`
- Extra options that allow for the customization of the upload behavior
- Optional
#### Available Options
- `retentionDays`
- Duration after which artifact will expire in days
- Minimum value: 1
- Maximum value: 90 unless changed by repository setting
- If this is set to a greater value than the retention settings allowed, the retention on artifacts will be reduced to match the max value allowed on the server, and the upload process will continue. An input of 0 assumes default retention value.
#### Example using Absolute File Paths
```js
const artifact = require('@actions/artifact');
const artifactClient = artifact.create()
const artifactName = 'my-artifact';
const files = [
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
]
const rootDirectory = '/home/user/files/plz-upload'
const options = {
continueOnError: true
}
const uploadResult = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options)
```
#### Example using Relative File Paths
```js
// Assuming the current working directory is /home/user/files/plz-upload
const artifact = require('@actions/artifact');
const artifactClient = artifact.create()
const artifactName = 'my-artifact';
const files = [
'file1.txt',
'file2.txt',
'dir/file3.txt'
]
const rootDirectory = '.' // Also possible to use __dirname
const options = {
continueOnError: false
}
const uploadResponse = await artifactClient.uploadArtifact(artifactName, files, rootDirectory, options)
```
#### Upload Result
The returned `UploadResponse` will contain the following information
- `artifactName`
- The name of the artifact that was uploaded
- `size`
- Total size of the artifact that was uploaded in bytes
## Contributions
See [contributor guidelines](https://github.com/actions/toolkit/blob/main/.github/CONTRIBUTING.md) for general guidelines and information about toolkit contributions.
For contributions related to this package, see [artifact contributions](CONTRIBUTIONS.md) for more information.
The upcoming `2.0.0` package and `v4` artifact Actions aim to solve some of the major pain-points that have made artifact usage difficult up until now.
@@ -0,0 +1,200 @@
import * as http from 'http'
import * as net from 'net'
import {HttpClient} from '@actions/http-client'
import * as config from '../src/internal/shared/config'
import {createArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client'
import * as core from '@actions/core'
jest.mock('@actions/http-client')
describe('artifact-http-client', () => {
beforeAll(() => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('http://localhost:8080')
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token')
})
beforeEach(() => {
jest.clearAllMocks()
})
it('should successfully create a client', () => {
const client = createArtifactTwirpClient('upload')
expect(client).toBeDefined()
})
it('should make a request', async () => {
const mockPost = jest.fn(() => {
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 200
return {
message: msg,
readBody: async () => {
return Promise.resolve(
`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`
)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = createArtifactTwirpClient('upload')
const artifact = await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
expect(artifact).toBeDefined()
expect(artifact.ok).toBe(true)
expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload')
})
it('should retry if the request fails', async () => {
const mockPost = jest
.fn(() => {
const msgSucceeded = new http.IncomingMessage(new net.Socket())
msgSucceeded.statusCode = 200
return {
message: msgSucceeded,
readBody: async () => {
return Promise.resolve(
`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`
)
}
}
})
.mockImplementationOnce(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 500
msgFailed.statusMessage = 'Internal Server Error'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = createArtifactTwirpClient(
'upload',
5, // retry 5 times
1, // wait 1 ms
1.5 // backoff factor
)
const artifact = await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(artifact).toBeDefined()
expect(artifact.ok).toBe(true)
expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload')
expect(mockPost).toHaveBeenCalledTimes(2)
})
it('should fail if the request fails 5 times', async () => {
const mockPost = jest.fn(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 500
msgFailed.statusMessage = 'Internal Server Error'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = createArtifactTwirpClient(
'upload',
5, // retry 5 times
1, // wait 1 ms
1.5 // backoff factor
)
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
}).rejects.toThrowError(
'Failed to make request after 5 attempts: Failed request: (500) Internal Server Error'
)
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(5)
})
it('should fail immediately if there is a non-retryable error', async () => {
const mockPost = jest.fn(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 401
msgFailed.statusMessage = 'Unauthorized'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve(`{"ok": false}`)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = createArtifactTwirpClient(
'upload',
5, // retry 5 times
1, // wait 1 ms
1.5 // backoff factor
)
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
}).rejects.toThrowError(
'Received non-retryable error: Failed request: (401) Unauthorized'
)
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
})
})
@@ -1,5 +0,0 @@
name: 'Set env variables'
description: 'Sets certain env variables so that e2e artifact upload and download can be tested in a shell'
runs:
using: 'node12'
main: 'index.js'
@@ -1,14 +0,0 @@
// Certain env variables are not set by default in a shell context and are only available in a node context from a running action
// In order to be able to upload and download artifacts e2e in a shell when running CI tests, we need these env variables set
const fs = require('fs');
const os = require('os');
const filePath = process.env[`GITHUB_ENV`]
fs.appendFileSync(filePath, `ACTIONS_RUNTIME_URL=${process.env.ACTIONS_RUNTIME_URL}${os.EOL}`, {
encoding: 'utf8'
})
fs.appendFileSync(filePath, `ACTIONS_RUNTIME_TOKEN=${process.env.ACTIONS_RUNTIME_TOKEN}${os.EOL}`, {
encoding: 'utf8'
})
fs.appendFileSync(filePath, `GITHUB_RUN_ID=${process.env.GITHUB_RUN_ID}${os.EOL}`, {
encoding: 'utf8'
})
@@ -0,0 +1,279 @@
import fs from 'fs'
import * as http from 'http'
import * as net from 'net'
import * as path from 'path'
import * as core from '@actions/core'
import * as github from '@actions/github'
import {HttpClient} from '@actions/http-client'
import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types'
import archiver from 'archiver'
import {downloadArtifact} from '../src/internal/download/download-artifact'
import {getUserAgentString} from '../src/internal/shared/user-agent'
type MockedDownloadArtifact = jest.MockedFunction<
RestEndpointMethods['actions']['downloadArtifact']
>
const testDir = path.join(__dirname, '_temp', 'download-artifact')
const fixtures = {
workspaceDir: path.join(testDir, 'workspace'),
exampleArtifact: {
path: path.join(testDir, 'artifact.zip'),
files: [
{
path: 'hello.txt',
content: 'Hello World!'
},
{
path: 'goodbye.txt',
content: 'Goodbye World!'
}
]
},
artifactID: 1234,
repositoryOwner: 'actions',
repositoryName: 'toolkit',
token: 'ghp_1234567890',
blobStorageUrl: 'https://blob-storage.local?signed=true'
}
jest.mock('@actions/github', () => ({
getOctokit: jest.fn().mockReturnValue({
rest: {
actions: {
downloadArtifact: jest.fn()
}
}
})
}))
jest.mock('@actions/http-client')
// Create a zip archive with the contents of the example artifact
const createTestArchive = async (): Promise<void> => {
const archive = archiver('zip', {
zlib: {level: 9}
})
for (const file of fixtures.exampleArtifact.files) {
archive.append(file.content, {name: file.path})
}
archive.finalize()
return new Promise((resolve, reject) => {
archive.pipe(fs.createWriteStream(fixtures.exampleArtifact.path))
archive.on('error', reject)
archive.on('finish', resolve)
})
}
const expectExtractedArchive = async (dir: string): Promise<void> => {
for (const file of fixtures.exampleArtifact.files) {
const filePath = path.join(dir, file.path)
expect(fs.readFileSync(filePath, 'utf8')).toEqual(file.content)
}
}
describe('download-artifact', () => {
beforeEach(async () => {
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
await fs.promises.mkdir(testDir, {recursive: true})
await createTestArchive()
process.env['GITHUB_WORKSPACE'] = fixtures.workspaceDir
})
afterEach(async () => {
jest.restoreAllMocks()
await fs.promises.rm(testDir, {recursive: true})
delete process.env['GITHUB_WORKSPACE']
})
it('should successfully download an artifact to $GITHUB_WORKSPACE', async () => {
const downloadArtifactMock = github.getOctokit(fixtures.token).rest.actions
.downloadArtifact as MockedDownloadArtifact
downloadArtifactMock.mockResolvedValueOnce({
headers: {
location: fixtures.blobStorageUrl
},
status: 302,
url: '',
data: Buffer.from('')
})
const getMock = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
return {
message
}
})
const httpClientMock = (HttpClient as jest.Mock).mockImplementation(() => {
return {
get: getMock
}
})
const response = await downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token
)
expect(downloadArtifactMock).toHaveBeenCalledWith({
owner: fixtures.repositoryOwner,
repo: fixtures.repositoryName,
artifact_id: fixtures.artifactID,
archive_format: 'zip',
request: {
redirect: 'manual'
}
})
expect(httpClientMock).toHaveBeenCalledWith(getUserAgentString())
expect(getMock).toHaveBeenCalledWith(fixtures.blobStorageUrl)
expectExtractedArchive(fixtures.workspaceDir)
expect(response.success).toBe(true)
expect(response.downloadPath).toBe(fixtures.workspaceDir)
})
it('should successfully download an artifact to user defined path', async () => {
const customPath = path.join(testDir, 'custom')
const downloadArtifactMock = github.getOctokit(fixtures.token).rest.actions
.downloadArtifact as MockedDownloadArtifact
downloadArtifactMock.mockResolvedValueOnce({
headers: {
location: fixtures.blobStorageUrl
},
status: 302,
url: '',
data: Buffer.from('')
})
const getMock = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
return {
message
}
})
const httpClientMock = (HttpClient as jest.Mock).mockImplementation(() => {
return {
get: getMock
}
})
const response = await downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token,
{
path: customPath
}
)
expect(downloadArtifactMock).toHaveBeenCalledWith({
owner: fixtures.repositoryOwner,
repo: fixtures.repositoryName,
artifact_id: fixtures.artifactID,
archive_format: 'zip',
request: {
redirect: 'manual'
}
})
expect(httpClientMock).toHaveBeenCalledWith(getUserAgentString())
expect(getMock).toHaveBeenCalledWith(fixtures.blobStorageUrl)
expectExtractedArchive(customPath)
expect(response.success).toBe(true)
expect(response.downloadPath).toBe(customPath)
})
it('should fail if download artifact API does not respond with location', async () => {
const downloadArtifactMock = github.getOctokit(fixtures.token).rest.actions
.downloadArtifact as MockedDownloadArtifact
downloadArtifactMock.mockResolvedValueOnce({
headers: {},
status: 302,
url: '',
data: Buffer.from('')
})
await expect(
downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token
)
).rejects.toBeInstanceOf(Error)
expect(downloadArtifactMock).toHaveBeenCalledWith({
owner: fixtures.repositoryOwner,
repo: fixtures.repositoryName,
artifact_id: fixtures.artifactID,
archive_format: 'zip',
request: {
redirect: 'manual'
}
})
})
it('should fail if blob storage response is non-200', async () => {
const downloadArtifactMock = github.getOctokit(fixtures.token).rest.actions
.downloadArtifact as MockedDownloadArtifact
downloadArtifactMock.mockResolvedValueOnce({
headers: {
location: fixtures.blobStorageUrl
},
status: 302,
url: '',
data: Buffer.from('')
})
const getMock = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 500
message.push('Internal Server Error')
return {
message
}
})
const httpClientMock = (HttpClient as jest.Mock).mockImplementation(() => {
return {
get: getMock
}
})
await expect(
downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token
)
).rejects.toBeInstanceOf(Error)
expect(downloadArtifactMock).toHaveBeenCalledWith({
owner: fixtures.repositoryOwner,
repo: fixtures.repositoryName,
artifact_id: fixtures.artifactID,
archive_format: 'zip',
request: {
redirect: 'manual'
}
})
expect(httpClientMock).toHaveBeenCalledWith(getUserAgentString())
expect(getMock).toHaveBeenCalledWith(fixtures.blobStorageUrl)
})
})
@@ -0,0 +1,79 @@
import {
validateArtifactName,
validateFilePath
} from '../src/internal/upload/path-and-artifact-name-validation'
import * as core from '@actions/core'
describe('Path and artifact name validation', () => {
beforeAll(() => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
})
it('Check Artifact Name for any invalid characters', () => {
const invalidNames = [
'my\\artifact',
'my/artifact',
'my"artifact',
'my:artifact',
'my<artifact',
'my>artifact',
'my|artifact',
'my*artifact',
'my?artifact',
''
]
for (const invalidName of invalidNames) {
expect(() => {
validateArtifactName(invalidName)
}).toThrow()
}
const validNames = [
'my-normal-artifact',
'myNormalArtifact',
'm¥ñðrmålÄr†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
validateArtifactName(validName)
}).not.toThrow()
}
})
it('Check Artifact File Path for any invalid characters', () => {
const invalidNames = [
'some/invalid"artifact/path',
'some/invalid:artifact/path',
'some/invalid<artifact/path',
'some/invalid>artifact/path',
'some/invalid|artifact/path',
'some/invalid*artifact/path',
'some/invalid?artifact/path',
'some/invalid\rartifact/path',
'some/invalid\nartifact/path',
'some/invalid\r\nartifact/path',
''
]
for (const invalidName of invalidNames) {
expect(() => {
validateFilePath(invalidName)
}).toThrow()
}
const validNames = [
'my/perfectly-normal/artifact-path',
'my/perfectly\\Normal/Artifact-path',
'm¥/ñðrmål/Är†ï£å¢†'
]
for (const validName of validNames) {
expect(() => {
validateFilePath(validName)
}).not.toThrow()
}
})
})
@@ -0,0 +1,65 @@
import {Timestamp} from '../src/generated'
import * as retention from '../src/internal/upload/retention'
describe('retention', () => {
beforeEach(() => {
delete process.env['GITHUB_RETENTION_DAYS']
})
it('should return the inputted retention days if it is less than the max retention days', () => {
// setup
const mockDate = new Date('2020-01-01')
jest.useFakeTimers().setSystemTime(mockDate)
process.env['GITHUB_RETENTION_DAYS'] = '90'
const exp = retention.getExpiration(30)
expect(exp).toBeDefined()
if (exp) {
const expDate = Timestamp.toDate(exp)
const expected = new Date()
expected.setDate(expected.getDate() + 30)
expect(expDate).toEqual(expected)
}
})
it('should return the max retention days if the inputted retention days is greater than the max retention days', () => {
// setup
const mockDate = new Date('2020-01-01')
jest.useFakeTimers().setSystemTime(mockDate)
process.env['GITHUB_RETENTION_DAYS'] = '90'
const exp = retention.getExpiration(120)
expect(exp).toBeDefined()
if (exp) {
const expDate = Timestamp.toDate(exp) // we check whether exp is defined above
const expected = new Date()
expected.setDate(expected.getDate() + 90)
expect(expDate).toEqual(expected)
}
})
it('should return undefined if the inputted retention days is undefined', () => {
const exp = retention.getExpiration()
expect(exp).toBeUndefined()
})
it('should return the inputted retention days if there is no max retention days', () => {
// setup
const mockDate = new Date('2020-01-01')
jest.useFakeTimers().setSystemTime(mockDate)
const exp = retention.getExpiration(30)
expect(exp).toBeDefined()
if (exp) {
const expDate = Timestamp.toDate(exp) // we check whether exp is defined above
const expected = new Date()
expected.setDate(expected.getDate() + 30)
expect(expDate).toEqual(expected)
}
})
})
@@ -1,27 +0,0 @@
#!/bin/bash
path="$1"
expectedContent="$2"
if [ "$path" == "" ]; then
echo "File path not provided"
exit 1
fi
if [ "$expectedContent" == "" ]; then
echo "Expected file contents not provided"
exit 1
fi
if [ ! -f "$path" ]; then
echo "Expected file $path does not exist"
exit 1
fi
actualContent=$(cat "$path")
if [ "$expectedContent" == "_EMPTY_" ] && [ ! -s "$path" ]; then
exit 0
elif [ "$actualContent" != "$expectedContent" ]; then
echo "File contents are not correct, expected $expectedContent, received $actualContent"
exit 1
fi
@@ -0,0 +1,362 @@
import * as core from '@actions/core'
import * as uploadZipSpecification from '../src/internal/upload/upload-zip-specification'
import * as zip from '../src/internal/upload/zip'
import * as util from '../src/internal/shared/util'
import * as retention from '../src/internal/upload/retention'
import * as config from '../src/internal/shared/config'
import {Timestamp, ArtifactServiceClientJSON} from '../src/generated'
import * as blobUpload from '../src/internal/upload/blob-upload'
import {uploadArtifact} from '../src/internal/upload/upload-artifact'
describe('upload-artifact', () => {
beforeEach(() => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
})
afterEach(() => {
jest.restoreAllMocks()
})
it('should successfully upload an artifact', () => {
const mockDate = new Date('2020-01-01')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: '/home/user/files/plz-upload/file1.txt',
destinationPath: 'file1.txt'
},
{
sourcePath: '/home/user/files/plz-upload/file2.txt',
destinationPath: 'file2.txt'
},
{
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
destinationPath: 'dir/file3.txt'
}
])
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678'
})
jest
.spyOn(retention, 'getExpiration')
.mockReturnValue(Timestamp.fromDate(mockDate))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.com'
})
)
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({
isSuccess: true,
uploadSize: 1234,
md5Hash: 'test-md5-hash'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(Promise.resolve({ok: true, artifactId: '1'}))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://test-url.com')
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: true, size: 1234, id: 1})
})
it('should throw an error if the root directory is invalid', () => {
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockImplementation(() => {
throw new Error('Invalid root directory')
})
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow('Invalid root directory')
})
it('should return false if there are no files to upload', () => {
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([])
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if no backend IDs are found', () => {
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: '/home/user/files/plz-upload/file1.txt',
destinationPath: 'file1.txt'
},
{
sourcePath: '/home/user/files/plz-upload/file2.txt',
destinationPath: 'file2.txt'
},
{
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
destinationPath: 'dir/file3.txt'
}
])
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest
.spyOn(util, 'getBackendIdsFromToken')
.mockReturnValue({workflowRunBackendId: '', workflowJobRunBackendId: ''})
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if the creation request fails', () => {
const mockDate = new Date('2020-01-01')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: '/home/user/files/plz-upload/file1.txt',
destinationPath: 'file1.txt'
},
{
sourcePath: '/home/user/files/plz-upload/file2.txt',
destinationPath: 'file2.txt'
},
{
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
destinationPath: 'dir/file3.txt'
}
])
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678'
})
jest
.spyOn(retention, 'getExpiration')
.mockReturnValue(Timestamp.fromDate(mockDate))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(Promise.resolve({ok: false, signedUploadUrl: ''}))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://test-url.com')
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if blob storage upload is unsuccessful', () => {
const mockDate = new Date('2020-01-01')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: '/home/user/files/plz-upload/file1.txt',
destinationPath: 'file1.txt'
},
{
sourcePath: '/home/user/files/plz-upload/file2.txt',
destinationPath: 'file2.txt'
},
{
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
destinationPath: 'dir/file3.txt'
}
])
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678'
})
jest
.spyOn(retention, 'getExpiration')
.mockReturnValue(Timestamp.fromDate(mockDate))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.com'
})
)
jest
.spyOn(blobUpload, 'uploadZipToBlobStorage')
.mockReturnValue(Promise.resolve({isSuccess: false}))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://test-url.com')
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if finalize artifact fails', () => {
const mockDate = new Date('2020-01-01')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: '/home/user/files/plz-upload/file1.txt',
destinationPath: 'file1.txt'
},
{
sourcePath: '/home/user/files/plz-upload/file2.txt',
destinationPath: 'file2.txt'
},
{
sourcePath: '/home/user/files/plz-upload/dir/file3.txt',
destinationPath: 'dir/file3.txt'
}
])
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678'
})
jest
.spyOn(retention, 'getExpiration')
.mockReturnValue(Timestamp.fromDate(mockDate))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.com'
})
)
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({
isSuccess: true,
uploadSize: 1234,
md5Hash: 'test-md5-hash'
})
)
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'FinalizeArtifact')
.mockReturnValue(Promise.resolve({ok: false, artifactId: ''}))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://test-url.com')
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({success: false})
})
})
@@ -0,0 +1,312 @@
import * as io from '../../io/src/io'
import * as path from 'path'
import {promises as fs} from 'fs'
import * as core from '@actions/core'
import {
getUploadZipSpecification,
validateRootDirectory
} from '../src/internal/upload/upload-zip-specification'
const root = path.join(__dirname, '_temp', 'upload-specification')
const goodItem1Path = path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'good-item1.txt'
)
const goodItem2Path = path.join(root, 'folder-d', 'good-item2.txt')
const goodItem3Path = path.join(root, 'folder-d', 'good-item3.txt')
const goodItem4Path = path.join(root, 'folder-d', 'good-item4.txt')
const goodItem5Path = path.join(root, 'good-item5.txt')
const badItem1Path = path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'bad-item1.txt'
)
const badItem2Path = path.join(root, 'folder-d', 'bad-item2.txt')
const badItem3Path = path.join(root, 'folder-f', 'bad-item3.txt')
const badItem4Path = path.join(root, 'folder-h', 'folder-i', 'bad-item4.txt')
const badItem5Path = path.join(root, 'folder-h', 'folder-i', 'bad-item5.txt')
const extraFileInFolderCPath = path.join(
root,
'folder-a',
'folder-b',
'folder-c',
'extra-file-in-folder-c.txt'
)
const amazingFileInFolderHPath = path.join(root, 'folder-h', 'amazing-item.txt')
const artifactFilesToUpload = [
goodItem1Path,
goodItem2Path,
goodItem3Path,
goodItem4Path,
goodItem5Path,
extraFileInFolderCPath,
amazingFileInFolderHPath
]
describe('Search', () => {
beforeAll(async () => {
// mock all output so that there is less noise when running tests
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
// clear temp directory
await io.rmRF(root)
await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-c'), {
recursive: true
})
await fs.mkdir(path.join(root, 'folder-a', 'folder-b', 'folder-e'), {
recursive: true
})
await fs.mkdir(path.join(root, 'folder-d'), {
recursive: true
})
await fs.mkdir(path.join(root, 'folder-f'), {
recursive: true
})
await fs.mkdir(path.join(root, 'folder-g'), {
recursive: true
})
await fs.mkdir(path.join(root, 'folder-h', 'folder-i'), {
recursive: true
})
await fs.writeFile(goodItem1Path, 'good item1 file')
await fs.writeFile(goodItem2Path, 'good item2 file')
await fs.writeFile(goodItem3Path, 'good item3 file')
await fs.writeFile(goodItem4Path, 'good item4 file')
await fs.writeFile(goodItem5Path, 'good item5 file')
await fs.writeFile(badItem1Path, 'bad item1 file')
await fs.writeFile(badItem2Path, 'bad item2 file')
await fs.writeFile(badItem3Path, 'bad item3 file')
await fs.writeFile(badItem4Path, 'bad item4 file')
await fs.writeFile(badItem5Path, 'bad item5 file')
await fs.writeFile(extraFileInFolderCPath, 'extra file')
await fs.writeFile(amazingFileInFolderHPath, 'amazing file')
/*
Directory structure of files that get created:
root/
folder-a/
folder-b/
folder-c/
good-item1.txt
bad-item1.txt
extra-file-in-folder-c.txt
folder-e/
folder-d/
good-item2.txt
good-item3.txt
good-item4.txt
bad-item2.txt
folder-f/
bad-item3.txt
folder-g/
folder-h/
amazing-item.txt
folder-i/
bad-item4.txt
bad-item5.txt
good-item5.txt
*/
})
it('Upload Specification - Fail non-existent rootDirectory', async () => {
const invalidRootDirectory = path.join(
__dirname,
'_temp',
'upload-specification-invalid'
)
expect(() => {
validateRootDirectory(invalidRootDirectory)
}).toThrow(
`The provided rootDirectory ${invalidRootDirectory} does not exist`
)
})
it('Upload Specification - Fail invalid rootDirectory', async () => {
expect(() => {
validateRootDirectory(goodItem1Path)
}).toThrow(
`The provided rootDirectory ${goodItem1Path} is not a valid directory`
)
})
it('Upload Specification - File does not exist', async () => {
const fakeFilePath = path.join(
'folder-a',
'folder-b',
'non-existent-file.txt'
)
expect(() => {
getUploadZipSpecification([fakeFilePath], root)
}).toThrow(`File ${fakeFilePath} does not exist`)
})
it('Upload Specification - Non parent directory', async () => {
const folderADirectory = path.join(root, 'folder-a')
const artifactFiles = [
goodItem1Path,
badItem1Path,
extraFileInFolderCPath,
goodItem5Path
]
expect(() => {
getUploadZipSpecification(artifactFiles, folderADirectory)
}).toThrow(
`The rootDirectory: ${folderADirectory} is not a parent directory of the file: ${goodItem5Path}`
)
})
it('Upload Specification - Success', async () => {
const specifications = getUploadZipSpecification(
artifactFilesToUpload,
root
)
expect(specifications.length).toEqual(7)
const absolutePaths = specifications.map(item => item.sourcePath)
expect(absolutePaths).toContain(goodItem1Path)
expect(absolutePaths).toContain(goodItem2Path)
expect(absolutePaths).toContain(goodItem3Path)
expect(absolutePaths).toContain(goodItem4Path)
expect(absolutePaths).toContain(goodItem5Path)
expect(absolutePaths).toContain(extraFileInFolderCPath)
expect(absolutePaths).toContain(amazingFileInFolderHPath)
for (const specification of specifications) {
if (specification.sourcePath === goodItem1Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt')
)
} else if (specification.sourcePath === goodItem2Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item2.txt')
)
} else if (specification.sourcePath === goodItem3Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item3.txt')
)
} else if (specification.sourcePath === goodItem4Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item4.txt')
)
} else if (specification.sourcePath === goodItem5Path) {
expect(specification.destinationPath).toEqual(
path.join('/good-item5.txt')
)
} else if (specification.sourcePath === extraFileInFolderCPath) {
expect(specification.destinationPath).toEqual(
path.join(
'/folder-a',
'folder-b',
'folder-c',
'extra-file-in-folder-c.txt'
)
)
} else if (specification.sourcePath === amazingFileInFolderHPath) {
expect(specification.destinationPath).toEqual(
path.join('/folder-h', 'amazing-item.txt')
)
} else {
throw new Error(
'Invalid specification found. This should never be reached'
)
}
}
})
it('Upload Specification - Success with extra slash', async () => {
const rootWithSlash = `${root}/`
const specifications = getUploadZipSpecification(
artifactFilesToUpload,
rootWithSlash
)
expect(specifications.length).toEqual(7)
const absolutePaths = specifications.map(item => item.sourcePath)
expect(absolutePaths).toContain(goodItem1Path)
expect(absolutePaths).toContain(goodItem2Path)
expect(absolutePaths).toContain(goodItem3Path)
expect(absolutePaths).toContain(goodItem4Path)
expect(absolutePaths).toContain(goodItem5Path)
expect(absolutePaths).toContain(extraFileInFolderCPath)
expect(absolutePaths).toContain(amazingFileInFolderHPath)
for (const specification of specifications) {
if (specification.sourcePath === goodItem1Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt')
)
} else if (specification.sourcePath === goodItem2Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item2.txt')
)
} else if (specification.sourcePath === goodItem3Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item3.txt')
)
} else if (specification.sourcePath === goodItem4Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-d', 'good-item4.txt')
)
} else if (specification.sourcePath === goodItem5Path) {
expect(specification.destinationPath).toEqual(
path.join('/good-item5.txt')
)
} else if (specification.sourcePath === extraFileInFolderCPath) {
expect(specification.destinationPath).toEqual(
path.join(
'/folder-a',
'folder-b',
'folder-c',
'extra-file-in-folder-c.txt'
)
)
} else if (specification.sourcePath === amazingFileInFolderHPath) {
expect(specification.destinationPath).toEqual(
path.join('/folder-h', 'amazing-item.txt')
)
} else {
throw new Error(
'Invalid specification found. This should never be reached'
)
}
}
})
it('Upload Specification - Empty Directories are included', async () => {
const folderEPath = path.join(root, 'folder-a', 'folder-b', 'folder-e')
const filesWithDirectory = [goodItem1Path, folderEPath]
const specifications = getUploadZipSpecification(filesWithDirectory, root)
expect(specifications.length).toEqual(2)
const absolutePaths = specifications.map(item => item.sourcePath)
expect(absolutePaths).toContain(goodItem1Path)
expect(absolutePaths).toContain(null)
for (const specification of specifications) {
if (specification.sourcePath === goodItem1Path) {
expect(specification.destinationPath).toEqual(
path.join('/folder-a', 'folder-b', 'folder-c', 'good-item1.txt')
)
} else if (specification.sourcePath === null) {
expect(specification.destinationPath).toEqual(
path.join('/folder-a', 'folder-b', 'folder-e')
)
} else {
throw new Error(
'Invalid specification found. This should never be reached'
)
}
}
})
})
+62
View File
@@ -0,0 +1,62 @@
import * as config from '../src/internal/shared/config'
import * as util from '../src/internal/shared/util'
describe('get-backend-ids-from-token', () => {
it('should return backend ids when the token is valid', () => {
jest
.spyOn(config, 'getRuntimeToken')
.mockReturnValue(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8'
)
const backendIds = util.getBackendIdsFromToken()
expect(backendIds.workflowRunBackendId).toBe(
'ce7f54c7-61c7-4aae-887f-30da475f5f1a'
)
expect(backendIds.workflowJobRunBackendId).toBe(
'ca395085-040a-526b-2ce8-bdc85f692774'
)
})
it("should throw an error when the token doesn't have the right scope", () => {
jest
.spyOn(config, 'getRuntimeToken')
.mockReturnValue(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCIsImlhdCI6MTUxNjIzOTAyMn0.K0IEoULZteGevF38G94xiaA8zcZ5UlKWfGfqE6q3dhw'
)
expect(util.getBackendIdsFromToken).toThrowError(
'Failed to get backend IDs: The provided JWT token is invalid'
)
})
it('should throw an error when the token has a malformed scope', () => {
jest
.spyOn(config, 'getRuntimeToken')
.mockReturnValue(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhIiwiaWF0IjoxNTE2MjM5MDIyfQ.7D0_LRfRFRZFImHQ7GxH2S6ZyFjjZ5U0ujjGCfle1XE'
)
expect(util.getBackendIdsFromToken).toThrowError(
'Failed to get backend IDs: The provided JWT token is invalid'
)
})
it('should throw an error when the token is in an invalid format', () => {
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token')
expect(util.getBackendIdsFromToken).toThrowError('Invalid token specified')
})
it("should throw an error when the token doesn't have the right field", () => {
jest
.spyOn(config, 'getRuntimeToken')
.mockReturnValue(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
)
expect(util.getBackendIdsFromToken).toThrowError(
'Failed to get backend IDs: The provided JWT token is invalid'
)
})
})
View File
+1
View File
@@ -0,0 +1 @@
Docs will be added here once development of version `2.0.0` has finished
+473 -868
View File
File diff suppressed because it is too large Load Diff
+17 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "1.1.1",
"version": "2.0.0",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -31,22 +31,32 @@
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
"bootstrap": "cd ../../ && npm run bootstrap",
"tsc-run": "tsc",
"tsc": "npm run bootstrap && npm run tsc-run"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@actions/http-client": "^2.1.0",
"@azure/storage-blob": "^12.15.0",
"@types/node": "^20.4.5",
"archiver": "^5.3.1"
"@octokit/core": "^3.5.1",
"@octokit/plugin-request-log": "^1.0.4",
"@octokit/plugin-retry": "^3.0.9",
"@octokit/request-error": "^5.0.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1",
"@types/unzipper": "^0.10.6",
"archiver": "^5.3.1",
"crypto": "^1.0.1",
"jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0",
"unzipper": "^0.10.14"
},
"devDependencies": {
"@protobuf-ts/plugin": "^2.2.3-alpha.1",
"@types/archiver": "^5.3.2",
"twirp-ts": "^2.5.0",
"typescript": "^3.9.10"
"typescript": "^5.2.2"
}
}
+3 -8
View File
@@ -1,15 +1,10 @@
import { ArtifactClient, Client} from './internal/client'
import { UploadOptions } from './internal/upload/upload-options'
import { UploadResponse } from './internal/upload/upload-response'
import {ArtifactClient, Client} from './internal/client'
/**
* Exported functionality that we want to expose for any users of @actions/artifact
*/
export {
ArtifactClient,
UploadOptions,
UploadResponse,
}
export * from './internal/shared/interfaces'
export {ArtifactClient}
export function create(): ArtifactClient {
return Client.create()
@@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.9.1 with parameter client_none,generate_dependencies
// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies
// @generated from protobuf file "google/protobuf/timestamp.proto" (package "google.protobuf", syntax proto3)
// tslint:disable
//
@@ -139,7 +139,7 @@ export interface Timestamp {
*
* @generated from protobuf field: int64 seconds = 1;
*/
seconds: bigint;
seconds: string;
/**
* Non-negative fractions of a second at nanosecond resolution. Negative
* second values with fractions must still have non-negative nanos values
@@ -154,7 +154,7 @@ export interface Timestamp {
class Timestamp$Type extends MessageType<Timestamp> {
constructor() {
super("google.protobuf.Timestamp", [
{ no: 1, name: "seconds", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 1, name: "seconds", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 2, name: "nanos", kind: "scalar", T: 5 /*ScalarType.INT32*/ }
]);
}
@@ -164,7 +164,7 @@ class Timestamp$Type extends MessageType<Timestamp> {
now(): Timestamp {
const msg = this.create();
const ms = Date.now();
msg.seconds = PbLong.from(Math.floor(ms / 1000)).toBigInt();
msg.seconds = PbLong.from(Math.floor(ms / 1000)).toString();
msg.nanos = (ms % 1000) * 1000000;
return msg;
}
@@ -180,7 +180,7 @@ class Timestamp$Type extends MessageType<Timestamp> {
fromDate(date: Date): Timestamp {
const msg = this.create();
const ms = date.getTime();
msg.seconds = PbLong.from(Math.floor(ms / 1000)).toBigInt();
msg.seconds = PbLong.from(Math.floor(ms / 1000)).toString();
msg.nanos = (ms % 1000) * 1000000;
return msg;
}
@@ -223,14 +223,14 @@ class Timestamp$Type extends MessageType<Timestamp> {
throw new globalThis.Error("Unable to parse Timestamp from JSON. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.");
if (!target)
target = this.create();
target.seconds = PbLong.from(ms / 1000).toBigInt();
target.seconds = PbLong.from(ms / 1000).toString();
target.nanos = 0;
if (matches[7])
target.nanos = (parseInt("1" + matches[7] + "0".repeat(9 - matches[7].length)) - 1000000000);
return target;
}
create(value?: PartialMessage<Timestamp>): Timestamp {
const message = { seconds: 0n, nanos: 0 };
const message = { seconds: "0", nanos: 0 };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<Timestamp>(this, message, value);
@@ -242,7 +242,7 @@ class Timestamp$Type extends MessageType<Timestamp> {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* int64 seconds */ 1:
message.seconds = reader.int64().toBigInt();
message.seconds = reader.int64().toString();
break;
case /* int32 nanos */ 2:
message.nanos = reader.int32();
@@ -260,7 +260,7 @@ class Timestamp$Type extends MessageType<Timestamp> {
}
internalBinaryWrite(message: Timestamp, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* int64 seconds = 1; */
if (message.seconds !== 0n)
if (message.seconds !== "0")
writer.tag(1, WireType.Varint).int64(message.seconds);
/* int32 nanos = 2; */
if (message.nanos !== 0)
@@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.9.1 with parameter client_none,generate_dependencies
// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies
// @generated from protobuf file "google/protobuf/wrappers.proto" (package "google.protobuf", syntax proto3)
// tslint:disable
//
@@ -96,7 +96,7 @@ export interface Int64Value {
*
* @generated from protobuf field: int64 value = 1;
*/
value: bigint;
value: string;
}
/**
* Wrapper message for `uint64`.
@@ -111,7 +111,7 @@ export interface UInt64Value {
*
* @generated from protobuf field: uint64 value = 1;
*/
value: bigint;
value: string;
}
/**
* Wrapper message for `int32`.
@@ -316,7 +316,7 @@ export const FloatValue = new FloatValue$Type();
class Int64Value$Type extends MessageType<Int64Value> {
constructor() {
super("google.protobuf.Int64Value", [
{ no: 1, name: "value", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }
{ no: 1, name: "value", kind: "scalar", T: 3 /*ScalarType.INT64*/ }
]);
}
/**
@@ -331,11 +331,11 @@ class Int64Value$Type extends MessageType<Int64Value> {
internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: Int64Value): Int64Value {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ScalarType.INT64, LongType.BIGINT, "value") as any;
target.value = this.refJsonReader.scalar(json, ScalarType.INT64, LongType.STRING, "value") as any;
return target;
}
create(value?: PartialMessage<Int64Value>): Int64Value {
const message = { value: 0n };
const message = { value: "0" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<Int64Value>(this, message, value);
@@ -347,7 +347,7 @@ class Int64Value$Type extends MessageType<Int64Value> {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* int64 value */ 1:
message.value = reader.int64().toBigInt();
message.value = reader.int64().toString();
break;
default:
let u = options.readUnknownField;
@@ -362,7 +362,7 @@ class Int64Value$Type extends MessageType<Int64Value> {
}
internalBinaryWrite(message: Int64Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* int64 value = 1; */
if (message.value !== 0n)
if (message.value !== "0")
writer.tag(1, WireType.Varint).int64(message.value);
let u = options.writeUnknownFields;
if (u !== false)
@@ -378,7 +378,7 @@ export const Int64Value = new Int64Value$Type();
class UInt64Value$Type extends MessageType<UInt64Value> {
constructor() {
super("google.protobuf.UInt64Value", [
{ no: 1, name: "value", kind: "scalar", T: 4 /*ScalarType.UINT64*/, L: 0 /*LongType.BIGINT*/ }
{ no: 1, name: "value", kind: "scalar", T: 4 /*ScalarType.UINT64*/ }
]);
}
/**
@@ -393,11 +393,11 @@ class UInt64Value$Type extends MessageType<UInt64Value> {
internalJsonRead(json: JsonValue, options: JsonReadOptions, target?: UInt64Value): UInt64Value {
if (!target)
target = this.create();
target.value = this.refJsonReader.scalar(json, ScalarType.UINT64, LongType.BIGINT, "value") as any;
target.value = this.refJsonReader.scalar(json, ScalarType.UINT64, LongType.STRING, "value") as any;
return target;
}
create(value?: PartialMessage<UInt64Value>): UInt64Value {
const message = { value: 0n };
const message = { value: "0" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<UInt64Value>(this, message, value);
@@ -409,7 +409,7 @@ class UInt64Value$Type extends MessageType<UInt64Value> {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* uint64 value */ 1:
message.value = reader.uint64().toBigInt();
message.value = reader.uint64().toString();
break;
default:
let u = options.readUnknownField;
@@ -424,7 +424,7 @@ class UInt64Value$Type extends MessageType<UInt64Value> {
}
internalBinaryWrite(message: UInt64Value, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* uint64 value = 1; */
if (message.value !== 0n)
if (message.value !== "0")
writer.tag(1, WireType.Varint).uint64(message.value);
let u = options.writeUnknownFields;
if (u !== false)
+4 -4
View File
@@ -1,4 +1,4 @@
export * from "../generated/google/protobuf/timestamp";
export * from "../generated/google/protobuf/wrappers";
export * from "../generated/results/api/v1/artifact";
export * from "../generated/results/api/v1/artifact.twirp";
export * from './google/protobuf/timestamp'
export * from './google/protobuf/wrappers'
export * from './results/api/v1/artifact'
export * from './results/api/v1/artifact.twirp'
@@ -1,4 +1,4 @@
// @generated by protobuf-ts 2.9.1 with parameter client_none,generate_dependencies
// @generated by protobuf-ts 2.9.1 with parameter long_type_string,client_none,generate_dependencies
// @generated from protobuf file "results/api/v1/artifact.proto" (package "github.actions.results.api.v1", syntax proto3)
// tslint:disable
import { ServiceType } from "@protobuf-ts/runtime-rpc";
@@ -71,7 +71,7 @@ export interface FinalizeArtifactRequest {
/**
* @generated from protobuf field: int64 size = 4;
*/
size: bigint;
size: string;
/**
* @generated from protobuf field: google.protobuf.StringValue hash = 5;
*/
@@ -85,6 +85,10 @@ export interface FinalizeArtifactResponse {
* @generated from protobuf field: bool ok = 1;
*/
ok: boolean;
/**
* @generated from protobuf field: int64 artifact_id = 2;
*/
artifactId: string;
}
// @generated message type with reflection information, may provide speed optimized methods
class CreateArtifactRequest$Type extends MessageType<CreateArtifactRequest> {
@@ -222,12 +226,12 @@ class FinalizeArtifactRequest$Type extends MessageType<FinalizeArtifactRequest>
{ no: 1, name: "workflow_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 2, name: "workflow_job_run_backend_id", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 3, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
{ no: 4, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 5, name: "hash", kind: "message", T: () => StringValue }
]);
}
create(value?: PartialMessage<FinalizeArtifactRequest>): FinalizeArtifactRequest {
const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "", size: 0n };
const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "", size: "0" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<FinalizeArtifactRequest>(this, message, value);
@@ -248,7 +252,7 @@ class FinalizeArtifactRequest$Type extends MessageType<FinalizeArtifactRequest>
message.name = reader.string();
break;
case /* int64 size */ 4:
message.size = reader.int64().toBigInt();
message.size = reader.int64().toString();
break;
case /* google.protobuf.StringValue hash */ 5:
message.hash = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.hash);
@@ -275,7 +279,7 @@ class FinalizeArtifactRequest$Type extends MessageType<FinalizeArtifactRequest>
if (message.name !== "")
writer.tag(3, WireType.LengthDelimited).string(message.name);
/* int64 size = 4; */
if (message.size !== 0n)
if (message.size !== "0")
writer.tag(4, WireType.Varint).int64(message.size);
/* google.protobuf.StringValue hash = 5; */
if (message.hash)
@@ -294,11 +298,12 @@ export const FinalizeArtifactRequest = new FinalizeArtifactRequest$Type();
class FinalizeArtifactResponse$Type extends MessageType<FinalizeArtifactResponse> {
constructor() {
super("github.actions.results.api.v1.FinalizeArtifactResponse", [
{ no: 1, name: "ok", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }
{ no: 1, name: "ok", kind: "scalar", T: 8 /*ScalarType.BOOL*/ },
{ no: 2, name: "artifact_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ }
]);
}
create(value?: PartialMessage<FinalizeArtifactResponse>): FinalizeArtifactResponse {
const message = { ok: false };
const message = { ok: false, artifactId: "0" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<FinalizeArtifactResponse>(this, message, value);
@@ -312,6 +317,9 @@ class FinalizeArtifactResponse$Type extends MessageType<FinalizeArtifactResponse
case /* bool ok */ 1:
message.ok = reader.bool();
break;
case /* int64 artifact_id */ 2:
message.artifactId = reader.int64().toString();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
@@ -327,6 +335,9 @@ class FinalizeArtifactResponse$Type extends MessageType<FinalizeArtifactResponse
/* bool ok = 1; */
if (message.ok !== false)
writer.tag(1, WireType.Varint).bool(message.ok);
/* int64 artifact_id = 2; */
if (message.artifactId !== "0")
writer.tag(2, WireType.Varint).int64(message.artifactId);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
@@ -1,140 +0,0 @@
import { HttpCodes, HttpClient, HttpClientResponse } from '@actions/http-client'
import { BearerCredentialHandler } from '@actions/http-client/lib/auth'
import { info } from '@actions/core'
import { getRuntimeToken, getResultsServiceUrl, getRetryMultiplier, getInitialRetryIntervalInMilliseconds, getRetryLimit } from './config'
interface Rpc { request(
service: string,
method: string,
contentType: "application/json" | "application/protobuf",
data: object | Uint8Array
): Promise<object | Uint8Array>
}
export class ArtifactHttpClient implements Rpc {
private httpClient: HttpClient
private baseUrl: string
constructor(userAgent: string) {
const token = getRuntimeToken()
this.httpClient = new HttpClient(userAgent, [
new BearerCredentialHandler(token)
])
this.baseUrl = getResultsServiceUrl()
}
async request(service: string, method: string, contentType: "application/json" | "application/protobuf", data: object | Uint8Array): Promise<object | Uint8Array> {
let url = `${this.baseUrl}/twirp/${service}/${method}`
let headers = {
"Content-Type": contentType
}
const resp = await this.retry(
`${method}`,
this.httpClient.post(url, JSON.stringify(data), headers),
)
const body = await resp.readBody()
return JSON.parse(body)
}
async retry(name: string, operation: Promise<HttpClientResponse>): Promise<HttpClientResponse> {
let response: HttpClientResponse | undefined = undefined
let statusCode: number | undefined = undefined
let isRetryable = false
let errorMessage = ''
let attempt = 1
const maxAttempts = getRetryLimit()
while (attempt <= maxAttempts) {
try {
response = await operation
statusCode = response.message.statusCode
if (this.isSuccessStatusCode(statusCode)) {
return response
}
isRetryable = this.isRetryableStatusCode(statusCode)
errorMessage = `Artifact service responded with ${statusCode}`
} catch (error: any) {
isRetryable = true
errorMessage = error.message
}
if (!isRetryable) {
info(`${name} - Error is not retryable`)
if (response) {
this.displayHttpDiagnostics(response)
}
break
}
info(
`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`
)
await this.sleep(this.getExponentialRetryTimeInMilliseconds(attempt))
attempt++
}
if (response) {
this.displayHttpDiagnostics(response)
}
throw Error(`${name} failed: ${errorMessage}`)
}
isSuccessStatusCode(statusCode?: number): boolean {
if (!statusCode) {
return false
}
return statusCode >= 200 && statusCode < 300
}
isRetryableStatusCode(statusCode: number | undefined): boolean {
if (!statusCode) {
return false
}
const retryableStatusCodes = [
HttpCodes.BadGateway,
HttpCodes.GatewayTimeout,
HttpCodes.InternalServerError,
HttpCodes.ServiceUnavailable,
HttpCodes.TooManyRequests,
413 // Payload Too Large
]
return retryableStatusCodes.includes(statusCode)
}
displayHttpDiagnostics(response: HttpClientResponse): void {
info(
`##### Begin Diagnostic HTTP information #####
Status Code: ${response.message.statusCode}
Status Message: ${response.message.statusMessage}
Header Information: ${JSON.stringify(response.message.headers, undefined, 2)}
###### End Diagnostic HTTP information ######`
)
}
getExponentialRetryTimeInMilliseconds(
retryCount: number
): number {
if (retryCount < 0) {
throw new Error('RetryCount should not be negative')
} else if (retryCount === 0) {
return getInitialRetryIntervalInMilliseconds()
}
const minTime =
getInitialRetryIntervalInMilliseconds() * getRetryMultiplier() * retryCount
const maxTime = minTime * getRetryMultiplier()
// returns a random number between the minTime (inclusive) and the maxTime (exclusive)
return Math.trunc(Math.random() * (maxTime - minTime) + minTime)
}
async sleep(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
}
+225 -12
View File
@@ -1,16 +1,27 @@
import { UploadOptions } from './upload/upload-options'
import { UploadResponse } from './upload/upload-response'
import { uploadArtifact } from './upload/upload-artifact'
import {warning} from '@actions/core'
import {isGhes} from './shared/config'
import {
UploadOptions,
UploadResponse,
DownloadArtifactOptions,
GetArtifactResponse,
ListArtifactsResponse,
DownloadArtifactResponse
} from './shared/interfaces'
import {uploadArtifact} from './upload/upload-artifact'
import {downloadArtifact} from './download/download-artifact'
import {getArtifact} from './find/get-artifact'
import {listArtifacts} from './find/list-artifacts'
export interface ArtifactClient {
/**
* Uploads an artifact
*
* @param name the name of the artifact, required
* @param files a list of absolute or relative paths that denote what files should be uploaded
* @param rootDirectory an absolute or relative file path that denotes the root parent directory of the files being uploaded
* @param options extra options for customizing the upload behavior
* @returns single UploadInfo object
* @param name The name of the artifact, required
* @param files A list of absolute or relative paths that denote what files should be uploaded
* @param rootDirectory An absolute or relative file path that denotes the root parent directory of the files being uploaded
* @param options Extra options for customizing the upload behavior
* @returns single UploadResponse object
*/
uploadArtifact(
name: string,
@@ -19,7 +30,64 @@ export interface ArtifactClient {
options?: UploadOptions
): Promise<UploadResponse>
// TODO Download functionality
/**
* Lists all artifacts that are part of a workflow run.
*
* This calls the public List-Artifacts API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
* Due to paginated responses from the public API. This function will return at most 1000 artifacts per workflow run (100 per page * maximum 10 calls)
*
* @param workflowRunId The workflow run id that the artifact belongs to
* @param repositoryOwner The owner of the repository that the artifact belongs to
* @param repositoryName The name of the repository that the artifact belongs to
* @param token A token with the appropriate permission to the repository to list artifacts
* @returns ListArtifactResponse object
*/
listArtifacts(
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<ListArtifactsResponse>
/**
* Finds an artifact by name given a repository and workflow run id.
*
* This calls the public List-Artifacts API with a name filter https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
* @actions/artifact > 2.0.0 does not allow for creating multiple artifacts with the same name in the same workflow run.
* It is possible to have multiple artifacts with the same name in the same workflow run by using old versions of upload-artifact (v1,v2 and v3) or @actions/artifact < v2.0.0
* If there are multiple artifacts with the same name in the same workflow run this function will return the first artifact that matches the name.
*
* @param artifactName The name of the artifact to find
* @param workflowRunId The workflow run id that the artifact belongs to
* @param repositoryOwner The owner of the repository that the artifact belongs to
* @param repositoryName The name of the repository that the artifact belongs to
* @param token A token with the appropriate permission to the repository to find the artifact
*/
getArtifact(
artifactName: string,
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<GetArtifactResponse>
/**
* Downloads an artifact and unzips the content
*
* @param artifactId The name of the artifact to download
* @param repositoryOwner The owner of the repository that the artifact belongs to
* @param repositoryName The name of the repository that the artifact belongs to
* @param token A token with the appropriate permission to the repository to download the artifact
* @param options Extra options that allow for the customization of the download behavior
* @returns single DownloadArtifactResponse object
*/
downloadArtifact(
artifactId: number,
repositoryOwner: string,
repositoryName: string,
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse>
}
export class Client implements ArtifactClient {
@@ -31,7 +99,7 @@ export class Client implements ArtifactClient {
}
/**
* Uploads an artifact
* Upload Artifact
*/
async uploadArtifact(
name: string,
@@ -39,6 +107,151 @@ export class Client implements ArtifactClient {
rootDirectory: string,
options?: UploadOptions | undefined
): Promise<UploadResponse> {
return uploadArtifact(name, files, rootDirectory, options)
if (isGhes()) {
warning(
`@actions/artifact v2.0.0+ and upload-artifact@v4+ are not currently supported on GHES.`
)
return {
success: false
}
}
try {
return uploadArtifact(name, files, rootDirectory, options)
} catch (error) {
warning(
`Artifact upload failed with error: ${error}.
Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information.
If the error persists, please check whether Actions is operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
return {
success: false
}
}
}
}
/**
* Download Artifact
*/
async downloadArtifact(
artifactId: number,
repositoryOwner: string,
repositoryName: string,
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse> {
if (isGhes()) {
warning(
`@actions/artifact v2.0.0+ and download-artifact@v4+ are not currently supported on GHES.`
)
return {
success: false
}
}
try {
return downloadArtifact(
artifactId,
repositoryOwner,
repositoryName,
token,
options
)
} catch (error) {
warning(
`Artifact download failed with error: ${error}.
Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information.
If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
return {
success: false
}
}
}
/**
* List Artifacts
*/
async listArtifacts(
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<ListArtifactsResponse> {
if (isGhes()) {
warning(
`@actions/artifact v2.0.0+ and download-artifact@v4+ are not currently supported on GHES.`
)
return {
artifacts: []
}
}
try {
return listArtifacts(
workflowRunId,
repositoryOwner,
repositoryName,
token
)
} catch (error: unknown) {
warning(
`Listing Artifacts failed with error: ${error}.
Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information.
If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
return {
artifacts: []
}
}
}
/**
* Get Artifact
*/
async getArtifact(
artifactName: string,
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<GetArtifactResponse> {
if (isGhes()) {
warning(
`@actions/artifact v2.0.0+ and download-artifact@v4+ are not currently supported on GHES.`
)
return {
success: false
}
}
try {
return getArtifact(
artifactName,
workflowRunId,
repositoryOwner,
repositoryName,
token
)
} catch (error: unknown) {
warning(
`Fetching Artifact failed with error: ${error}.
Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information.
If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
return {
success: false
}
}
}
}
-50
View File
@@ -1,50 +0,0 @@
export function getRuntimeToken(): string {
const token = process.env['ACTIONS_RUNTIME_TOKEN']
if (!token) {
throw new Error('Unable to get the ACTIONS_RUNTIME_TOKEN environment variable which is required')
}
return token
}
export function getResultsServiceUrl(): string {
const resultsUrl = process.env['ACTIONS_RESULTS_URL']
if (!resultsUrl) {
throw new Error('Unable to get the ACTIONS_RESULTS_URL environment variable which is required')
}
return resultsUrl
}
export function getWorkFlowRunId(): string {
const workFlowRunId = process.env['GITHUB_RUN_ID']
if (!workFlowRunId) {
throw new Error('Unable to get the GITHUB_RUN_ID environment variable which is required')
}
return workFlowRunId
}
export function getWorkSpaceDirectory(): string {
const workspaceDirectory = process.env['GITHUB_WORKSPACE']
if (!workspaceDirectory) {
throw new Error('Unable to get the GITHUB_WORKSPACE environment variable which is required')
}
return workspaceDirectory
}
export function getRetentionDays(): string | undefined {
return process.env['GITHUB_RETENTION_DAYS']
}
export function getInitialRetryIntervalInMilliseconds(): number {
return 3000
}
// With exponential backoff, the larger the retry count, the larger the wait time before another attempt
// The retry multiplier controls by how much the backOff time increases depending on the number of retries
export function getRetryMultiplier(): number {
return 1.5
}
// The maximum number of retries that can be attempted before an upload or download fails
export function getRetryLimit(): number {
return 5
}
@@ -0,0 +1,101 @@
import fs from 'fs/promises'
import * as github from '@actions/github'
import * as core from '@actions/core'
import * as httpClient from '@actions/http-client'
import unzipper from 'unzipper'
import {
DownloadArtifactOptions,
DownloadArtifactResponse
} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getGitHubWorkspaceDir} from '../shared/config'
const scrubQueryParameters = (url: string): string => {
const parsed = new URL(url)
parsed.search = ''
return parsed.toString()
}
async function exists(path: string): Promise<boolean> {
try {
await fs.access(path)
return true
} catch (error) {
if (error.code === 'ENOENT') {
return false
} else {
throw error
}
}
}
async function streamExtract(url: string, directory: string): Promise<void> {
const client = new httpClient.HttpClient(getUserAgentString())
const response = await client.get(url)
if (response.message.statusCode !== 200) {
throw new Error(
`Unexpected HTTP response from blob storage: ${response.message.statusCode} ${response.message.statusMessage}`
)
}
return response.message.pipe(unzipper.Extract({path: directory})).promise()
}
export async function downloadArtifact(
artifactId: number,
repositoryOwner: string,
repositoryName: string,
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse> {
const downloadPath = options?.path || getGitHubWorkspaceDir()
if (!(await exists(downloadPath))) {
core.debug(
`Artifact destination folder does not exist, creating: ${downloadPath}`
)
await fs.mkdir(downloadPath, {recursive: true})
} else {
core.debug(`Artifact destination folder already exists: ${downloadPath}`)
}
const api = github.getOctokit(token)
core.info(
`Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'`
)
const {headers, status} = await api.rest.actions.downloadArtifact({
owner: repositoryOwner,
repo: repositoryName,
artifact_id: artifactId,
archive_format: 'zip',
request: {
redirect: 'manual'
}
})
if (status !== 302) {
throw new Error(`Unable to download artifact. Unexpected status: ${status}`)
}
const {location} = headers
if (!location) {
throw new Error(`Unable to redirect to artifact download url`)
}
core.info(
`Redirecting to blob download url: ${scrubQueryParameters(location)}`
)
try {
core.info(`Starting download of artifact to: ${downloadPath}`)
await streamExtract(location, downloadPath)
core.info(`Artifact download completed successfully.`)
} catch (error) {
throw new Error(`Unable to download and extract artifact: ${error.message}`)
}
return {success: true, downloadPath}
}
@@ -0,0 +1,69 @@
import {GetArtifactResponse} from '../shared/interfaces'
import {getOctokit} from '@actions/github'
import {getUserAgentString} from '../shared/user-agent'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import {getRetryOptions} from './retry-options'
import {requestLog} from '@octokit/plugin-request-log'
import {retry} from '@octokit/plugin-retry'
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
export async function getArtifact(
artifactName: string,
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<GetArtifactResponse> {
const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions)
const opts: OctokitOptions = {
log: undefined,
userAgent: getUserAgentString(),
previews: undefined,
retry: retryOpts,
request: requestOpts
}
const github = getOctokit(token, opts, retry, requestLog)
const getArtifactResp = await github.request(
'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts{?name}',
{
owner: repositoryOwner,
repo: repositoryName,
run_id: workflowRunId,
name: artifactName
}
)
if (getArtifactResp.status !== 200) {
core.warning(`non-200 response from GitHub API: ${getArtifactResp.status}`)
return {
success: false
}
}
if (getArtifactResp.data.artifacts.length === 0) {
core.warning('no artifacts found')
return {
success: false
}
}
if (getArtifactResp.data.artifacts.length > 1) {
core.warning(
'more than one artifact found for a single name, returning first'
)
}
return {
success: true,
artifact: {
name: getArtifactResp.data.artifacts[0].name,
id: getArtifactResp.data.artifacts[0].id,
url: getArtifactResp.data.artifacts[0].url,
size: getArtifactResp.data.artifacts[0].size_in_bytes
}
}
}
@@ -0,0 +1,103 @@
import {info, warning, debug} from '@actions/core'
import {getOctokit} from '@actions/github'
import {ListArtifactsResponse, Artifact} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getRetryOptions} from './retry-options'
import {defaults as defaultGitHubOptions} from '@actions/github/lib/utils'
import {requestLog} from '@octokit/plugin-request-log'
import {retry} from '@octokit/plugin-retry'
import {OctokitOptions} from '@octokit/core/dist-types/types'
// Limiting to 1000 for perf reasons
const maximumArtifactCount = 1000
const paginationCount = 100
const maxNumberOfPages = maximumArtifactCount / paginationCount
export async function listArtifacts(
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<ListArtifactsResponse> {
info(
`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`
)
const artifacts: Artifact[] = []
const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions)
const opts: OctokitOptions = {
log: undefined,
userAgent: getUserAgentString(),
previews: undefined,
retry: retryOpts,
request: requestOpts
}
const github = getOctokit(token, opts, retry, requestLog)
let currentPageNumber = 1
const {data: listArtifactResponse} =
await github.rest.actions.listWorkflowRunArtifacts({
owner: repositoryOwner,
repo: repositoryName,
run_id: workflowRunId,
per_page: paginationCount,
page: currentPageNumber
})
let numberOfPages = Math.ceil(
listArtifactResponse.total_count / paginationCount
)
const totalArtifactCount = listArtifactResponse.total_count
if (totalArtifactCount > maximumArtifactCount) {
warning(
`Workflow run ${workflowRunId} has more than 1000 artifacts. Results will be incomplete as only the first ${maximumArtifactCount} artifacts will be returned`
)
numberOfPages = maxNumberOfPages
}
// Iterate over the first page
for (const artifact of listArtifactResponse.artifacts) {
artifacts.push({
name: artifact.name,
id: artifact.id,
url: artifact.url,
size: artifact.size_in_bytes
})
}
// Iterate over any remaining pages
for (
currentPageNumber;
currentPageNumber < numberOfPages;
currentPageNumber++
) {
currentPageNumber++
debug(`Fetching page ${currentPageNumber} of artifact list`)
const {data: listArtifactResponse} =
await github.rest.actions.listWorkflowRunArtifacts({
owner: repositoryOwner,
repo: repositoryName,
run_id: workflowRunId,
per_page: paginationCount,
page: currentPageNumber
})
for (const artifact of listArtifactResponse.artifacts) {
artifacts.push({
name: artifact.name,
id: artifact.id,
url: artifact.url,
size: artifact.size_in_bytes
})
}
}
info(`Finished fetching artifact list`)
return {
artifacts
}
}
@@ -0,0 +1,48 @@
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import {RequestRequestOptions} from '@octokit/types'
export type RetryOptions = {
doNotRetry?: number[]
enabled?: boolean
}
// Defaults for fetching artifacts
const defaultMaxRetryNumber = 5
const defaultExemptStatusCodes = [400, 401, 403, 404, 422] // https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14
export function getRetryOptions(
defaultOptions: OctokitOptions,
retries: number = defaultMaxRetryNumber,
exemptStatusCodes: number[] = defaultExemptStatusCodes
): [RetryOptions, RequestRequestOptions | undefined] {
if (retries <= 0) {
return [{enabled: false}, defaultOptions.request]
}
const retryOptions: RetryOptions = {
enabled: true
}
if (exemptStatusCodes.length > 0) {
retryOptions.doNotRetry = exemptStatusCodes
}
// The GitHub type has some defaults for `options.request`
// see: https://github.com/actions/toolkit/blob/4fbc5c941a57249b19562015edbd72add14be93d/packages/github/src/utils.ts#L15
// We pass these in here so they are not overridden.
const requestOptions: RequestRequestOptions = {
...defaultOptions.request,
retries
}
core.debug(
`GitHub client configured with: (retries: ${
requestOptions.retries
}, retry-exempt-status-code: ${
retryOptions.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]'
})`
)
return [retryOptions, requestOptions]
}
@@ -0,0 +1,173 @@
import {HttpClient, HttpClientResponse, HttpCodes} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {info, debug} from '@actions/core'
import {ArtifactServiceClientJSON} from '../../generated'
import {getResultsServiceUrl, getRuntimeToken} from './config'
// The twirp http client must implement this interface
interface Rpc {
request(
service: string,
method: string,
contentType: 'application/json' | 'application/protobuf',
data: object | Uint8Array
): Promise<object | Uint8Array>
}
class ArtifactHttpClient implements Rpc {
private httpClient: HttpClient
private baseUrl: string
private maxAttempts = 5
private baseRetryIntervalMilliseconds = 3000
private retryMultiplier = 1.5
constructor(
userAgent: string,
maxAttempts?: number,
baseRetryIntervalMilliseconds?: number,
retryMultiplier?: number
) {
const token = getRuntimeToken()
this.baseUrl = getResultsServiceUrl()
if (maxAttempts) {
this.maxAttempts = maxAttempts
}
if (baseRetryIntervalMilliseconds) {
this.baseRetryIntervalMilliseconds = baseRetryIntervalMilliseconds
}
if (retryMultiplier) {
this.retryMultiplier = retryMultiplier
}
this.httpClient = new HttpClient(userAgent, [
new BearerCredentialHandler(token)
])
}
// This function satisfies the Rpc interface. It is compatible with the JSON
// JSON generated client.
async request(
service: string,
method: string,
contentType: 'application/json' | 'application/protobuf',
data: object | Uint8Array
): Promise<object | Uint8Array> {
const url = `${this.baseUrl}/twirp/${service}/${method}`
debug(`Requesting ${url}`)
const headers = {
'Content-Type': contentType
}
try {
const response = await this.retryableRequest(async () =>
this.httpClient.post(url, JSON.stringify(data), headers)
)
const body = await response.readBody()
return JSON.parse(body)
} catch (error) {
throw new Error(`Failed to ${method}: ${error.message}`)
}
}
async retryableRequest(
operation: () => Promise<HttpClientResponse>
): Promise<HttpClientResponse> {
let attempt = 0
let errorMessage = ''
while (attempt < this.maxAttempts) {
let isRetryable = false
try {
const response = await operation()
const statusCode = response.message.statusCode
if (this.isSuccessStatusCode(statusCode)) {
return response
}
isRetryable = this.isRetryableHttpStatusCode(statusCode)
errorMessage = `Failed request: (${statusCode}) ${response.message.statusMessage}`
} catch (error) {
isRetryable = true
errorMessage = error.message
}
if (!isRetryable) {
throw new Error(`Received non-retryable error: ${errorMessage}`)
}
if (attempt + 1 === this.maxAttempts) {
throw new Error(
`Failed to make request after ${this.maxAttempts} attempts: ${errorMessage}`
)
}
const retryTimeMilliseconds =
this.getExponentialRetryTimeMilliseconds(attempt)
info(
`Attempt ${attempt + 1} of ${
this.maxAttempts
} failed with error: ${errorMessage}. Retrying request in ${retryTimeMilliseconds} ms...`
)
await this.sleep(retryTimeMilliseconds)
attempt++
}
throw new Error(`Request failed`)
}
isSuccessStatusCode(statusCode?: number): boolean {
if (!statusCode) return false
return statusCode >= 200 && statusCode < 300
}
isRetryableHttpStatusCode(statusCode?: number): boolean {
if (!statusCode) return false
const retryableStatusCodes = [
HttpCodes.BadGateway,
HttpCodes.GatewayTimeout,
HttpCodes.InternalServerError,
HttpCodes.ServiceUnavailable,
HttpCodes.TooManyRequests,
413 // Payload Too Large
]
return retryableStatusCodes.includes(statusCode)
}
async sleep(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
getExponentialRetryTimeMilliseconds(attempt: number): number {
if (attempt < 0) {
throw new Error('attempt should be a positive integer')
}
if (attempt === 0) {
return this.baseRetryIntervalMilliseconds
}
const minTime =
this.baseRetryIntervalMilliseconds * this.retryMultiplier ** attempt
const maxTime = minTime * this.retryMultiplier
// returns a random number between minTime and maxTime (exclusive)
return Math.trunc(Math.random() * (maxTime - minTime) + minTime)
}
}
export function createArtifactTwirpClient(
type: 'upload' | 'download',
maxAttempts?: number,
baseRetryIntervalMilliseconds?: number,
retryMultiplier?: number
): ArtifactServiceClientJSON {
const client = new ArtifactHttpClient(
`@actions/artifact-${type}`,
maxAttempts,
baseRetryIntervalMilliseconds,
retryMultiplier
)
return new ArtifactServiceClientJSON(client)
}
@@ -0,0 +1,36 @@
// Used for controlling the highWaterMark value of the zip that is being streamed
// The same value is used as the chunk size that is use during upload to blob storage
export function getUploadChunkSize(): number {
return 8 * 1024 * 1024 // 8 MB Chunks
}
export function getRuntimeToken(): string {
const token = process.env['ACTIONS_RUNTIME_TOKEN']
if (!token) {
throw new Error('Unable to get the ACTIONS_RUNTIME_TOKEN env variable')
}
return token
}
export function getResultsServiceUrl(): string {
const resultsUrl = process.env['ACTIONS_RESULTS_URL']
if (!resultsUrl) {
throw new Error('Unable to get the ACTIONS_RESULTS_URL env variable')
}
return resultsUrl
}
export function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}
export function getGitHubWorkspaceDir(): string {
const ghWorkspaceDir = process.env['GITHUB_WORKSPACE']
if (!ghWorkspaceDir) {
throw new Error('Unable to get the GITHUB_WORKSPACE env variable')
}
return ghWorkspaceDir
}
@@ -0,0 +1,121 @@
/*****************************************************************************
* *
* UploadArtifact *
* *
*****************************************************************************/
export interface UploadResponse {
/**
* Denotes if an artifact was successfully uploaded
*/
success: boolean
/**
* Total size of the artifact in bytes. Not provided if no artifact was uploaded
*/
size?: number
/**
* The id of the artifact that was created. Not provided if no artifact was uploaded
* This ID can be used as input to other APIs to download, delete or get more information about an artifact: https://docs.github.com/en/rest/actions/artifacts
*/
id?: number
}
export interface UploadOptions {
/**
* Duration after which artifact will expire in days.
*
* By default artifact expires after 90 days:
* https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete
*
* Use this option to override the default expiry.
*
* Min value: 1
* Max value: 90 unless changed by repository setting
*
* If this is set to a greater value than the retention settings allowed, the retention on artifacts
* will be reduced to match the max value allowed on server, and the upload process will continue. An
* input of 0 assumes default retention setting.
*/
retentionDays?: number
}
/*****************************************************************************
* *
* GetArtifact *
* *
*****************************************************************************/
export interface GetArtifactResponse {
/**
* If an artifact was found
*/
success: boolean
/**
* Metadata about the artifact that was found
*/
artifact?: Artifact
}
/*****************************************************************************
* *
* ListArtifact *
* *
*****************************************************************************/
export interface ListArtifactsResponse {
/**
* A list of artifacts that were found
*/
artifacts: Artifact[]
}
/*****************************************************************************
* *
* DownloadArtifact *
* *
*****************************************************************************/
export interface DownloadArtifactResponse {
/**
* If the artifact download was successful
*/
success: boolean
/**
* The path where the artifact was downloaded to
*/
downloadPath?: string
}
export interface DownloadArtifactOptions {
/**
* Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE
*/
path?: string
}
/*****************************************************************************
* *
* Shared *
* *
*****************************************************************************/
export interface Artifact {
/**
* The name of the artifact
*/
name: string
/**
* The ID of the artifact
*/
id: number
/**
* The URL of the artifact
*/
url: string
/**
* The size of the artifact in bytes
*/
size: number
}
@@ -0,0 +1,9 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const packageJson = require('../../../package.json')
/**
* Ensure that this User Agent String is used in all HTTP calls so that we can monitor telemetry between different versions of this package
*/
export function getUserAgentString(): string {
return `@actions/artifact-${packageJson.version}`
}
@@ -0,0 +1,65 @@
import {getRuntimeToken} from './config'
import jwt_decode from 'jwt-decode'
export interface BackendIds {
workflowRunBackendId: string
workflowJobRunBackendId: string
}
interface ActionsToken {
scp: string
}
const InvalidJwtError = new Error(
'Failed to get backend IDs: The provided JWT token is invalid'
)
// uses the JWT token claims to get the
// workflow run and workflow job run backend ids
export function getBackendIdsFromToken(): BackendIds {
const token = getRuntimeToken()
const decoded = jwt_decode<ActionsToken>(token)
if (!decoded.scp) {
throw InvalidJwtError
}
/*
* example decoded:
* {
* scp: "Actions.ExampleScope Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774"
* }
*/
const scpParts = decoded.scp.split(' ')
if (scpParts.length === 0) {
throw InvalidJwtError
}
/*
* example scpParts:
* ["Actions.ExampleScope", "Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774"]
*/
for (const scopes of scpParts) {
const scopeParts = scopes.split(':')
/*
* example scopeParts:
* ["Actions.Results", "ce7f54c7-61c7-4aae-887f-30da475f5f1a", "ca395085-040a-526b-2ce8-bdc85f692774"]
*/
if (scopeParts.length !== 3) {
// not the Actions.Results scope
continue
}
if (scopeParts[0] !== 'Actions.Results') {
// not the Actions.Results scope
continue
}
return {
workflowRunBackendId: scopeParts[1],
workflowJobRunBackendId: scopeParts[2]
}
}
throw InvalidJwtError
}
@@ -0,0 +1,96 @@
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import {TransferProgressEvent} from '@azure/core-http'
import {ZipUploadStream} from './zip'
import {getUploadChunkSize} from '../shared/config'
import * as core from '@actions/core'
import * as crypto from 'crypto'
import * as stream from 'stream'
export interface BlobUploadResponse {
/**
* If the upload was successful or not
*/
isSuccess: boolean
/**
* The total reported upload size in bytes. Empty if the upload failed
*/
uploadSize?: number
/**
* The MD5 hash of the uploaded file. Empty if the upload failed
*/
md5Hash?: string
}
export async function uploadZipToBlobStorage(
authenticatedUploadURL: string,
zipUploadStream: ZipUploadStream
): Promise<BlobUploadResponse> {
let uploadByteCount = 0
const maxBuffers = 5
const bufferSize = getUploadChunkSize()
const blobClient = new BlobClient(authenticatedUploadURL)
const blockBlobClient = blobClient.getBlockBlobClient()
core.debug(
`Uploading artifact zip to blob storage with maxBuffers: ${maxBuffers}, bufferSize: ${bufferSize}`
)
const uploadCallback = (progress: TransferProgressEvent): void => {
core.info(`Uploaded bytes ${progress.loadedBytes}`)
uploadByteCount = progress.loadedBytes
}
const options: BlockBlobUploadStreamOptions = {
blobHTTPHeaders: {blobContentType: 'zip'},
onProgress: uploadCallback
}
let md5Hash: string | undefined = undefined
const uploadStream = new stream.PassThrough()
const hashStream = crypto.createHash('md5')
zipUploadStream.pipe(uploadStream) // This stream is used for the upload
zipUploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the zip content that gets used. Integrity check
try {
core.info('Beginning upload of artifact content to blob storage')
await blockBlobClient.uploadStream(
uploadStream,
bufferSize,
maxBuffers,
options
)
core.info('Finished uploading artifact content to blob storage!')
hashStream.end()
md5Hash = hashStream.read() as string
core.info(`MD5 hash of uploaded artifact zip is ${md5Hash}`)
} catch (error) {
core.warning(
`Failed to upload artifact zip to blob storage, error: ${error}`
)
return {
isSuccess: false
}
}
if (uploadByteCount === 0) {
core.warning(
`No data was uploaded to blob storage. Reported upload byte count is 0`
)
return {
isSuccess: false
}
}
return {
isSuccess: true,
uploadSize: uploadByteCount,
md5Hash
}
}
@@ -27,11 +27,11 @@ const invalidArtifactNameCharacters = new Map<string, string>([
])
/**
* Scans the name of the artifact to make sure there are no illegal characters
* Validates the name of the artifact to check to make sure there are no illegal characters
*/
export function checkArtifactName(name: string): void {
export function validateArtifactName(name: string): void {
if (!name) {
throw new Error(`Artifact name: ${name}, is incorrectly provided`)
throw new Error(`Provided artifact name input during validation is empty`)
}
for (const [
@@ -40,7 +40,7 @@ export function checkArtifactName(name: string): void {
] of invalidArtifactNameCharacters) {
if (name.includes(invalidCharacterKey)) {
throw new Error(
`Artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter}
`The artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter}
Invalid characters include: ${Array.from(
invalidArtifactNameCharacters.values()
@@ -55,11 +55,11 @@ These characters are not allowed in the artifact name due to limitations with ce
}
/**
* Scans the name of the filePath used to make sure there are no illegal characters
* Validates file paths to check for any illegal characters that can cause problems on different file systems
*/
export function checkArtifactFilePath(path: string): void {
export function validateFilePath(path: string): void {
if (!path) {
throw new Error(`Artifact path: ${path}, is incorrectly provided`)
throw new Error(`Provided file path input during validation is empty`)
}
for (const [
@@ -68,7 +68,7 @@ export function checkArtifactFilePath(path: string): void {
] of invalidArtifactFilePathCharacters) {
if (path.includes(invalidCharacterKey)) {
throw new Error(
`Artifact path is not valid: ${path}. Contains the following character: ${errorMessageForCharacter}
`The path for one of the files in artifact is not valid: ${path}. Contains the following character: ${errorMessageForCharacter}
Invalid characters include: ${Array.from(
invalidArtifactFilePathCharacters.values()
@@ -79,4 +79,4 @@ The following characters are not allowed in files that are uploaded due to limit
)
}
}
}
}
@@ -0,0 +1,34 @@
import {Timestamp} from '../../generated'
import * as core from '@actions/core'
export function getExpiration(retentionDays?: number): Timestamp | undefined {
if (!retentionDays) {
return undefined
}
const maxRetentionDays = getRetentionDays()
if (maxRetentionDays && maxRetentionDays < retentionDays) {
core.warning(
`Retention days cannot be greater than the maximum allowed retention set within the repository. Using ${maxRetentionDays} instead.`
)
retentionDays = maxRetentionDays
}
const expirationDate = new Date()
expirationDate.setDate(expirationDate.getDate() + retentionDays)
return Timestamp.fromDate(expirationDate)
}
function getRetentionDays(): number | undefined {
const retentionDays = process.env['GITHUB_RETENTION_DAYS']
if (!retentionDays) {
return undefined
}
const days = parseInt(retentionDays)
if (isNaN(days)) {
return undefined
}
return days
}
@@ -1 +0,0 @@
hello there! This is from a.txt
@@ -1 +0,0 @@
This is from b.txt
@@ -1,123 +0,0 @@
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import { TransferProgressEvent } from '@azure/core-http';
import * as a from 'archiver'
import * as fs from 'fs'
import * as stream from 'stream'
const bufferSize = 1024 * 1024 * 8 // 8 MB
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class ZipUploadStream extends stream.Transform {
constructor(bufferSize: number) {
super({
highWaterMark: bufferSize
})
}
_transform(chunk:any, enc:any, cb:any) {
cb(null, chunk)
}
}
// for local testing, run this using ts-node testing.ts
export async function test(){
let sasURL = "paste here"
sasURL = sasURL.replace("http://devstoreaccount1.blob.codedev.localhost", "http://127.0.0.1:11000/devstoreaccount1")
const blobClient = new BlobClient(sasURL);
const zip = a.create('zip', {
zlib: { level: 9 } // Sets the compression level.
// Available options are 0-9
// 0 => no compression
// 1 => fastest with low compression
// 9 => highest compression ratio but the slowest
});
// append files that are going to be part of the final zip
zip.append('this is file 1', { name: 'file1.txt' });
zip.append('this is file 2', { name: 'file2.txt' });
zip.append('this is file 1 in a directory', { name: 'dir/file1.txt' });
zip.append('this is file 2 in a directory', { name: 'dir/file2.txt' });
zip.append('this is a live demo!!!', { name: 'dir/alive.txt' });
zip.append(fs.createReadStream('a.txt'), { name: 'dir2/a.txt' })
zip.append(fs.createReadStream('b.txt'), { name: 'dir2/b.txt' })
const zipUploadStream = new ZipUploadStream(bufferSize)
zip.pipe(zipUploadStream)
zip.finalize();
console.log("Write high watermark value " + zipUploadStream.writableHighWaterMark)
console.log("Read high watermark value " + zipUploadStream.readableHighWaterMark)
// good practice to catch warnings (ie stat failures and other non-blocking errors)
zip.on('warning', function(err) {
if (err.code === 'ENOENT') {
console.log("zip error ENOENT")
} else {
console.log("some other warning ")
console.log(err)
}
});
// good practice to catch this error explicitly
zip.on('error', function(err) {
console.log("some error with zip ")
console.log(err)
});
zip.on("progress", function(progress: a.ProgressData) {
console.log(progress)
/* This outputs data like this, we could potentially do something with this for even more logging to show the status of the zip creation
{
entries: { total: 7, processed: 1 },
fs: { totalBytes: 0, processedBytes: 0 }
}
{
entries: { total: 7, processed: 2 },
fs: { totalBytes: 0, processedBytes: 0 }
}
*/
})
// We can add these to debug logging
zip.on('end', function() {
console.log("zip ending")
});
zip.on('finish', function() {
console.log("zip finished")
});
// Upload options
const maxBuffers = 5
const blockBlobClient = blobClient.getBlockBlobClient()
let uploadByteCount = 0
var myCallback = function(progress: TransferProgressEvent) {
console.log("Byte upload count " + progress.loadedBytes)
uploadByteCount = progress.loadedBytes
};
const options: BlockBlobUploadStreamOptions = {
blobHTTPHeaders: { "blobContentType": "zip" },
onProgress: myCallback
}
// Upload!
try {
const aa = await blockBlobClient.uploadStream(
zipUploadStream,
bufferSize,
maxBuffers,
options
);
} catch (error){
console.log(error)
}
console.log("final upload size in bytes is " + uploadByteCount)
}
test()
@@ -1,32 +0,0 @@
import { ArtifactHttpClient } from '../../artifact-http-client'
import { ArtifactServiceClientJSON } from '../../../generated/results/api/v1/artifact.twirp'
export async function twirpTest(){
const artifactClient = new ArtifactHttpClient('@actions/artifact-upload')
const jsonClient = new ArtifactServiceClientJSON(artifactClient)
try {
const createResp = await jsonClient.CreateArtifact({workflowRunBackendId: "ce7f54c7-61c7-4aae-887f-30da475f5f1a", workflowJobRunBackendId: "ca395085-040a-526b-2ce8-bdc85f692774", name: Math.random().toString(), version: 4})
if (!createResp.ok) {
console.log("CreateArtifact failed")
return
}
console.log(createResp.signedUploadUrl)
const finalizeResp = await jsonClient.FinalizeArtifact({workflowRunBackendId: "ce7f54c7-61c7-4aae-887f-30da475f5f1a", workflowJobRunBackendId: "ca395085-040a-526b-2ce8-bdc85f692774", name: Math.random().toString(), size: BigInt(5)})
if (!finalizeResp.ok) {
console.log("FinalizeArtifact failed")
return
}
} catch (e) {
console.log(e)
return
}
console.log("FinalizeArtifact succeeded")
}
twirpTest()
@@ -1,190 +1,129 @@
import * as core from '@actions/core'
import {checkArtifactName} from './path-and-artifact-name-validation'
import {UploadOptions} from './upload-options'
import {UploadResponse} from './upload-response'
import { UploadSpecification, getUploadSpecification } from './upload-specification'
import { ArtifactHttpClient } from '../artifact-http-client'
import { ArtifactServiceClientJSON } from '../../generated/results/api/v1/artifact.twirp'
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import { TransferProgressEvent } from '@azure/core-http';
import * as a from 'archiver'
import * as fs from 'fs'
import * as stream from 'stream'
import {getBackendIds, BackendIds} from '../util'
const bufferSize = 1024 * 1024 * 8 // 8 MB
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class ZipUploadStream extends stream.Transform {
constructor(bufferSize: number) {
super({
highWaterMark: bufferSize
})
}
_transform(chunk:any, enc:any, cb:any) {
cb(null, chunk)
}
}
import {UploadOptions, UploadResponse} from '../shared/interfaces'
import {getExpiration} from './retention'
import {validateArtifactName} from './path-and-artifact-name-validation'
import {createArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {
UploadZipSpecification,
getUploadZipSpecification,
validateRootDirectory
} from './upload-zip-specification'
import {getBackendIdsFromToken} from '../shared/util'
import {uploadZipToBlobStorage} from './blob-upload'
import {createZipUploadStream} from './zip'
import {
CreateArtifactRequest,
FinalizeArtifactRequest,
StringValue
} from '../../generated'
export async function uploadArtifact(
name: string,
files: string[],
rootDirectory: string,
options?: UploadOptions | undefined
): Promise<UploadResponse> {
name: string,
files: string[],
rootDirectory: string,
options?: UploadOptions | undefined
): Promise<UploadResponse> {
validateArtifactName(name)
validateRootDirectory(rootDirectory)
let uploadByteCount = 0
const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification(
files,
rootDirectory
)
if (zipSpecification.length === 0) {
core.warning(`No files were found to upload`)
return {
success: false
}
}
// Need to keep checking the artifact name
checkArtifactName(name)
const zipUploadStream = await createZipUploadStream(zipSpecification)
// Get specification for the files being uploaded
const uploadSpecification: UploadSpecification[] = getUploadSpecification(
name,
rootDirectory,
files
// get the IDs needed for the artifact creation
const backendIds = getBackendIdsFromToken()
if (!backendIds.workflowRunBackendId || !backendIds.workflowJobRunBackendId) {
core.warning(
`Failed to get the necessary backend ids which are required to create the artifact`
)
if (uploadSpecification.length === 0) {
core.warning(`No files found that can be uploaded`)
} else {
const artifactClient = new ArtifactHttpClient('@actions/artifact-upload')
const jsonClient = new ArtifactServiceClientJSON(artifactClient)
const backendIDs: BackendIds = getBackendIds()
console.log("workflow Run Backend ID " + backendIDs.workflowRunBackendId)
console.log("workflow Job Run Backend ID " + backendIDs.workflowJobRunBackendId)
console.log("hello Rob!!")
try {
const createResp = await jsonClient.CreateArtifact({workflowRunBackendId: backendIDs.workflowRunBackendId, workflowJobRunBackendId: backendIDs.workflowJobRunBackendId, name: name, version: 4})
if (!createResp.ok) {
core.error("CreateArtifact failed")
}
console.log(createResp.signedUploadUrl)
// Blob upload start
const blobClient = new BlobClient(createResp.signedUploadUrl);
const zip = a.create('zip', {
zlib: { level: 9 } // Sets the compression level.
// Available options are 0-9
// 0 => no compression
// 1 => fastest with low compression
// 9 => highest compression ratio but the slowest
});
console.log("file specification")
for (const file of uploadSpecification) {
console.log("uploadPath:" + file.uploadFilePath + " absolute:" + file.absoluteFilePath)
zip.append(fs.createReadStream(file.absoluteFilePath), {name: file.uploadFilePath})
}
const zipUploadStream = new ZipUploadStream(bufferSize)
zip.pipe(zipUploadStream)
zip.finalize();
console.log("Write high watermark value " + zipUploadStream.writableHighWaterMark)
console.log("Read high watermark value " + zipUploadStream.readableHighWaterMark)
// good practice to catch warnings (ie stat failures and other non-blocking errors)
zip.on('warning', function(err) {
if (err.code === 'ENOENT') {
console.log("zip error ENOENT")
} else {
console.log("some other warning ")
console.log(err)
}
});
// good practice to catch this error explicitly
zip.on('error', function(err) {
console.log("some error with zip ")
console.log(err)
});
zip.on("progress", function(progress: a.ProgressData) {
console.log(progress)
/* This outputs data like this, we could potentially do something with this for even more logging to show the status of the zip creation
{
entries: { total: 7, processed: 1 },
fs: { totalBytes: 0, processedBytes: 0 }
}
{
entries: { total: 7, processed: 2 },
fs: { totalBytes: 0, processedBytes: 0 }
}
*/
})
// We can add these to debug logging
zip.on('end', function() {
console.log("zip ending")
});
zip.on('finish', function() {
console.log("zip finished")
});
// Upload options
const maxBuffers = 5
const blockBlobClient = blobClient.getBlockBlobClient()
var myCallback = function(progress: TransferProgressEvent) {
console.log("Byte upload count " + progress.loadedBytes)
uploadByteCount = progress.loadedBytes
};
const options: BlockBlobUploadStreamOptions = {
blobHTTPHeaders: { "blobContentType": "zip" },
onProgress: myCallback
}
// Upload!
try {
await blockBlobClient.uploadStream(
zipUploadStream,
bufferSize,
maxBuffers,
options
);
} catch (error){
console.log(error)
}
console.log("final upload size in bytes is " + uploadByteCount)
console.log("we are done with the blob upload!")
// Blob upload end
const finalizeResp = await jsonClient.FinalizeArtifact({workflowRunBackendId: backendIDs.workflowRunBackendId, workflowJobRunBackendId: backendIDs.workflowJobRunBackendId, name: name, size: BigInt(5)})
if (!finalizeResp.ok) {
core.error("FinalizeArtifact failed")
}
} catch (e) {
console.log(e)
}
console.log("FinalizeArtifact succeeded")
return {
success: false
}
}
core.debug(`Workflow Run Backend ID: ${backendIds.workflowRunBackendId}`)
core.debug(
`Workflow Job Run Backend ID: ${backendIds.workflowJobRunBackendId}`
)
const uploadResponse: UploadResponse = {
artifactName: name,
size: uploadByteCount
// create the artifact client
const artifactClient = createArtifactTwirpClient('upload')
// create the artifact
const createArtifactReq: CreateArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name,
version: 4
}
// if there is a retention period, add it to the request
const expiresAt = getExpiration(options?.retentionDays)
if (expiresAt) {
createArtifactReq.expiresAt = expiresAt
}
const createArtifactResp =
await artifactClient.CreateArtifact(createArtifactReq)
if (!createArtifactResp.ok) {
core.warning(`Failed to create artifact`)
return {
success: false
}
}
return uploadResponse
}
// Upload zip to blob storage
const uploadResult = await uploadZipToBlobStorage(
createArtifactResp.signedUploadUrl,
zipUploadStream
)
if (uploadResult.isSuccess === false) {
return {
success: false
}
}
// finalize the artifact
const finalizeArtifactReq: FinalizeArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name,
size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0'
}
if (uploadResult.md5Hash) {
finalizeArtifactReq.hash = StringValue.create({
value: `md5:${uploadResult.md5Hash}`
})
}
core.info(`Finalizing artifact upload`)
const finalizeArtifactResp =
await artifactClient.FinalizeArtifact(finalizeArtifactReq)
if (!finalizeArtifactResp.ok) {
core.warning(`Failed to finalize artifact`)
return {
success: false
}
}
const artifactId = BigInt(finalizeArtifactResp.artifactId)
core.info(
`Artifact ${name}.zip successfully finalized. Artifact ID ${artifactId}`
)
return {
success: true,
size: uploadResult.uploadSize,
id: Number(artifactId)
}
}
@@ -1,18 +0,0 @@
export interface UploadOptions {
/**
* Duration after which artifact will expire in days.
*
* By default artifact expires after 90 days:
* https://docs.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts#downloading-and-deleting-artifacts-after-a-workflow-run-is-complete
*
* Use this option to override the default expiry.
*
* Min value: 1
* Max value: 90 unless changed by repository setting
*
* If this is set to a greater value than the retention settings allowed, the retention on artifacts
* will be reduced to match the max value allowed on server, and the upload process will continue. An
* input of 0 assumes default retention setting.
*/
retentionDays?: number
}
@@ -1,11 +0,0 @@
export interface UploadResponse {
/**
* The name of the artifact that was uploaded
*/
artifactName: string
/**
* Total size of the artifact that was uploaded in bytes
*/
size: number
}
@@ -1,94 +0,0 @@
import * as fs from 'fs'
import {debug} from '@actions/core'
import {join, normalize, resolve} from 'path'
import {checkArtifactFilePath} from './path-and-artifact-name-validation'
export interface UploadSpecification {
absoluteFilePath: string
uploadFilePath: string
}
/**
* Creates a specification that describes how each file that is part of the artifact will be uploaded
* @param artifactName the name of the artifact being uploaded. Used during upload to denote where the artifact is stored on the server
* @param rootDirectory an absolute file path that denotes the path that should be removed from the beginning of each artifact file
* @param artifactFiles a list of absolute file paths that denote what should be uploaded as part of the artifact
*/
export function getUploadSpecification(
artifactName: string,
rootDirectory: string,
artifactFiles: string[]
): UploadSpecification[] {
// artifact name was checked earlier on, no need to check again
const specifications: UploadSpecification[] = []
if (!fs.existsSync(rootDirectory)) {
throw new Error(`Provided rootDirectory ${rootDirectory} does not exist`)
}
if (!fs.statSync(rootDirectory).isDirectory()) {
throw new Error(
`Provided rootDirectory ${rootDirectory} is not a valid directory`
)
}
// Normalize and resolve, this allows for either absolute or relative paths to be used
rootDirectory = normalize(rootDirectory)
rootDirectory = resolve(rootDirectory)
/*
Example to demonstrate behavior
Input:
artifactName: my-artifact
rootDirectory: '/home/user/files/plz-upload'
artifactFiles: [
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
]
Output:
specifications: [
['/home/user/files/plz-upload/file1.txt', 'my-artifact/file1.txt'],
['/home/user/files/plz-upload/file1.txt', 'my-artifact/file2.txt'],
['/home/user/files/plz-upload/file1.txt', 'my-artifact/dir/file3.txt']
]
*/
for (let file of artifactFiles) {
if (!fs.existsSync(file)) {
throw new Error(`File ${file} does not exist`)
}
if (!fs.statSync(file).isDirectory()) {
// Normalize and resolve, this allows for either absolute or relative paths to be used
file = normalize(file)
file = resolve(file)
if (!file.startsWith(rootDirectory)) {
throw new Error(
`The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}`
)
}
// Check for forbidden characters in file paths that will be rejected during upload
const uploadPath = file.replace(rootDirectory, '')
checkArtifactFilePath(uploadPath)
/*
uploadFilePath denotes where the file will be uploaded in the file container on the server. During a run, if multiple artifacts are uploaded, they will all
be saved in the same container. The artifact name is used as the root directory in the container to separate and distinguish uploaded artifacts
path.join handles all the following cases and would return 'artifact-name/file-to-upload.txt
join('artifact-name/', 'file-to-upload.txt')
join('artifact-name/', '/file-to-upload.txt')
join('artifact-name', 'file-to-upload.txt')
join('artifact-name', '/file-to-upload.txt')
*/
specifications.push({
absoluteFilePath: file,
uploadFilePath: join(artifactName, uploadPath)
})
} else {
// Directories are rejected by the server during upload
debug(`Removing ${file} from rawSearchResults because it is a directory`)
}
}
return specifications
}
@@ -0,0 +1,111 @@
import * as fs from 'fs'
import {info} from '@actions/core'
import {normalize, resolve} from 'path'
import {validateFilePath} from './path-and-artifact-name-validation'
export interface UploadZipSpecification {
/**
* An absolute source path that points to a file that will be added to a zip. Null if creating a new directory
*/
sourcePath: string | null
/**
* The destination path in a zip for a file
*/
destinationPath: string
}
/**
* Checks if a root directory exists and is valid
* @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure
*/
export function validateRootDirectory(rootDirectory: string): void {
if (!fs.existsSync(rootDirectory)) {
throw new Error(
`The provided rootDirectory ${rootDirectory} does not exist`
)
}
if (!fs.statSync(rootDirectory).isDirectory()) {
throw new Error(
`The provided rootDirectory ${rootDirectory} is not a valid directory`
)
}
info(`Root directory input is valid!`)
}
/**
* Creates a specification that describes how a zip file will be created for a set of input files
* @param filesToZip a list of file that should be included in the zip
* @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure
*/
export function getUploadZipSpecification(
filesToZip: string[],
rootDirectory: string
): UploadZipSpecification[] {
const specification: UploadZipSpecification[] = []
// Normalize and resolve, this allows for either absolute or relative paths to be used
rootDirectory = normalize(rootDirectory)
rootDirectory = resolve(rootDirectory)
/*
Example
Input:
rootDirectory: '/home/user/files/plz-upload'
artifactFiles: [
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
]
Output:
specifications: [
['/home/user/files/plz-upload/file1.txt', '/file1.txt'],
['/home/user/files/plz-upload/file1.txt', '/file2.txt'],
['/home/user/files/plz-upload/file1.txt', '/dir/file3.txt']
]
The final zip that is later uploaded will look like this:
my-artifact.zip
- file.txt
- file2.txt
- dir/
- file3.txt
*/
for (let file of filesToZip) {
if (!fs.existsSync(file)) {
throw new Error(`File ${file} does not exist`)
}
if (!fs.statSync(file).isDirectory()) {
// Normalize and resolve, this allows for either absolute or relative paths to be used
file = normalize(file)
file = resolve(file)
if (!file.startsWith(rootDirectory)) {
throw new Error(
`The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}`
)
}
// Check for forbidden characters in file paths that may cause ambiguous behavior if downloaded on different file systems
const uploadPath = file.replace(rootDirectory, '')
validateFilePath(uploadPath)
specification.push({
sourcePath: file,
destinationPath: uploadPath
})
} else {
// Empty directory
const directoryPath = file.replace(rootDirectory, '')
validateFilePath(directoryPath)
specification.push({
sourcePath: null,
destinationPath: directoryPath
})
}
}
return specification
}
@@ -0,0 +1,97 @@
import * as stream from 'stream'
import * as archiver from 'archiver'
import * as core from '@actions/core'
import {createReadStream} from 'fs'
import {UploadZipSpecification} from './upload-zip-specification'
import {getUploadChunkSize} from '../shared/config'
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class ZipUploadStream extends stream.Transform {
constructor(bufferSize: number) {
super({
highWaterMark: bufferSize
})
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
_transform(chunk: any, enc: any, cb: any): void {
cb(null, chunk)
}
}
export async function createZipUploadStream(
uploadSpecification: UploadZipSpecification[]
): Promise<ZipUploadStream> {
const zip = archiver.create('zip', {
zlib: {level: 9} // Sets the compression level.
// Available options are 0-9
// 0 => no compression
// 1 => fastest with low compression
// 9 => highest compression ratio but the slowest
})
// register callbacks for various events during the zip lifecycle
zip.on('error', zipErrorCallback)
zip.on('warning', zipWarningCallback)
zip.on('finish', zipFinishCallback)
zip.on('end', zipEndCallback)
for (const file of uploadSpecification) {
if (file.sourcePath !== null) {
// Add a normal file to the zip
zip.append(createReadStream(file.sourcePath), {
name: file.destinationPath
})
} else {
// Add a directory to the zip
zip.append('', {name: file.destinationPath})
}
}
const bufferSize = getUploadChunkSize()
const zipUploadStream = new ZipUploadStream(bufferSize)
core.debug(
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
)
core.debug(
`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`
)
zip.pipe(zipUploadStream)
zip.finalize()
return zipUploadStream
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipErrorCallback = (error: any): void => {
core.error('An error has occurred while creating the zip file for upload')
core.info(error)
throw new Error('An error has occurred during zip creation for the artifact')
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipWarningCallback = (error: any): void => {
if (error.code === 'ENOENT') {
core.warning(
'ENOENT warning during artifact zip creation. No such file or directory'
)
core.info(error)
} else {
core.warning(
`A non-blocking warning has occurred during artifact zip creation: ${error.code}`
)
core.info(error)
}
}
const zipFinishCallback = (): void => {
core.debug('Zip stream for upload has finished.')
}
const zipEndCallback = (): void => {
core.debug('Zip stream for upload has ended.')
}
-36
View File
@@ -1,36 +0,0 @@
import { getRuntimeToken } from './config';
export interface BackendIds {
workflowRunBackendId: string;
workflowJobRunBackendId: string;
}
export function getBackendIds(): BackendIds {
const token = getRuntimeToken();
const parsedToken = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString())
if (!parsedToken["scp"]) {
throw new Error('Unable to get scp from token')
}
const scp = parsedToken["scp"]
const scpParts = scp.split(' ')
if (scpParts.length == 0) {
throw new Error('No scp parts found')
}
for (const part of scpParts) {
const partParts = part.split(':')
if(partParts.length == 0) {
continue
}
if (partParts[0] == "Actions.Results") {
if (partParts.length == 3) {
return {workflowRunBackendId: partParts[1], workflowJobRunBackendId: partParts[2]}
}
throw new Error('Unable to parse Actions.Results scp part')
}
}
throw new Error('Unable to find ids')
}
+9 -5
View File
@@ -4,11 +4,15 @@
"baseUrl": "./",
"outDir": "./lib",
"rootDir": "./src",
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020"
"paths": {
"@actions/core": [
"../core"
],
"@actions/http-client": [
"../http-client"
]
},
"useUnknownInCatchVariables": false,
},
"include": [
"./src"
+4
View File
@@ -160,3 +160,7 @@
### 3.2.1
- Updated @azure/storage-blob to `v12.13.0`
### 3.2.2
- Add new default cache download method to improve performance and reduce hangs [#1484](https://github.com/actions/toolkit/pull/1484)
+21 -6
View File
@@ -84,18 +84,24 @@ test('downloadCache uses storage SDK for Azure storage URLs', async () => {
'downloadCacheStorageSDK'
)
const downloadCacheHttpClientConcurrentMock = jest.spyOn(
downloadUtils,
'downloadCacheHttpClientConcurrent'
)
const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz'
const archivePath = '/foo/bar'
await downloadCache(archiveLocation, archivePath)
expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(1)
expect(downloadCacheStorageSDKMock).toHaveBeenCalledWith(
expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledTimes(1)
expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledWith(
archiveLocation,
archivePath,
getDownloadOptions()
)
expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0)
expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0)
})
@@ -109,20 +115,26 @@ test('downloadCache passes options to download methods', async () => {
'downloadCacheStorageSDK'
)
const downloadCacheHttpClientConcurrentMock = jest.spyOn(
downloadUtils,
'downloadCacheHttpClientConcurrent'
)
const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz'
const archivePath = '/foo/bar'
const options: DownloadOptions = {downloadConcurrency: 4}
await downloadCache(archiveLocation, archivePath, options)
expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(1)
expect(downloadCacheStorageSDKMock).toHaveBeenCalled()
expect(downloadCacheStorageSDKMock).toHaveBeenCalledWith(
expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledTimes(1)
expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalled()
expect(downloadCacheHttpClientConcurrentMock).toHaveBeenCalledWith(
archiveLocation,
archivePath,
getDownloadOptions(options)
)
expect(downloadCacheStorageSDKMock).toHaveBeenCalledTimes(0)
expect(downloadCacheHttpClientMock).toHaveBeenCalledTimes(0)
})
@@ -138,7 +150,10 @@ test('downloadCache uses http-client when overridden', async () => {
const archiveLocation = 'http://foo.blob.core.windows.net/bar/baz'
const archivePath = '/foo/bar'
const options: DownloadOptions = {useAzureSdk: false}
const options: DownloadOptions = {
useAzureSdk: false,
concurrentBlobDownloads: false
}
await downloadCache(archiveLocation, archivePath, options)
+5 -2
View File
@@ -5,7 +5,8 @@ import {
getUploadOptions
} from '../src/options'
const useAzureSdk = true
const useAzureSdk = false
const concurrentBlobDownloads = true
const downloadConcurrency = 8
const timeoutInMs = 30000
const segmentTimeoutInMs = 600000
@@ -18,6 +19,7 @@ test('getDownloadOptions sets defaults', async () => {
expect(actualOptions).toEqual({
useAzureSdk,
concurrentBlobDownloads,
downloadConcurrency,
timeoutInMs,
segmentTimeoutInMs,
@@ -27,7 +29,8 @@ test('getDownloadOptions sets defaults', async () => {
test('getDownloadOptions overrides all settings', async () => {
const expectedOptions: DownloadOptions = {
useAzureSdk: false,
useAzureSdk: true,
concurrentBlobDownloads: false,
downloadConcurrency: 14,
timeoutInMs: 20000,
segmentTimeoutInMs: 3600000,
+175 -344
View File
@@ -1,29 +1,29 @@
{
"name": "@actions/cache",
"version": "3.2.1",
"version": "3.2.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/cache",
"version": "3.2.1",
"version": "3.2.2",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.0",
"@actions/exec": "^1.0.1",
"@actions/glob": "^0.1.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^2.1.1",
"@actions/io": "^1.0.1",
"@azure/abort-controller": "^1.1.0",
"@azure/ms-rest-js": "^2.6.0",
"@azure/storage-blob": "^12.13.0",
"semver": "^6.1.0",
"semver": "^6.3.1",
"uuid": "^3.3.3"
},
"devDependencies": {
"@types/semver": "^6.0.0",
"@types/uuid": "^3.4.5",
"typescript": "^4.8.0"
"typescript": "^5.2.2"
}
},
"node_modules/@actions/core": {
@@ -52,26 +52,26 @@
}
},
"node_modules/@actions/glob": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz",
"integrity": "sha512-lx8SzyQ2FE9+UUvjqY1f28QbTJv+w8qP7kHHbfQRhphrlcx0Mdmm1tZdGJzfxv1jxREa/sLW4Oy8CbGQKCJySA==",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz",
"integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==",
"dependencies": {
"@actions/core": "^1.2.0",
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz",
"integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
},
"node_modules/@azure/abort-controller": {
"version": "1.1.0",
@@ -84,20 +84,10 @@
"node": ">=12.0.0"
}
},
"node_modules/@azure/abort-controller/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/core-asynciterator-polyfill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz",
"integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg=="
},
"node_modules/@azure/core-auth": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz",
"integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
"integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
@@ -106,15 +96,10 @@
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-auth/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/core-http": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.1.tgz",
"integrity": "sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz",
"integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
@@ -148,11 +133,6 @@
"node": ">= 6"
}
},
"node_modules/@azure/core-http/node_modules/tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"node_modules/@azure/core-http/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -162,41 +142,30 @@
}
},
"node_modules/@azure/core-lro": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.2.3.tgz",
"integrity": "sha512-UMdlR9NsqDCLTba3EUbRjfMF4gDmWvld196JmUjbz9WWhJ2XT00OR5MXeWiR+vmGT+ETiO4hHFCi2/eGO5YVtg==",
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz",
"integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-tracing": "1.0.0-preview.13",
"@azure/core-util": "^1.2.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-lro/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/core-paging": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.2.1.tgz",
"integrity": "sha512-UtH5iMlYsvg+nQYIl4UHlvvSrsBjOlRF4fs0j7mxd3rWdAStrKYrh2durOpHs5C9yZbVhsVDaisoyaf/lL1EVA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
"integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
"dependencies": {
"@azure/core-asynciterator-polyfill": "^1.0.0",
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-paging/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/core-tracing": {
"version": "1.0.0-preview.13",
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz",
@@ -209,15 +178,10 @@
"node": ">=12.0.0"
}
},
"node_modules/@azure/core-tracing/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/core-util": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.2.0.tgz",
"integrity": "sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz",
"integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
@@ -226,43 +190,37 @@
"node": ">=14.0.0"
}
},
"node_modules/@azure/core-util/node_modules/tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"node_modules/@azure/logger": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
"integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
"integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==",
"dependencies": {
"tslib": "^2.2.0"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.0.0"
}
},
"node_modules/@azure/logger/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@azure/ms-rest-js": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.6.6.tgz",
"integrity": "sha512-WYIda8VvrkZE68xHgOxUXvjThxNf1nnGPPe0rAljqK5HJHIZ12Pi3YhEDOn3Ge7UnwaaM3eFO0VtAy4nGVI27Q==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz",
"integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==",
"dependencies": {
"@azure/core-auth": "^1.1.4",
"abort-controller": "^3.0.0",
"form-data": "^2.5.0",
"node-fetch": "^2.6.7",
"tough-cookie": "^3.0.1",
"tslib": "^1.10.0",
"tunnel": "0.0.6",
"uuid": "^8.3.2",
"xml2js": "^0.5.0"
}
},
"node_modules/@azure/ms-rest-js/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"node_modules/@azure/ms-rest-js/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -272,9 +230,9 @@
}
},
"node_modules/@azure/storage-blob": {
"version": "12.13.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.13.0.tgz",
"integrity": "sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==",
"version": "12.15.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz",
"integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-http": "^3.0.0",
@@ -289,28 +247,23 @@
"node": ">=14.0.0"
}
},
"node_modules/@azure/storage-blob/node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/@opentelemetry/api": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz",
"integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
"integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA==",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/@types/node": {
"version": "18.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz",
"integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA=="
"version": "20.4.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz",
"integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA=="
},
"node_modules/@types/node-fetch": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz",
"integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==",
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
"integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
"dependencies": {
"@types/node": "*",
"form-data": "^3.0.0"
@@ -330,9 +283,9 @@
}
},
"node_modules/@types/semver": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz",
"integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.3.tgz",
"integrity": "sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==",
"dev": true
},
"node_modules/@types/tunnel": {
@@ -344,9 +297,9 @@
}
},
"node_modules/@types/uuid": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz",
"integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==",
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz",
"integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==",
"dev": true
},
"node_modules/abort-controller": {
@@ -363,12 +316,12 @@
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
@@ -393,12 +346,12 @@
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
@@ -432,28 +385,20 @@
"node": ">= 0.12"
}
},
"node_modules/ip-regex": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.34",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.51.0"
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
@@ -471,9 +416,9 @@
}
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
@@ -497,54 +442,28 @@
"node": ">= 0.6.0"
}
},
"node_modules/psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"engines": {
"node": ">=6"
}
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
"dependencies": {
"ip-regex": "^2.1.0",
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
},
"node_modules/tunnel": {
"version": "0.0.6",
@@ -555,16 +474,16 @@
}
},
"node_modules/typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
"node": ">=14.17"
}
},
"node_modules/uuid": {
@@ -579,12 +498,12 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
@@ -637,26 +556,26 @@
}
},
"@actions/glob": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.0.tgz",
"integrity": "sha512-lx8SzyQ2FE9+UUvjqY1f28QbTJv+w8qP7kHHbfQRhphrlcx0Mdmm1tZdGJzfxv1jxREa/sLW4Oy8CbGQKCJySA==",
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.1.2.tgz",
"integrity": "sha512-SclLR7Ia5sEqjkJTPs7Sd86maMDw43p769YxBOxvPvEWuPEhpAnBsQfENOpXjFYMmhCqd127bmf+YdvJqVqR4A==",
"requires": {
"@actions/core": "^1.2.0",
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz",
"integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==",
"requires": {
"tunnel": "^0.0.6"
}
},
"@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
},
"@azure/abort-controller": {
"version": "1.1.0",
@@ -664,40 +583,21 @@
"integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==",
"requires": {
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/core-asynciterator-polyfill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz",
"integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg=="
},
"@azure/core-auth": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.2.tgz",
"integrity": "sha512-7CU6DmCHIZp5ZPiZ9r3J17lTKMmYsm/zGvNkjArQwPkrLlZ1TZ+EUYfGgh2X31OLMVAQCTJZW4cXHJi02EbJnA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.4.0.tgz",
"integrity": "sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/core-http": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.1.tgz",
"integrity": "sha512-A3x+um3cAPgQe42Lu7Iv/x8/fNjhL/nIoEfqFxfn30EyxK6zC13n+OUxzZBRC0IzQqssqIbt4INf5YG7lYYFtw==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.2.tgz",
"integrity": "sha512-o1wR9JrmoM0xEAa0Ue7Sp8j+uJvmqYaGoHOCT5qaVYmvgmnZDC0OvQimPA/JR3u77Sz6D1y3Xmk1y69cDU9q9A==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",
@@ -725,11 +625,6 @@
"mime-types": "^2.1.12"
}
},
"tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -738,37 +633,22 @@
}
},
"@azure/core-lro": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.2.3.tgz",
"integrity": "sha512-UMdlR9NsqDCLTba3EUbRjfMF4gDmWvld196JmUjbz9WWhJ2XT00OR5MXeWiR+vmGT+ETiO4hHFCi2/eGO5YVtg==",
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz",
"integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-tracing": "1.0.0-preview.13",
"@azure/core-util": "^1.2.0",
"@azure/logger": "^1.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/core-paging": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.2.1.tgz",
"integrity": "sha512-UtH5iMlYsvg+nQYIl4UHlvvSrsBjOlRF4fs0j7mxd3rWdAStrKYrh2durOpHs5C9yZbVhsVDaisoyaf/lL1EVA==",
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz",
"integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==",
"requires": {
"@azure/core-asynciterator-polyfill": "^1.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/core-tracing": {
@@ -778,62 +658,45 @@
"requires": {
"@opentelemetry/api": "^1.0.1",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/core-util": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.2.0.tgz",
"integrity": "sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.3.2.tgz",
"integrity": "sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
}
}
},
"@azure/logger": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.3.tgz",
"integrity": "sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz",
"integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==",
"requires": {
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@azure/ms-rest-js": {
"version": "2.6.6",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.6.6.tgz",
"integrity": "sha512-WYIda8VvrkZE68xHgOxUXvjThxNf1nnGPPe0rAljqK5HJHIZ12Pi3YhEDOn3Ge7UnwaaM3eFO0VtAy4nGVI27Q==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.7.0.tgz",
"integrity": "sha512-ngbzWbqF+NmztDOpLBVDxYM+XLcUj7nKhxGbSU9WtIsXfRB//cf2ZbAG5HkOrhU9/wd/ORRB6lM/d69RKVjiyA==",
"requires": {
"@azure/core-auth": "^1.1.4",
"abort-controller": "^3.0.0",
"form-data": "^2.5.0",
"node-fetch": "^2.6.7",
"tough-cookie": "^3.0.1",
"tslib": "^1.10.0",
"tunnel": "0.0.6",
"uuid": "^8.3.2",
"xml2js": "^0.5.0"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
@@ -842,9 +705,9 @@
}
},
"@azure/storage-blob": {
"version": "12.13.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.13.0.tgz",
"integrity": "sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA==",
"version": "12.15.0",
"resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.15.0.tgz",
"integrity": "sha512-e7JBKLOFi0QVJqqLzrjx1eL3je3/Ug2IQj24cTM9b85CsnnFjLGeGjJVIjbGGZaytewiCEG7r3lRwQX7fKj0/w==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-http": "^3.0.0",
@@ -854,29 +717,22 @@
"@azure/logger": "^1.0.0",
"events": "^3.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
}
}
},
"@opentelemetry/api": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.4.tgz",
"integrity": "sha512-BuJuXRSJNQ3QoKA6GWWDyuLpOUck+9hAXNMCnrloc1aWVoy6Xq6t9PUV08aBZ4Lutqq2LEHM486bpZqoViScog=="
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.4.1.tgz",
"integrity": "sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA=="
},
"@types/node": {
"version": "18.14.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.6.tgz",
"integrity": "sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA=="
"version": "20.4.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz",
"integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA=="
},
"@types/node-fetch": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz",
"integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==",
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
"integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
@@ -895,9 +751,9 @@
}
},
"@types/semver": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.1.tgz",
"integrity": "sha512-+beqKQOh9PYxuHvijhVl+tIHvT6tuwOrE9m14zd+MT2A38KoKZhh7pYJ0SNleLtwDsiIxHDsIk9bv01oOxvSvA==",
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-6.2.3.tgz",
"integrity": "sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==",
"dev": true
},
"@types/tunnel": {
@@ -909,9 +765,9 @@
}
},
"@types/uuid": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz",
"integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==",
"version": "3.4.10",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz",
"integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==",
"dev": true
},
"abort-controller": {
@@ -925,12 +781,12 @@
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"brace-expansion": {
"version": "1.1.11",
@@ -952,12 +808,12 @@
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"event-target-shim": {
"version": "5.0.1",
@@ -979,22 +835,17 @@
"mime-types": "^2.1.12"
}
},
"ip-regex": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
},
"mime-db": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.34",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.51.0"
"mime-db": "1.52.0"
}
},
"minimatch": {
@@ -1006,9 +857,9 @@
}
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
"requires": {
"whatwg-url": "^5.0.0"
}
@@ -1018,45 +869,25 @@
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
},
"psl": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
"requires": {
"ip-regex": "^2.1.0",
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
},
"tunnel": {
"version": "0.0.6",
@@ -1064,9 +895,9 @@
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"typescript": {
"version": "4.8.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true
},
"uuid": {
@@ -1077,12 +908,12 @@
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "3.2.1",
"version": "3.2.2",
"preview": true,
"description": "Actions cache lib",
"keywords": [
@@ -40,17 +40,17 @@
"@actions/core": "^1.10.0",
"@actions/exec": "^1.0.1",
"@actions/glob": "^0.1.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^2.1.1",
"@actions/io": "^1.0.1",
"@azure/abort-controller": "^1.1.0",
"@azure/ms-rest-js": "^2.6.0",
"@azure/storage-blob": "^12.13.0",
"semver": "^6.1.0",
"semver": "^6.3.1",
"uuid": "^3.3.3"
},
"devDependencies": {
"@types/semver": "^6.0.0",
"@types/uuid": "^3.4.5",
"typescript": "^4.8.0"
"typescript": "^5.2.2"
}
}
+28 -15
View File
@@ -20,7 +20,11 @@ import {
ITypedResponseWithError,
ArtifactCacheList
} from './contracts'
import {downloadCacheHttpClient, downloadCacheStorageSDK} from './downloadUtils'
import {
downloadCacheHttpClient,
downloadCacheHttpClientConcurrent,
downloadCacheStorageSDK
} from './downloadUtils'
import {
DownloadOptions,
UploadOptions,
@@ -92,10 +96,7 @@ export function getCacheVersion(
// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt)
return crypto
.createHash('sha256')
.update(components.join('|'))
.digest('hex')
return crypto.createHash('sha256').update(components.join('|')).digest('hex')
}
export async function getCacheEntry(
@@ -174,14 +175,26 @@ export async function downloadCache(
const archiveUrl = new URL(archiveLocation)
const downloadOptions = getDownloadOptions(options)
if (
downloadOptions.useAzureSdk &&
archiveUrl.hostname.endsWith('.blob.core.windows.net')
) {
// Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability.
await downloadCacheStorageSDK(archiveLocation, archivePath, downloadOptions)
if (archiveUrl.hostname.endsWith('.blob.core.windows.net')) {
if (downloadOptions.useAzureSdk) {
// Use Azure storage SDK to download caches hosted on Azure to improve speed and reliability.
await downloadCacheStorageSDK(
archiveLocation,
archivePath,
downloadOptions
)
} else if (downloadOptions.concurrentBlobDownloads) {
// Use concurrent implementation with HttpClient to work around blob SDK issue
await downloadCacheHttpClientConcurrent(
archiveLocation,
archivePath,
downloadOptions
)
} else {
// Otherwise, download using the Actions http-client.
await downloadCacheHttpClient(archiveLocation, archivePath)
}
} else {
// Otherwise, download using the Actions http-client.
await downloadCacheHttpClient(archiveLocation, archivePath)
}
}
@@ -230,9 +243,9 @@ async function uploadChunk(
end: number
): Promise<void> {
core.debug(
`Uploading chunk of size ${end -
start +
1} bytes at offset ${start} with content range: ${getContentRange(
`Uploading chunk of size ${
end - start + 1
} bytes at offset ${start} with content range: ${getContentRange(
start,
end
)}`
+164 -4
View File
@@ -203,6 +203,166 @@ export async function downloadCacheHttpClient(
}
}
/**
* Download the cache using the Actions toolkit http-client concurrently
*
* @param archiveLocation the URL for the cache
* @param archivePath the local path where the cache is saved
*/
export async function downloadCacheHttpClientConcurrent(
archiveLocation: string,
archivePath: fs.PathLike,
options: DownloadOptions
): Promise<void> {
const archiveDescriptor = await fs.promises.open(archivePath, 'w')
const httpClient = new HttpClient('actions/cache', undefined, {
socketTimeout: options.timeoutInMs,
keepAlive: true
})
try {
const res = await retryHttpClientResponse(
'downloadCacheMetadata',
async () => await httpClient.request('HEAD', archiveLocation, null, {})
)
const lengthHeader = res.message.headers['content-length']
if (lengthHeader === undefined || lengthHeader === null) {
throw new Error('Content-Length not found on blob response')
}
const length = parseInt(lengthHeader)
if (Number.isNaN(length)) {
throw new Error(`Could not interpret Content-Length: ${length}`)
}
const downloads: {
offset: number
promiseGetter: () => Promise<DownloadSegment>
}[] = []
const blockSize = 4 * 1024 * 1024
for (let offset = 0; offset < length; offset += blockSize) {
const count = Math.min(blockSize, length - offset)
downloads.push({
offset,
promiseGetter: async () => {
return await downloadSegmentRetry(
httpClient,
archiveLocation,
offset,
count
)
}
})
}
// reverse to use .pop instead of .shift
downloads.reverse()
let actives = 0
let bytesDownloaded = 0
const progress = new DownloadProgress(length)
progress.startDisplayTimer()
const progressFn = progress.onProgress()
const activeDownloads: {[offset: number]: Promise<DownloadSegment>} = []
let nextDownload:
| {offset: number; promiseGetter: () => Promise<DownloadSegment>}
| undefined
const waitAndWrite: () => Promise<void> = async () => {
const segment = await Promise.race(Object.values(activeDownloads))
await archiveDescriptor.write(
segment.buffer,
0,
segment.count,
segment.offset
)
actives--
delete activeDownloads[segment.offset]
bytesDownloaded += segment.count
progressFn({loadedBytes: bytesDownloaded})
}
while ((nextDownload = downloads.pop())) {
activeDownloads[nextDownload.offset] = nextDownload.promiseGetter()
actives++
if (actives >= (options.downloadConcurrency ?? 10)) {
await waitAndWrite()
}
}
while (actives > 0) {
await waitAndWrite()
}
} finally {
httpClient.dispose()
await archiveDescriptor.close()
}
}
async function downloadSegmentRetry(
httpClient: HttpClient,
archiveLocation: string,
offset: number,
count: number
): Promise<DownloadSegment> {
const retries = 5
let failures = 0
while (true) {
try {
const timeout = 30000
const result = await promiseWithTimeout(
timeout,
downloadSegment(httpClient, archiveLocation, offset, count)
)
if (typeof result === 'string') {
throw new Error('downloadSegmentRetry failed due to timeout')
}
return result
} catch (err) {
if (failures >= retries) {
throw err
}
failures++
}
}
}
async function downloadSegment(
httpClient: HttpClient,
archiveLocation: string,
offset: number,
count: number
): Promise<DownloadSegment> {
const partRes = await retryHttpClientResponse(
'downloadCachePart',
async () =>
await httpClient.get(archiveLocation, {
Range: `bytes=${offset}-${offset + count - 1}`
})
)
if (!partRes.readBodyBuffer) {
throw new Error('Expected HttpClientResponse to implement readBodyBuffer')
}
return {
offset,
count,
buffer: await partRes.readBodyBuffer()
}
}
declare class DownloadSegment {
offset: number
count: number
buffer: Buffer
}
/**
* Download the cache using the Azure Storage SDK. Only call this method if the
* URL points to an Azure Storage endpoint.
@@ -287,12 +447,12 @@ export async function downloadCacheStorageSDK(
}
}
const promiseWithTimeout = async (
const promiseWithTimeout = async <T>(
timeoutMs: number,
promise: Promise<Buffer>
): Promise<unknown> => {
promise: Promise<T>
): Promise<T | string> => {
let timeoutHandle: NodeJS.Timeout
const timeoutPromise = new Promise(resolve => {
const timeoutPromise = new Promise<string>(resolve => {
timeoutHandle = setTimeout(() => resolve('timeout'), timeoutMs)
})
+12 -1
View File
@@ -39,6 +39,12 @@ export interface DownloadOptions {
*/
downloadConcurrency?: number
/**
* Indicates whether to use Actions HttpClient with concurrency
* for Azure Blob Storage
*/
concurrentBlobDownloads?: boolean
/**
* Maximum time for each download request, in milliseconds (this
* option only applies when using the Azure SDK)
@@ -98,7 +104,8 @@ export function getUploadOptions(copy?: UploadOptions): UploadOptions {
*/
export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions {
const result: DownloadOptions = {
useAzureSdk: true,
useAzureSdk: false,
concurrentBlobDownloads: true,
downloadConcurrency: 8,
timeoutInMs: 30000,
segmentTimeoutInMs: 600000,
@@ -110,6 +117,10 @@ export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions {
result.useAzureSdk = copy.useAzureSdk
}
if (typeof copy.concurrentBlobDownloads === 'boolean') {
result.concurrentBlobDownloads = copy.concurrentBlobDownloads
}
if (typeof copy.downloadConcurrency === 'number') {
result.downloadConcurrency = copy.downloadConcurrency
}
+4 -1
View File
@@ -4,7 +4,10 @@
"baseUrl": "./",
"outDir": "./lib",
"rootDir": "./src",
"lib": ["es6", "dom"],
"lib": [
"es6",
"dom"
],
"useUnknownInCatchVariables": false
},
"include": [
+3
View File
@@ -1,5 +1,8 @@
# @actions/core Releases
### 1.10.1
- Fix error message reference in oidc utils [#1511](https://github.com/actions/toolkit/pull/1511)
### 1.10.0
- `saveState` and `setOutput` now use environment files if available [#1178](https://github.com/actions/toolkit/pull/1178)
- `getMultilineInput` now correctly trims whitespace by default [#1185](https://github.com/actions/toolkit/pull/1185)
+6 -7
View File
@@ -17,7 +17,7 @@ describe('@actions/core/src/command', () => {
afterEach(() => {})
afterAll(() => {
process.stdout.write = (originalWriteFunction as unknown) as (
process.stdout.write = originalWriteFunction as unknown as (
str: string
) => boolean
})
@@ -51,8 +51,7 @@ describe('@actions/core/src/command', () => {
command.issueCommand(
'some-command',
{
name:
'percent % percent % cr \r cr \r lf \n lf \n colon : colon : comma , comma ,'
name: 'percent % percent % cr \r cr \r lf \n lf \n colon : colon : comma , comma ,'
},
''
)
@@ -117,11 +116,11 @@ describe('@actions/core/src/command', () => {
command.issueCommand(
'some-command',
{
prop1: ({test: 'object'} as unknown) as string,
prop2: (123 as unknown) as string,
prop3: (true as unknown) as string
prop1: {test: 'object'} as unknown as string,
prop2: 123 as unknown as string,
prop3: true as unknown as string
},
({test: 'object'} as unknown) as string
{test: 'object'} as unknown as string
)
assertWriteCalls([
`::some-command prop1={"test"%3A"object"},prop2=123,prop3=true::{"test":"object"}${os.EOL}`
+1 -4
View File
@@ -150,10 +150,7 @@ describe('@actions/core/src/summary', () => {
})
it('adds EOL', async () => {
await summary
.addRaw(fixtures.text)
.addEOL()
.write()
await summary.addRaw(fixtures.text).addEOL().write()
await assertSummary(fixtures.text + os.EOL)
})
+8 -8
View File
@@ -1,12 +1,12 @@
{
"name": "@actions/core",
"version": "1.10.0",
"version": "1.10.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/core",
"version": "1.10.0",
"version": "1.10.1",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.0.1",
@@ -18,9 +18,9 @@
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
"dependencies": {
"tunnel": "^0.0.6"
}
@@ -56,9 +56,9 @@
},
"dependencies": {
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
"requires": {
"tunnel": "^0.0.6"
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "1.10.0",
"version": "1.10.1",
"description": "Actions core lib",
"keywords": [
"github",
@@ -30,7 +30,7 @@
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"tsc": "tsc"
"tsc": "tsc -p tsconfig.json"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
@@ -43,4 +43,4 @@
"@types/node": "^12.0.2",
"@types/uuid": "^8.3.4"
}
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ export class OidcClient {
throw new Error(
`Failed to get ID Token. \n
Error Code : ${error.statusCode}\n
Error Message: ${error.result.message}`
Error Message: ${error.message}`
)
})
+1
View File
@@ -3,6 +3,7 @@
"compilerOptions": {
"baseUrl": "./",
"outDir": "./lib",
"declaration": true,
"rootDir": "./src"
},
"include": [
+3 -3
View File
@@ -357,7 +357,7 @@ describe('@actions/exec', () => {
expect(exitCode).toBe(0)
})
it('Handles child process holding streams open', async function() {
it('Handles child process holding streams open', async function () {
const semaphorePath = path.join(
getTestTemp(),
'child-process-semaphore.txt'
@@ -403,7 +403,7 @@ describe('@actions/exec', () => {
fs.unlinkSync(semaphorePath)
}, 10000) // this was timing out on some slower hosted macOS runs at default 5s
it('Handles child process holding streams open and non-zero exit code', async function() {
it('Handles child process holding streams open and non-zero exit code', async function () {
const semaphorePath = path.join(
getTestTemp(),
'child-process-semaphore.txt'
@@ -457,7 +457,7 @@ describe('@actions/exec', () => {
fs.unlinkSync(semaphorePath)
}, 10000) // this was timing out on some slower hosted macOS runs at default 5s
it('Handles child process holding streams open and stderr', async function() {
it('Handles child process holding streams open and stderr', async function () {
const semaphorePath = path.join(
getTestTemp(),
'child-process-semaphore.txt'
+6 -11
View File
@@ -267,10 +267,7 @@ export class ToolRunner extends events.EventEmitter {
}
reverse += '"'
return reverse
.split('')
.reverse()
.join('')
return reverse.split('').reverse().join('')
}
private _uvQuoteCmdArg(arg: string): string {
@@ -350,10 +347,7 @@ export class ToolRunner extends events.EventEmitter {
}
reverse += '"'
return reverse
.split('')
.reverse()
.join('')
return reverse.split('').reverse().join('')
}
private _cloneExecOptions(options?: im.ExecOptions): im.ExecOptions {
@@ -637,7 +631,7 @@ class ExecState extends events.EventEmitter {
private delay = 10000 // 10 seconds
private done = false
private options: im.ExecOptions
private timeout: NodeJS.Timer | null = null
private timeout: NodeJS.Timeout | null = null
private toolPath: string
CheckComplete(): void {
@@ -691,8 +685,9 @@ class ExecState extends events.EventEmitter {
}
if (!state.processClosed && state.processExited) {
const message = `The STDIO streams did not close within ${state.delay /
1000} seconds of the exit event from process '${
const message = `The STDIO streams did not close within ${
state.delay / 1000
} seconds of the exit event from process '${
state.toolPath
}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`
state._debug(message)
+4
View File
@@ -1,5 +1,9 @@
# @actions/github Releases
### 6.0.0
- Support the latest Octokit in @actions/github [#1553](https://github.com/actions/toolkit/pull/1553)
- Drop support of NodeJS v14, v16
### 5.1.1
- Export default octokit options [#1188](https://github.com/actions/toolkit/pull/1188)
@@ -1,6 +1,6 @@
import * as http from 'http'
import * as https from 'https'
import proxy from 'proxy'
import {createProxy} from 'proxy'
// Default values are set when the module is imported, so we need to set proxy first.
const proxyUrl = 'http://127.0.0.1:8081'
@@ -16,8 +16,8 @@ describe('@actions/github', () => {
beforeAll(async () => {
// Start proxy server
proxyServer = proxy()
await new Promise(resolve => {
proxyServer = createProxy()
await new Promise<void>(resolve => {
const port = Number(proxyUrl.split(':')[2])
proxyServer.listen(port, () => resolve())
})
@@ -32,7 +32,7 @@ describe('@actions/github', () => {
afterAll(async () => {
// Stop proxy server
await new Promise(resolve => {
await new Promise<void>(resolve => {
proxyServer.once('close', () => resolve())
proxyServer.close()
})
+6 -6
View File
@@ -1,5 +1,5 @@
import * as http from 'http'
import proxy from 'proxy'
import {createProxy} from 'proxy'
import {getOctokit} from '../src/github'
import {GitHub, getOctokitOptions} from '../src/utils'
@@ -12,10 +12,10 @@ describe('@actions/github', () => {
beforeAll(async () => {
// Start proxy server
proxyServer = proxy()
await new Promise(resolve => {
proxyServer = createProxy()
await new Promise<void>(resolve => {
const port = Number(proxyUrl.split(':')[2])
proxyServer.listen(port, () => resolve(null))
proxyServer.listen(port, () => resolve())
})
proxyServer.on('connect', req => {
proxyConnects.push(req.url ?? '')
@@ -29,8 +29,8 @@ describe('@actions/github', () => {
afterAll(async () => {
// Stop proxy server
await new Promise(resolve => {
proxyServer.once('close', () => resolve(null))
await new Promise<void>(resolve => {
proxyServer.once('close', () => resolve())
proxyServer.close()
})
-5
View File
@@ -1,5 +0,0 @@
declare module 'proxy' {
import * as http from 'http'
function internal(): http.Server
export = internal
}
+331 -345
View File
@@ -1,148 +1,161 @@
{
"name": "@actions/github",
"version": "5.1.1",
"version": "6.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/github",
"version": "5.1.1",
"version": "6.0.0",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
"@actions/http-client": "^2.2.0",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.0.0",
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
},
"devDependencies": {
"proxy": "^1.0.2"
"proxy": "^2.1.1"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz",
"integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "^0.0.6",
"undici": "^5.25.4"
}
},
"node_modules/@fastify/busboy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==",
"engines": {
"node": ">=14"
}
},
"node_modules/@octokit/auth-token": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
"dependencies": {
"@octokit/types": "^6.0.3"
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz",
"integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==",
"dependencies": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.0.0",
"@octokit/request": "^8.0.2",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/endpoint": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz",
"integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==",
"dependencies": {
"@octokit/types": "^6.0.3",
"@octokit/types": "^12.0.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/graphql": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz",
"integrity": "sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"dependencies": {
"@octokit/request": "^5.3.0",
"@octokit/types": "^6.0.3",
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/openapi-types": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz",
"integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==",
"dependencies": {
"@octokit/types": "^6.34.0"
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=2"
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz",
"integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz",
"integrity": "sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g==",
"dependencies": {
"@octokit/types": "^6.34.0",
"deprecation": "^2.3.1"
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@octokit/core": ">=3"
"@octokit/core": ">=5"
}
},
"node_modules/@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"version": "8.1.4",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz",
"integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==",
"dependencies": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"dependencies": {
"@octokit/types": "^6.0.3",
"@octokit/types": "^12.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/types": {
"version": "6.34.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz",
"integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==",
"dependencies": {
"@octokit/openapi-types": "^11.2.0"
"@octokit/openapi-types": "^19.0.0"
}
},
"node_modules/args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
"integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
"dev": true,
"dependencies": {
"camelcase": "5.0.0",
"chalk": "2.4.2",
"leven": "2.1.0",
"mri": "1.1.4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/args/node_modules/ansi-styles": {
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
@@ -154,7 +167,33 @@
"node": ">=4"
}
},
"node_modules/args/node_modules/camelcase": {
"node_modules/args": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"dev": true,
"dependencies": {
"camelcase": "5.0.0",
"chalk": "2.4.2",
"leven": "2.1.0",
"mri": "1.1.4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/basic-auth-parser": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz",
"integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==",
"dev": true
},
"node_modules/before-after-hook": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
},
"node_modules/camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
@@ -163,7 +202,7 @@
"node": ">=6"
}
},
"node_modules/args/node_modules/chalk": {
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
@@ -177,7 +216,7 @@
"node": ">=4"
}
},
"node_modules/args/node_modules/color-convert": {
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
@@ -186,61 +225,27 @@
"color-name": "1.1.3"
}
},
"node_modules/args/node_modules/color-name": {
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"node_modules/args/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/args/node_modules/leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/args/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/basic-auth-parser": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
"integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
"dev": true
},
"node_modules/before-after-hook": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
},
"node_modules/debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/deprecation": {
@@ -251,12 +256,21 @@
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/is-plain-object": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
@@ -265,6 +279,15 @@
"node": ">=0.10.0"
}
},
"node_modules/leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/mri": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
@@ -280,64 +303,38 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"dev": true,
"dependencies": {
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
"args": "^5.0.3",
"basic-auth-parser": "0.0.2-1",
"debug": "^4.3.4"
},
"bin": {
"proxy": "bin/proxy.js"
"engines": {
"node": ">= 14"
}
},
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/tunnel": {
@@ -348,6 +345,17 @@
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/undici": {
"version": "5.25.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz",
"integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
@@ -356,193 +364,139 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
},
"dependencies": {
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz",
"integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "^0.0.6",
"undici": "^5.25.4"
}
},
"@fastify/busboy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ=="
},
"@octokit/auth-token": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.5.tgz",
"integrity": "sha512-BpGYsPgJt05M7/L/5FoE1PiAbdxXFZkX/3kDYcsvd1v6UhlnE5e96dTDr0ezX/EFwciQxf3cNV0loipsURU+WA==",
"requires": {
"@octokit/types": "^6.0.3"
}
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="
},
"@octokit/core": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz",
"integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==",
"requires": {
"@octokit/auth-token": "^2.4.4",
"@octokit/graphql": "^4.5.8",
"@octokit/request": "^5.6.3",
"@octokit/request-error": "^2.0.5",
"@octokit/types": "^6.0.3",
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.0.0",
"@octokit/request": "^8.0.2",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz",
"integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==",
"requires": {
"@octokit/types": "^6.0.3",
"@octokit/types": "^12.0.0",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz",
"integrity": "sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"requires": {
"@octokit/request": "^5.3.0",
"@octokit/types": "^6.0.3",
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw=="
},
"@octokit/plugin-paginate-rest": {
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz",
"integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==",
"requires": {
"@octokit/types": "^6.34.0"
"@octokit/types": "^12.0.0"
}
},
"@octokit/plugin-rest-endpoint-methods": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz",
"integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==",
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz",
"integrity": "sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g==",
"requires": {
"@octokit/types": "^6.34.0",
"deprecation": "^2.3.1"
"@octokit/types": "^12.0.0"
}
},
"@octokit/request": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"version": "8.1.4",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz",
"integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==",
"requires": {
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"requires": {
"@octokit/types": "^6.0.3",
"@octokit/types": "^12.0.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/types": {
"version": "6.34.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz",
"integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==",
"requires": {
"@octokit/openapi-types": "^11.2.0"
"@octokit/openapi-types": "^19.0.0"
}
},
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
"integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"dev": true,
"requires": {
"camelcase": "5.0.0",
"chalk": "2.4.2",
"leven": "2.1.0",
"mri": "1.1.4"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"basic-auth-parser": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
"integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz",
"integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==",
"dev": true
},
"before-after-hook": {
@@ -550,13 +504,45 @@
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": {
"ms": "2.1.2"
}
},
"deprecation": {
@@ -567,7 +553,13 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
"is-plain-object": {
@@ -575,6 +567,12 @@
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
"dev": true
},
"mri": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
@@ -587,52 +585,32 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": {
"whatwg-url": "^5.0.0"
},
"dependencies": {
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
}
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"requires": {
"wrappy": "1"
}
},
"proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"dev": true,
"requires": {
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
"args": "^5.0.3",
"basic-auth-parser": "0.0.2-1",
"debug": "^4.3.4"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"tunnel": {
@@ -640,6 +618,14 @@
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"undici": {
"version": "5.25.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz",
"integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==",
"requires": {
"@fastify/busboy": "^2.0.0"
}
},
"universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
@@ -648,7 +634,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
}
}
}
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "5.1.1",
"version": "6.0.0",
"description": "Actions github lib",
"keywords": [
"github",
@@ -38,12 +38,12 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
"@actions/http-client": "^2.2.0",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.0.0",
"@octokit/plugin-rest-endpoint-methods": "^10.0.0"
},
"devDependencies": {
"proxy": "^1.0.2"
"proxy": "^2.1.1"
}
}
}
+19
View File
@@ -1,6 +1,7 @@
import * as http from 'http'
import * as httpClient from '@actions/http-client'
import {OctokitOptions} from '@octokit/core/dist-types/types'
import {ProxyAgent, fetch} from 'undici'
export function getAuthString(
token: string,
@@ -20,6 +21,24 @@ export function getProxyAgent(destinationUrl: string): http.Agent {
return hc.getAgent(destinationUrl)
}
export function getProxyAgentDispatcher(
destinationUrl: string
): ProxyAgent | undefined {
const hc = new httpClient.HttpClient()
return hc.getAgentDispatcher(destinationUrl)
}
export function getProxyFetch(destinationUrl): typeof fetch {
const httpDispatcher = getProxyAgentDispatcher(destinationUrl)
const proxyFetch: typeof fetch = async (url, opts) => {
return fetch(url, {
...opts,
dispatcher: httpDispatcher
})
}
return proxyFetch
}
export function getApiBaseUrl(): string {
return process.env['GITHUB_API_URL'] || 'https://api.github.com'
}
+2 -1
View File
@@ -13,7 +13,8 @@ const baseUrl = Utils.getApiBaseUrl()
export const defaults: OctokitOptions = {
baseUrl,
request: {
agent: Utils.getProxyAgent(baseUrl)
agent: Utils.getProxyAgent(baseUrl),
fetch: Utils.getProxyFetch(baseUrl)
}
}
@@ -255,6 +255,7 @@ describe('globber', () => {
'file-under-a'
)
])
await unlinkSymlinkDir(itemPaths)
})
it('detects cycle starting from symlink when followSymbolicLinks=true', async () => {
@@ -861,6 +862,14 @@ async function createHiddenFile(file: string, content: string): Promise<void> {
function getTestTemp(): string {
return path.join(__dirname, '_temp', 'glob')
}
/**
* Deletes a symlink directory
*/
async function unlinkSymlinkDir(links: string[]): Promise<void> {
for (const link of links) {
await fs.rm(link, {recursive: true, force: true})
}
}
/**
* Creates a symlink directory on OSX/Linux, and a junction point directory on Windows.
+25
View File
@@ -30,12 +30,21 @@
},
"node_modules/@actions/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
"dependencies": {
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
}
},
"node_modules/@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
"integrity": "sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -65,6 +74,22 @@
"engines": {
"node": "*"
}
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
}
},
"bugs": {
+6
View File
@@ -1,5 +1,11 @@
## Releases
## 2.2.0
- Add function to return proxy agent dispatcher for compatibility with latest octokit packages [#1547](https://github.com/actions/toolkit/pull/1547)
## 2.1.1
- Add `HttpClientResponse.readBodyBuffer` method to read from a response stream and return a buffer [#1475](https://github.com/actions/toolkit/pull/1475)
## 2.1.0
- Add support for `*` and subdomains in `no_proxy` [#1355](https://github.com/actions/toolkit/pull/1355) [#1223](https://github.com/actions/toolkit/pull/1223)
- Bypass proxy for loopback IP adresses [#1360](https://github.com/actions/toolkit/pull/1360))
+2 -3
View File
@@ -31,9 +31,8 @@ describe('auth', () => {
it('does basic http get request with pat token auth', async () => {
const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
const ph: am.PersonalAccessTokenCredentialHandler = new am.PersonalAccessTokenCredentialHandler(
token
)
const ph: am.PersonalAccessTokenCredentialHandler =
new am.PersonalAccessTokenCredentialHandler(token)
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [
ph
+11 -1
View File
@@ -3,6 +3,7 @@
import * as http from 'http'
import * as httpm from '../lib/'
import * as pm from '../lib/proxy'
import {ProxyAgent} from 'undici'
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const proxy = require('proxy')
@@ -13,7 +14,7 @@ const _proxyUrl = 'http://127.0.0.1:8080'
describe('proxy', () => {
beforeAll(async () => {
// Start proxy server
_proxyServer = proxy()
_proxyServer = proxy.createProxy()
await new Promise<void>(resolve => {
const port = Number(_proxyUrl.split(':')[2])
_proxyServer.listen(port, () => resolve())
@@ -294,6 +295,15 @@ describe('proxy', () => {
expect(agent.proxyOptions.port).toBe('8080')
expect(agent.proxyOptions.proxyAuth).toBe('user:password')
})
it('ProxyAgent is returned when proxy setting are provided', async () => {
process.env['https_proxy'] = 'http://127.0.0.1:8080'
const httpClient = new httpm.HttpClient()
const agent = httpClient.getAgentDispatcher('https://some-url')
// eslint-disable-next-line no-console
console.log(agent)
expect(agent instanceof ProxyAgent).toBe(true)
})
})
function _clearVars(): void {
+113 -53
View File
@@ -1,27 +1,47 @@
{
"name": "@actions/http-client",
"version": "2.0.1",
"version": "2.2.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/http-client",
"version": "2.0.1",
"version": "2.2.0",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "^0.0.6",
"undici": "^5.25.4"
},
"devDependencies": {
"@types/node": "20.7.1",
"@types/proxy": "^1.0.1",
"@types/tunnel": "0.0.3",
"proxy": "^1.0.1"
"proxy": "^2.1.1"
}
},
"node_modules/@fastify/busboy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==",
"engines": {
"node": ">=14"
}
},
"node_modules/@types/node": {
"version": "12.12.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.31.tgz",
"integrity": "sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg==",
"version": "20.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz",
"integrity": "sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==",
"dev": true
},
"node_modules/@types/proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-NDNsg7YuClVzEenn9SUButu43blypWvljGsIkDV7HI4N9apjrS0aeeMTUG0PYa71lD1AvIgvjkBagqHDiomDjA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/tunnel": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
@@ -44,9 +64,9 @@
}
},
"node_modules/args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
"integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"dev": true,
"dependencies": {
"camelcase": "5.0.0",
@@ -59,9 +79,9 @@
}
},
"node_modules/basic-auth-parser": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
"integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz",
"integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==",
"dev": true
},
"node_modules/camelcase": {
@@ -99,23 +119,30 @@
"node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"node_modules/debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true,
"engines": {
"node": ">=0.8.0"
@@ -124,7 +151,7 @@
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
@@ -133,7 +160,7 @@
"node_modules/leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -155,17 +182,17 @@
"dev": true
},
"node_modules/proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"dev": true,
"dependencies": {
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
"args": "^5.0.3",
"basic-auth-parser": "0.0.2-1",
"debug": "^4.3.4"
},
"bin": {
"proxy": "bin/proxy.js"
"engines": {
"node": ">= 14"
}
},
"node_modules/supports-color": {
@@ -187,15 +214,40 @@
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/undici": {
"version": "5.25.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz",
"integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
}
}
},
"dependencies": {
"@fastify/busboy": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ=="
},
"@types/node": {
"version": "12.12.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.31.tgz",
"integrity": "sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg==",
"version": "20.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.7.1.tgz",
"integrity": "sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==",
"dev": true
},
"@types/proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-NDNsg7YuClVzEenn9SUButu43blypWvljGsIkDV7HI4N9apjrS0aeeMTUG0PYa71lD1AvIgvjkBagqHDiomDjA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/tunnel": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
@@ -215,9 +267,9 @@
}
},
"args": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
"integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"dev": true,
"requires": {
"camelcase": "5.0.0",
@@ -227,9 +279,9 @@
}
},
"basic-auth-parser": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
"integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz",
"integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==",
"dev": true
},
"camelcase": {
@@ -261,34 +313,34 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
"ms": "2.1.2"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
"dev": true
},
"mri": {
@@ -304,14 +356,14 @@
"dev": true
},
"proxy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"dev": true,
"requires": {
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
"args": "^5.0.3",
"basic-auth-parser": "0.0.2-1",
"debug": "^4.3.4"
}
},
"supports-color": {
@@ -327,6 +379,14 @@
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"undici": {
"version": "5.25.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.25.4.tgz",
"integrity": "sha512-450yJxT29qKMf3aoudzFpIciqpx6Pji3hEWaXqXmanbXF58LTAGCKxcJjxMXWu3iG+Mudgo3ZUfDB6YDFd/dAw==",
"requires": {
"@fastify/busboy": "^2.0.0"
}
}
}
}
+6 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/http-client",
"version": "2.1.0",
"version": "2.2.0",
"description": "Actions Http Client",
"keywords": [
"github",
@@ -39,10 +39,13 @@
"url": "https://github.com/actions/toolkit/issues"
},
"devDependencies": {
"@types/node": "20.7.1",
"@types/tunnel": "0.0.3",
"proxy": "^1.0.1"
"proxy": "^2.1.1",
"@types/proxy": "^1.0.1"
},
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "^0.0.6",
"undici": "^5.25.4"
}
}
+2 -1
View File
@@ -57,7 +57,8 @@ export class BearerCredentialHandler implements ifm.RequestHandler {
}
export class PersonalAccessTokenCredentialHandler
implements ifm.RequestHandler {
implements ifm.RequestHandler
{
token: string
constructor(token: string) {
+63 -2
View File
@@ -6,6 +6,7 @@ import * as ifm from './interfaces'
import * as net from 'net'
import * as pm from './proxy'
import * as tunnel from 'tunnel'
import {ProxyAgent} from 'undici'
export enum HttpCodes {
OK = 200,
@@ -102,6 +103,20 @@ export class HttpClientResponse {
})
})
}
async readBodyBuffer?(): Promise<Buffer> {
return new Promise<Buffer>(async resolve => {
const chunks: Buffer[] = []
this.message.on('data', (chunk: Buffer) => {
chunks.push(chunk)
})
this.message.on('end', () => {
resolve(Buffer.concat(chunks))
})
})
}
}
export function isHttps(requestUrl: string): boolean {
@@ -123,6 +138,7 @@ export class HttpClient {
private _maxRetries = 1
private _agent: any
private _proxyAgent: any
private _proxyAgentDispatcher: any
private _keepAlive = false
private _disposed = false
@@ -519,7 +535,7 @@ export class HttpClient {
handleResult(new Error(`Request timeout: ${info.options.path}`))
})
req.on('error', function(err) {
req.on('error', function (err) {
// err has statusCode property
// res should have headers
handleResult(err)
@@ -530,7 +546,7 @@ export class HttpClient {
}
if (data && typeof data !== 'string') {
data.on('close', function() {
data.on('close', function () {
req.end()
})
@@ -550,6 +566,17 @@ export class HttpClient {
return this._getAgent(parsedUrl)
}
getAgentDispatcher(serverUrl: string): ProxyAgent | undefined {
const parsedUrl = new URL(serverUrl)
const proxyUrl = pm.getProxyUrl(parsedUrl)
const useProxy = proxyUrl && proxyUrl.hostname
if (!useProxy) {
return
}
return this._getProxyAgentDispatcher(parsedUrl, proxyUrl)
}
private _prepareRequest(
method: string,
requestUrl: URL,
@@ -687,6 +714,40 @@ export class HttpClient {
return agent
}
private _getProxyAgentDispatcher(parsedUrl: URL, proxyUrl: URL): ProxyAgent {
let proxyAgent
if (this._keepAlive) {
proxyAgent = this._proxyAgentDispatcher
}
// if agent is already assigned use that agent.
if (proxyAgent) {
return proxyAgent
}
const usingSsl = parsedUrl.protocol === 'https:'
proxyAgent = new ProxyAgent({
uri: proxyUrl.href,
pipelining: !this._keepAlive ? 0 : 1,
...((proxyUrl.username || proxyUrl.password) && {
token: `${proxyUrl.username}:${proxyUrl.password}`
})
})
this._proxyAgentDispatcher = proxyAgent
if (usingSsl && this._ignoreSslError) {
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
// we have to cast it to any and change it directly
proxyAgent.options = Object.assign(proxyAgent.options.requestTls || {}, {
rejectUnauthorized: false
})
}
return proxyAgent
}
private async _performExponentialBackoff(retryNumber: number): Promise<void> {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
+2 -1
View File
@@ -3,7 +3,8 @@
"compilerOptions": {
"outDir": "./lib",
"rootDir": "./src",
"moduleResolution": "node"
"moduleResolution": "node",
"declaration": true,
},
"include": [
"./src"
+6 -2
View File
@@ -167,8 +167,12 @@ function normalizeSeparators(p: string): string {
function isUnixExecutable(stats: fs.Stats): boolean {
return (
(stats.mode & 1) > 0 ||
((stats.mode & 8) > 0 && stats.gid === process.getgid()) ||
((stats.mode & 64) > 0 && stats.uid === process.getuid())
((stats.mode & 8) > 0 &&
process.getgid !== undefined &&
stats.gid === process.getgid()) ||
((stats.mode & 64) > 0 &&
process.getuid !== undefined &&
stats.uid === process.getuid())
)
}

Some files were not shown because too many files have changed in this diff Show More