Compare commits

..

1 Commits

Author SHA1 Message Date
Tatyana Kostromskaya 0a23c371a8 update release notes 2023-10-06 12:57:25 +00:00
66 changed files with 1331 additions and 5178 deletions
+47 -78
View File
@@ -1,3 +1,5 @@
# Temporarily disabled while v2.0.0 of @actions/artifact is under development
name: artifact-unit-tests
on:
push:
@@ -10,8 +12,8 @@ on:
- '**.md'
jobs:
upload:
name: Upload
build:
name: Build
strategy:
matrix:
@@ -22,10 +24,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set Node.js 20.x
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 20.x
@@ -40,47 +42,51 @@ jobs:
npm run tsc
working-directory: packages/artifact
- name: Set artifact file contents
shell: bash
run: |
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 -n 'hello from file 1' > artifact-path/first.txt
echo -n 'hello from file 2' > artifact-path/second.txt
echo '${{ env.file1 }}' > artifact-path/first.txt
echo '${{ env.file2 }}' > artifact-path/second.txt
- name: Upload Artifacts
uses: actions/github-script@v7
- name: Upload Artifacts using actions/github-script@v6
uses: actions/github-script@v6
with:
script: |
const {default: artifact} = require('./packages/artifact/lib/artifact')
const artifact = require('./packages/artifact/lib/artifact')
const artifactName = 'my-artifact-${{ matrix.runs-on }}'
console.log('artifactName: ' + artifactName)
const fileContents = ['artifact-path/first.txt','artifact-path/second.txt']
const uploadResult = await artifact.uploadArtifact(artifactName, fileContents, './')
const uploadResult = await artifact.create().uploadArtifact(artifactName, fileContents, './')
console.log(uploadResult)
const success = uploadResult.success
const size = uploadResult.size
const id = uploadResult.id
console.log(`Successfully uploaded artifact ${id}`)
try {
await artifact.uploadArtifact(artifactName, fileContents, './')
throw new Error('should have failed second upload')
} catch (err) {
console.log('Successfully blocked second artifact upload')
if (!success) {
throw new Error('Failed to upload artifact')
} else {
console.log(`Successfully uploaded artifact ${id}`)
}
verify:
name: Verify
runs-on: ubuntu-latest
needs: [upload]
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set Node.js 20.x
uses: actions/setup-node@v4
uses: actions/setup-node@v3
with:
node-version: 20.x
@@ -95,72 +101,35 @@ jobs:
npm run tsc
working-directory: packages/artifact
- name: List and Download Artifacts
uses: actions/github-script@v7
- name: List artifacts using actions/github-script@v6
uses: actions/github-script@v6
with:
script: |
const {default: artifactClient} = require('./packages/artifact/lib/artifact')
const artifact = require('./packages/artifact/lib/artifact')
const {readFile} = require('fs/promises')
const path = require('path')
const workflowRunId = process.env.GITHUB_RUN_ID
const repository = process.env.GITHUB_REPOSITORY
const repositoryOwner = repository.split('/')[0]
const repositoryName = repository.split('/')[1]
const findBy = {
repositoryOwner: process.env.GITHUB_REPOSITORY.split('/')[0],
repositoryName: process.env.GITHUB_REPOSITORY.split('/')[1],
token: '${{ secrets.GITHUB_TOKEN }}',
workflowRunId: process.env.GITHUB_RUN_ID
}
const listResult = await artifactClient.listArtifacts({latest: true, findBy})
const listResult = await artifact.create().listArtifacts(workflowRunId, repositoryOwner, repositoryName, '${{ secrets.GITHUB_TOKEN }}')
console.log(listResult)
const artifacts = listResult.artifacts
const expected = [
'my-artifact-ubuntu-latest',
'my-artifact-windows-latest',
'my-artifact-macos-latest'
]
const foundArtifacts = artifacts.filter(artifact =>
expected.includes(artifact.name)
)
if (artifacts.length !== 3) {
throw new Error('Expected 3 artifacts but only found ' + artifacts.length + ' artifacts')
}
if (foundArtifacts.length !== 3) {
console.log('Unexpected length of found artifacts', foundArtifacts)
throw new Error(
`Expected 3 artifacts but found ${foundArtifacts.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')
const files = [
{name: 'artifact-path/first.txt', content: 'hello from file 1'},
{name: 'artifact-path/second.txt', content: 'hello from file 2'}
]
for (const artifact of foundArtifacts) {
const {downloadPath} = await artifactClient.downloadArtifact(artifact.id, {
path: artifact.name,
findBy
})
console.log('Downloaded artifact to:', downloadPath)
for (const file of files) {
const filepath = path.join(
process.env.GITHUB_WORKSPACE,
downloadPath,
file.name
)
console.log('Checking file:', filepath)
const content = await readFile(filepath, 'utf8')
if (content.trim() !== file.content.trim()) {
throw new Error(
`Expected file '${file.name}' to contain '${file.content}' but found '${content}'`
)
}
}
}
+1 -1
View File
@@ -8,4 +8,4 @@ module.exports = {
'^.+\\.ts$': 'ts-jest'
},
verbose: true
}
}
+43 -43
View File
@@ -185,12 +185,12 @@
}
},
"node_modules/@babel/generator": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"version": "7.22.10",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
"integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
"dev": true,
"dependencies": {
"@babel/types": "^7.23.0",
"@babel/types": "^7.22.10",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
@@ -225,22 +225,22 @@
}
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
"dev": true,
"dependencies": {
"@babel/template": "^7.22.15",
"@babel/types": "^7.23.0"
"@babel/template": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
@@ -332,9 +332,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@@ -449,9 +449,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.13.tgz",
"integrity": "sha512-3l6+4YOvc9wx7VlCSw4yQfcBo01ECA8TicQfbnCPuCEpRQrf+gTUyGdxNw+pyTUyywp6JRD1w0YQs9TpBXYlkw==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -650,33 +650,33 @@
}
},
"node_modules/@babel/template": {
"version": "7.22.15",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.22.13",
"@babel/parser": "^7.22.15",
"@babel/types": "^7.22.15"
"@babel/code-frame": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz",
"integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"@babel/parser": "^7.22.11",
"@babel/types": "^7.22.11",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@@ -694,13 +694,13 @@
}
},
"node_modules/@babel/types": {
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
"integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.20",
"@babel/helper-validator-identifier": "^7.22.5",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -3262,9 +3262,9 @@
}
},
"node_modules/axios": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz",
"integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
"integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.0",
@@ -5781,9 +5781,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"dev": true,
"funding": [
{
+1 -1
View File
@@ -13,7 +13,7 @@
"lint": "eslint packages/**/*.ts",
"lint-fix": "eslint packages/**/*.ts --fix",
"new-package": "scripts/create-package",
"test": "jest --testTimeout 60000"
"test": "jest --testTimeout 10000"
},
"devDependencies": {
"@types/jest": "^29.5.4",
+12 -25
View File
@@ -1,43 +1,30 @@
# Contributions
This package is used internally by the v4 versions of [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact). This package can also be used by other actions to interact with artifacts. Any changes or updates to this package will propagate updates to these actions so it is important that major changes or updates get properly tested.
This package is used internally by the v2+ versions of [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact). This package can also be used by other actions to interact with artifacts. Any changes or updates to this package will propagate updates to these actions so it is important that major changes or updates get properly tested.
Any issues or feature requests that are related to the artifact actions should be filled in the appropriate repo.
A limited range of unit tests run as part of each PR when making changes to the artifact packages. For small contributions and fixes, they should be sufficient.
If making large changes, there are a few scenarios that should be tested:
If making large changes, there are a few scenarios that should be tested.
- Uploading very large artifacts
- Uploading artifacts with lots of small files
- Uploading very large artifacts (large artifacts get compressed using gzip so compression/decompression must be tested)
- Uploading artifacts with lots of small files (each file is uploaded with its own HTTP call, timeouts and non-success HTTP responses can be expected so they must be properly handled)
- Uploading artifacts using a self-hosted runner (uploads and downloads behave differently due to extra latency)
- Downloading a single artifact (large and small, if lots of small files are part of an artifact, timeouts and non-success HTTP responses can be expected)
- Downloading all artifacts at once
Large architectural changes can impact upload/download performance so it is important to separately run extra tests. We request that any large contributions/changes have extra detailed testing so we can verify performance and possible regressions.
Tests will run for every push/pull_request [via Actions](https://github.com/actions/toolkit/blob/main/.github/workflows/artifact-tests.yml).
It is not possible to run end-to-end tests for artifacts as part of a PR in this repo because certain env variables such as `ACTIONS_RUNTIME_URL` are only available from the context of an action as opposed to a shell script. These env variables are needed in order to make the necessary API calls.
# Testing
## Package tests
Any easy way to test changes is to fork the artifact actions and to use `npm link` to test your changes.
To run unit tests for the `@actions/artifact` package:
1. Clone `actions/toolkit` locally
2. Install dependencies: `npm bootstrap`
3. Change working directory to `packages/artifact`
4. Run jest tests: `npm run test`
## Within upload-artifact or download-artifact actions
Any easy way to test changes for the official upload/download actions is to fork them, compile changes and run them.
1. For your local `actions/toolkit` changes:
1. Change directory to `packages/artifact`
2. Compile the changes: `npm run tsc`
3. Symlink your package change: `npm link`
2. Fork and clone either [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact)
1. In the locally cloned fork, link to your local toolkit changes: `npm link @actions/artifact`
2. Then, compile your changes with: `npm run release`. The local `dist/index.js` should be updated with your changes.
3. Commit and push to your fork, you can then test with a `uses:` in your workflow pointed at your fork.
1. Fork the [upload-artifact](https://github.com/actions/upload-artifact) and [download-artifact](https://github.com/actions/download-artifact) repos
2. Clone the forks locally
3. With your local changes to the toolkit repo, type `npm link` after ensuring there are no errors when running `tsc`
4. In the locally cloned fork, type `npm link @actions/artifact`
4. Create a new release for your local fork using `tsc` and `npm run release` (this will create a new `dist/index.js` file using `@vercel/ncc`)
5. Commit and push your local changes, you will then be able to test your changes with your forked action
+6 -149
View File
@@ -1,156 +1,13 @@
# `@actions/artifact`
Interact programmatically with [Actions Artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts).
## Usage
This is the core library that powers the [`@actions/upload-artifact`](https://github.com/actions/upload-artifact) and [`@actions/download-artifact`](https://github.com/actions/download-artifact) actions.
You can use this package to interact with the Actions artifacts.
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)
- [`@actions/artifact`](#actionsartifact)
- [v2 - What's New](#v2---whats-new)
- [Improvements](#improvements)
- [Breaking changes](#breaking-changes)
- [Quick Start](#quick-start)
- [Examples](#examples)
- [Upload and Download](#upload-and-download)
- [Downloading from other workflow runs or repos](#downloading-from-other-workflow-runs-or-repos)
- [Speeding up large uploads](#speeding-up-large-uploads)
- [Additional Resources](#additional-resources)
## 🚧 Under construction 🚧
## v2 - What's New
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.
> [!IMPORTANT]
> @actions/artifact v2+, upload-artifact@v4+, and download-artifact@v4+ are not currently supported on GHES yet. The previous version of this package can be found at [this tag](https://github.com/actions/toolkit/tree/@actions/artifact@1.1.2/packages/artifact) and [on npm](https://www.npmjs.com/package/@actions/artifact/v/1.1.2).
The release of `@actions/artifact@v2` (including `upload-artifact@v4` and `download-artifact@v4`) are major changes to the backend architecture of Artifacts. They have numerous performance and behavioral improvements.
### Improvements
1. All upload and download operations are much quicker, up to 80% faster download times and 96% faster upload times in worst case scenarios.
2. Once uploaded, an Artifact ID is returned and Artifacts are immediately available in the UI and [REST API](https://docs.github.com/en/rest/actions/artifacts). Previously, you would have to wait for the run to be completed before an ID was available or any APIs could be utilized.
3. Artifacts can now be downloaded and deleted from the UI _before_ the entire workflow run finishes.
4. The contents of an Artifact are uploaded together into an _immutable_ archive. They cannot be altered by subsequent jobs. Both of these factors help reduce the possibility of accidentally corrupting Artifact files. (Digest/integrity hash coming soon in the API!)
5. This library (and `actions/download-artifact`) now support downloading Artifacts from _other_ repositories and runs if a `GITHUB_TOKEN` with sufficient `actions:read` permissions are provided.
### Breaking changes
1. Firewall rules required for self-hosted runners.
If you are using self-hosted runners behind a firewall, you must have flows open to [Actions endpoints](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github). If you cannot use wildcard rules for your firewall, see the GitHub [meta endpoint](https://api.github.com/meta) for specific endpoints.
e.g.
```bash
curl https://api.github.com/meta | jq .domains.actions
```
2. Uploading to the same named Artifact multiple times.
Due to how Artifacts are created in this new version, it is no longer possible to upload to the same named Artifact multiple times. You must either split the uploads into multiple Artifacts with different names, or only upload once.
3. Limit of Artifacts for an individual job.
Each job in a workflow run now has a limit of 10 artifacts.
## Quick Start
Install the package:
```bash
npm i @actions/artifact
```
Import the module:
```js
// ES6 module
import {DefaultArtifactClient} from '@actions/artifact'
// CommonJS
const {DefaultArtifactClient} = require('@actions/artifact')
```
Then instantiate:
```js
const artifact = new DefaultArtifactClient()
```
️ For a comprehensive list of classes, interfaces, functions and more, see the [generated documentation](./docs/generated/README.md).
## Examples
### Upload and Download
The most basic scenario is uploading one or more files to an Artifact, then downloading that Artifact. Downloads are based on the Artifact ID, which can be obtained in the response of `uploadArtifact`, `getArtifact`, `listArtifacts` or via the [REST API](https://docs.github.com/en/rest/actions/artifacts).
```js
const {id, size} = await artifact.uploadArtifact(
// name of the artifact
'my-artifact',
// files to include (supports absolute and relative paths)
['/absolute/path/file1.txt', './relative/file2.txt'],
{
// optional: how long to retain the artifact
// if unspecified, defaults to repository/org retention settings (the limit of this value)
retentionDays: 10
}
)
console.log(`Created artifact with id: ${id} (bytes: ${size}`)
const {downloadPath} = await artifact.downloadArtifact(id, {
// optional: download destination path. otherwise defaults to $GITHUB_WORKSPACE
path: '/tmp/dst/path',
})
console.log(`Downloaded artifact ${id} to: ${downloadPath}`)
```
### Downloading from other workflow runs or repos
It may be useful to download Artifacts from other workflow runs, or even other repositories. By default, the permissions are scoped so they can only download Artifacts within the current workflow run. To elevate permissions for this scenario, you must specify `options.findBy` to `downloadArtifact`.
```ts
const findBy = {
// must have actions:read permission on target repository
token: process.env['GITHUB_TOKEN'],
workflowRunId: 123,
repositoryOwner: 'actions',
repositoryName: 'toolkit'
}
await artifact.downloadArtifact(1337, {
findBy
})
// can also be used in other methods
await artifact.getArtifact('my-artifact', {
findBy
})
await artifact.listArtifacts({
findBy
})
```
### Speeding up large uploads
If you have large files that need to be uploaded (or file types that don't compress well), you may benefit from changing the compression level of the Artifact archive. NOTE: This is a tradeoff between artifact upload time and stored data size.
```ts
await artifact.uploadArtifact('my-massive-artifact', ['big_file.bin'], {
// The level of compression for Zlib to be applied to the artifact archive.
// - 0: No compression
// - 1: Best speed
// - 6: Default compression (same as GNU Gzip)
// - 9: Best compression
compressionLevel: 0
})
```
## Additional Resources
- [Releases](./RELEASES.md)
- [Contribution Guide](./CONTRIBUTIONS.md)
- [Frequently Asked Questions](./docs/faq.md)
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.
-11
View File
@@ -94,14 +94,3 @@
### 1.1.1
- Fixed a bug in Node16 where if an HTTP download finished too quickly (<1ms, e.g. when it's mocked) we attempt to delete a temp file that has not been created yet [#1278](https://github.com/actions/toolkit/pull/1278/commits/b9de68a590daf37c6747e38d3cb4f1dd2cfb791c)
### 2.0.0
- Major release. Supports new Artifact backend for improved speed, reliability and behavior.
- Numerous API changes, [some breaking](./README.md#breaking-changes).
- Blog post with more info: TBD
### 2.0.1
- Patch to fix transient request timeouts https://github.com/actions/download-artifact/issues/249
@@ -2,21 +2,18 @@ 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 {internalArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client'
import {noopLogs} from './common'
import {NetworkError, UsageError} from '../src/internal/shared/errors'
import {createArtifactTwirpClient} from '../src/internal/shared/artifact-twirp-client'
import * as core from '@actions/core'
jest.mock('@actions/http-client')
const clientOptions = {
maxAttempts: 5,
retryIntervalMs: 1,
retryMultiplier: 1.5
}
describe('artifact-http-client', () => {
beforeAll(() => {
noopLogs()
// 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')
@@ -28,7 +25,7 @@ describe('artifact-http-client', () => {
})
it('should successfully create a client', () => {
const client = internalArtifactTwirpClient()
const client = createArtifactTwirpClient('upload')
expect(client).toBeDefined()
})
@@ -53,7 +50,7 @@ describe('artifact-http-client', () => {
}
})
const client = internalArtifactTwirpClient()
const client = createArtifactTwirpClient('upload')
const artifact = await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
@@ -101,7 +98,12 @@ describe('artifact-http-client', () => {
}
})
const client = internalArtifactTwirpClient(clientOptions)
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',
@@ -136,7 +138,12 @@ describe('artifact-http-client', () => {
post: mockPost
}
})
const client = internalArtifactTwirpClient(clientOptions)
const client = createArtifactTwirpClient(
'upload',
5, // retry 5 times
1, // wait 1 ms
1.5 // backoff factor
)
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
@@ -171,7 +178,12 @@ describe('artifact-http-client', () => {
post: mockPost
}
})
const client = internalArtifactTwirpClient(clientOptions)
const client = createArtifactTwirpClient(
'upload',
5, // retry 5 times
1, // wait 1 ms
1.5 // backoff factor
)
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
@@ -185,116 +197,4 @@ describe('artifact-http-client', () => {
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
})
it('should fail with a descriptive error', async () => {
// 409 duplicate error
const mockPost = jest.fn(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 409
msgFailed.statusMessage = 'Conflict'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve(
`{"msg": "an artifact with this name already exists on the workflow run"}`
)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalArtifactTwirpClient(clientOptions)
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
}).rejects.toThrowError(
'Failed to CreateArtifact: Received non-retryable error: Failed request: (409) Conflict: an artifact with this name already exists on the workflow run'
)
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
})
it('should properly describe a network failure', async () => {
class FakeNodeError extends Error {
code: string
constructor(code: string) {
super()
this.code = code
}
}
const mockPost = jest.fn(() => {
throw new FakeNodeError('ENOTFOUND')
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalArtifactTwirpClient()
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
}).rejects.toThrowError(new NetworkError('ENOTFOUND').message)
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
})
it('should properly describe a usage error', async () => {
const mockPost = jest.fn(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 403
msgFailed.statusMessage = 'Forbidden'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve(
`{"msg": "insufficient usage to create artifact"}`
)
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalArtifactTwirpClient()
await expect(async () => {
await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
}).rejects.toThrowError(new UsageError().message)
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(mockPost).toHaveBeenCalledTimes(1)
})
})
-9
View File
@@ -1,9 +0,0 @@
import * as core from '@actions/core'
// noopLogs mocks the console.log and core.* functions to prevent output in the console while testing
export const noopLogs = (): void => {
jest.spyOn(console, 'log').mockImplementation(() => {})
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
}
@@ -2,21 +2,14 @@ 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 {
downloadArtifactInternal,
downloadArtifactPublic,
streamExtractExternal
} from '../src/internal/download/download-artifact'
import {downloadArtifact} from '../src/internal/download/download-artifact'
import {getUserAgentString} from '../src/internal/shared/user-agent'
import {noopLogs} from './common'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON} from '../src/generated'
import * as util from '../src/internal/shared/util'
type MockedDownloadArtifact = jest.MockedFunction<
RestEndpointMethods['actions']['downloadArtifact']
@@ -39,16 +32,10 @@ const fixtures = {
]
},
artifactID: 1234,
artifactName: 'my-artifact',
artifactSize: 123456,
repositoryOwner: 'actions',
repositoryName: 'toolkit',
token: 'ghp_1234567890',
blobStorageUrl: 'https://blob-storage.local?signed=true',
backendIds: {
workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422',
workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf'
}
blobStorageUrl: 'https://blob-storage.local?signed=true'
}
jest.mock('@actions/github', () => ({
@@ -87,473 +74,206 @@ const expectExtractedArchive = async (dir: string): Promise<void> => {
}
}
const setup = async (): Promise<void> => {
noopLogs()
await fs.promises.mkdir(testDir, {recursive: true})
await createTestArchive()
process.env['GITHUB_WORKSPACE'] = fixtures.workspaceDir
}
const cleanup = async (): Promise<void> => {
jest.restoreAllMocks()
await fs.promises.rm(testDir, {recursive: true})
delete process.env['GITHUB_WORKSPACE']
}
const mockGetArtifactSuccess = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 200
message.push(fs.readFileSync(fixtures.exampleArtifact.path))
message.push(null)
return {
message
}
})
const mockGetArtifactFailure = jest.fn(() => {
const message = new http.IncomingMessage(new net.Socket())
message.statusCode = 500
message.push('Internal Server Error')
message.push(null)
return {
message
}
})
describe('download-artifact', () => {
describe('public', () => {
beforeEach(setup)
afterEach(cleanup)
beforeEach(async () => {
jest.spyOn(core, 'debug').mockImplementation(() => {})
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
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('')
})
await fs.promises.mkdir(testDir, {recursive: true})
await createTestArchive()
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
const response = await downloadArtifactPublic(
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(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockGetArtifactSuccess).toHaveBeenCalledWith(
fixtures.blobStorageUrl
)
expectExtractedArchive(fixtures.workspaceDir)
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 mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
const response = await downloadArtifactPublic(
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(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockGetArtifactSuccess).toHaveBeenCalledWith(
fixtures.blobStorageUrl
)
expectExtractedArchive(customPath)
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(
downloadArtifactPublic(
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 storage chunk does not respond within 30s', async () => {
// mock http client to delay response data by 30s
const msg = new http.IncomingMessage(new net.Socket())
msg.statusCode = 200
const mockGet = jest.fn(async () => {
return new Promise((resolve, reject) => {
// Resolve with a 200 status code immediately
resolve({
message: msg,
readBody: async () => {
return Promise.resolve(`{"ok": true}`)
}
})
// Reject with an error after 31 seconds
setTimeout(() => {
reject(new Error('Request timeout'))
}, 31000) // Timeout after 31 seconds
})
})
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGet
}
}
)
await expect(
streamExtractExternal(fixtures.blobStorageUrl, fixtures.workspaceDir)
).rejects.toBeInstanceOf(Error)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
}, 35000) // add longer timeout to allow for timer to run out
it('should fail if blob storage response is non-200 after 5 retries', 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 mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactFailure
}
}
)
await expect(
downloadArtifactPublic(
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(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockGetArtifactFailure).toHaveBeenCalledWith(
fixtures.blobStorageUrl
)
expect(mockGetArtifactFailure).toHaveBeenCalledTimes(5)
}, 38000)
it('should retry if blob storage response is non-200 and then succeed with a 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 mockGetArtifact = jest
.fn(mockGetArtifactSuccess)
.mockImplementationOnce(mockGetArtifactFailure)
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifact
}
}
)
const response = await downloadArtifactPublic(
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(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockGetArtifactFailure).toHaveBeenCalledWith(
fixtures.blobStorageUrl
)
expect(mockGetArtifactFailure).toHaveBeenCalledTimes(1)
expect(mockGetArtifactSuccess).toHaveBeenCalledWith(
fixtures.blobStorageUrl
)
expect(mockGetArtifactSuccess).toHaveBeenCalledTimes(1)
expect(response.downloadPath).toBe(fixtures.workspaceDir)
}, 28000)
process.env['GITHUB_WORKSPACE'] = fixtures.workspaceDir
})
describe('internal', () => {
beforeEach(async () => {
await setup()
afterEach(async () => {
jest.restoreAllMocks()
await fs.promises.rm(testDir, {recursive: true})
delete process.env['GITHUB_WORKSPACE']
})
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(util, 'getBackendIdsFromToken')
.mockReturnValue(fixtures.backendIds)
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://results.local')
})
afterEach(async () => {
await cleanup()
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('')
})
it('should successfully download an artifact to $GITHUB_WORKSPACE', async () => {
const mockListArtifacts = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: [
{
...fixtures.backendIds,
databaseId: fixtures.artifactID.toString(),
name: fixtures.artifactName,
size: fixtures.artifactSize.toString()
}
]
})
const mockGetSignedArtifactURL = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL')
.mockReturnValue(
Promise.resolve({
signedUrl: fixtures.blobStorageUrl
})
)
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
const response = await downloadArtifactInternal(fixtures.artifactID)
expectExtractedArchive(fixtures.workspaceDir)
expect(response.downloadPath).toBe(fixtures.workspaceDir)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockListArtifacts).toHaveBeenCalledWith({
idFilter: {
value: fixtures.artifactID.toString()
},
...fixtures.backendIds
})
expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({
...fixtures.backendIds,
name: fixtures.artifactName
})
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
}
})
it('should successfully download an artifact to user defined path', async () => {
const customPath = path.join(testDir, 'custom')
const response = await downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token
)
const mockListArtifacts = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: [
{
...fixtures.backendIds,
databaseId: fixtures.artifactID.toString(),
name: fixtures.artifactName,
size: fixtures.artifactSize.toString()
}
]
})
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)
const mockGetSignedArtifactURL = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL')
.mockReturnValue(
Promise.resolve({
signedUrl: fixtures.blobStorageUrl
})
)
expectExtractedArchive(fixtures.workspaceDir)
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactSuccess
}
}
)
expect(response.success).toBe(true)
expect(response.downloadPath).toBe(fixtures.workspaceDir)
})
const response = await downloadArtifactInternal(fixtures.artifactID, {
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
})
}
)
expectExtractedArchive(customPath)
expect(response.downloadPath).toBe(customPath)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockListArtifacts).toHaveBeenCalledWith({
idFilter: {
value: fixtures.artifactID.toString()
},
...fixtures.backendIds
})
expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({
...fixtures.backendIds,
name: fixtures.artifactName
})
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('')
})
it('should fail if download artifact API does not respond with location', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockRejectedValue(new Error('boom'))
await expect(
downloadArtifactInternal(fixtures.artifactID)
).rejects.toBeInstanceOf(Error)
})
it('should fail if blob storage response is non-200', async () => {
const mockListArtifacts = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: [
{
...fixtures.backendIds,
databaseId: fixtures.artifactID.toString(),
name: fixtures.artifactName,
size: fixtures.artifactSize.toString()
}
]
})
const mockGetSignedArtifactURL = jest
.spyOn(ArtifactServiceClientJSON.prototype, 'GetSignedArtifactURL')
.mockReturnValue(
Promise.resolve({
signedUrl: fixtures.blobStorageUrl
})
)
const mockHttpClient = (HttpClient as jest.Mock).mockImplementation(
() => {
return {
get: mockGetArtifactFailure
}
}
await expect(
downloadArtifact(
fixtures.artifactID,
fixtures.repositoryOwner,
fixtures.repositoryName,
fixtures.token
)
).rejects.toBeInstanceOf(Error)
await expect(
downloadArtifactInternal(fixtures.artifactID)
).rejects.toBeInstanceOf(Error)
expect(mockHttpClient).toHaveBeenCalledWith(getUserAgentString())
expect(mockListArtifacts).toHaveBeenCalledWith({
idFilter: {
value: fixtures.artifactID.toString()
},
...fixtures.backendIds
})
expect(mockGetSignedArtifactURL).toHaveBeenCalledWith({
...fixtures.backendIds,
name: fixtures.artifactName
})
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)
})
})
@@ -1,239 +0,0 @@
import * as github from '@actions/github'
import type {RequestInterface} from '@octokit/types'
import {
getArtifactInternal,
getArtifactPublic
} from '../src/internal/find/get-artifact'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated'
import * as util from '../src/internal/shared/util'
import {noopLogs} from './common'
import {
ArtifactNotFoundError,
InvalidResponseError
} from '../src/internal/shared/errors'
type MockedRequest = jest.MockedFunction<RequestInterface<object>>
jest.mock('@actions/github', () => ({
getOctokit: jest.fn().mockReturnValue({
request: jest.fn()
})
}))
const fixtures = {
repo: 'toolkit',
owner: 'actions',
token: 'ghp_1234567890',
runId: 123,
backendIds: {
workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422',
workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf'
},
artifacts: [
{
id: 1,
name: 'my-artifact',
size: 456,
createdAt: new Date('2023-12-01')
},
{
id: 2,
name: 'my-artifact',
size: 456,
createdAt: new Date('2023-12-02')
}
]
}
describe('get-artifact', () => {
beforeAll(() => {
noopLogs()
})
describe('public', () => {
it('should return the artifact if it is found', async () => {
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
artifacts: [
{
name: fixtures.artifacts[0].name,
id: fixtures.artifacts[0].id,
size_in_bytes: fixtures.artifacts[0].size,
created_at: fixtures.artifacts[0].createdAt.toISOString()
}
]
}
})
const response = await getArtifactPublic(
fixtures.artifacts[0].name,
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token
)
expect(response).toEqual({
artifact: fixtures.artifacts[0]
})
})
it('should return the latest artifact if multiple are found', async () => {
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
artifacts: fixtures.artifacts.map(artifact => ({
name: artifact.name,
id: artifact.id,
size_in_bytes: artifact.size,
created_at: artifact.createdAt.toISOString()
}))
}
})
const response = await getArtifactPublic(
fixtures.artifacts[0].name,
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token
)
expect(response).toEqual({
artifact: fixtures.artifacts[1]
})
})
it('should fail if no artifacts are found', async () => {
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockRequest.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
artifacts: []
}
})
const response = getArtifactPublic(
fixtures.artifacts[0].name,
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token
)
expect(response).rejects.toThrowError(ArtifactNotFoundError)
})
it('should fail if non-200 response', async () => {
const mockRequest = github.getOctokit(fixtures.token)
.request as MockedRequest
mockRequest.mockResolvedValueOnce({
status: 404,
headers: {},
url: '',
data: {}
})
const response = getArtifactPublic(
fixtures.artifacts[0].name,
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token
)
expect(response).rejects.toThrowError(InvalidResponseError)
})
})
describe('internal', () => {
beforeEach(() => {
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(util, 'getBackendIdsFromToken')
.mockReturnValue(fixtures.backendIds)
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://results.local')
})
it('should return the artifact if it is found', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: [
{
...fixtures.backendIds,
databaseId: fixtures.artifacts[0].id.toString(),
name: fixtures.artifacts[0].name,
size: fixtures.artifacts[0].size.toString(),
createdAt: Timestamp.fromDate(fixtures.artifacts[0].createdAt)
}
]
})
const response = await getArtifactInternal(fixtures.artifacts[0].name)
expect(response).toEqual({
artifact: fixtures.artifacts[0]
})
})
it('should return the latest artifact if multiple are found', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: fixtures.artifacts.map(artifact => ({
...fixtures.backendIds,
databaseId: artifact.id.toString(),
name: artifact.name,
size: artifact.size.toString(),
createdAt: Timestamp.fromDate(artifact.createdAt)
}))
})
const response = await getArtifactInternal(fixtures.artifacts[0].name)
expect(response).toEqual({
artifact: fixtures.artifacts[1]
})
})
it('should fail if no artifacts are found', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: []
})
const response = getArtifactInternal(fixtures.artifacts[0].name)
expect(response).rejects.toThrowError(ArtifactNotFoundError)
})
it('should fail if non-200 response', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockRejectedValue(new Error('boom'))
const response = getArtifactInternal(fixtures.artifacts[0].name)
expect(response).rejects.toThrow()
})
})
})
@@ -1,242 +0,0 @@
import * as github from '@actions/github'
import type {RestEndpointMethods} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/method-types'
import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'
import {
listArtifactsInternal,
listArtifactsPublic
} from '../src/internal/find/list-artifacts'
import * as config from '../src/internal/shared/config'
import {ArtifactServiceClientJSON, Timestamp} from '../src/generated'
import * as util from '../src/internal/shared/util'
import {noopLogs} from './common'
import {Artifact} from '../src/internal/shared/interfaces'
type MockedListWorkflowRunArtifacts = jest.MockedFunction<
RestEndpointMethods['actions']['listWorkflowRunArtifacts']
>
jest.mock('@actions/github', () => ({
getOctokit: jest.fn().mockReturnValue({
rest: {
actions: {
listWorkflowRunArtifacts: jest.fn()
}
}
})
}))
const artifactsToListResponse = (
artifacts: Artifact[]
): RestEndpointMethodTypes['actions']['listWorkflowRunArtifacts']['response']['data'] => {
return {
total_count: artifacts.length,
artifacts: artifacts.map(artifact => ({
name: artifact.name,
id: artifact.id,
size_in_bytes: artifact.size,
created_at: artifact.createdAt?.toISOString() || '',
run_id: fixtures.runId,
// unused fields for tests
url: '',
archive_download_url: '',
expired: false,
expires_at: '',
node_id: '',
run_url: '',
type: '',
updated_at: ''
}))
}
}
const fixtures = {
repo: 'toolkit',
owner: 'actions',
token: 'ghp_1234567890',
runId: 123,
backendIds: {
workflowRunBackendId: 'c4d7c21f-ba3f-4ddc-a8c8-6f2f626f8422',
workflowJobRunBackendId: '760803a1-f890-4d25-9a6e-a3fc01a0c7cf'
},
artifacts: [
{
id: 1,
name: 'my-artifact',
size: 456,
createdAt: new Date('2023-12-01')
},
{
id: 2,
name: 'my-artifact',
size: 456,
createdAt: new Date('2023-12-02')
}
]
}
describe('list-artifact', () => {
beforeAll(() => {
noopLogs()
})
describe('public', () => {
it('should return a list of artifacts', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
mockListArtifacts.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: artifactsToListResponse(fixtures.artifacts)
})
const response = await listArtifactsPublic(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
false
)
expect(response).toEqual({
artifacts: fixtures.artifacts
})
})
it('should return the latest artifact when latest is specified', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
mockListArtifacts.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: artifactsToListResponse(fixtures.artifacts)
})
const response = await listArtifactsPublic(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
true
)
expect(response).toEqual({
artifacts: [fixtures.artifacts[1]]
})
})
it('can return empty artifacts', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
mockListArtifacts.mockResolvedValueOnce({
status: 200,
headers: {},
url: '',
data: {
total_count: 0,
artifacts: []
}
})
const response = await listArtifactsPublic(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
true
)
expect(response).toEqual({
artifacts: []
})
})
it('should fail if non-200 response', async () => {
const mockListArtifacts = github.getOctokit(fixtures.token).rest.actions
.listWorkflowRunArtifacts as MockedListWorkflowRunArtifacts
mockListArtifacts.mockRejectedValue(new Error('boom'))
await expect(
listArtifactsPublic(
fixtures.runId,
fixtures.owner,
fixtures.repo,
fixtures.token,
false
)
).rejects.toThrow('boom')
})
})
describe('internal', () => {
beforeEach(() => {
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(util, 'getBackendIdsFromToken')
.mockReturnValue(fixtures.backendIds)
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://results.local')
})
it('should return a list of artifacts', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: fixtures.artifacts.map(artifact => ({
...fixtures.backendIds,
databaseId: artifact.id.toString(),
name: artifact.name,
size: artifact.size.toString(),
createdAt: Timestamp.fromDate(artifact.createdAt)
}))
})
const response = await listArtifactsInternal(false)
expect(response).toEqual({
artifacts: fixtures.artifacts
})
})
it('should return the latest artifact when latest is specified', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: fixtures.artifacts.map(artifact => ({
...fixtures.backendIds,
databaseId: artifact.id.toString(),
name: artifact.name,
size: artifact.size.toString(),
createdAt: Timestamp.fromDate(artifact.createdAt)
}))
})
const response = await listArtifactsInternal(true)
expect(response).toEqual({
artifacts: [fixtures.artifacts[1]]
})
})
it('can return empty artifacts', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockResolvedValue({
artifacts: []
})
const response = await listArtifactsInternal(false)
expect(response).toEqual({
artifacts: []
})
})
it('should fail if non-200 response', async () => {
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'ListArtifacts')
.mockRejectedValue(new Error('boom'))
await expect(listArtifactsInternal(false)).rejects.toThrow('boom')
})
})
})
@@ -3,11 +3,15 @@ import {
validateFilePath
} from '../src/internal/upload/path-and-artifact-name-validation'
import {noopLogs} from './common'
import * as core from '@actions/core'
describe('Path and artifact name validation', () => {
beforeAll(() => {
noopLogs()
// 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', () => {
@@ -1,3 +1,4 @@
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'
@@ -6,12 +7,14 @@ 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'
import {noopLogs} from './common'
import {FilesNotFoundError} from '../src/internal/shared/errors'
describe('upload-artifact', () => {
beforeEach(() => {
noopLogs()
// 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(() => {
@@ -60,8 +63,9 @@ describe('upload-artifact', () => {
)
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({
isSuccess: true,
uploadSize: 1234,
sha256Hash: 'test-sha256-hash'
md5Hash: 'test-md5-hash'
})
)
jest
@@ -84,7 +88,7 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload'
)
expect(uploadResp).resolves.toEqual({size: 1234, id: 1})
expect(uploadResp).resolves.toEqual({success: true, size: 1234, id: 1})
})
it('should throw an error if the root directory is invalid', () => {
@@ -107,7 +111,7 @@ describe('upload-artifact', () => {
expect(uploadResp).rejects.toThrow('Invalid root directory')
})
it('should reject if there are no files to upload', () => {
it('should return false if there are no files to upload', () => {
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
@@ -124,10 +128,10 @@ describe('upload-artifact', () => {
],
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrowError(FilesNotFoundError)
expect(uploadResp).resolves.toEqual({success: false})
})
it('should reject if no backend IDs are found', () => {
it('should return false if no backend IDs are found', () => {
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
@@ -151,6 +155,9 @@ describe('upload-artifact', () => {
jest
.spyOn(zip, 'createZipUploadStream')
.mockReturnValue(Promise.resolve(new zip.ZipUploadStream(1)))
jest
.spyOn(util, 'getBackendIdsFromToken')
.mockReturnValue({workflowRunBackendId: '', workflowJobRunBackendId: ''})
const uploadResp = uploadArtifact(
'test-artifact',
@@ -162,7 +169,7 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow()
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if the creation request fails', () => {
@@ -217,7 +224,7 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow()
expect(uploadResp).resolves.toEqual({success: false})
})
it('should return false if blob storage upload is unsuccessful', () => {
@@ -262,7 +269,7 @@ describe('upload-artifact', () => {
)
jest
.spyOn(blobUpload, 'uploadZipToBlobStorage')
.mockReturnValue(Promise.reject(new Error('boom')))
.mockReturnValue(Promise.resolve({isSuccess: false}))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
@@ -280,10 +287,10 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow()
expect(uploadResp).resolves.toEqual({success: false})
})
it('should reject if finalize artifact fails', () => {
it('should return false if finalize artifact fails', () => {
const mockDate = new Date('2020-01-01')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
@@ -325,8 +332,9 @@ describe('upload-artifact', () => {
)
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({
isSuccess: true,
uploadSize: 1234,
sha256Hash: 'test-sha256-hash'
md5Hash: 'test-md5-hash'
})
)
jest
@@ -349,6 +357,6 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow()
expect(uploadResp).resolves.toEqual({success: false})
})
})
@@ -1,11 +1,11 @@
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'
import {noopLogs} from './common'
const root = path.join(__dirname, '_temp', 'upload-specification')
const goodItem1Path = path.join(
@@ -51,7 +51,11 @@ const artifactFilesToUpload = [
describe('Search', () => {
beforeAll(async () => {
noopLogs()
// 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)
+5 -4
View File
@@ -1,12 +1,13 @@
import * as config from '../src/internal/shared/config'
import * as util from '../src/internal/shared/util'
export const testRuntimeToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8'
describe('get-backend-ids-from-token', () => {
it('should return backend ids when the token is valid', () => {
jest.spyOn(config, 'getRuntimeToken').mockReturnValue(testRuntimeToken)
jest
.spyOn(config, 'getRuntimeToken')
.mockReturnValue(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8'
)
const backendIds = util.getBackendIdsFromToken()
expect(backendIds.workflowRunBackendId).toBe(
+1
View File
@@ -0,0 +1 @@
Docs will be added here once development of version `2.0.0` has finished
-35
View File
@@ -1,35 +0,0 @@
# Frequently Asked Questions
- [Frequently Asked Questions](#frequently-asked-questions)
- [Supported Characters](#supported-characters)
- [Compression? ZIP? How is my artifact stored?](#compression-zip-how-is-my-artifact-stored)
## Supported Characters
When uploading an artifact, the inputted `name` parameter along with the files specified in `files` cannot contain any of the following characters. If they are present in `name` or `files`, the Artifact will be rejected by the server and the upload will fail. These characters are not allowed due to limitations and restrictions with certain file systems such as NTFS. To maintain platform-agnostic behavior, characters that are not supported by an individual filesystem/platform will not be supported on all filesystems/platforms.
- "
- :
- <
- \>
- |
- \*
- ?
In addition to the aforementioned characters, the inputted `name` also cannot include the following
- \
- /
## Compression? ZIP? How is my artifact stored?
When creating an Artifact, the files are dynamically compressed and streamed into a ZIP archive. Since they are stored in a ZIP, they can be compressed by Zlib in varying levels.
The value can range from 0 to 9:
- 0: No compression
- 1: Best speed
- 6: Default compression (same as GNU Gzip)
- 9: Best compression
Higher levels will result in better compression, but will take longer to complete.
For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads.
@@ -1,40 +0,0 @@
@actions/artifact
# @actions/artifact
## Table of contents
### Classes
- [ArtifactNotFoundError](classes/ArtifactNotFoundError.md)
- [DefaultArtifactClient](classes/DefaultArtifactClient.md)
- [FilesNotFoundError](classes/FilesNotFoundError.md)
- [GHESNotSupportedError](classes/GHESNotSupportedError.md)
- [InvalidResponseError](classes/InvalidResponseError.md)
### Interfaces
- [Artifact](interfaces/Artifact.md)
- [ArtifactClient](interfaces/ArtifactClient.md)
- [DownloadArtifactOptions](interfaces/DownloadArtifactOptions.md)
- [DownloadArtifactResponse](interfaces/DownloadArtifactResponse.md)
- [FindOptions](interfaces/FindOptions.md)
- [GetArtifactResponse](interfaces/GetArtifactResponse.md)
- [ListArtifactsOptions](interfaces/ListArtifactsOptions.md)
- [ListArtifactsResponse](interfaces/ListArtifactsResponse.md)
- [UploadArtifactOptions](interfaces/UploadArtifactOptions.md)
- [UploadArtifactResponse](interfaces/UploadArtifactResponse.md)
### Variables
- [default](README.md#default)
## Variables
### default
`Const` **default**: [`ArtifactClient`](interfaces/ArtifactClient.md)
#### Defined in
[src/artifact.ts:7](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/artifact.ts#L7)
@@ -1,169 +0,0 @@
[@actions/artifact](../README.md) / ArtifactNotFoundError
# Class: ArtifactNotFoundError
## Hierarchy
- `Error`
**`ArtifactNotFoundError`**
## Table of contents
### Constructors
- [constructor](ArtifactNotFoundError.md#constructor)
### Properties
- [message](ArtifactNotFoundError.md#message)
- [name](ArtifactNotFoundError.md#name)
- [stack](ArtifactNotFoundError.md#stack)
- [prepareStackTrace](ArtifactNotFoundError.md#preparestacktrace)
- [stackTraceLimit](ArtifactNotFoundError.md#stacktracelimit)
### Methods
- [captureStackTrace](ArtifactNotFoundError.md#capturestacktrace)
## Constructors
### constructor
**new ArtifactNotFoundError**(`message?`): [`ArtifactNotFoundError`](ArtifactNotFoundError.md)
#### Parameters
| Name | Type | Default value |
| :------ | :------ | :------ |
| `message` | `string` | `'Artifact not found'` |
#### Returns
[`ArtifactNotFoundError`](ArtifactNotFoundError.md)
#### Overrides
Error.constructor
#### Defined in
[src/internal/shared/errors.ts:24](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L24)
## Properties
### message
**message**: `string`
#### Inherited from
Error.message
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1068
___
### name
**name**: `string`
#### Inherited from
Error.name
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1067
___
### stack
`Optional` **stack**: `string`
#### Inherited from
Error.stack
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1069
___
### prepareStackTrace
`Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any`
#### Type declaration
▸ (`err`, `stackTraces`): `any`
Optional override for formatting stack traces
##### Parameters
| Name | Type |
| :------ | :------ |
| `err` | `Error` |
| `stackTraces` | `CallSite`[] |
##### Returns
`any`
**`See`**
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
#### Inherited from
Error.prepareStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:11
___
### stackTraceLimit
`Static` **stackTraceLimit**: `number`
#### Inherited from
Error.stackTraceLimit
#### Defined in
node_modules/@types/node/globals.d.ts:13
## Methods
### captureStackTrace
**captureStackTrace**(`targetObject`, `constructorOpt?`): `void`
Create .stack property on a target object
#### Parameters
| Name | Type |
| :------ | :------ |
| `targetObject` | `object` |
| `constructorOpt?` | `Function` |
#### Returns
`void`
#### Inherited from
Error.captureStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:4
@@ -1,161 +0,0 @@
[@actions/artifact](../README.md) / DefaultArtifactClient
# Class: DefaultArtifactClient
The default artifact client that is used by the artifact action(s).
## Implements
- [`ArtifactClient`](../interfaces/ArtifactClient.md)
## Table of contents
### Constructors
- [constructor](DefaultArtifactClient.md#constructor)
### Methods
- [downloadArtifact](DefaultArtifactClient.md#downloadartifact)
- [getArtifact](DefaultArtifactClient.md#getartifact)
- [listArtifacts](DefaultArtifactClient.md#listartifacts)
- [uploadArtifact](DefaultArtifactClient.md#uploadartifact)
## Constructors
### constructor
**new DefaultArtifactClient**(): [`DefaultArtifactClient`](DefaultArtifactClient.md)
#### Returns
[`DefaultArtifactClient`](DefaultArtifactClient.md)
## Methods
### downloadArtifact
**downloadArtifact**(`artifactId`, `options?`): `Promise`\<[`DownloadArtifactResponse`](../interfaces/DownloadArtifactResponse.md)\>
Downloads an artifact and unzips the content.
If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `artifactId` | `number` | The name of the artifact to download |
| `options?` | [`DownloadArtifactOptions`](../interfaces/DownloadArtifactOptions.md) & [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the download behavior |
#### Returns
`Promise`\<[`DownloadArtifactResponse`](../interfaces/DownloadArtifactResponse.md)\>
single DownloadArtifactResponse object
#### Implementation of
[ArtifactClient](../interfaces/ArtifactClient.md).[downloadArtifact](../interfaces/ArtifactClient.md#downloadartifact)
#### Defined in
[src/internal/client.ts:119](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L119)
___
### getArtifact
**getArtifact**(`artifactName`, `options?`): `Promise`\<[`GetArtifactResponse`](../interfaces/GetArtifactResponse.md)\>
Finds an artifact by name.
If there are multiple artifacts with the same name in the same workflow run, this will return the latest.
If the artifact is not found, it will throw.
If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs.
https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
`@actions/artifact` v2+ 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), @actions/artifact < v2 or it is a rerun.
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.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `artifactName` | `string` | The name of the artifact to find |
| `options?` | [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the get behavior |
#### Returns
`Promise`\<[`GetArtifactResponse`](../interfaces/GetArtifactResponse.md)\>
#### Implementation of
[ArtifactClient](../interfaces/ArtifactClient.md).[getArtifact](../interfaces/ArtifactClient.md#getartifact)
#### Defined in
[src/internal/client.ts:193](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L193)
___
### listArtifacts
**listArtifacts**(`options?`): `Promise`\<[`ListArtifactsResponse`](../interfaces/ListArtifactsResponse.md)\>
Lists all artifacts that are part of the current workflow run.
This function will return at most 1000 artifacts per workflow run.
If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs.
https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `options?` | [`ListArtifactsOptions`](../interfaces/ListArtifactsOptions.md) & [`FindOptions`](../interfaces/FindOptions.md) | Extra options that allow for the customization of the list behavior |
#### Returns
`Promise`\<[`ListArtifactsResponse`](../interfaces/ListArtifactsResponse.md)\>
ListArtifactResponse object
#### Implementation of
[ArtifactClient](../interfaces/ArtifactClient.md).[listArtifacts](../interfaces/ArtifactClient.md#listartifacts)
#### Defined in
[src/internal/client.ts:157](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L157)
___
### uploadArtifact
**uploadArtifact**(`name`, `files`, `rootDirectory`, `options?`): `Promise`\<[`UploadArtifactResponse`](../interfaces/UploadArtifactResponse.md)\>
Uploads an artifact.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `name` | `string` | The name of the artifact, required |
| `files` | `string`[] | A list of absolute or relative paths that denote what files should be uploaded |
| `rootDirectory` | `string` | An absolute or relative file path that denotes the root parent directory of the files being uploaded |
| `options?` | [`UploadArtifactOptions`](../interfaces/UploadArtifactOptions.md) | Extra options for customizing the upload behavior |
#### Returns
`Promise`\<[`UploadArtifactResponse`](../interfaces/UploadArtifactResponse.md)\>
single UploadArtifactResponse object
#### Implementation of
[ArtifactClient](../interfaces/ArtifactClient.md).[uploadArtifact](../interfaces/ArtifactClient.md#uploadartifact)
#### Defined in
[src/internal/client.ts:94](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L94)
@@ -1,180 +0,0 @@
[@actions/artifact](../README.md) / FilesNotFoundError
# Class: FilesNotFoundError
## Hierarchy
- `Error`
**`FilesNotFoundError`**
## Table of contents
### Constructors
- [constructor](FilesNotFoundError.md#constructor)
### Properties
- [files](FilesNotFoundError.md#files)
- [message](FilesNotFoundError.md#message)
- [name](FilesNotFoundError.md#name)
- [stack](FilesNotFoundError.md#stack)
- [prepareStackTrace](FilesNotFoundError.md#preparestacktrace)
- [stackTraceLimit](FilesNotFoundError.md#stacktracelimit)
### Methods
- [captureStackTrace](FilesNotFoundError.md#capturestacktrace)
## Constructors
### constructor
**new FilesNotFoundError**(`files?`): [`FilesNotFoundError`](FilesNotFoundError.md)
#### Parameters
| Name | Type | Default value |
| :------ | :------ | :------ |
| `files` | `string`[] | `[]` |
#### Returns
[`FilesNotFoundError`](FilesNotFoundError.md)
#### Overrides
Error.constructor
#### Defined in
[src/internal/shared/errors.ts:4](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L4)
## Properties
### files
**files**: `string`[]
#### Defined in
[src/internal/shared/errors.ts:2](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L2)
___
### message
**message**: `string`
#### Inherited from
Error.message
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1068
___
### name
**name**: `string`
#### Inherited from
Error.name
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1067
___
### stack
`Optional` **stack**: `string`
#### Inherited from
Error.stack
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1069
___
### prepareStackTrace
`Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any`
#### Type declaration
▸ (`err`, `stackTraces`): `any`
Optional override for formatting stack traces
##### Parameters
| Name | Type |
| :------ | :------ |
| `err` | `Error` |
| `stackTraces` | `CallSite`[] |
##### Returns
`any`
**`See`**
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
#### Inherited from
Error.prepareStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:11
___
### stackTraceLimit
`Static` **stackTraceLimit**: `number`
#### Inherited from
Error.stackTraceLimit
#### Defined in
node_modules/@types/node/globals.d.ts:13
## Methods
### captureStackTrace
**captureStackTrace**(`targetObject`, `constructorOpt?`): `void`
Create .stack property on a target object
#### Parameters
| Name | Type |
| :------ | :------ |
| `targetObject` | `object` |
| `constructorOpt?` | `Function` |
#### Returns
`void`
#### Inherited from
Error.captureStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:4
@@ -1,169 +0,0 @@
[@actions/artifact](../README.md) / GHESNotSupportedError
# Class: GHESNotSupportedError
## Hierarchy
- `Error`
**`GHESNotSupportedError`**
## Table of contents
### Constructors
- [constructor](GHESNotSupportedError.md#constructor)
### Properties
- [message](GHESNotSupportedError.md#message)
- [name](GHESNotSupportedError.md#name)
- [stack](GHESNotSupportedError.md#stack)
- [prepareStackTrace](GHESNotSupportedError.md#preparestacktrace)
- [stackTraceLimit](GHESNotSupportedError.md#stacktracelimit)
### Methods
- [captureStackTrace](GHESNotSupportedError.md#capturestacktrace)
## Constructors
### constructor
**new GHESNotSupportedError**(`message?`): [`GHESNotSupportedError`](GHESNotSupportedError.md)
#### Parameters
| Name | Type | Default value |
| :------ | :------ | :------ |
| `message` | `string` | `'@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.'` |
#### Returns
[`GHESNotSupportedError`](GHESNotSupportedError.md)
#### Overrides
Error.constructor
#### Defined in
[src/internal/shared/errors.ts:31](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L31)
## Properties
### message
**message**: `string`
#### Inherited from
Error.message
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1068
___
### name
**name**: `string`
#### Inherited from
Error.name
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1067
___
### stack
`Optional` **stack**: `string`
#### Inherited from
Error.stack
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1069
___
### prepareStackTrace
`Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any`
#### Type declaration
▸ (`err`, `stackTraces`): `any`
Optional override for formatting stack traces
##### Parameters
| Name | Type |
| :------ | :------ |
| `err` | `Error` |
| `stackTraces` | `CallSite`[] |
##### Returns
`any`
**`See`**
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
#### Inherited from
Error.prepareStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:11
___
### stackTraceLimit
`Static` **stackTraceLimit**: `number`
#### Inherited from
Error.stackTraceLimit
#### Defined in
node_modules/@types/node/globals.d.ts:13
## Methods
### captureStackTrace
**captureStackTrace**(`targetObject`, `constructorOpt?`): `void`
Create .stack property on a target object
#### Parameters
| Name | Type |
| :------ | :------ |
| `targetObject` | `object` |
| `constructorOpt?` | `Function` |
#### Returns
`void`
#### Inherited from
Error.captureStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:4
@@ -1,169 +0,0 @@
[@actions/artifact](../README.md) / InvalidResponseError
# Class: InvalidResponseError
## Hierarchy
- `Error`
**`InvalidResponseError`**
## Table of contents
### Constructors
- [constructor](InvalidResponseError.md#constructor)
### Properties
- [message](InvalidResponseError.md#message)
- [name](InvalidResponseError.md#name)
- [stack](InvalidResponseError.md#stack)
- [prepareStackTrace](InvalidResponseError.md#preparestacktrace)
- [stackTraceLimit](InvalidResponseError.md#stacktracelimit)
### Methods
- [captureStackTrace](InvalidResponseError.md#capturestacktrace)
## Constructors
### constructor
**new InvalidResponseError**(`message`): [`InvalidResponseError`](InvalidResponseError.md)
#### Parameters
| Name | Type |
| :------ | :------ |
| `message` | `string` |
#### Returns
[`InvalidResponseError`](InvalidResponseError.md)
#### Overrides
Error.constructor
#### Defined in
[src/internal/shared/errors.ts:17](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/errors.ts#L17)
## Properties
### message
**message**: `string`
#### Inherited from
Error.message
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1068
___
### name
**name**: `string`
#### Inherited from
Error.name
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1067
___
### stack
`Optional` **stack**: `string`
#### Inherited from
Error.stack
#### Defined in
node_modules/typescript/lib/lib.es5.d.ts:1069
___
### prepareStackTrace
`Static` `Optional` **prepareStackTrace**: (`err`: `Error`, `stackTraces`: `CallSite`[]) => `any`
#### Type declaration
▸ (`err`, `stackTraces`): `any`
Optional override for formatting stack traces
##### Parameters
| Name | Type |
| :------ | :------ |
| `err` | `Error` |
| `stackTraces` | `CallSite`[] |
##### Returns
`any`
**`See`**
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
#### Inherited from
Error.prepareStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:11
___
### stackTraceLimit
`Static` **stackTraceLimit**: `number`
#### Inherited from
Error.stackTraceLimit
#### Defined in
node_modules/@types/node/globals.d.ts:13
## Methods
### captureStackTrace
**captureStackTrace**(`targetObject`, `constructorOpt?`): `void`
Create .stack property on a target object
#### Parameters
| Name | Type |
| :------ | :------ |
| `targetObject` | `object` |
| `constructorOpt?` | `Function` |
#### Returns
`void`
#### Inherited from
Error.captureStackTrace
#### Defined in
node_modules/@types/node/globals.d.ts:4
@@ -1,62 +0,0 @@
[@actions/artifact](../README.md) / Artifact
# Interface: Artifact
An Actions Artifact
## Table of contents
### Properties
- [createdAt](Artifact.md#createdat)
- [id](Artifact.md#id)
- [name](Artifact.md#name)
- [size](Artifact.md#size)
## Properties
### createdAt
`Optional` **createdAt**: `Date`
The time when the artifact was created
#### Defined in
[src/internal/shared/interfaces.ts:123](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L123)
___
### id
**id**: `number`
The ID of the artifact
#### Defined in
[src/internal/shared/interfaces.ts:113](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L113)
___
### name
**name**: `string`
The name of the artifact
#### Defined in
[src/internal/shared/interfaces.ts:108](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L108)
___
### size
**size**: `number`
The size of the artifact in bytes
#### Defined in
[src/internal/shared/interfaces.ts:118](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L118)
@@ -1,131 +0,0 @@
[@actions/artifact](../README.md) / ArtifactClient
# Interface: ArtifactClient
Generic interface for the artifact client.
## Implemented by
- [`DefaultArtifactClient`](../classes/DefaultArtifactClient.md)
## Table of contents
### Methods
- [downloadArtifact](ArtifactClient.md#downloadartifact)
- [getArtifact](ArtifactClient.md#getartifact)
- [listArtifacts](ArtifactClient.md#listartifacts)
- [uploadArtifact](ArtifactClient.md#uploadartifact)
## Methods
### downloadArtifact
**downloadArtifact**(`artifactId`, `options?`): `Promise`\<[`DownloadArtifactResponse`](DownloadArtifactResponse.md)\>
Downloads an artifact and unzips the content.
If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `artifactId` | `number` | The name of the artifact to download |
| `options?` | [`DownloadArtifactOptions`](DownloadArtifactOptions.md) & [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the download behavior |
#### Returns
`Promise`\<[`DownloadArtifactResponse`](DownloadArtifactResponse.md)\>
single DownloadArtifactResponse object
#### Defined in
[src/internal/client.ts:84](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L84)
___
### getArtifact
**getArtifact**(`artifactName`, `options?`): `Promise`\<[`GetArtifactResponse`](GetArtifactResponse.md)\>
Finds an artifact by name.
If there are multiple artifacts with the same name in the same workflow run, this will return the latest.
If the artifact is not found, it will throw.
If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs.
https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
`@actions/artifact` v2+ 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), @actions/artifact < v2 or it is a rerun.
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.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `artifactName` | `string` | The name of the artifact to find |
| `options?` | [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the get behavior |
#### Returns
`Promise`\<[`GetArtifactResponse`](GetArtifactResponse.md)\>
#### Defined in
[src/internal/client.ts:70](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L70)
___
### listArtifacts
**listArtifacts**(`options?`): `Promise`\<[`ListArtifactsResponse`](ListArtifactsResponse.md)\>
Lists all artifacts that are part of the current workflow run.
This function will return at most 1000 artifacts per workflow run.
If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs.
https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `options?` | [`ListArtifactsOptions`](ListArtifactsOptions.md) & [`FindOptions`](FindOptions.md) | Extra options that allow for the customization of the list behavior |
#### Returns
`Promise`\<[`ListArtifactsResponse`](ListArtifactsResponse.md)\>
ListArtifactResponse object
#### Defined in
[src/internal/client.ts:52](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L52)
___
### uploadArtifact
**uploadArtifact**(`name`, `files`, `rootDirectory`, `options?`): `Promise`\<[`UploadArtifactResponse`](UploadArtifactResponse.md)\>
Uploads an artifact.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `name` | `string` | The name of the artifact, required |
| `files` | `string`[] | A list of absolute or relative paths that denote what files should be uploaded |
| `rootDirectory` | `string` | An absolute or relative file path that denotes the root parent directory of the files being uploaded |
| `options?` | [`UploadArtifactOptions`](UploadArtifactOptions.md) | Extra options for customizing the upload behavior |
#### Returns
`Promise`\<[`UploadArtifactResponse`](UploadArtifactResponse.md)\>
single UploadArtifactResponse object
#### Defined in
[src/internal/client.ts:35](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/client.ts#L35)
@@ -1,23 +0,0 @@
[@actions/artifact](../README.md) / DownloadArtifactOptions
# Interface: DownloadArtifactOptions
Options for downloading an artifact
## Table of contents
### Properties
- [path](DownloadArtifactOptions.md#path)
## Properties
### path
`Optional` **path**: `string`
Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE
#### Defined in
[src/internal/shared/interfaces.ts:98](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L98)
@@ -1,23 +0,0 @@
[@actions/artifact](../README.md) / DownloadArtifactResponse
# Interface: DownloadArtifactResponse
Response from the server when downloading an artifact
## Table of contents
### Properties
- [downloadPath](DownloadArtifactResponse.md#downloadpath)
## Properties
### downloadPath
`Optional` **downloadPath**: `string`
The path where the artifact was downloaded to
#### Defined in
[src/internal/shared/interfaces.ts:88](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L88)
@@ -1,30 +0,0 @@
[@actions/artifact](../README.md) / FindOptions
# Interface: FindOptions
## Table of contents
### Properties
- [findBy](FindOptions.md#findby)
## Properties
### findBy
`Optional` **findBy**: `Object`
The criteria for finding Artifact(s) out of the scope of the current run.
#### Type declaration
| Name | Type | Description |
| :------ | :------ | :------ |
| `repositoryName` | `string` | Repository owner (eg. 'toolkit') |
| `repositoryOwner` | `string` | Repository owner (eg. 'actions') |
| `token` | `string` | Token with actions:read permissions |
| `workflowRunId` | `number` | WorkflowRun of the artifact(s) to lookup |
#### Defined in
[src/internal/shared/interfaces.ts:131](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L131)
@@ -1,23 +0,0 @@
[@actions/artifact](../README.md) / GetArtifactResponse
# Interface: GetArtifactResponse
Response from the server when getting an artifact
## Table of contents
### Properties
- [artifact](GetArtifactResponse.md#artifact)
## Properties
### artifact
**artifact**: [`Artifact`](Artifact.md)
Metadata about the artifact that was found
#### Defined in
[src/internal/shared/interfaces.ts:57](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L57)
@@ -1,24 +0,0 @@
[@actions/artifact](../README.md) / ListArtifactsOptions
# Interface: ListArtifactsOptions
Options for listing artifacts
## Table of contents
### Properties
- [latest](ListArtifactsOptions.md#latest)
## Properties
### latest
`Optional` **latest**: `boolean`
Filter the workflow run's artifacts to the latest by name
In the case of reruns, this can be useful to avoid duplicates
#### Defined in
[src/internal/shared/interfaces.ts:68](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L68)
@@ -1,23 +0,0 @@
[@actions/artifact](../README.md) / ListArtifactsResponse
# Interface: ListArtifactsResponse
Response from the server when listing artifacts
## Table of contents
### Properties
- [artifacts](ListArtifactsResponse.md#artifacts)
## Properties
### artifacts
**artifacts**: [`Artifact`](Artifact.md)[]
A list of artifacts that were found
#### Defined in
[src/internal/shared/interfaces.ts:78](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L78)
@@ -1,55 +0,0 @@
[@actions/artifact](../README.md) / UploadArtifactOptions
# Interface: UploadArtifactOptions
Options for uploading an artifact
## Table of contents
### Properties
- [compressionLevel](UploadArtifactOptions.md#compressionlevel)
- [retentionDays](UploadArtifactOptions.md#retentiondays)
## Properties
### compressionLevel
`Optional` **compressionLevel**: `number`
The level of compression for Zlib to be applied to the artifact archive.
The value can range from 0 to 9:
- 0: No compression
- 1: Best speed
- 6: Default compression (same as GNU Gzip)
- 9: Best compression
Higher levels will result in better compression, but will take longer to complete.
For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads.
#### Defined in
[src/internal/shared/interfaces.ts:47](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L47)
___
### retentionDays
`Optional` **retentionDays**: `number`
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.
#### Defined in
[src/internal/shared/interfaces.ts:36](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L36)
@@ -1,37 +0,0 @@
[@actions/artifact](../README.md) / UploadArtifactResponse
# Interface: UploadArtifactResponse
Response from the server when an artifact is uploaded
## Table of contents
### Properties
- [id](UploadArtifactResponse.md#id)
- [size](UploadArtifactResponse.md#size)
## Properties
### id
`Optional` **id**: `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
#### Defined in
[src/internal/shared/interfaces.ts:14](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L14)
___
### size
`Optional` **size**: `number`
Total size of the artifact in bytes. Not provided if no artifact was uploaded
#### Defined in
[src/internal/shared/interfaces.ts:8](https://github.com/actions/toolkit/blob/e3764a5/packages/artifact/src/internal/shared/interfaces.ts#L8)
+139 -182
View File
@@ -1,12 +1,12 @@
{
"name": "@actions/artifact",
"version": "2.0.1",
"version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@actions/artifact",
"version": "2.0.1",
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.0",
@@ -18,17 +18,15 @@
"@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",
"unzip-stream": "^0.3.1"
"unzipper": "^0.10.14"
},
"devDependencies": {
"@types/archiver": "^5.3.2",
"@types/unzip-stream": "^0.3.4",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1",
"typescript": "^5.2.2"
}
},
@@ -471,21 +469,14 @@
"@types/node": "*"
}
},
"node_modules/@types/unzip-stream": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/@types/unzip-stream/-/unzip-stream-0.3.4.tgz",
"integrity": "sha512-ud0vtsNRF+joUCyvNMyo0j5DKX2Lh/im+xVgRzBEsfHhQYZ+i4fKTveova9XxLzt6Jl6G0e/0mM4aC0gqZYSnA==",
"dev": true,
"node_modules/@types/unzipper": {
"version": "0.10.6",
"resolved": "https://registry.npmjs.org/@types/unzipper/-/unzipper-0.10.6.tgz",
"integrity": "sha512-zcBj329AHgKLQyz209N/S9R0GZqXSkUQO4tJSYE3x02qg4JuDFpgKMj50r82Erk1natCWQDIvSccDddt7jPzjA==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/ansi-sequence-parser": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz",
"integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==",
"dev": true
},
"node_modules/archiver": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz",
@@ -589,6 +580,14 @@
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
},
"node_modules/big-integer": {
"version": "1.6.51",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/binary": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
@@ -611,6 +610,11 @@
"readable-stream": "^3.4.0"
}
},
"node_modules/bluebird": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
"integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="
},
"node_modules/bottleneck": {
"version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
@@ -656,6 +660,14 @@
"node": "*"
}
},
"node_modules/buffer-indexof-polyfill": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
"integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/buffers": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
@@ -781,6 +793,41 @@
"dot-object": "bin/dot-object"
}
},
"node_modules/duplexer2": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
"integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==",
"dependencies": {
"readable-stream": "^2.0.2"
}
},
"node_modules/duplexer2/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/duplexer2/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/duplexer2/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -820,6 +867,20 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
},
"node_modules/fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"dependencies": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
},
"engines": {
"node": ">=0.6"
}
},
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -844,27 +905,6 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
"integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
"dev": true,
"dependencies": {
"minimist": "^1.2.5",
"neo-async": "^2.6.2",
"source-map": "^0.6.1",
"wordwrap": "^1.0.0"
},
"bin": {
"handlebars": "bin/handlebars"
},
"engines": {
"node": ">=0.4.7"
},
"optionalDependencies": {
"uglify-js": "^3.1.4"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@@ -911,12 +951,6 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/jsonc-parser": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==",
"dev": true
},
"node_modules/jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
@@ -960,6 +994,11 @@
"safe-buffer": "~5.1.0"
}
},
"node_modules/listenercount": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
"integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ=="
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -998,24 +1037,6 @@
"tslib": "^2.0.3"
}
},
"node_modules/lunr": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
"dev": true
},
"node_modules/marked": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
"dev": true,
"bin": {
"marked": "bin/marked.js"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -1065,12 +1086,6 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/neo-async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"node_modules/no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
@@ -1204,6 +1219,17 @@
"node": ">=10"
}
},
"node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -1228,26 +1254,10 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/shiki": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz",
"integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==",
"dev": true,
"dependencies": {
"ansi-sequence-parser": "^1.1.0",
"jsonc-parser": "^3.2.0",
"vscode-oniguruma": "^1.7.0",
"vscode-textmate": "^8.0.0"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"node_modules/string_decoder": {
"version": "1.3.0",
@@ -1335,63 +1345,6 @@
}
}
},
"node_modules/typedoc": {
"version": "0.25.4",
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.4.tgz",
"integrity": "sha512-Du9ImmpBCw54bX275yJrxPVnjdIyJO/84co0/L9mwe0R3G4FSR6rQ09AlXVRvZEGMUg09+z/usc8mgygQ1aidA==",
"dev": true,
"dependencies": {
"lunr": "^2.3.9",
"marked": "^4.3.0",
"minimatch": "^9.0.3",
"shiki": "^0.14.1"
},
"bin": {
"typedoc": "bin/typedoc"
},
"engines": {
"node": ">= 16"
},
"peerDependencies": {
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x"
}
},
"node_modules/typedoc-plugin-markdown": {
"version": "3.17.1",
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz",
"integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==",
"dev": true,
"dependencies": {
"handlebars": "^4.7.7"
},
"peerDependencies": {
"typedoc": ">=0.24.0"
}
},
"node_modules/typedoc/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/typedoc/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@@ -1405,31 +1358,53 @@
"node": ">=14.17"
}
},
"node_modules/uglify-js": {
"version": "3.17.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
"integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
"dev": true,
"optional": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.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",
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
},
"node_modules/unzip-stream": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.1.tgz",
"integrity": "sha512-RzaGXLNt+CW+T41h1zl6pGz3EaeVhYlK+rdAap+7DxW5kqsqePO8kRtWPaCiVqdhZc86EctSPVYNix30YOMzmw==",
"node_modules/unzipper": {
"version": "0.10.14",
"resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz",
"integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==",
"dependencies": {
"binary": "^0.3.0",
"mkdirp": "^0.5.1"
"big-integer": "^1.6.17",
"binary": "~0.3.0",
"bluebird": "~3.4.1",
"buffer-indexof-polyfill": "~1.0.0",
"duplexer2": "~0.1.4",
"fstream": "^1.0.12",
"graceful-fs": "^4.2.2",
"listenercount": "~1.0.1",
"readable-stream": "~2.3.6",
"setimmediate": "~1.0.4"
}
},
"node_modules/unzipper/node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/unzipper/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/unzipper/node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/util-deprecate": {
@@ -1445,18 +1420,6 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/vscode-oniguruma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
"integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==",
"dev": true
},
"node_modules/vscode-textmate": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz",
"integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==",
"dev": true
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -1471,12 +1434,6 @@
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wordwrap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
"integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
"dev": true
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+6 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "2.0.1",
"version": "2.0.0",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -30,11 +30,10 @@
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "cd ../../ && npm run test ./packages/artifact",
"test": "echo \"Error: run tests from root\" && exit 1",
"bootstrap": "cd ../../ && npm run bootstrap",
"tsc-run": "tsc",
"tsc": "npm run bootstrap && npm run tsc-run",
"gen:docs": "typedoc --plugin typedoc-plugin-markdown --out docs/generated src/artifact.ts --githubPages false --readme none"
"tsc": "npm run bootstrap && npm run tsc-run"
},
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
@@ -49,17 +48,15 @@
"@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",
"unzip-stream": "^0.3.1"
"unzipper": "^0.10.14"
},
"devDependencies": {
"@types/archiver": "^5.3.2",
"@types/unzip-stream": "^0.3.4",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1",
"typescript": "^5.2.2"
}
}
}
+8 -5
View File
@@ -1,8 +1,11 @@
import {ArtifactClient, DefaultArtifactClient} from './internal/client'
import {ArtifactClient, Client} from './internal/client'
/**
* Exported functionality that we want to expose for any users of @actions/artifact
*/
export * from './internal/shared/interfaces'
export * from './internal/shared/errors'
export * from './internal/client'
export {ArtifactClient}
const client: ArtifactClient = new DefaultArtifactClient()
export default client
export function create(): ArtifactClient {
return Client.create()
}
@@ -12,7 +12,6 @@ import type { PartialMessage } from "@protobuf-ts/runtime";
import { reflectionMergePartial } from "@protobuf-ts/runtime";
import { MESSAGE_TYPE } from "@protobuf-ts/runtime";
import { MessageType } from "@protobuf-ts/runtime";
import { Int64Value } from "../../../google/protobuf/wrappers";
import { StringValue } from "../../../google/protobuf/wrappers";
import { Timestamp } from "../../../google/protobuf/timestamp";
/**
@@ -91,111 +90,6 @@ export interface FinalizeArtifactResponse {
*/
artifactId: string;
}
/**
* @generated from protobuf message github.actions.results.api.v1.ListArtifactsRequest
*/
export interface ListArtifactsRequest {
/**
* The backend plan ID
*
* @generated from protobuf field: string workflow_run_backend_id = 1;
*/
workflowRunBackendId: string;
/**
* The backend job ID
*
* @generated from protobuf field: string workflow_job_run_backend_id = 2;
*/
workflowJobRunBackendId: string;
/**
* Name of the artifact to filter on
*
* @generated from protobuf field: google.protobuf.StringValue name_filter = 3;
*/
nameFilter?: StringValue; // optional
/**
* Monolith Database ID of the artifact to filter on
*
* @generated from protobuf field: google.protobuf.Int64Value id_filter = 4;
*/
idFilter?: Int64Value; // optional
}
/**
* @generated from protobuf message github.actions.results.api.v1.ListArtifactsResponse
*/
export interface ListArtifactsResponse {
/**
* @generated from protobuf field: repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts = 1;
*/
artifacts: ListArtifactsResponse_MonolithArtifact[];
}
/**
* @generated from protobuf message github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact
*/
export interface ListArtifactsResponse_MonolithArtifact {
/**
* The backend plan ID
*
* @generated from protobuf field: string workflow_run_backend_id = 1;
*/
workflowRunBackendId: string;
/**
* The backend job ID
*
* @generated from protobuf field: string workflow_job_run_backend_id = 2;
*/
workflowJobRunBackendId: string;
/**
* Monolith database ID of the artifact
*
* @generated from protobuf field: int64 database_id = 3;
*/
databaseId: string;
/**
* Name of the artifact
*
* @generated from protobuf field: string name = 4;
*/
name: string;
/**
* Size of the artifact in bytes
*
* @generated from protobuf field: int64 size = 5;
*/
size: string;
/**
* When the artifact was created in the monolith
*
* @generated from protobuf field: google.protobuf.Timestamp created_at = 6;
*/
createdAt?: Timestamp;
}
/**
* @generated from protobuf message github.actions.results.api.v1.GetSignedArtifactURLRequest
*/
export interface GetSignedArtifactURLRequest {
/**
* @generated from protobuf field: string workflow_run_backend_id = 1;
*/
workflowRunBackendId: string;
/**
* @generated from protobuf field: string workflow_job_run_backend_id = 2;
*/
workflowJobRunBackendId: string;
/**
* @generated from protobuf field: string name = 3;
*/
name: string;
}
/**
* @generated from protobuf message github.actions.results.api.v1.GetSignedArtifactURLResponse
*/
export interface GetSignedArtifactURLResponse {
/**
* @generated from protobuf field: string signed_url = 1;
*/
signedUrl: string;
}
// @generated message type with reflection information, may provide speed optimized methods
class CreateArtifactRequest$Type extends MessageType<CreateArtifactRequest> {
constructor() {
@@ -454,317 +348,10 @@ class FinalizeArtifactResponse$Type extends MessageType<FinalizeArtifactResponse
* @generated MessageType for protobuf message github.actions.results.api.v1.FinalizeArtifactResponse
*/
export const FinalizeArtifactResponse = new FinalizeArtifactResponse$Type();
// @generated message type with reflection information, may provide speed optimized methods
class ListArtifactsRequest$Type extends MessageType<ListArtifactsRequest> {
constructor() {
super("github.actions.results.api.v1.ListArtifactsRequest", [
{ 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_filter", kind: "message", T: () => StringValue },
{ no: 4, name: "id_filter", kind: "message", T: () => Int64Value }
]);
}
create(value?: PartialMessage<ListArtifactsRequest>): ListArtifactsRequest {
const message = { workflowRunBackendId: "", workflowJobRunBackendId: "" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<ListArtifactsRequest>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsRequest): ListArtifactsRequest {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string workflow_run_backend_id */ 1:
message.workflowRunBackendId = reader.string();
break;
case /* string workflow_job_run_backend_id */ 2:
message.workflowJobRunBackendId = reader.string();
break;
case /* google.protobuf.StringValue name_filter */ 3:
message.nameFilter = StringValue.internalBinaryRead(reader, reader.uint32(), options, message.nameFilter);
break;
case /* google.protobuf.Int64Value id_filter */ 4:
message.idFilter = Int64Value.internalBinaryRead(reader, reader.uint32(), options, message.idFilter);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: ListArtifactsRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string workflow_run_backend_id = 1; */
if (message.workflowRunBackendId !== "")
writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId);
/* string workflow_job_run_backend_id = 2; */
if (message.workflowJobRunBackendId !== "")
writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId);
/* google.protobuf.StringValue name_filter = 3; */
if (message.nameFilter)
StringValue.internalBinaryWrite(message.nameFilter, writer.tag(3, WireType.LengthDelimited).fork(), options).join();
/* google.protobuf.Int64Value id_filter = 4; */
if (message.idFilter)
Int64Value.internalBinaryWrite(message.idFilter, writer.tag(4, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsRequest
*/
export const ListArtifactsRequest = new ListArtifactsRequest$Type();
// @generated message type with reflection information, may provide speed optimized methods
class ListArtifactsResponse$Type extends MessageType<ListArtifactsResponse> {
constructor() {
super("github.actions.results.api.v1.ListArtifactsResponse", [
{ no: 1, name: "artifacts", kind: "message", repeat: 1 /*RepeatType.PACKED*/, T: () => ListArtifactsResponse_MonolithArtifact }
]);
}
create(value?: PartialMessage<ListArtifactsResponse>): ListArtifactsResponse {
const message = { artifacts: [] };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<ListArtifactsResponse>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsResponse): ListArtifactsResponse {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts */ 1:
message.artifacts.push(ListArtifactsResponse_MonolithArtifact.internalBinaryRead(reader, reader.uint32(), options));
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: ListArtifactsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* repeated github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact artifacts = 1; */
for (let i = 0; i < message.artifacts.length; i++)
ListArtifactsResponse_MonolithArtifact.internalBinaryWrite(message.artifacts[i], writer.tag(1, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsResponse
*/
export const ListArtifactsResponse = new ListArtifactsResponse$Type();
// @generated message type with reflection information, may provide speed optimized methods
class ListArtifactsResponse_MonolithArtifact$Type extends MessageType<ListArtifactsResponse_MonolithArtifact> {
constructor() {
super("github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact", [
{ 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: "database_id", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 4, name: "name", kind: "scalar", T: 9 /*ScalarType.STRING*/ },
{ no: 5, name: "size", kind: "scalar", T: 3 /*ScalarType.INT64*/ },
{ no: 6, name: "created_at", kind: "message", T: () => Timestamp }
]);
}
create(value?: PartialMessage<ListArtifactsResponse_MonolithArtifact>): ListArtifactsResponse_MonolithArtifact {
const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", databaseId: "0", name: "", size: "0" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<ListArtifactsResponse_MonolithArtifact>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: ListArtifactsResponse_MonolithArtifact): ListArtifactsResponse_MonolithArtifact {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string workflow_run_backend_id */ 1:
message.workflowRunBackendId = reader.string();
break;
case /* string workflow_job_run_backend_id */ 2:
message.workflowJobRunBackendId = reader.string();
break;
case /* int64 database_id */ 3:
message.databaseId = reader.int64().toString();
break;
case /* string name */ 4:
message.name = reader.string();
break;
case /* int64 size */ 5:
message.size = reader.int64().toString();
break;
case /* google.protobuf.Timestamp created_at */ 6:
message.createdAt = Timestamp.internalBinaryRead(reader, reader.uint32(), options, message.createdAt);
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: ListArtifactsResponse_MonolithArtifact, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string workflow_run_backend_id = 1; */
if (message.workflowRunBackendId !== "")
writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId);
/* string workflow_job_run_backend_id = 2; */
if (message.workflowJobRunBackendId !== "")
writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId);
/* int64 database_id = 3; */
if (message.databaseId !== "0")
writer.tag(3, WireType.Varint).int64(message.databaseId);
/* string name = 4; */
if (message.name !== "")
writer.tag(4, WireType.LengthDelimited).string(message.name);
/* int64 size = 5; */
if (message.size !== "0")
writer.tag(5, WireType.Varint).int64(message.size);
/* google.protobuf.Timestamp created_at = 6; */
if (message.createdAt)
Timestamp.internalBinaryWrite(message.createdAt, writer.tag(6, WireType.LengthDelimited).fork(), options).join();
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message github.actions.results.api.v1.ListArtifactsResponse.MonolithArtifact
*/
export const ListArtifactsResponse_MonolithArtifact = new ListArtifactsResponse_MonolithArtifact$Type();
// @generated message type with reflection information, may provide speed optimized methods
class GetSignedArtifactURLRequest$Type extends MessageType<GetSignedArtifactURLRequest> {
constructor() {
super("github.actions.results.api.v1.GetSignedArtifactURLRequest", [
{ 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*/ }
]);
}
create(value?: PartialMessage<GetSignedArtifactURLRequest>): GetSignedArtifactURLRequest {
const message = { workflowRunBackendId: "", workflowJobRunBackendId: "", name: "" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<GetSignedArtifactURLRequest>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetSignedArtifactURLRequest): GetSignedArtifactURLRequest {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string workflow_run_backend_id */ 1:
message.workflowRunBackendId = reader.string();
break;
case /* string workflow_job_run_backend_id */ 2:
message.workflowJobRunBackendId = reader.string();
break;
case /* string name */ 3:
message.name = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: GetSignedArtifactURLRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string workflow_run_backend_id = 1; */
if (message.workflowRunBackendId !== "")
writer.tag(1, WireType.LengthDelimited).string(message.workflowRunBackendId);
/* string workflow_job_run_backend_id = 2; */
if (message.workflowJobRunBackendId !== "")
writer.tag(2, WireType.LengthDelimited).string(message.workflowJobRunBackendId);
/* string name = 3; */
if (message.name !== "")
writer.tag(3, WireType.LengthDelimited).string(message.name);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message github.actions.results.api.v1.GetSignedArtifactURLRequest
*/
export const GetSignedArtifactURLRequest = new GetSignedArtifactURLRequest$Type();
// @generated message type with reflection information, may provide speed optimized methods
class GetSignedArtifactURLResponse$Type extends MessageType<GetSignedArtifactURLResponse> {
constructor() {
super("github.actions.results.api.v1.GetSignedArtifactURLResponse", [
{ no: 1, name: "signed_url", kind: "scalar", T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<GetSignedArtifactURLResponse>): GetSignedArtifactURLResponse {
const message = { signedUrl: "" };
globalThis.Object.defineProperty(message, MESSAGE_TYPE, { enumerable: false, value: this });
if (value !== undefined)
reflectionMergePartial<GetSignedArtifactURLResponse>(this, message, value);
return message;
}
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetSignedArtifactURLResponse): GetSignedArtifactURLResponse {
let message = target ?? this.create(), end = reader.pos + length;
while (reader.pos < end) {
let [fieldNo, wireType] = reader.tag();
switch (fieldNo) {
case /* string signed_url */ 1:
message.signedUrl = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
let d = reader.skip(wireType);
if (u !== false)
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
}
}
return message;
}
internalBinaryWrite(message: GetSignedArtifactURLResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
/* string signed_url = 1; */
if (message.signedUrl !== "")
writer.tag(1, WireType.LengthDelimited).string(message.signedUrl);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
return writer;
}
}
/**
* @generated MessageType for protobuf message github.actions.results.api.v1.GetSignedArtifactURLResponse
*/
export const GetSignedArtifactURLResponse = new GetSignedArtifactURLResponse$Type();
/**
* @generated ServiceType for protobuf service github.actions.results.api.v1.ArtifactService
*/
export const ArtifactService = new ServiceType("github.actions.results.api.v1.ArtifactService", [
{ name: "CreateArtifact", options: {}, I: CreateArtifactRequest, O: CreateArtifactResponse },
{ name: "FinalizeArtifact", options: {}, I: FinalizeArtifactRequest, O: FinalizeArtifactResponse },
{ name: "ListArtifacts", options: {}, I: ListArtifactsRequest, O: ListArtifactsResponse },
{ name: "GetSignedArtifactURL", options: {}, I: GetSignedArtifactURLRequest, O: GetSignedArtifactURLResponse }
{ name: "FinalizeArtifact", options: {}, I: FinalizeArtifactRequest, O: FinalizeArtifactResponse }
]);
@@ -6,18 +6,14 @@ import {
TwirpErrorCode,
Interceptor,
TwirpContentType,
chainInterceptors,
} from "twirp-ts";
chainInterceptors
} from 'twirp-ts'
import {
CreateArtifactRequest,
CreateArtifactResponse,
FinalizeArtifactRequest,
FinalizeArtifactResponse,
ListArtifactsRequest,
ListArtifactsResponse,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse,
} from "./artifact";
FinalizeArtifactResponse
} from './artifact'
//==================================//
// Client Code //
@@ -27,51 +23,43 @@ interface Rpc {
request(
service: string,
method: string,
contentType: "application/json" | "application/protobuf",
contentType: 'application/json' | 'application/protobuf',
data: object | Uint8Array
): Promise<object | Uint8Array>;
): Promise<object | Uint8Array>
}
export interface ArtifactServiceClient {
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse>;
): Promise<CreateArtifactResponse>
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse>;
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse>;
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse>;
): Promise<FinalizeArtifactResponse>
}
export class ArtifactServiceClientJSON implements ArtifactServiceClient {
private readonly rpc: Rpc;
private readonly rpc: Rpc
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.rpc = rpc
this.CreateArtifact.bind(this)
this.FinalizeArtifact.bind(this)
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
emitDefaultValues: false
})
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/json",
'github.actions.results.api.v1.ArtifactService',
'CreateArtifact',
'application/json',
data as object
);
return promise.then((data) =>
CreateArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
)
return promise.then(data =>
CreateArtifactResponse.fromJson(data as any, {ignoreUnknownFields: true})
)
}
FinalizeArtifact(
@@ -79,123 +67,57 @@ export class ArtifactServiceClientJSON implements ArtifactServiceClient {
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
emitDefaultValues: false
})
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/json",
'github.actions.results.api.v1.ArtifactService',
'FinalizeArtifact',
'application/json',
data as object
);
return promise.then((data) =>
)
return promise.then(data =>
FinalizeArtifactResponse.fromJson(data as any, {
ignoreUnknownFields: true,
ignoreUnknownFields: true
})
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/json",
data as object
);
return promise.then((data) =>
ListArtifactsResponse.fromJson(data as any, { ignoreUnknownFields: true })
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toJson(request, {
useProtoFieldName: true,
emitDefaultValues: false,
});
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/json",
data as object
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromJson(data as any, {
ignoreUnknownFields: true,
})
);
)
}
}
export class ArtifactServiceClientProtobuf implements ArtifactServiceClient {
private readonly rpc: Rpc;
private readonly rpc: Rpc
constructor(rpc: Rpc) {
this.rpc = rpc;
this.CreateArtifact.bind(this);
this.FinalizeArtifact.bind(this);
this.ListArtifacts.bind(this);
this.GetSignedArtifactURL.bind(this);
this.rpc = rpc
this.CreateArtifact.bind(this)
this.FinalizeArtifact.bind(this)
}
CreateArtifact(
request: CreateArtifactRequest
): Promise<CreateArtifactResponse> {
const data = CreateArtifactRequest.toBinary(request);
const data = CreateArtifactRequest.toBinary(request)
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"CreateArtifact",
"application/protobuf",
'github.actions.results.api.v1.ArtifactService',
'CreateArtifact',
'application/protobuf',
data
);
return promise.then((data) =>
)
return promise.then(data =>
CreateArtifactResponse.fromBinary(data as Uint8Array)
);
)
}
FinalizeArtifact(
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse> {
const data = FinalizeArtifactRequest.toBinary(request);
const data = FinalizeArtifactRequest.toBinary(request)
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"FinalizeArtifact",
"application/protobuf",
'github.actions.results.api.v1.ArtifactService',
'FinalizeArtifact',
'application/protobuf',
data
);
return promise.then((data) =>
)
return promise.then(data =>
FinalizeArtifactResponse.fromBinary(data as Uint8Array)
);
}
ListArtifacts(request: ListArtifactsRequest): Promise<ListArtifactsResponse> {
const data = ListArtifactsRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"ListArtifacts",
"application/protobuf",
data
);
return promise.then((data) =>
ListArtifactsResponse.fromBinary(data as Uint8Array)
);
}
GetSignedArtifactURL(
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse> {
const data = GetSignedArtifactURLRequest.toBinary(request);
const promise = this.rpc.request(
"github.actions.results.api.v1.ArtifactService",
"GetSignedArtifactURL",
"application/protobuf",
data
);
return promise.then((data) =>
GetSignedArtifactURLResponse.fromBinary(data as Uint8Array)
);
)
}
}
@@ -207,45 +129,33 @@ export interface ArtifactServiceTwirp<T extends TwirpContext = TwirpContext> {
CreateArtifact(
ctx: T,
request: CreateArtifactRequest
): Promise<CreateArtifactResponse>;
): Promise<CreateArtifactResponse>
FinalizeArtifact(
ctx: T,
request: FinalizeArtifactRequest
): Promise<FinalizeArtifactResponse>;
ListArtifacts(
ctx: T,
request: ListArtifactsRequest
): Promise<ListArtifactsResponse>;
GetSignedArtifactURL(
ctx: T,
request: GetSignedArtifactURLRequest
): Promise<GetSignedArtifactURLResponse>;
): Promise<FinalizeArtifactResponse>
}
export enum ArtifactServiceMethod {
CreateArtifact = "CreateArtifact",
FinalizeArtifact = "FinalizeArtifact",
ListArtifacts = "ListArtifacts",
GetSignedArtifactURL = "GetSignedArtifactURL",
CreateArtifact = 'CreateArtifact',
FinalizeArtifact = 'FinalizeArtifact'
}
export const ArtifactServiceMethodList = [
ArtifactServiceMethod.CreateArtifact,
ArtifactServiceMethod.FinalizeArtifact,
ArtifactServiceMethod.ListArtifacts,
ArtifactServiceMethod.GetSignedArtifactURL,
];
ArtifactServiceMethod.FinalizeArtifact
]
export function createArtifactServiceServer<
T extends TwirpContext = TwirpContext
>(service: ArtifactServiceTwirp<T>) {
return new TwirpServer<ArtifactServiceTwirp, T>({
service,
packageName: "github.actions.results.api.v1",
serviceName: "ArtifactService",
packageName: 'github.actions.results.api.v1',
serviceName: 'ArtifactService',
methodList: ArtifactServiceMethodList,
matchRoute: matchArtifactServiceRoute,
});
matchRoute: matchArtifactServiceRoute
})
}
function matchArtifactServiceRoute<T extends TwirpContext = TwirpContext>(
@@ -253,7 +163,7 @@ function matchArtifactServiceRoute<T extends TwirpContext = TwirpContext>(
events: RouterEvents<T>
) {
switch (method) {
case "CreateArtifact":
case 'CreateArtifact':
return async (
ctx: T,
service: ArtifactServiceTwirp,
@@ -264,16 +174,16 @@ function matchArtifactServiceRoute<T extends TwirpContext = TwirpContext>(
CreateArtifactResponse
>[]
) => {
ctx = { ...ctx, methodName: "CreateArtifact" };
await events.onMatch(ctx);
ctx = {...ctx, methodName: 'CreateArtifact'}
await events.onMatch(ctx)
return handleArtifactServiceCreateArtifactRequest(
ctx,
service,
data,
interceptors
);
};
case "FinalizeArtifact":
)
}
case 'FinalizeArtifact':
return async (
ctx: T,
service: ArtifactServiceTwirp,
@@ -284,59 +194,19 @@ function matchArtifactServiceRoute<T extends TwirpContext = TwirpContext>(
FinalizeArtifactResponse
>[]
) => {
ctx = { ...ctx, methodName: "FinalizeArtifact" };
await events.onMatch(ctx);
ctx = {...ctx, methodName: 'FinalizeArtifact'}
await events.onMatch(ctx)
return handleArtifactServiceFinalizeArtifactRequest(
ctx,
service,
data,
interceptors
);
};
case "ListArtifacts":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>[]
) => {
ctx = { ...ctx, methodName: "ListArtifacts" };
await events.onMatch(ctx);
return handleArtifactServiceListArtifactsRequest(
ctx,
service,
data,
interceptors
);
};
case "GetSignedArtifactURL":
return async (
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) => {
ctx = { ...ctx, methodName: "GetSignedArtifactURL" };
await events.onMatch(ctx);
return handleArtifactServiceGetSignedArtifactURLRequest(
ctx,
service,
data,
interceptors
);
};
)
}
default:
events.onNotFound();
const msg = `no handler found`;
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
events.onNotFound()
const msg = `no handler found`
throw new TwirpError(TwirpErrorCode.BadRoute, msg)
}
}
@@ -355,17 +225,17 @@ function handleArtifactServiceCreateArtifactRequest<
service,
data,
interceptors
);
)
case TwirpContentType.Protobuf:
return handleArtifactServiceCreateArtifactProtobuf<T>(
ctx,
service,
data,
interceptors
);
)
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
const msg = 'unexpected Content-Type'
throw new TwirpError(TwirpErrorCode.BadRoute, msg)
}
}
@@ -388,79 +258,17 @@ function handleArtifactServiceFinalizeArtifactRequest<
service,
data,
interceptors
);
)
case TwirpContentType.Protobuf:
return handleArtifactServiceFinalizeArtifactProtobuf<T>(
ctx,
service,
data,
interceptors
);
)
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceListArtifactsRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceListArtifactsJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceListArtifactsProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
}
}
function handleArtifactServiceGetSignedArtifactURLRequest<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
): Promise<string | Uint8Array> {
switch (ctx.contentType) {
case TwirpContentType.JSON:
return handleArtifactServiceGetSignedArtifactURLJSON<T>(
ctx,
service,
data,
interceptors
);
case TwirpContentType.Protobuf:
return handleArtifactServiceGetSignedArtifactURLProtobuf<T>(
ctx,
service,
data,
interceptors
);
default:
const msg = "unexpected Content-Type";
throw new TwirpError(TwirpErrorCode.BadRoute, msg);
const msg = 'unexpected Content-Type'
throw new TwirpError(TwirpErrorCode.BadRoute, msg)
}
}
async function handleArtifactServiceCreateArtifactJSON<
@@ -471,18 +279,16 @@ async function handleArtifactServiceCreateArtifactJSON<
data: Buffer,
interceptors?: Interceptor<T, CreateArtifactRequest, CreateArtifactResponse>[]
) {
let request: CreateArtifactRequest;
let response: CreateArtifactResponse;
let request: CreateArtifactRequest
let response: CreateArtifactResponse
try {
const body = JSON.parse(data.toString() || "{}");
request = CreateArtifactRequest.fromJson(body, {
ignoreUnknownFields: true,
});
const body = JSON.parse(data.toString() || '{}')
request = CreateArtifactRequest.fromJson(body, {ignoreUnknownFields: true})
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
const msg = 'the json request could not be decoded'
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true)
}
}
@@ -491,20 +297,20 @@ async function handleArtifactServiceCreateArtifactJSON<
T,
CreateArtifactRequest,
CreateArtifactResponse
>;
>
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.CreateArtifact(ctx, inputReq);
});
return service.CreateArtifact(ctx, inputReq)
})
} else {
response = await service.CreateArtifact(ctx, request!);
response = await service.CreateArtifact(ctx, request!)
}
return JSON.stringify(
CreateArtifactResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
emitDefaultValues: false
}) as string
);
)
}
async function handleArtifactServiceFinalizeArtifactJSON<
@@ -519,18 +325,18 @@ async function handleArtifactServiceFinalizeArtifactJSON<
FinalizeArtifactResponse
>[]
) {
let request: FinalizeArtifactRequest;
let response: FinalizeArtifactResponse;
let request: FinalizeArtifactRequest
let response: FinalizeArtifactResponse
try {
const body = JSON.parse(data.toString() || "{}");
const body = JSON.parse(data.toString() || '{}')
request = FinalizeArtifactRequest.fromJson(body, {
ignoreUnknownFields: true,
});
ignoreUnknownFields: true
})
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
const msg = 'the json request could not be decoded'
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true)
}
}
@@ -539,112 +345,20 @@ async function handleArtifactServiceFinalizeArtifactJSON<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>;
>
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.FinalizeArtifact(ctx, inputReq);
});
return service.FinalizeArtifact(ctx, inputReq)
})
} else {
response = await service.FinalizeArtifact(ctx, request!);
response = await service.FinalizeArtifact(ctx, request!)
}
return JSON.stringify(
FinalizeArtifactResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
emitDefaultValues: false
}) as string
);
}
async function handleArtifactServiceListArtifactsJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
) {
let request: ListArtifactsRequest;
let response: ListArtifactsResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = ListArtifactsRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.ListArtifacts(ctx, inputReq);
});
} else {
response = await service.ListArtifacts(ctx, request!);
}
return JSON.stringify(
ListArtifactsResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
}
async function handleArtifactServiceGetSignedArtifactURLJSON<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) {
let request: GetSignedArtifactURLRequest;
let response: GetSignedArtifactURLResponse;
try {
const body = JSON.parse(data.toString() || "{}");
request = GetSignedArtifactURLRequest.fromJson(body, {
ignoreUnknownFields: true,
});
} catch (e) {
if (e instanceof Error) {
const msg = "the json request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.GetSignedArtifactURL(ctx, inputReq);
});
} else {
response = await service.GetSignedArtifactURL(ctx, request!);
}
return JSON.stringify(
GetSignedArtifactURLResponse.toJson(response, {
useProtoFieldName: true,
emitDefaultValues: false,
}) as string
);
)
}
async function handleArtifactServiceCreateArtifactProtobuf<
T extends TwirpContext = TwirpContext
@@ -654,15 +368,15 @@ async function handleArtifactServiceCreateArtifactProtobuf<
data: Buffer,
interceptors?: Interceptor<T, CreateArtifactRequest, CreateArtifactResponse>[]
) {
let request: CreateArtifactRequest;
let response: CreateArtifactResponse;
let request: CreateArtifactRequest
let response: CreateArtifactResponse
try {
request = CreateArtifactRequest.fromBinary(data);
request = CreateArtifactRequest.fromBinary(data)
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
const msg = 'the protobuf request could not be decoded'
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true)
}
}
@@ -671,15 +385,15 @@ async function handleArtifactServiceCreateArtifactProtobuf<
T,
CreateArtifactRequest,
CreateArtifactResponse
>;
>
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.CreateArtifact(ctx, inputReq);
});
return service.CreateArtifact(ctx, inputReq)
})
} else {
response = await service.CreateArtifact(ctx, request!);
response = await service.CreateArtifact(ctx, request!)
}
return Buffer.from(CreateArtifactResponse.toBinary(response));
return Buffer.from(CreateArtifactResponse.toBinary(response))
}
async function handleArtifactServiceFinalizeArtifactProtobuf<
@@ -694,15 +408,15 @@ async function handleArtifactServiceFinalizeArtifactProtobuf<
FinalizeArtifactResponse
>[]
) {
let request: FinalizeArtifactRequest;
let response: FinalizeArtifactResponse;
let request: FinalizeArtifactRequest
let response: FinalizeArtifactResponse
try {
request = FinalizeArtifactRequest.fromBinary(data);
request = FinalizeArtifactRequest.fromBinary(data)
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
const msg = 'the protobuf request could not be decoded'
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true)
}
}
@@ -711,89 +425,13 @@ async function handleArtifactServiceFinalizeArtifactProtobuf<
T,
FinalizeArtifactRequest,
FinalizeArtifactResponse
>;
>
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.FinalizeArtifact(ctx, inputReq);
});
return service.FinalizeArtifact(ctx, inputReq)
})
} else {
response = await service.FinalizeArtifact(ctx, request!);
response = await service.FinalizeArtifact(ctx, request!)
}
return Buffer.from(FinalizeArtifactResponse.toBinary(response));
}
async function handleArtifactServiceListArtifactsProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<T, ListArtifactsRequest, ListArtifactsResponse>[]
) {
let request: ListArtifactsRequest;
let response: ListArtifactsResponse;
try {
request = ListArtifactsRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
ListArtifactsRequest,
ListArtifactsResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.ListArtifacts(ctx, inputReq);
});
} else {
response = await service.ListArtifacts(ctx, request!);
}
return Buffer.from(ListArtifactsResponse.toBinary(response));
}
async function handleArtifactServiceGetSignedArtifactURLProtobuf<
T extends TwirpContext = TwirpContext
>(
ctx: T,
service: ArtifactServiceTwirp,
data: Buffer,
interceptors?: Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>[]
) {
let request: GetSignedArtifactURLRequest;
let response: GetSignedArtifactURLResponse;
try {
request = GetSignedArtifactURLRequest.fromBinary(data);
} catch (e) {
if (e instanceof Error) {
const msg = "the protobuf request could not be decoded";
throw new TwirpError(TwirpErrorCode.Malformed, msg).withCause(e, true);
}
}
if (interceptors && interceptors.length > 0) {
const interceptor = chainInterceptors(...interceptors) as Interceptor<
T,
GetSignedArtifactURLRequest,
GetSignedArtifactURLResponse
>;
response = await interceptor(ctx, request!, (ctx, inputReq) => {
return service.GetSignedArtifactURL(ctx, inputReq);
});
} else {
response = await service.GetSignedArtifactURL(ctx, request!);
}
return Buffer.from(GetSignedArtifactURLResponse.toBinary(response));
return Buffer.from(FinalizeArtifactResponse.toBinary(response))
}
+144 -115
View File
@@ -1,107 +1,122 @@
import {warning} from '@actions/core'
import {isGhes} from './shared/config'
import {
UploadArtifactOptions,
UploadArtifactResponse,
UploadOptions,
UploadResponse,
DownloadArtifactOptions,
GetArtifactResponse,
ListArtifactsOptions,
ListArtifactsResponse,
DownloadArtifactResponse,
FindOptions
DownloadArtifactResponse
} from './shared/interfaces'
import {uploadArtifact} from './upload/upload-artifact'
import {
downloadArtifactPublic,
downloadArtifactInternal
} from './download/download-artifact'
import {getArtifactPublic, getArtifactInternal} from './find/get-artifact'
import {listArtifactsPublic, listArtifactsInternal} from './find/list-artifacts'
import {GHESNotSupportedError} from './shared/errors'
import {downloadArtifact} from './download/download-artifact'
import {getArtifact} from './find/get-artifact'
import {listArtifacts} from './find/list-artifacts'
/**
* Generic interface for the artifact client.
*/
export interface ArtifactClient {
/**
* Uploads an artifact.
* 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 UploadArtifactResponse object
* @returns single UploadResponse object
*/
uploadArtifact(
name: string,
files: string[],
rootDirectory: string,
options?: UploadArtifactOptions
): Promise<UploadArtifactResponse>
options?: UploadOptions
): Promise<UploadResponse>
/**
* Lists all artifacts that are part of the current workflow run.
* This function will return at most 1000 artifacts per workflow run.
* Lists all artifacts that are part of a workflow run.
*
* If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs.
* https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
* 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 options Extra options that allow for the customization of the list behavior
* @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(
options?: ListArtifactsOptions & FindOptions
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<ListArtifactsResponse>
/**
* Finds an artifact by name.
* If there are multiple artifacts with the same name in the same workflow run, this will return the latest.
* If the artifact is not found, it will throw.
* Finds an artifact by name given a repository and workflow run id.
*
* If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs.
* https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts
* `@actions/artifact` v2+ 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), @actions/artifact < v2 or it is a rerun.
* 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 options Extra options that allow for the customization of the get behavior
* @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,
options?: FindOptions
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string
): Promise<GetArtifactResponse>
/**
* Downloads an artifact and unzips the content.
*
* If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact
* 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,
options?: DownloadArtifactOptions & FindOptions
repositoryOwner: string,
repositoryName: string,
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse>
}
/**
* The default artifact client that is used by the artifact action(s).
*/
export class DefaultArtifactClient implements ArtifactClient {
export class Client implements ArtifactClient {
/**
* Constructs a Client
*/
static create(): Client {
return new Client()
}
/**
* Upload Artifact
*/
async uploadArtifact(
name: string,
files: string[],
rootDirectory: string,
options?: UploadArtifactOptions
): Promise<UploadArtifactResponse> {
try {
if (isGhes()) {
throw new GHESNotSupportedError()
options?: UploadOptions | undefined
): Promise<UploadResponse> {
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(
@@ -111,72 +126,79 @@ Errors can be temporary, so please try again and optionally run the action with
If the error persists, please check whether Actions is operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
throw error
return {
success: false
}
}
}
/**
* Download Artifact
*/
async downloadArtifact(
artifactId: number,
options?: DownloadArtifactOptions & FindOptions
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 {
if (isGhes()) {
throw new GHESNotSupportedError()
}
if (options?.findBy) {
const {
findBy: {repositoryOwner, repositoryName, token},
...downloadOptions
} = options
return downloadArtifactPublic(
artifactId,
repositoryOwner,
repositoryName,
token,
downloadOptions
)
}
return downloadArtifactInternal(artifactId, options)
return downloadArtifact(
artifactId,
repositoryOwner,
repositoryName,
token,
options
)
} catch (error) {
warning(
`Download Artifact failed with error: ${error}.
`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).`
)
throw error
return {
success: false
}
}
}
/**
* List Artifacts
*/
async listArtifacts(
options?: ListArtifactsOptions & FindOptions
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 {
if (isGhes()) {
throw new GHESNotSupportedError()
}
if (options?.findBy) {
const {
findBy: {workflowRunId, repositoryOwner, repositoryName, token}
} = options
return listArtifactsPublic(
workflowRunId,
repositoryOwner,
repositoryName,
token,
options?.latest
)
}
return listArtifactsInternal(options?.latest)
return listArtifacts(
workflowRunId,
repositoryOwner,
repositoryName,
token
)
} catch (error: unknown) {
warning(
`Listing Artifacts failed with error: ${error}.
@@ -186,43 +208,50 @@ Errors can be temporary, so please try again and optionally run the action with
If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).`
)
throw error
return {
artifacts: []
}
}
}
/**
* Get Artifact
*/
async getArtifact(
artifactName: string,
options?: FindOptions
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 {
if (isGhes()) {
throw new GHESNotSupportedError()
}
if (options?.findBy) {
const {
findBy: {workflowRunId, repositoryOwner, repositoryName, token}
} = options
return getArtifactPublic(
artifactName,
workflowRunId,
repositoryOwner,
repositoryName,
token
)
}
return getArtifactInternal(artifactName)
return getArtifact(
artifactName,
workflowRunId,
repositoryOwner,
repositoryName,
token
)
} catch (error: unknown) {
warning(
`Get Artifact failed with error: ${error}.
`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).`
)
throw error
return {
success: false
}
}
}
}
@@ -2,21 +2,13 @@ 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 unzip from 'unzip-stream'
import unzipper from 'unzipper'
import {
DownloadArtifactOptions,
DownloadArtifactResponse
} from '../shared/interfaces'
import {getUserAgentString} from '../shared/user-agent'
import {getGitHubWorkspaceDir} from '../shared/config'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {
GetSignedArtifactURLRequest,
Int64Value,
ListArtifactsRequest
} from '../../generated'
import {getBackendIdsFromToken} from '../shared/util'
import {ArtifactNotFoundError} from '../shared/errors'
const scrubQueryParameters = (url: string): string => {
const parsed = new URL(url)
@@ -38,76 +30,35 @@ async function exists(path: string): Promise<boolean> {
}
async function streamExtract(url: string, directory: string): Promise<void> {
let retryCount = 0
while (retryCount < 5) {
try {
await streamExtractExternal(url, directory)
return
} catch (error) {
retryCount++
core.debug(
`Failed to download artifact after ${retryCount} retries due to ${error.message}. Retrying in 5 seconds...`
)
// wait 5 seconds before retrying
await new Promise(resolve => setTimeout(resolve, 5000))
}
}
throw new Error(`Artifact download failed after ${retryCount} retries.`)
}
export async function streamExtractExternal(
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}`
)
}
const timeout = 30 * 1000 // 30 seconds
return new Promise((resolve, reject) => {
const timerFn = (): void => {
response.message.destroy(
new Error(`Blob storage chunk did not respond in ${timeout}ms`)
)
}
const timer = setTimeout(timerFn, timeout)
response.message
.on('data', () => {
timer.refresh()
})
.on('error', (error: Error) => {
core.debug(
`response.message: Artifact download failed: ${error.message}`
)
clearTimeout(timer)
reject(error)
})
.pipe(unzip.Extract({path: directory}))
.on('close', () => {
clearTimeout(timer)
resolve()
})
.on('error', (error: Error) => {
reject(error)
})
})
return response.message.pipe(unzipper.Extract({path: directory})).promise()
}
export async function downloadArtifactPublic(
export async function downloadArtifact(
artifactId: number,
repositoryOwner: string,
repositoryName: string,
token: string,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse> {
const downloadPath = await resolveOrCreateDirectory(options?.path)
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)
@@ -146,72 +97,5 @@ export async function downloadArtifactPublic(
throw new Error(`Unable to download and extract artifact: ${error.message}`)
}
return {downloadPath}
}
export async function downloadArtifactInternal(
artifactId: number,
options?: DownloadArtifactOptions
): Promise<DownloadArtifactResponse> {
const downloadPath = await resolveOrCreateDirectory(options?.path)
const artifactClient = internalArtifactTwirpClient()
const {workflowRunBackendId, workflowJobRunBackendId} =
getBackendIdsFromToken()
const listReq: ListArtifactsRequest = {
workflowRunBackendId,
workflowJobRunBackendId,
idFilter: Int64Value.create({value: artifactId.toString()})
}
const {artifacts} = await artifactClient.ListArtifacts(listReq)
if (artifacts.length === 0) {
throw new ArtifactNotFoundError(
`No artifacts found for ID: ${artifactId}\nAre you trying to download from a different run? Try specifying a github-token with \`actions:read\` scope.`
)
}
if (artifacts.length > 1) {
core.warning('Multiple artifacts found, defaulting to first.')
}
const signedReq: GetSignedArtifactURLRequest = {
workflowRunBackendId: artifacts[0].workflowRunBackendId,
workflowJobRunBackendId: artifacts[0].workflowJobRunBackendId,
name: artifacts[0].name
}
const {signedUrl} = await artifactClient.GetSignedArtifactURL(signedReq)
core.info(
`Redirecting to blob download url: ${scrubQueryParameters(signedUrl)}`
)
try {
core.info(`Starting download of artifact to: ${downloadPath}`)
await streamExtract(signedUrl, downloadPath)
core.info(`Artifact download completed successfully.`)
} catch (error) {
throw new Error(`Unable to download and extract artifact: ${error.message}`)
}
return {downloadPath}
}
async function resolveOrCreateDirectory(
downloadPath = getGitHubWorkspaceDir()
): Promise<string> {
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}`)
}
return downloadPath
return {success: true, downloadPath}
}
@@ -1,18 +1,14 @@
import {GetArtifactResponse} from '../shared/interfaces'
import {getOctokit} from '@actions/github'
import {retry} from '@octokit/plugin-retry'
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
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 {GetArtifactResponse} from '../shared/interfaces'
import {getBackendIdsFromToken} from '../shared/util'
import {getUserAgentString} from '../shared/user-agent'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {ListArtifactsRequest, StringValue, Timestamp} from '../../generated'
import {ArtifactNotFoundError, InvalidResponseError} from '../shared/errors'
import {retry} from '@octokit/plugin-retry'
import * as core from '@actions/core'
import {OctokitOptions} from '@octokit/core/dist-types/types'
export async function getArtifactPublic(
export async function getArtifact(
artifactName: string,
workflowRunId: number,
repositoryOwner: string,
@@ -42,76 +38,32 @@ export async function getArtifactPublic(
)
if (getArtifactResp.status !== 200) {
throw new InvalidResponseError(
`Invalid response from GitHub API: ${getArtifactResp.status} (${getArtifactResp?.headers?.['x-github-request-id']})`
)
core.warning(`non-200 response from GitHub API: ${getArtifactResp.status}`)
return {
success: false
}
}
if (getArtifactResp.data.artifacts.length === 0) {
throw new ArtifactNotFoundError(
`Artifact not found for name: ${artifactName}`
)
core.warning('no artifacts found')
return {
success: false
}
}
let artifact = getArtifactResp.data.artifacts[0]
if (getArtifactResp.data.artifacts.length > 1) {
artifact = getArtifactResp.data.artifacts.sort((a, b) => b.id - a.id)[0]
core.debug(
`More than one artifact found for a single name, returning newest (id: ${artifact.id})`
core.warning(
'more than one artifact found for a single name, returning first'
)
}
return {
success: true,
artifact: {
name: artifact.name,
id: artifact.id,
size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
}
}
}
export async function getArtifactInternal(
artifactName: string
): Promise<GetArtifactResponse> {
const artifactClient = internalArtifactTwirpClient()
const {workflowRunBackendId, workflowJobRunBackendId} =
getBackendIdsFromToken()
const req: ListArtifactsRequest = {
workflowRunBackendId,
workflowJobRunBackendId,
nameFilter: StringValue.create({value: artifactName})
}
const res = await artifactClient.ListArtifacts(req)
if (res.artifacts.length === 0) {
throw new ArtifactNotFoundError(
`Artifact not found for name: ${artifactName}`
)
}
let artifact = res.artifacts[0]
if (res.artifacts.length > 1) {
artifact = res.artifacts.sort(
(a, b) => Number(b.databaseId) - Number(a.databaseId)
)[0]
core.debug(
`More than one artifact found for a single name, returning newest (id: ${artifact.databaseId})`
)
}
return {
artifact: {
name: artifact.name,
id: Number(artifact.databaseId),
size: Number(artifact.size),
createdAt: artifact.createdAt
? Timestamp.toDate(artifact.createdAt)
: undefined
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
}
}
}
@@ -7,27 +7,23 @@ 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'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {getBackendIdsFromToken} from '../shared/util'
import {ListArtifactsRequest, Timestamp} from '../../generated'
// Limiting to 1000 for perf reasons
const maximumArtifactCount = 1000
const paginationCount = 100
const maxNumberOfPages = maximumArtifactCount / paginationCount
export async function listArtifactsPublic(
export async function listArtifacts(
workflowRunId: number,
repositoryOwner: string,
repositoryName: string,
token: string,
latest = false
token: string
): Promise<ListArtifactsResponse> {
info(
`Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}`
)
let artifacts: Artifact[] = []
const artifacts: Artifact[] = []
const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions)
const opts: OctokitOptions = {
@@ -66,8 +62,8 @@ export async function listArtifactsPublic(
artifacts.push({
name: artifact.name,
id: artifact.id,
size: artifact.size_in_bytes,
createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined
url: artifact.url,
size: artifact.size_in_bytes
})
}
@@ -93,73 +89,15 @@ export async function listArtifactsPublic(
artifacts.push({
name: artifact.name,
id: artifact.id,
size: artifact.size_in_bytes,
createdAt: artifact.created_at
? new Date(artifact.created_at)
: undefined
url: artifact.url,
size: artifact.size_in_bytes
})
}
}
if (latest) {
artifacts = filterLatest(artifacts)
}
info(`Found ${artifacts.length} artifact(s)`)
info(`Finished fetching artifact list`)
return {
artifacts
}
}
export async function listArtifactsInternal(
latest = false
): Promise<ListArtifactsResponse> {
const artifactClient = internalArtifactTwirpClient()
const {workflowRunBackendId, workflowJobRunBackendId} =
getBackendIdsFromToken()
const req: ListArtifactsRequest = {
workflowRunBackendId,
workflowJobRunBackendId
}
const res = await artifactClient.ListArtifacts(req)
let artifacts: Artifact[] = res.artifacts.map(artifact => ({
name: artifact.name,
id: Number(artifact.databaseId),
size: Number(artifact.size),
createdAt: artifact.createdAt
? Timestamp.toDate(artifact.createdAt)
: undefined
}))
if (latest) {
artifacts = filterLatest(artifacts)
}
info(`Found ${artifacts.length} artifact(s)`)
return {
artifacts
}
}
/**
* Filters a list of artifacts to only include the latest artifact for each name
* @param artifacts The artifacts to filter
* @returns The filtered list of artifacts
*/
function filterLatest(artifacts: Artifact[]): Artifact[] {
artifacts.sort((a, b) => b.id - a.id)
const latestArtifacts: Artifact[] = []
const seenArtifactNames = new Set<string>()
for (const artifact of artifacts) {
if (!seenArtifactNames.has(artifact.name)) {
latestArtifacts.push(artifact)
seenArtifactNames.add(artifact.name)
}
}
return latestArtifacts
}
@@ -3,8 +3,6 @@ import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {info, debug} from '@actions/core'
import {ArtifactServiceClientJSON} from '../../generated'
import {getResultsServiceUrl, getRuntimeToken} from './config'
import {getUserAgentString} from './user-agent'
import {NetworkError, UsageError} from './errors'
// The twirp http client must implement this interface
interface Rpc {
@@ -54,17 +52,17 @@ class ArtifactHttpClient implements Rpc {
contentType: 'application/json' | 'application/protobuf',
data: object | Uint8Array
): Promise<object | Uint8Array> {
const url = new URL(`/twirp/${service}/${method}`, this.baseUrl).href
debug(`[Request] ${method} ${url}`)
const url = `${this.baseUrl}/twirp/${service}/${method}`
debug(`Requesting ${url}`)
const headers = {
'Content-Type': contentType
}
try {
const {body} = await this.retryableRequest(async () =>
const response = await this.retryableRequest(async () =>
this.httpClient.post(url, JSON.stringify(data), headers)
)
return body
const body = await response.readBody()
return JSON.parse(body)
} catch (error) {
throw new Error(`Failed to ${method}: ${error.message}`)
}
@@ -72,47 +70,23 @@ class ArtifactHttpClient implements Rpc {
async retryableRequest(
operation: () => Promise<HttpClientResponse>
): Promise<{response: HttpClientResponse; body: object}> {
): Promise<HttpClientResponse> {
let attempt = 0
let errorMessage = ''
let rawBody = ''
while (attempt < this.maxAttempts) {
let isRetryable = false
try {
const response = await operation()
const statusCode = response.message.statusCode
rawBody = await response.readBody()
debug(`[Response] - ${response.message.statusCode}`)
debug(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`)
const body = JSON.parse(rawBody)
debug(`Body: ${JSON.stringify(body, null, 2)}`)
if (this.isSuccessStatusCode(statusCode)) {
return {response, body}
return response
}
isRetryable = this.isRetryableHttpStatusCode(statusCode)
errorMessage = `Failed request: (${statusCode}) ${response.message.statusMessage}`
if (body.msg) {
if (UsageError.isUsageErrorMessage(body.msg)) {
throw new UsageError()
}
errorMessage = `${errorMessage}: ${body.msg}`
}
} catch (error) {
if (error instanceof SyntaxError) {
debug(`Raw Body: ${rawBody}`)
throw error
}
if (error instanceof UsageError) {
throw error
}
if (NetworkError.isNetworkErrorCode(error?.code)) {
throw new NetworkError(error?.code)
}
isRetryable = true
errorMessage = error.message
}
@@ -154,7 +128,8 @@ class ArtifactHttpClient implements Rpc {
HttpCodes.GatewayTimeout,
HttpCodes.InternalServerError,
HttpCodes.ServiceUnavailable,
HttpCodes.TooManyRequests
HttpCodes.TooManyRequests,
413 // Payload Too Large
]
return retryableStatusCodes.includes(statusCode)
@@ -182,16 +157,17 @@ class ArtifactHttpClient implements Rpc {
}
}
export function internalArtifactTwirpClient(options?: {
maxAttempts?: number
retryIntervalMs?: number
export function createArtifactTwirpClient(
type: 'upload' | 'download',
maxAttempts?: number,
baseRetryIntervalMilliseconds?: number,
retryMultiplier?: number
}): ArtifactServiceClientJSON {
): ArtifactServiceClientJSON {
const client = new ArtifactHttpClient(
getUserAgentString(),
options?.maxAttempts,
options?.retryIntervalMs,
options?.retryMultiplier
`@actions/artifact-${type}`,
maxAttempts,
baseRetryIntervalMilliseconds,
retryMultiplier
)
return new ArtifactServiceClientJSON(client)
}
@@ -1,5 +1,3 @@
import os from 'os'
// 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 {
@@ -19,8 +17,7 @@ export function getResultsServiceUrl(): string {
if (!resultsUrl) {
throw new Error('Unable to get the ACTIONS_RESULTS_URL env variable')
}
return new URL(resultsUrl).origin
return resultsUrl
}
export function isGhes(): boolean {
@@ -37,17 +34,3 @@ export function getGitHubWorkspaceDir(): string {
}
return ghWorkspaceDir
}
// Mimics behavior of azcopy: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-optimize
// If your machine has fewer than 5 CPUs, then the value of this variable is set to 32.
// Otherwise, the default value is equal to 16 multiplied by the number of CPUs. The maximum value of this variable is 300.
export function getConcurrency(): number {
const numCPUs = os.cpus().length
if (numCPUs <= 4) {
return 32
}
const concurrency = 16 * numCPUs
return concurrency > 300 ? 300 : concurrency
}
@@ -1,72 +0,0 @@
export class FilesNotFoundError extends Error {
files: string[]
constructor(files: string[] = []) {
let message = 'No files were found to upload'
if (files.length > 0) {
message += `: ${files.join(', ')}`
}
super(message)
this.files = files
this.name = 'FilesNotFoundError'
}
}
export class InvalidResponseError extends Error {
constructor(message: string) {
super(message)
this.name = 'InvalidResponseError'
}
}
export class ArtifactNotFoundError extends Error {
constructor(message = 'Artifact not found') {
super(message)
this.name = 'ArtifactNotFoundError'
}
}
export class GHESNotSupportedError extends Error {
constructor(
message = '@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.'
) {
super(message)
this.name = 'GHESNotSupportedError'
}
}
export class NetworkError extends Error {
code: string
constructor(code: string) {
const message = `Unable to make request: ${code}\nIf you are using self-hosted runners, please make sure your runner has access to all GitHub endpoints: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github`
super(message)
this.code = code
this.name = 'NetworkError'
}
static isNetworkErrorCode = (code?: string): boolean => {
if (!code) return false
return [
'ECONNRESET',
'ENOTFOUND',
'ETIMEDOUT',
'ECONNREFUSED',
'EHOSTUNREACH'
].includes(code)
}
}
export class UsageError extends Error {
constructor() {
const message = `Artifact storage quota has been hit. Unable to upload any new artifacts. Usage is recalculated every 6-12 hours.\nMore info on storage limits: https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions#calculating-minute-and-storage-spending`
super(message)
this.name = 'UsageError'
}
static isUsageErrorMessage = (msg?: string): boolean => {
if (!msg) return false
return msg.includes('insufficient usage')
}
}
@@ -1,7 +1,14 @@
/**
* Response from the server when an artifact is uploaded
*/
export interface UploadArtifactResponse {
/*****************************************************************************
* *
* 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
*/
@@ -14,10 +21,7 @@ export interface UploadArtifactResponse {
id?: number
}
/**
* Options for uploading an artifact
*/
export interface UploadArtifactOptions {
export interface UploadOptions {
/**
* Duration after which artifact will expire in days.
*
@@ -34,43 +38,31 @@ export interface UploadArtifactOptions {
* input of 0 assumes default retention setting.
*/
retentionDays?: number
/**
* The level of compression for Zlib to be applied to the artifact archive.
* The value can range from 0 to 9:
* - 0: No compression
* - 1: Best speed
* - 6: Default compression (same as GNU Gzip)
* - 9: Best compression
* Higher levels will result in better compression, but will take longer to complete.
* For large files that are not easily compressed, a value of 0 is recommended for significantly faster uploads.
*/
compressionLevel?: number
}
/**
* Response from the server when getting an artifact
*/
/*****************************************************************************
* *
* GetArtifact *
* *
*****************************************************************************/
export interface GetArtifactResponse {
/**
* If an artifact was found
*/
success: boolean
/**
* Metadata about the artifact that was found
*/
artifact: Artifact
artifact?: Artifact
}
/**
* Options for listing artifacts
*/
export interface ListArtifactsOptions {
/**
* Filter the workflow run's artifacts to the latest by name
* In the case of reruns, this can be useful to avoid duplicates
*/
latest?: boolean
}
/**
* Response from the server when listing artifacts
*/
/*****************************************************************************
* *
* ListArtifact *
* *
*****************************************************************************/
export interface ListArtifactsResponse {
/**
* A list of artifacts that were found
@@ -78,19 +70,22 @@ export interface ListArtifactsResponse {
artifacts: Artifact[]
}
/**
* Response from the server when downloading an artifact
*/
/*****************************************************************************
* *
* DownloadArtifact *
* *
*****************************************************************************/
export interface DownloadArtifactResponse {
/**
* If the artifact download was successful
*/
success: boolean
/**
* The path where the artifact was downloaded to
*/
downloadPath?: string
}
/**
* Options for downloading an artifact
*/
export interface DownloadArtifactOptions {
/**
* Denotes where the artifact will be downloaded to. If not specified then the artifact is download to GITHUB_WORKSPACE
@@ -98,9 +93,11 @@ export interface DownloadArtifactOptions {
path?: string
}
/**
* An Actions Artifact
*/
/*****************************************************************************
* *
* Shared *
* *
*****************************************************************************/
export interface Artifact {
/**
* The name of the artifact
@@ -112,38 +109,13 @@ export interface Artifact {
*/
id: number
/**
* The URL of the artifact
*/
url: string
/**
* The size of the artifact in bytes
*/
size: number
/**
* The time when the artifact was created
*/
createdAt?: Date
}
// FindOptions are for fetching Artifact(s) out of the scope of the current run.
export interface FindOptions {
/**
* The criteria for finding Artifact(s) out of the scope of the current run.
*/
findBy?: {
/**
* Token with actions:read permissions
*/
token: string
/**
* WorkflowRun of the artifact(s) to lookup
*/
workflowRunId: number
/**
* Repository owner (eg. 'actions')
*/
repositoryOwner: string
/**
* Repository owner (eg. 'toolkit')
*/
repositoryName: string
}
}
+9 -15
View File
@@ -1,4 +1,3 @@
import * as core from '@actions/core'
import {getRuntimeToken} from './config'
import jwt_decode from 'jwt-decode'
@@ -12,7 +11,7 @@ interface ActionsToken {
}
const InvalidJwtError = new Error(
'Failed to get backend IDs: The provided JWT token is invalid and/or missing claims'
'Failed to get backend IDs: The provided JWT token is invalid'
)
// uses the JWT token claims to get the
@@ -42,29 +41,24 @@ export function getBackendIdsFromToken(): BackendIds {
for (const scopes of scpParts) {
const scopeParts = scopes.split(':')
if (scopeParts?.[0] !== 'Actions.Results') {
// not the Actions.Results scope
continue
}
/*
* example scopeParts:
* ["Actions.Results", "ce7f54c7-61c7-4aae-887f-30da475f5f1a", "ca395085-040a-526b-2ce8-bdc85f692774"]
*/
if (scopeParts.length !== 3) {
// missing expected number of claims
throw InvalidJwtError
// not the Actions.Results scope
continue
}
const ids = {
if (scopeParts[0] !== 'Actions.Results') {
// not the Actions.Results scope
continue
}
return {
workflowRunBackendId: scopeParts[1],
workflowJobRunBackendId: scopeParts[2]
}
core.debug(`Workflow Run Backend ID: ${ids.workflowRunBackendId}`)
core.debug(`Workflow Job Run Backend ID: ${ids.workflowJobRunBackendId}`)
return ids
}
throw InvalidJwtError
@@ -1,22 +1,26 @@
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
import {TransferProgressEvent} from '@azure/core-http'
import {ZipUploadStream} from './zip'
import {getUploadChunkSize, getConcurrency} from '../shared/config'
import {getUploadChunkSize} from '../shared/config'
import * as core from '@actions/core'
import * as crypto from 'crypto'
import * as stream from 'stream'
import {NetworkError} from '../shared/errors'
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 SHA256 hash of the uploaded file. Empty if the upload failed
* The MD5 hash of the uploaded file. Empty if the upload failed
*/
sha256Hash?: string
md5Hash?: string
}
export async function uploadZipToBlobStorage(
@@ -25,13 +29,13 @@ export async function uploadZipToBlobStorage(
): Promise<BlobUploadResponse> {
let uploadByteCount = 0
const maxConcurrency = getConcurrency()
const maxBuffers = 5
const bufferSize = getUploadChunkSize()
const blobClient = new BlobClient(authenticatedUploadURL)
const blockBlobClient = blobClient.getBlockBlobClient()
core.debug(
`Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}`
`Uploading artifact zip to blob storage with maxBuffers: ${maxBuffers}, bufferSize: ${bufferSize}`
)
const uploadCallback = (progress: TransferProgressEvent): void => {
@@ -44,44 +48,49 @@ export async function uploadZipToBlobStorage(
onProgress: uploadCallback
}
let sha256Hash: string | undefined = undefined
let md5Hash: string | undefined = undefined
const uploadStream = new stream.PassThrough()
const hashStream = crypto.createHash('sha256')
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
core.info('Beginning upload of artifact content to blob storage')
try {
core.info('Beginning upload of artifact content to blob storage')
await blockBlobClient.uploadStream(
uploadStream,
bufferSize,
maxConcurrency,
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) {
if (NetworkError.isNetworkErrorCode(error?.code)) {
throw new NetworkError(error?.code)
core.warning(
`Failed to upload artifact zip to blob storage, error: ${error}`
)
return {
isSuccess: false
}
throw error
}
core.info('Finished uploading artifact content to blob storage!')
hashStream.end()
sha256Hash = hashStream.read() as string
core.info(`SHA256 hash of uploaded artifact zip is ${sha256Hash}`)
if (uploadByteCount === 0) {
core.warning(
`No data was uploaded to blob storage. Reported upload byte count is 0.`
`No data was uploaded to blob storage. Reported upload byte count is 0`
)
return {
isSuccess: false
}
}
return {
isSuccess: true,
uploadSize: uploadByteCount,
sha256Hash
md5Hash
}
}
@@ -1,11 +1,8 @@
import * as core from '@actions/core'
import {
UploadArtifactOptions,
UploadArtifactResponse
} from '../shared/interfaces'
import {UploadOptions, UploadResponse} from '../shared/interfaces'
import {getExpiration} from './retention'
import {validateArtifactName} from './path-and-artifact-name-validation'
import {internalArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {createArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {
UploadZipSpecification,
getUploadZipSpecification,
@@ -19,14 +16,13 @@ import {
FinalizeArtifactRequest,
StringValue
} from '../../generated'
import {FilesNotFoundError, InvalidResponseError} from '../shared/errors'
export async function uploadArtifact(
name: string,
files: string[],
rootDirectory: string,
options?: UploadArtifactOptions | undefined
): Promise<UploadArtifactResponse> {
options?: UploadOptions | undefined
): Promise<UploadResponse> {
validateArtifactName(name)
validateRootDirectory(rootDirectory)
@@ -35,16 +31,31 @@ export async function uploadArtifact(
rootDirectory
)
if (zipSpecification.length === 0) {
throw new FilesNotFoundError(
zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : []))
)
core.warning(`No files were found to upload`)
return {
success: false
}
}
const zipUploadStream = await createZipUploadStream(zipSpecification)
// 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`
)
return {
success: false
}
}
core.debug(`Workflow Run Backend ID: ${backendIds.workflowRunBackendId}`)
core.debug(
`Workflow Job Run Backend ID: ${backendIds.workflowJobRunBackendId}`
)
// create the artifact client
const artifactClient = internalArtifactTwirpClient()
const artifactClient = createArtifactTwirpClient('upload')
// create the artifact
const createArtifactReq: CreateArtifactRequest = {
@@ -63,21 +74,22 @@ export async function uploadArtifact(
const createArtifactResp =
await artifactClient.CreateArtifact(createArtifactReq)
if (!createArtifactResp.ok) {
throw new InvalidResponseError(
'CreateArtifact: response from backend was not ok'
)
core.warning(`Failed to create artifact`)
return {
success: false
}
}
const zipUploadStream = await createZipUploadStream(
zipSpecification,
options?.compressionLevel
)
// 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 = {
@@ -87,9 +99,9 @@ export async function uploadArtifact(
size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0'
}
if (uploadResult.sha256Hash) {
if (uploadResult.md5Hash) {
finalizeArtifactReq.hash = StringValue.create({
value: `sha256:${uploadResult.sha256Hash}`
value: `md5:${uploadResult.md5Hash}`
})
}
@@ -98,9 +110,10 @@ export async function uploadArtifact(
const finalizeArtifactResp =
await artifactClient.FinalizeArtifact(finalizeArtifactReq)
if (!finalizeArtifactResp.ok) {
throw new InvalidResponseError(
'FinalizeArtifact: response from backend was not ok'
)
core.warning(`Failed to finalize artifact`)
return {
success: false
}
}
const artifactId = BigInt(finalizeArtifactResp.artifactId)
@@ -109,6 +122,7 @@ export async function uploadArtifact(
)
return {
success: true,
size: uploadResult.uploadSize,
id: Number(artifactId)
}
+6 -10
View File
@@ -5,8 +5,6 @@ import {createReadStream} from 'fs'
import {UploadZipSpecification} from './upload-zip-specification'
import {getUploadChunkSize} from '../shared/config'
export const DEFAULT_COMPRESSION_LEVEL = 6
// Custom stream transformer so we can set the highWaterMark property
// See https://github.com/nodejs/node/issues/8855
export class ZipUploadStream extends stream.Transform {
@@ -23,16 +21,14 @@ export class ZipUploadStream extends stream.Transform {
}
export async function createZipUploadStream(
uploadSpecification: UploadZipSpecification[],
compressionLevel: number = DEFAULT_COMPRESSION_LEVEL
uploadSpecification: UploadZipSpecification[]
): Promise<ZipUploadStream> {
core.debug(
`Creating Artifact archive with compressionLevel: ${compressionLevel}`
)
const zip = archiver.create('zip', {
highWaterMark: getUploadChunkSize(),
zlib: {level: compressionLevel}
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
-151
View File
@@ -333,154 +333,3 @@ toPlatformPath('/foo/bar') // => \foo\bar
// On a Linux runner.
toPlatformPath('\\foo\\bar') // => /foo/bar
```
#### Platform helper
Provides shorthands for getting information about platform action is running on.
```js
import { platform } from '@actions/core'
/* equals to a call of os.platform() */
platform.platform // 'win32' | 'darwin' | 'linux' | 'freebsd' | 'openbsd' | 'android' | 'cygwin' | 'sunos'
/* equals to a call of os.arch() */
platform.arch // 'x64' | 'arm' | 'arm64' | 'ia32' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 'riscv64' | 's390' | 's390x'
/* common shorthands for platform-specific logic */
platform.isWindows // true
platform.isMacOS // false
platform.isLinux // false
/* run platform-specific script to get more details about the exact platform, works on Windows, MacOS and Linux */
const {
name, // Microsoft Windows 11 Enterprise
version, // 10.0.22621
} = await platform.getDetails()
```
#### Populating job summary
These methods can be used to populate a [job summary](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary). A job summary is a buffer that can be added to throughout your job via `core.summary` methods.
Job summaries when complete must be written to the summary buffer file via the `core.summary.write()` method.
All methods except `addRaw()` utilize the `addRaw()` method to append to the buffer, followed by an EOL using the `addEOL()` method.
```typescript
// Write raw text, optionally add an EOL after the content, defaults to false
core.summary.addRaw('Some content here :speech_balloon:', true)
// Output: Some content here :speech_balloon:\n
// Add an operating system-specific end-of-line marker
core.summary.addEOL()
// Output (POSIX): \n
// Output (Windows): \r\n
// Add a codeblock with an optional language for syntax highlighting
core.summary.addCodeBlock('console.log(\'hello world\')', 'javascript')
// Output: <pre lang="javascript"><code>console.log('hello world')</code></pre>
// Add a list, second parameter indicates if list is ordered, defaults to false
core.summary.addList(['item1','item2','item3'], true)
// Output: <ol><li>item1</li><li>item2</li><li>item3</li></ol>
// Add a collapsible HTML details element
core.summary.addDetails('Label', 'Some detail that will be collapsed')
// Output: <details><summary>Label</summary>Some detail that will be collapsed</details>
// Add an image, image options parameter is optional, you can supply one of or both width and height in pixels
core.summary.addImage('example.png', 'alt description of img', {width: '100', height: '100'})
// Output: <img src="example.png" alt="alt description of img" width="100" height="100">
// Add an HTML section heading element, optionally pass a level that translates to 'hX' ie. h2. Defaults to h1
core.summary.addHeading('My Heading', '2')
// Output: <h2>My Heading</h2>
// Add an HTML thematic break <hr>
core.summary.addSeparator()
// Output: <hr>
// Add an HTML line break <br>
core.summary.addBreak()
// Output: <br>
// Add an HTML blockquote with an optional citation
core.summary.addQuote('To be or not to be', 'Shakespeare')
// Output: <blockquote cite="Shakespeare">To be or not to be</blockquote>
// Add an HTML anchor tag
core.summary.addLink('click here', 'https://github.com')
// Output: <a href="https://github.com">click here</a>
```
Tables are added using the `addTable()` method, and an array of `SummaryTableRow`.
```typescript
export type SummaryTableRow = (SummaryTableCell | string)[]
export interface SummaryTableCell {
/**
* Cell content
*/
data: string
/**
* Render cell as header
* (optional) default: false
*/
header?: boolean
/**
* Number of columns the cell extends
* (optional) default: '1'
*/
colspan?: string
/**
* Number of rows the cell extends
* (optional) default: '1'
*/
rowspan?: string
}
```
For example
```typescript
const tableData = [
{data: 'Header1', header: true},
{data: 'Header2', header: true},
{data: 'Header3', header: true},
{data: 'MyData1'},
{data: 'MyData2'},
{data: 'MyData3'}
]
// Add an HTML table
core.summary.addTable([tableData])
// Output: <table><tr><th>Header1</th><th>Header2</th><th>Header3</th></tr><tr></tr><td>MyData1</td><td>MyData2</td><td>MyData3</td></tr></table>
```
In addition to job summary content, there are utility functions for interfacing with the buffer.
```typescript
// Empties the summary buffer AND wipes the summary file on disk
core.summary.clear()
// Returns the current summary buffer as a string
core.summary.stringify()
// If the summary buffer is empty
core.summary.isEmptyBuffer()
// Resets the summary buffer without writing to the summary file on disk
core.summary.emptyBuffer()
// Writes text in the buffer to the summary buffer file and empties the buffer, optionally overwriting all existing content in the summary file with buffer contents. Defaults to false.
core.summary.write({overwrite: true})
```
-29
View File
@@ -1,29 +0,0 @@
import os from 'os'
import {platform} from '../src/core'
describe('getInfo', () => {
it('returns the platform info', async () => {
const info = await platform.getDetails()
expect(info).toEqual({
name: expect.any(String),
platform: expect.any(String),
arch: expect.any(String),
version: expect.any(String),
isWindows: expect.any(Boolean),
isMacOS: expect.any(Boolean),
isLinux: expect.any(Boolean)
})
})
it('returns the platform info with the correct name', async () => {
const isWindows = os.platform() === 'win32'
const isMacOS = os.platform() === 'darwin'
const isLinux = os.platform() === 'linux'
const info = await platform.getDetails()
expect(info.platform).toEqual(os.platform())
expect(info.isWindows).toEqual(isWindows)
expect(info.isMacOS).toEqual(isMacOS)
expect(info.isLinux).toEqual(isLinux)
})
})
-27
View File
@@ -9,7 +9,6 @@
"version": "1.10.1",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
},
@@ -18,14 +17,6 @@
"@types/uuid": "^8.3.4"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
@@ -34,11 +25,6 @@
"tunnel": "^0.0.6"
}
},
"node_modules/@actions/io": {
"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/@types/node": {
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz",
@@ -69,14 +55,6 @@
}
},
"dependencies": {
"@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/http-client": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.0.tgz",
@@ -85,11 +63,6 @@
"tunnel": "^0.0.6"
}
},
"@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="
},
"@types/node": {
"version": "12.0.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.2.tgz",
-1
View File
@@ -36,7 +36,6 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1",
"uuid": "^8.3.2"
},
-5
View File
@@ -386,8 +386,3 @@ export {markdownSummary} from './summary'
* Path exports
*/
export {toPosixPath, toWin32Path, toPlatformPath} from './path-utils'
/**
* Platform utilities exports
*/
export * as platform from './platform'
-87
View File
@@ -1,87 +0,0 @@
import os from 'os'
import * as exec from '@actions/exec'
const getWindowsInfo = async (): Promise<{name: string; version: string}> => {
const {stdout: version} = await exec.getExecOutput(
'powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"',
undefined,
{
silent: true
}
)
const {stdout: name} = await exec.getExecOutput(
'powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"',
undefined,
{
silent: true
}
)
return {
name: name.trim(),
version: version.trim()
}
}
const getMacOsInfo = async (): Promise<{
name: string
version: string
}> => {
const {stdout} = await exec.getExecOutput('sw_vers', undefined, {
silent: true
})
const version = stdout.match(/ProductVersion:\s*(.+)/)?.[1] ?? ''
const name = stdout.match(/ProductName:\s*(.+)/)?.[1] ?? ''
return {
name,
version
}
}
const getLinuxInfo = async (): Promise<{
name: string
version: string
}> => {
const {stdout} = await exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], {
silent: true
})
const [name, version] = stdout.trim().split('\n')
return {
name,
version
}
}
export const platform = os.platform()
export const arch = os.arch()
export const isWindows = platform === 'win32'
export const isMacOS = platform === 'darwin'
export const isLinux = platform === 'linux'
export async function getDetails(): Promise<{
name: string
platform: string
arch: string
version: string
isWindows: boolean
isMacOS: boolean
isLinux: boolean
}> {
return {
...(await (isWindows
? getWindowsInfo()
: isMacOS
? getMacOsInfo()
: getLinuxInfo())),
platform,
arch,
isWindows,
isMacOS,
isLinux
}
}
-4
View File
@@ -1,9 +1,5 @@
# @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 {createProxy} from 'proxy'
import proxy 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,7 +16,7 @@ describe('@actions/github', () => {
beforeAll(async () => {
// Start proxy server
proxyServer = createProxy()
proxyServer = proxy()
await new Promise<void>(resolve => {
const port = Number(proxyUrl.split(':')[2])
proxyServer.listen(port, () => resolve())
+2 -2
View File
@@ -1,5 +1,5 @@
import * as http from 'http'
import {createProxy} from 'proxy'
import proxy from 'proxy'
import {getOctokit} from '../src/github'
import {GitHub, getOctokitOptions} from '../src/utils'
@@ -12,7 +12,7 @@ describe('@actions/github', () => {
beforeAll(async () => {
// Start proxy server
proxyServer = createProxy()
proxyServer = proxy()
await new Promise<void>(resolve => {
const port = Number(proxyUrl.split(':')[2])
proxyServer.listen(port, () => resolve())
+5
View File
@@ -0,0 +1,5 @@
declare module 'proxy' {
import * as http from 'http'
function internal(): http.Server
export = internal
}
+333 -312
View File
@@ -1,176 +1,136 @@
{
"name": "@actions/github",
"version": "6.0.0",
"version": "5.1.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/github",
"version": "6.0.0",
"version": "5.1.1",
"license": "MIT",
"dependencies": {
"@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"
"@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"
},
"devDependencies": {
"proxy": "^2.1.1"
"proxy": "^1.0.2"
}
},
"node_modules/@actions/http-client": {
"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==",
"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",
"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"
"tunnel": "^0.0.6"
}
},
"node_modules/@octokit/auth-token": {
"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"
"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"
}
},
"node_modules/@octokit/core": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz",
"integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"dependencies": {
"@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",
"@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",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/endpoint": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz",
"integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==",
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
"dependencies": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/graphql": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz",
"integrity": "sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA==",
"dependencies": {
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"@octokit/request": "^5.3.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/openapi-types": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw=="
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
},
"node_modules/@octokit/plugin-paginate-rest": {
"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==",
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
"dependencies": {
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
"@octokit/types": "^6.34.0"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@octokit/core": ">=2"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"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==",
"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==",
"dependencies": {
"@octokit/types": "^12.0.0"
},
"engines": {
"node": ">= 18"
"@octokit/types": "^6.34.0",
"deprecation": "^2.3.1"
},
"peerDependencies": {
"@octokit/core": ">=5"
"@octokit/core": ">=3"
}
},
"node_modules/@octokit/request": {
"version": "8.1.4",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz",
"integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==",
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"dependencies": {
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"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": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"dependencies": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@octokit/types": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz",
"integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==",
"version": "6.34.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
"dependencies": {
"@octokit/openapi-types": "^19.0.0"
}
},
"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==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
"@octokit/openapi-types": "^11.2.0"
}
},
"node_modules/args": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"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",
@@ -182,18 +142,19 @@
"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/args/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==",
"dev": true,
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"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": {
"node_modules/args/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==",
@@ -202,7 +163,7 @@
"node": ">=6"
}
},
"node_modules/chalk": {
"node_modules/args/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==",
@@ -216,7 +177,7 @@
"node": ">=4"
}
},
"node_modules/color-convert": {
"node_modules/args/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==",
@@ -225,12 +186,53 @@
"color-name": "1.1.3"
}
},
"node_modules/color-name": {
"node_modules/args/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"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.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -256,21 +258,12 @@
"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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"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",
@@ -279,15 +272,6 @@
"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",
@@ -303,38 +287,64 @@
"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": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/proxy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"dev": true,
"dependencies": {
"args": "^5.0.3",
"basic-auth-parser": "0.0.2-1",
"debug": "^4.3.4"
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
},
"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"
"bin": {
"proxy": "bin/proxy.js"
}
},
"node_modules/tunnel": {
@@ -345,17 +355,6 @@
"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",
@@ -364,139 +363,193 @@
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
},
"dependencies": {
"@actions/http-client": {
"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==",
"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",
"undici": "^5.25.4"
"tunnel": "^0.0.6"
}
},
"@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": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="
"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"
}
},
"@octokit/core": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz",
"integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==",
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
"requires": {
"@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",
"@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",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/endpoint": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz",
"integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==",
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.11.tgz",
"integrity": "sha512-fUIPpx+pZyoLW4GCs3yMnlj2LfoXTWDUVPTC4V3MUEKZm48W+XYpeWSZCv+vYF1ZABUm2CqnDVf1sFtIYrj7KQ==",
"requires": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^6.0.3",
"is-plain-object": "^5.0.0",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/graphql": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz",
"integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.6.1.tgz",
"integrity": "sha512-2lYlvf4YTDgZCTXTW4+OX+9WTLFtEUc6hGm4qM1nlZjzxj+arizM4aHWzBVBCxY9glh7GIs0WEuiSgbVzv8cmA==",
"requires": {
"@octokit/request": "^8.0.1",
"@octokit/types": "^12.0.0",
"@octokit/request": "^5.3.0",
"@octokit/types": "^6.0.3",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/openapi-types": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz",
"integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw=="
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
},
"@octokit/plugin-paginate-rest": {
"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==",
"version": "2.17.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
"requires": {
"@octokit/types": "^12.0.0"
"@octokit/types": "^6.34.0"
}
},
"@octokit/plugin-rest-endpoint-methods": {
"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==",
"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==",
"requires": {
"@octokit/types": "^12.0.0"
"@octokit/types": "^6.34.0",
"deprecation": "^2.3.1"
}
},
"@octokit/request": {
"version": "8.1.4",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz",
"integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==",
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
"requires": {
"@octokit/endpoint": "^9.0.0",
"@octokit/request-error": "^5.0.0",
"@octokit/types": "^12.0.0",
"@octokit/endpoint": "^6.0.1",
"@octokit/request-error": "^2.1.0",
"@octokit/types": "^6.16.1",
"is-plain-object": "^5.0.0",
"node-fetch": "^2.6.7",
"universal-user-agent": "^6.0.0"
}
},
"@octokit/request-error": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz",
"integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
"requires": {
"@octokit/types": "^12.0.0",
"@octokit/types": "^6.0.3",
"deprecation": "^2.0.0",
"once": "^1.4.0"
}
},
"@octokit/types": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz",
"integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==",
"version": "6.34.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
"requires": {
"@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"
"@octokit/openapi-types": "^11.2.0"
}
},
"args": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.3.tgz",
"integrity": "sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
"integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
"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-1",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2-1.tgz",
"integrity": "sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==",
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
"integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
"dev": true
},
"before-after-hook": {
@@ -504,38 +557,6 @@
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.1.tgz",
"integrity": "sha512-/6FKxSTWoJdbsLDF8tdIjaRiFXiE6UHsEHE3OPI/cwPURCVi1ukP0gmLn7XWEiFk5TcwQjjY5PWsU+j+tgXgmw=="
},
"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": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
"dev": true
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -553,13 +574,7 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"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==",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"is-plain-object": {
@@ -567,12 +582,6 @@
"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",
@@ -585,32 +594,52 @@
"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": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"proxy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-2.1.1.tgz",
"integrity": "sha512-nLgd7zdUAOpB3ZO/xCkU8gy74UER7P0aihU8DkUsDS5ZoFwVCX7u8dy+cv5tVK8UaB/yminU1GiLWE26TKPYpg==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
"integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
"dev": true,
"requires": {
"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"
"args": "5.0.1",
"basic-auth-parser": "0.0.2",
"debug": "^4.1.1"
}
},
"tunnel": {
@@ -618,14 +647,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"
}
},
"universal-user-agent": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
@@ -634,7 +655,7 @@
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "6.0.0",
"version": "5.1.1",
"description": "Actions github lib",
"keywords": [
"github",
@@ -38,12 +38,12 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@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"
"@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"
},
"devDependencies": {
"proxy": "^2.1.1"
"proxy": "^1.0.2"
}
}
-2
View File
@@ -16,7 +16,6 @@ export class Context {
action: string
actor: string
job: string
runAttempt: number
runNumber: number
runId: number
apiUrl: string
@@ -45,7 +44,6 @@ export class Context {
this.action = process.env.GITHUB_ACTION as string
this.actor = process.env.GITHUB_ACTOR as string
this.job = process.env.GITHUB_JOB as string
this.runAttempt = parseInt(process.env.GITHUB_RUN_ATTEMPT as string, 10)
this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER as string, 10)
this.runId = parseInt(process.env.GITHUB_RUN_ID as string, 10)
this.apiUrl = process.env.GITHUB_API_URL ?? `https://api.github.com`
-19
View File
@@ -1,7 +1,6 @@
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,
@@ -21,24 +20,6 @@ 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'
}
+1 -2
View File
@@ -13,8 +13,7 @@ const baseUrl = Utils.getApiBaseUrl()
export const defaults: OctokitOptions = {
baseUrl,
request: {
agent: Utils.getProxyAgent(baseUrl),
fetch: Utils.getProxyFetch(baseUrl)
agent: Utils.getProxyAgent(baseUrl)
}
}