Compare commits

..
Author SHA1 Message Date
BethanyandGitHub d73abc633f Merge branch 'main' into bethanyj28/fix-test 2024-04-03 17:06:22 -04:00
bethanyj28 3ca50668ea update zip stream logic 2024-04-03 17:05:08 -04:00
bethanyj28 f9bafe97e6 update logging 2024-04-03 15:27:58 -04:00
bethanyj28 e94937595b add throw to function 2024-04-03 14:06:37 -04:00
Vallie Joseph 38f54e9d73 Adding debug statement for upload 2024-04-02 05:39:02 +00:00
Vallie Joseph c5f3463c0b adding more descriptive error 2024-04-02 05:25:10 +00:00
Vallie Joseph 96ef8d5d33 adding more error handling 2024-04-01 20:21:01 +00:00
Vallie Joseph 84d3cef541 Removing extra logs 2024-04-01 20:14:07 +00:00
Vallie Joseph a926fff385 adding queue back 2024-04-01 20:04:49 +00:00
Vallie Joseph a2a8a724c2 test queue 2024-04-01 20:01:19 +00:00
Vallie Joseph a8fa53b609 test async eachof 2024-04-01 19:57:11 +00:00
Vallie Joseph 23039a4345 adding asnyc handler back 2024-04-01 16:57:50 +00:00
Vallie Joseph 4778aebf5b adding asnyc handler back 2024-04-01 16:54:55 +00:00
Vallie Joseph 6eff4e928d adding more debug statements 2024-04-01 15:15:28 +00:00
Vallie Joseph 2c4f0f555e adding error checks 2024-04-01 15:07:28 +00:00
Vallie Joseph dea3595881 re-adding archiver for download 2024-04-01 14:33:36 +00:00
Vallie Joseph 4c878a66e7 adding back archiver 2024-04-01 14:30:14 +00:00
Vallie Joseph 2f2738e4ae removing old packages 2024-04-01 14:01:29 +00:00
Vallie Joseph 2d065559f7 Merge branch 'main' into vmjoseph/node-js-monitor 2024-04-01 13:59:46 +00:00
Vallie Joseph 2da528819c cleanup 2024-04-01 13:58:57 +00:00
Vallie Joseph 90ee020ccd cleaning up debug statements 2024-04-01 13:52:01 +00:00
Vallie Joseph c14e304555 adding more debugging statements 2024-03-28 21:17:54 +00:00
Vallie Joseph a2852cee6f adding catch to upload zip 2024-03-28 21:12:51 +00:00
Vallie Joseph 6d94ad125b Removing uneeded libs 2024-03-28 20:38:01 +00:00
Vallie Joseph d02a834fe3 Remove other debug statements 2024-03-28 20:12:52 +00:00
Vallie Joseph 0f23ae1184 re-adding old async/await pattern 2024-03-28 20:05:16 +00:00
Vallie Joseph 5288db3953 removing debugging statements 2024-03-28 19:57:01 +00:00
Vallie Joseph bc893bf1b4 adding compression level back 2024-03-28 19:48:15 +00:00
Vallie Joseph 9322468ef0 Adding pipe back 2024-03-28 19:32:40 +00:00
Vallie Joseph b6c87ceea7 Adding more error handlrs 2024-03-28 19:22:29 +00:00
Vallie Joseph 17c4cab8b2 adding constant zlib compression level 2024-03-28 19:16:10 +00:00
Vallie Joseph 59593338a6 defaulting compression level 2024-03-28 19:15:10 +00:00
Vallie Joseph 0a0e70d1cd Add another catch to see if upload stream is failing 2024-03-28 19:09:53 +00:00
Vallie Joseph c9e825e841 add blob client property check 2024-03-28 19:06:47 +00:00
Vallie Joseph 31a6086649 Adding concurrency and upload response logs 2024-03-28 18:59:41 +00:00
Vallie Joseph d2d69999e3 adding write check 2024-03-28 18:53:15 +00:00
Vallie Joseph 6ac9cbfda9 check if upload and zipload streams are writable 2024-03-28 18:51:00 +00:00
Vallie Joseph d597cf234f adding upload stream check 2024-03-28 18:46:31 +00:00
Vallie Joseph a768aa30c7 adding more logging 2024-03-28 18:43:58 +00:00
Vallie Joseph e4c0440c3a adding more logging 2024-03-28 18:40:37 +00:00
Vallie Joseph 26b62e0fd5 adding more logging 2024-03-28 18:38:52 +00:00
Vallie Joseph 73f526b642 adding closed check 2024-03-28 18:36:40 +00:00
Vallie Joseph 077846ed34 adding readable check 2024-03-28 18:34:07 +00:00
Vallie Joseph 180b75bf01 wrap promises in chain 2024-03-28 18:29:20 +00:00
Vallie Joseph f33a3f4748 Remove finalize 2024-03-28 18:14:40 +00:00
Vallie Joseph 97e4fcfcd5 wait on write stream 2024-03-28 18:07:20 +00:00
Vallie Joseph fe0c0de7db adding lock 2024-03-28 17:53:08 +00:00
Vallie Joseph c7de68f215 updating resolves 2024-03-28 17:48:56 +00:00
Vallie Joseph 66343faec4 add resolve all promises again 2024-03-28 17:43:17 +00:00
Vallie Joseph fd88cbe6df adding check for running 2024-03-28 17:32:01 +00:00
Vallie Joseph bef1fc5f67 adding check for running 2024-03-28 17:30:06 +00:00
Vallie Joseph 18751738a8 update loop for upload 2024-03-28 17:25:01 +00:00
Vallie Joseph b1f55c6942 adding finalize 2024-03-28 17:15:08 +00:00
Vallie Joseph 9299663297 adding promise all 2024-03-28 17:13:32 +00:00
Vallie Joseph 088b9761b4 cleaning up callback methods 2024-03-28 16:15:29 +00:00
Vallie Joseph d97edf7f61 cleaning up warning callack 2024-03-28 16:11:50 +00:00
Vallie Joseph 06482c6da1 adding promise to upload 2024-03-28 16:06:54 +00:00
Vallie Joseph 7441cc7b8b Removing pipe 2024-03-28 15:55:09 +00:00
Vallie Joseph ca87c5e124 testing debug statements 2024-03-28 15:53:03 +00:00
Vallie Joseph 97629b8767 adding archiver back for download 2024-03-28 15:47:34 +00:00
Vallie Joseph 8e29fb2e30 adding default back to zipstream call 2024-03-28 15:44:27 +00:00
Vallie Joseph 560ae4debd updating archive import 2024-03-28 15:41:52 +00:00
Vallie Joseph fda9d58d06 replacing constructor for zipstream with default 2024-03-28 15:28:14 +00:00
Vallie Joseph 6defe19df1 replacing constructor for zipstream 2024-03-28 15:25:37 +00:00
Vallie Joseph 6e4aebbd4e cleaning up unused imports 2024-03-28 15:17:40 +00:00
Vallie Joseph 14eee6f54a updating event handlers 2024-03-28 15:10:34 +00:00
Vallie Joseph e71ddb9e5a updating append and directory zips 2024-03-28 14:44:20 +00:00
Vallie Joseph 13d981a863 Adding buffer and zlib options 2024-03-28 14:32:03 +00:00
Vallie Joseph 4090623397 update debug statement 2024-03-28 04:20:40 +00:00
Vallie Joseph 4617da254f audit fix 2024-03-15 15:01:45 +00:00
Vallie Joseph 1f22f9faf9 adding debug for mac upload testing 2024-03-15 14:53:33 +00:00
30 changed files with 2788 additions and 4454 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ Note that before a PR will be accepted, you must ensure:
1. In a new branch, create a new Lerna package: 1. In a new branch, create a new Lerna package:
```console ```console
$ npm run new-package [name] $ npm run create-package new-package
``` ```
This will ask you some questions about the new package. Start with `0.0.0` as the first version (look generally at some of the other packages for how the package.json is structured). This will ask you some questions about the new package. Start with `0.0.0` as the first version (look generally at some of the other packages for how the package.json is structured).
+1839 -3449
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -13,7 +13,7 @@
"lint": "eslint packages/**/*.ts", "lint": "eslint packages/**/*.ts",
"lint-fix": "eslint packages/**/*.ts --fix", "lint-fix": "eslint packages/**/*.ts --fix",
"new-package": "scripts/create-package", "new-package": "scripts/create-package",
"test": "jest --testTimeout 70000" "test": "jest --testTimeout 60000"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.4", "@types/jest": "^29.5.4",
@@ -27,10 +27,10 @@
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.0.0",
"flow-bin": "^0.115.0", "flow-bin": "^0.115.0",
"jest": "^29.6.4", "jest": "^29.6.4",
"lerna": "^6.4.1", "lerna": "^7.1.4",
"nx": "16.6.0", "nx": "16.6.0",
"prettier": "^3.0.0", "prettier": "^3.0.0",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",
"typescript": "^5.2.2" "typescript": "^5.2.2"
} }
} }
+2 -18
View File
@@ -1,21 +1,5 @@
# @actions/artifact Releases # @actions/artifact Releases
### 2.1.8
- Allows `*.localhost` domains for hostname checks for local development.
### 2.1.7
- Update unzip-stream dependency and reverted to using `unzip.Extract()`
### 2.1.6
- Will retry on invalid request responses.
### 2.1.5
- Bumped `archiver` dependency to 7.0.1
### 2.1.4 ### 2.1.4
- Adds info-level logging for zip extraction - Adds info-level logging for zip extraction
@@ -27,9 +11,9 @@
### 2.1.2 ### 2.1.2
- Updated the stream extract functionality to use `unzip.Parse()` instead of `unzip.Extract()` for greater control of unzipping artifacts - Updated the stream extract functionality to use `unzip.Parse()` instead of `unzip.Extract()` for greater control of unzipping artifacts
### 2.1.1 ### 2.1.1
- Updated `isGhes` check to include `.ghe.com` and `.ghe.localhost` as accepted hosts - Updated `isGhes` check to include `.ghe.com` and `.ghe.localhost` as accepted hosts
### 2.1.0 ### 2.1.0
@@ -116,54 +116,6 @@ describe('artifact-http-client', () => {
expect(mockPost).toHaveBeenCalledTimes(2) expect(mockPost).toHaveBeenCalledTimes(2)
}) })
it('should retry if invalid body response', async () => {
const mockPost = jest
.fn(() => {
const msgSucceeded = new http.IncomingMessage(new net.Socket())
msgSucceeded.statusCode = 200
return {
message: msgSucceeded,
readBody: async () => {
return Promise.resolve(
`{"ok": true, "signedUploadUrl": "http://localhost:8080/upload"}`
)
}
}
})
.mockImplementationOnce(() => {
const msgFailed = new http.IncomingMessage(new net.Socket())
msgFailed.statusCode = 502
msgFailed.statusMessage = 'Bad Gateway'
return {
message: msgFailed,
readBody: async () => {
return Promise.resolve('💥')
}
}
})
const mockHttpClient = (
HttpClient as unknown as jest.Mock
).mockImplementation(() => {
return {
post: mockPost
}
})
const client = internalArtifactTwirpClient(clientOptions)
const artifact = await client.CreateArtifact({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678',
name: 'artifact',
version: 4
})
expect(mockHttpClient).toHaveBeenCalledTimes(1)
expect(artifact).toBeDefined()
expect(artifact.ok).toBe(true)
expect(artifact.signedUploadUrl).toBe('http://localhost:8080/upload')
expect(mockPost).toHaveBeenCalledTimes(2)
})
it('should fail if the request fails 5 times', async () => { it('should fail if the request fails 5 times', async () => {
const mockPost = jest.fn(() => { const mockPost = jest.fn(() => {
const msgFailed = new http.IncomingMessage(new net.Socket()) const msgFailed = new http.IncomingMessage(new net.Socket())
@@ -20,11 +20,6 @@ describe('isGhes', () => {
expect(config.isGhes()).toBe(false) expect(config.isGhes()).toBe(false)
}) })
it('should return false when the request domain ends with .localhost', () => {
process.env.GITHUB_SERVER_URL = 'https://github.localhost'
expect(config.isGhes()).toBe(false)
})
it('should return false when the request domain is specific to an enterprise', () => { it('should return false when the request domain is specific to an enterprise', () => {
process.env.GITHUB_SERVER_URL = 'https://my-enterprise.github.com' process.env.GITHUB_SERVER_URL = 'https://my-enterprise.github.com'
expect(config.isGhes()).toBe(true) expect(config.isGhes()).toBe(true)
@@ -200,12 +200,14 @@ describe('download-artifact', () => {
} }
) )
const response = await downloadArtifactPublic( await expect(
fixtures.artifactID, downloadArtifactPublic(
fixtures.repositoryOwner, fixtures.artifactID,
fixtures.repositoryName, fixtures.repositoryOwner,
fixtures.token fixtures.repositoryName,
) fixtures.token
)
).rejects.toBeInstanceOf(Error)
expect(downloadArtifactMock).toHaveBeenCalledWith({ expect(downloadArtifactMock).toHaveBeenCalledWith({
owner: fixtures.repositoryOwner, owner: fixtures.repositoryOwner,
@@ -221,16 +223,6 @@ describe('download-artifact', () => {
expect(mockGetArtifactMalicious).toHaveBeenCalledWith( expect(mockGetArtifactMalicious).toHaveBeenCalledWith(
fixtures.blobStorageUrl fixtures.blobStorageUrl
) )
// ensure path traversal was not possible
expect(
fs.existsSync(path.join(fixtures.workspaceDir, 'x/etc/hosts'))
).toBe(true)
expect(
fs.existsSync(path.join(fixtures.workspaceDir, 'y/etc/hosts'))
).toBe(true)
expect(response.downloadPath).toBe(fixtures.workspaceDir)
}) })
it('should successfully download an artifact to user defined path', async () => { it('should successfully download an artifact to user defined path', async () => {
@@ -8,9 +8,6 @@ import * as blobUpload from '../src/internal/upload/blob-upload'
import {uploadArtifact} from '../src/internal/upload/upload-artifact' import {uploadArtifact} from '../src/internal/upload/upload-artifact'
import {noopLogs} from './common' import {noopLogs} from './common'
import {FilesNotFoundError} from '../src/internal/shared/errors' import {FilesNotFoundError} from '../src/internal/shared/errors'
import {BlockBlobClient} from '@azure/storage-blob'
import * as fs from 'fs'
import * as path from 'path'
describe('upload-artifact', () => { describe('upload-artifact', () => {
beforeEach(() => { beforeEach(() => {
@@ -354,94 +351,4 @@ describe('upload-artifact', () => {
expect(uploadResp).rejects.toThrow() expect(uploadResp).rejects.toThrow()
}) })
it('should throw an error uploading blob chunks get delayed', async () => {
const mockDate = new Date('2020-01-01')
const dirPath = path.join(__dirname, `plz-upload`)
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, {recursive: true})
}
fs.writeFileSync(path.join(dirPath, 'file1.txt'), 'test file content')
fs.writeFileSync(path.join(dirPath, 'file2.txt'), 'test file content')
fs.writeFileSync(path.join(dirPath, 'file3.txt'), 'test file content')
jest
.spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue()
jest
.spyOn(uploadZipSpecification, 'getUploadZipSpecification')
.mockReturnValue([
{
sourcePath: path.join(dirPath, 'file1.txt'),
destinationPath: 'file1.txt'
},
{
sourcePath: path.join(dirPath, 'file2.txt'),
destinationPath: 'file2.txt'
},
{
sourcePath: path.join(dirPath, 'file3.txt'),
destinationPath: 'dir/file3.txt'
}
])
jest.spyOn(util, 'getBackendIdsFromToken').mockReturnValue({
workflowRunBackendId: '1234',
workflowJobRunBackendId: '5678'
})
jest
.spyOn(retention, 'getExpiration')
.mockReturnValue(Timestamp.fromDate(mockDate))
jest
.spyOn(ArtifactServiceClientJSON.prototype, 'CreateArtifact')
.mockReturnValue(
Promise.resolve({
ok: true,
signedUploadUrl: 'https://signed-upload-url.com'
})
)
jest
.spyOn(blobUpload, 'uploadZipToBlobStorage')
.mockReturnValue(Promise.reject(new Error('Upload progress stalled.')))
// ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
jest
.spyOn(config, 'getResultsServiceUrl')
.mockReturnValue('https://test-url.com')
BlockBlobClient.prototype.uploadStream = jest
.fn()
.mockImplementation(
async (stream, bufferSize, maxConcurrency, options) => {
return new Promise<void>(resolve => {
// Call the onProgress callback with a progress event
options.onProgress({loadedBytes: 0})
// Wait for 31 seconds before resolving the promise
setTimeout(() => {
// Call the onProgress callback again to simulate progress
options.onProgress({loadedBytes: 100})
resolve()
}, 31000) // Delay longer than your timeout
})
}
)
jest.mock('fs')
const uploadResp = uploadArtifact(
'test-artifact',
[
'/home/user/files/plz-upload/file1.txt',
'/home/user/files/plz-upload/file2.txt',
'/home/user/files/plz-upload/dir/file3.txt'
],
'/home/user/files/plz-upload'
)
expect(uploadResp).rejects.toThrow('Upload progress stalled.')
})
}) })
+354 -133
View File
@@ -1,12 +1,12 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "2.1.8", "version": "2.1.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "2.1.8", "version": "2.1.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",
@@ -18,14 +18,15 @@
"@octokit/plugin-retry": "^3.0.9", "@octokit/plugin-retry": "^3.0.9",
"@octokit/request-error": "^5.0.0", "@octokit/request-error": "^5.0.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1",
"archiver": "^7.0.1", "archiver": "^5.3.1",
"async": "^3.2.5",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0", "twirp-ts": "^2.5.0",
"unzip-stream": "^0.3.1" "unzip-stream": "^0.3.1",
"zip-stream": "^6.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/archiver": "^5.3.2",
"@types/unzip-stream": "^0.3.4", "@types/unzip-stream": "^0.3.4",
"typedoc": "^0.25.4", "typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1", "typedoc-plugin-markdown": "^3.17.1",
@@ -443,15 +444,6 @@
"@protobuf-ts/runtime": "^2.9.1" "@protobuf-ts/runtime": "^2.9.1"
} }
}, },
"node_modules/@types/archiver": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.2.tgz",
"integrity": "sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==",
"dev": true,
"dependencies": {
"@types/readdir-glob": "*"
}
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.4.9", "version": "20.4.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.9.tgz",
@@ -479,15 +471,6 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/@types/readdir-glob": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.1.tgz",
"integrity": "sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/tunnel": { "node_modules/@types/tunnel": {
"version": "0.0.3", "version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
@@ -545,108 +528,143 @@
} }
}, },
"node_modules/archiver": { "node_modules/archiver": {
"version": "7.0.1", "version": "5.3.1",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz",
"integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==",
"dependencies": { "dependencies": {
"archiver-utils": "^5.0.2", "archiver-utils": "^2.1.0",
"async": "^3.2.4", "async": "^3.2.3",
"buffer-crc32": "^1.0.0", "buffer-crc32": "^0.2.1",
"readable-stream": "^4.0.0", "readable-stream": "^3.6.0",
"readdir-glob": "^1.1.2", "readdir-glob": "^1.0.0",
"tar-stream": "^3.0.0", "tar-stream": "^2.2.0",
"zip-stream": "^6.0.1" "zip-stream": "^4.1.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">= 10"
} }
}, },
"node_modules/archiver-utils": { "node_modules/archiver-utils": {
"version": "5.0.2", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
"integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"dependencies": { "dependencies": {
"glob": "^10.0.0", "glob": "^7.1.4",
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
"is-stream": "^2.0.1",
"lazystream": "^1.0.0", "lazystream": "^1.0.0",
"lodash": "^4.17.15", "lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
"readable-stream": "^4.0.0" "readable-stream": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">= 14" "node": ">= 6"
} }
}, },
"node_modules/archiver-utils/node_modules/brace-expansion": { "node_modules/archiver-utils/node_modules/readable-stream": {
"version": "2.0.1", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0" "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/archiver-utils/node_modules/glob": { "node_modules/archiver-utils/node_modules/safe-buffer": {
"version": "10.3.12", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/archiver-utils/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": { "dependencies": {
"foreground-child": "^3.1.0", "safe-buffer": "~5.1.0"
"jackspeak": "^2.3.6", }
"minimatch": "^9.0.1", },
"minipass": "^7.0.4", "node_modules/archiver/node_modules/compress-commons": {
"path-scurry": "^1.10.2" "version": "4.1.2",
}, "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
"bin": { "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
"glob": "dist/esm/bin.mjs" "dependencies": {
"buffer-crc32": "^0.2.13",
"crc32-stream": "^4.0.2",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
}, },
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": ">= 10"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/archiver-utils/node_modules/minimatch": { "node_modules/archiver/node_modules/crc32-stream": {
"version": "9.0.4", "version": "4.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
"dependencies": { "dependencies": {
"brace-expansion": "^2.0.1" "crc-32": "^1.2.0",
"readable-stream": "^3.4.0"
}, },
"engines": { "engines": {
"node": ">=16 || 14 >=14.17" "node": ">= 10"
}
},
"node_modules/archiver/node_modules/zip-stream": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
"integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
"dependencies": {
"archiver-utils": "^3.0.4",
"compress-commons": "^4.1.2",
"readable-stream": "^3.6.0"
}, },
"funding": { "engines": {
"url": "https://github.com/sponsors/isaacs" "node": ">= 10"
}
},
"node_modules/archiver/node_modules/zip-stream/node_modules/archiver-utils": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
"integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
"dependencies": {
"glob": "^7.2.3",
"graceful-fs": "^4.2.0",
"lazystream": "^1.0.0",
"lodash.defaults": "^4.2.0",
"lodash.difference": "^4.5.0",
"lodash.flatten": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
"lodash.union": "^4.6.0",
"normalize-path": "^3.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">= 10"
} }
}, },
"node_modules/async": { "node_modules/async": {
"version": "3.2.4", "version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
}, },
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
}, },
"node_modules/b4a": {
"version": "1.6.6",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz",
"integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg=="
},
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"node_modules/bare-events": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.2.tgz",
"integrity": "sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ==",
"optional": true
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -683,6 +701,16 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"node_modules/bottleneck": { "node_modules/bottleneck": {
"version": "2.19.5", "version": "2.19.5",
"resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz",
@@ -698,9 +726,9 @@
} }
}, },
"node_modules/buffer": { "node_modules/buffer": {
"version": "6.0.3", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -717,15 +745,15 @@
], ],
"dependencies": { "dependencies": {
"base64-js": "^1.3.1", "base64-js": "^1.3.1",
"ieee754": "^1.2.1" "ieee754": "^1.1.13"
} }
}, },
"node_modules/buffer-crc32": { "node_modules/buffer-crc32": {
"version": "1.0.0", "version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"engines": { "engines": {
"node": ">=8.0.0" "node": "*"
} }
}, },
"node_modules/buffers": { "node_modules/buffers": {
@@ -806,6 +834,44 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/compress-commons/node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/compress-commons/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -839,6 +905,44 @@
"node": ">= 14" "node": ">= 14"
} }
}, },
"node_modules/crc32-stream/node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/crc32-stream/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -893,6 +997,14 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
}, },
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/event-target-shim": { "node_modules/event-target-shim": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
@@ -909,11 +1021,6 @@
"node": ">=0.8.x" "node": ">=0.8.x"
} }
}, },
"node_modules/fast-fifo": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -942,6 +1049,11 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -1133,6 +1245,31 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}, },
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
},
"node_modules/lodash.difference": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
"integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
},
"node_modules/lodash.flatten": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
},
"node_modules/lodash.union": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
"integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
},
"node_modules/lower-case": { "node_modules/lower-case": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
@@ -1346,24 +1483,17 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"node_modules/queue-tick": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag=="
},
"node_modules/readable-stream": { "node_modules/readable-stream": {
"version": "4.5.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": { "dependencies": {
"abort-controller": "^3.0.0", "inherits": "^2.0.3",
"buffer": "^6.0.3", "string_decoder": "^1.1.1",
"events": "^3.3.0", "util-deprecate": "^1.0.1"
"process": "^0.11.10",
"string_decoder": "^1.3.0"
}, },
"engines": { "engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": ">= 6"
} }
}, },
"node_modules/readdir-glob": { "node_modules/readdir-glob": {
@@ -1468,18 +1598,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/streamx": {
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.16.1.tgz",
"integrity": "sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==",
"dependencies": {
"fast-fifo": "^1.1.0",
"queue-tick": "^1.0.1"
},
"optionalDependencies": {
"bare-events": "^2.2.0"
}
},
"node_modules/string_decoder": { "node_modules/string_decoder": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -1577,13 +1695,18 @@
} }
}, },
"node_modules/tar-stream": { "node_modules/tar-stream": {
"version": "3.1.7", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"dependencies": { "dependencies": {
"b4a": "^1.6.4", "bl": "^4.0.3",
"fast-fifo": "^1.2.0", "end-of-stream": "^1.4.1",
"streamx": "^2.15.0" "fs-constants": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1"
},
"engines": {
"node": ">=6"
} }
}, },
"node_modules/tr46": { "node_modules/tr46": {
@@ -1738,9 +1861,9 @@
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
}, },
"node_modules/unzip-stream": { "node_modules/unzip-stream": {
"version": "0.3.4", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.4.tgz", "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.1.tgz",
"integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==", "integrity": "sha512-RzaGXLNt+CW+T41h1zl6pGz3EaeVhYlK+rdAap+7DxW5kqsqePO8kRtWPaCiVqdhZc86EctSPVYNix30YOMzmw==",
"dependencies": { "dependencies": {
"binary": "^0.3.0", "binary": "^0.3.0",
"mkdirp": "^0.5.1" "mkdirp": "^0.5.1"
@@ -1934,6 +2057,104 @@
"engines": { "engines": {
"node": ">= 14" "node": ">= 14"
} }
},
"node_modules/zip-stream/node_modules/archiver-utils": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
"integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
"dependencies": {
"glob": "^10.0.0",
"graceful-fs": "^4.2.0",
"is-stream": "^2.0.1",
"lazystream": "^1.0.0",
"lodash": "^4.17.15",
"normalize-path": "^3.0.0",
"readable-stream": "^4.0.0"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/zip-stream/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==",
"dependencies": {
"balanced-match": "^1.0.0"
}
},
"node_modules/zip-stream/node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/zip-stream/node_modules/glob": {
"version": "10.3.10",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
"dependencies": {
"foreground-child": "^3.1.0",
"jackspeak": "^2.3.5",
"minimatch": "^9.0.1",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
"path-scurry": "^1.10.1"
},
"bin": {
"glob": "dist/esm/bin.mjs"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/zip-stream/node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
"node": ">=16 || 14 >=14.17"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/zip-stream/node_modules/readable-stream": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz",
"integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==",
"dependencies": {
"abort-controller": "^3.0.0",
"buffer": "^6.0.3",
"events": "^3.3.0",
"process": "^0.11.10",
"string_decoder": "^1.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
} }
} }
} }
+5 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/artifact", "name": "@actions/artifact",
"version": "2.1.8", "version": "2.1.4",
"preview": true, "preview": true,
"description": "Actions artifact lib", "description": "Actions artifact lib",
"keywords": [ "keywords": [
@@ -49,14 +49,15 @@
"@octokit/plugin-retry": "^3.0.9", "@octokit/plugin-retry": "^3.0.9",
"@octokit/request-error": "^5.0.0", "@octokit/request-error": "^5.0.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1",
"archiver": "^7.0.1", "archiver": "^5.3.1",
"async": "^3.2.5",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0", "twirp-ts": "^2.5.0",
"unzip-stream": "^0.3.1" "unzip-stream": "^0.3.1",
"zip-stream": "^6.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/archiver": "^5.3.2",
"@types/unzip-stream": "^0.3.4", "@types/unzip-stream": "^0.3.4",
"typedoc": "^0.25.4", "typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1", "typedoc-plugin-markdown": "^3.17.1",
@@ -1,4 +1,7 @@
import fs from 'fs/promises' import fs from 'fs/promises'
import * as stream from 'stream'
import {createWriteStream} from 'fs'
import * as path from 'path'
import * as github from '@actions/github' import * as github from '@actions/github'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as httpClient from '@actions/http-client' import * as httpClient from '@actions/http-client'
@@ -44,6 +47,11 @@ async function streamExtract(url: string, directory: string): Promise<void> {
await streamExtractExternal(url, directory) await streamExtractExternal(url, directory)
return return
} catch (error) { } catch (error) {
if (error.message.includes('Malformed extraction path')) {
throw new Error(
`Artifact download failed with unretryable error: ${error.message}`
)
}
retryCount++ retryCount++
core.debug( core.debug(
`Failed to download artifact after ${retryCount} retries due to ${error.message}. Retrying in 5 seconds...` `Failed to download artifact after ${retryCount} retries due to ${error.message}. Retrying in 5 seconds...`
@@ -78,6 +86,8 @@ export async function streamExtractExternal(
} }
const timer = setTimeout(timerFn, timeout) const timer = setTimeout(timerFn, timeout)
const createdDirectories = new Set<string>()
createdDirectories.add(directory)
response.message response.message
.on('data', () => { .on('data', () => {
timer.refresh() timer.refresh()
@@ -89,8 +99,46 @@ export async function streamExtractExternal(
clearTimeout(timer) clearTimeout(timer)
reject(error) reject(error)
}) })
.pipe(unzip.Extract({path: directory})) .pipe(unzip.Parse())
.on('close', () => { .pipe(
new stream.Transform({
objectMode: true,
transform: async (entry, _, callback) => {
const fullPath = path.normalize(path.join(directory, entry.path))
if (!directory.endsWith(path.sep)) {
directory += path.sep
}
if (!fullPath.startsWith(directory)) {
reject(new Error(`Malformed extraction path: ${fullPath}`))
}
if (entry.type === 'Directory') {
if (!createdDirectories.has(fullPath)) {
createdDirectories.add(fullPath)
await resolveOrCreateDirectory(fullPath).then(() => {
entry.autodrain()
callback()
})
} else {
entry.autodrain()
callback()
}
} else {
core.info(`Extracting artifact entry: ${fullPath}`)
if (!createdDirectories.has(path.dirname(fullPath))) {
createdDirectories.add(path.dirname(fullPath))
await resolveOrCreateDirectory(path.dirname(fullPath))
}
const writeStream = createWriteStream(fullPath)
writeStream.on('finish', callback)
writeStream.on('error', reject)
entry.pipe(writeStream)
}
}
})
)
.on('finish', async () => {
clearTimeout(timer) clearTimeout(timer)
resolve() resolve()
}) })
@@ -102,6 +102,7 @@ class ArtifactHttpClient implements Rpc {
} catch (error) { } catch (error) {
if (error instanceof SyntaxError) { if (error instanceof SyntaxError) {
debug(`Raw Body: ${rawBody}`) debug(`Raw Body: ${rawBody}`)
throw error
} }
if (error instanceof UsageError) { if (error instanceof UsageError) {
@@ -30,10 +30,10 @@ export function isGhes(): boolean {
const hostname = ghUrl.hostname.trimEnd().toUpperCase() const hostname = ghUrl.hostname.trimEnd().toUpperCase()
const isGitHubHost = hostname === 'GITHUB.COM' const isGitHubHost = hostname === 'GITHUB.COM'
const isGheHost = hostname.endsWith('.GHE.COM') const isGheHost =
const isLocalHost = hostname.endsWith('.LOCALHOST') hostname.endsWith('.GHE.COM') || hostname.endsWith('.GHE.LOCALHOST')
return !isGitHubHost && !isGheHost && !isLocalHost return !isGitHubHost && !isGheHost
} }
export function getGitHubWorkspaceDir(): string { export function getGitHubWorkspaceDir(): string {
@@ -24,30 +24,11 @@ export async function uploadZipToBlobStorage(
zipUploadStream: ZipUploadStream zipUploadStream: ZipUploadStream
): Promise<BlobUploadResponse> { ): Promise<BlobUploadResponse> {
let uploadByteCount = 0 let uploadByteCount = 0
let lastProgressTime = Date.now()
let timeoutId: NodeJS.Timeout | undefined
const chunkTimer = (timeout: number): NodeJS.Timeout => {
// clear the previous timeout
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(() => {
const now = Date.now()
// if there's been more than 30 seconds since the
// last progress event, then we'll consider the upload stalled
if (now - lastProgressTime > timeout) {
throw new Error('Upload progress stalled.')
}
}, timeout)
return timeoutId
}
const maxConcurrency = getConcurrency() const maxConcurrency = getConcurrency()
const bufferSize = getUploadChunkSize() const bufferSize = getUploadChunkSize()
const blobClient = new BlobClient(authenticatedUploadURL) const blobClient = new BlobClient(authenticatedUploadURL)
const blockBlobClient = blobClient.getBlockBlobClient() const blockBlobClient = blobClient.getBlockBlobClient()
const timeoutDuration = 300000 // 30 seconds
core.debug( core.debug(
`Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}` `Uploading artifact zip to blob storage with maxConcurrency: ${maxConcurrency}, bufferSize: ${bufferSize}`
@@ -56,8 +37,6 @@ export async function uploadZipToBlobStorage(
const uploadCallback = (progress: TransferProgressEvent): void => { const uploadCallback = (progress: TransferProgressEvent): void => {
core.info(`Uploaded bytes ${progress.loadedBytes}`) core.info(`Uploaded bytes ${progress.loadedBytes}`)
uploadByteCount = progress.loadedBytes uploadByteCount = progress.loadedBytes
chunkTimer(timeoutDuration)
lastProgressTime = Date.now()
} }
const options: BlockBlobUploadStreamOptions = { const options: BlockBlobUploadStreamOptions = {
@@ -75,8 +54,6 @@ export async function uploadZipToBlobStorage(
core.info('Beginning upload of artifact content to blob storage') core.info('Beginning upload of artifact content to blob storage')
try { try {
// Start the chunk timer
timeoutId = chunkTimer(timeoutDuration)
await blockBlobClient.uploadStream( await blockBlobClient.uploadStream(
uploadStream, uploadStream,
bufferSize, bufferSize,
@@ -87,12 +64,8 @@ export async function uploadZipToBlobStorage(
if (NetworkError.isNetworkErrorCode(error?.code)) { if (NetworkError.isNetworkErrorCode(error?.code)) {
throw new NetworkError(error?.code) throw new NetworkError(error?.code)
} }
throw error throw error
} finally {
// clear the timeout whether or not the upload completes
if (timeoutId) {
clearTimeout(timeoutId)
}
} }
core.info('Finished uploading artifact content to blob storage!') core.info('Finished uploading artifact content to blob storage!')
@@ -106,6 +79,7 @@ export async function uploadZipToBlobStorage(
`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 { return {
uploadSize: uploadByteCount, uploadSize: uploadByteCount,
sha256Hash sha256Hash
@@ -67,18 +67,25 @@ export async function uploadArtifact(
'CreateArtifact: response from backend was not ok' 'CreateArtifact: response from backend was not ok'
) )
} }
// Create the zipupload stream for use in blob upload
const zipUploadStream = await createZipUploadStream( const zipUploadStream = await createZipUploadStream(
zipSpecification, zipSpecification,
options?.compressionLevel options?.compressionLevel
) ).catch(err => {
throw new InvalidResponseError(
`createZipUploadStream: response from backend was not ok: ${err}`
)
})
// Upload zip to blob storage // Upload zip to blob storage
const uploadResult = await uploadZipToBlobStorage( const uploadResult = await uploadZipToBlobStorage(
createArtifactResp.signedUploadUrl, createArtifactResp.signedUploadUrl,
zipUploadStream zipUploadStream
) ).catch(err => {
throw new InvalidResponseError(
`uploadZipToBlobStorage: response blob was not ok: ${err}`
)
})
// finalize the artifact // finalize the artifact
const finalizeArtifactReq: FinalizeArtifactRequest = { const finalizeArtifactReq: FinalizeArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId, workflowRunBackendId: backendIds.workflowRunBackendId,
@@ -86,15 +93,12 @@ export async function uploadArtifact(
name, name,
size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0' size: uploadResult.uploadSize ? uploadResult.uploadSize.toString() : '0'
} }
if (uploadResult.sha256Hash) { if (uploadResult.sha256Hash) {
finalizeArtifactReq.hash = StringValue.create({ finalizeArtifactReq.hash = StringValue.create({
value: `sha256:${uploadResult.sha256Hash}` value: `sha256:${uploadResult.sha256Hash}`
}) })
} }
core.info(`Finalizing artifact upload`) core.info(`Finalizing artifact upload`)
const finalizeArtifactResp = const finalizeArtifactResp =
await artifactClient.FinalizeArtifact(finalizeArtifactReq) await artifactClient.FinalizeArtifact(finalizeArtifactReq)
if (!finalizeArtifactResp.ok) { if (!finalizeArtifactResp.ok) {
+34 -18
View File
@@ -1,6 +1,7 @@
import * as stream from 'stream' import * as stream from 'stream'
import * as archiver from 'archiver' import * as ZipStream from 'zip-stream'
import * as core from '@actions/core' import * as core from '@actions/core'
import async from 'async'
import {createReadStream} from 'fs' import {createReadStream} from 'fs'
import {UploadZipSpecification} from './upload-zip-specification' import {UploadZipSpecification} from './upload-zip-specification'
import {getUploadChunkSize} from '../shared/config' import {getUploadChunkSize} from '../shared/config'
@@ -30,31 +31,50 @@ export async function createZipUploadStream(
`Creating Artifact archive with compressionLevel: ${compressionLevel}` `Creating Artifact archive with compressionLevel: ${compressionLevel}`
) )
const zip = archiver.create('zip', { const zlibOptions = {
highWaterMark: getUploadChunkSize(), zlib: {
zlib: {level: compressionLevel} level: compressionLevel,
}) bufferSize: getUploadChunkSize()
}
}
const zip = new ZipStream.default(zlibOptions)
const bufferSize = getUploadChunkSize()
const zipUploadStream = new ZipUploadStream(bufferSize)
zip.pipe(zipUploadStream)
// register callbacks for various events during the zip lifecycle // register callbacks for various events during the zip lifecycle
zip.on('error', zipErrorCallback) zip.on('error', zipErrorCallback)
zip.on('warning', zipWarningCallback) zip.on('warning', zipWarningCallback)
zip.on('finish', zipFinishCallback) zip.on('finish', zipFinishCallback)
zip.on('end', zipEndCallback) zip.on('end', zipEndCallback)
const addFileToZip = (file: UploadZipSpecification, callback: (error?: Error) => void) => {
for (const file of uploadSpecification) {
if (file.sourcePath !== null) { if (file.sourcePath !== null) {
// Add a normal file to the zip zip.entry(createReadStream(file.sourcePath), { name: file.destinationPath }, (error: any) => {
zip.append(createReadStream(file.sourcePath), { if (error) {
name: file.destinationPath callback(error)
return
}
callback()
}) })
} else { } else {
// Add a directory to the zip zip.entry('', { name: file.destinationPath }, (error: any) => {
zip.append('', {name: file.destinationPath}) if (error) {
callback(error)
return
}
callback()
})
} }
} }
const bufferSize = getUploadChunkSize() async.eachSeries(uploadSpecification, addFileToZip, (error: any) => {
const zipUploadStream = new ZipUploadStream(bufferSize) if (error) {
core.error('Failed to add a file to the zip:')
core.info(error)
return
}
zip.finalize() // Finalize the archive once all files have been added
})
core.debug( core.debug(
`Zip write high watermark value ${zipUploadStream.writableHighWaterMark}` `Zip write high watermark value ${zipUploadStream.writableHighWaterMark}`
@@ -63,9 +83,6 @@ export async function createZipUploadStream(
`Zip read high watermark value ${zipUploadStream.readableHighWaterMark}` `Zip read high watermark value ${zipUploadStream.readableHighWaterMark}`
) )
zip.pipe(zipUploadStream)
zip.finalize()
return zipUploadStream return zipUploadStream
} }
@@ -76,7 +93,6 @@ const zipErrorCallback = (error: any): void => {
throw new Error('An error has occurred during zip creation for the artifact') throw new Error('An error has occurred during zip creation for the artifact')
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const zipWarningCallback = (error: any): void => { const zipWarningCallback = (error: any): void => {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
-3
View File
@@ -12,9 +12,6 @@ Once the attestation has been created and signed, it will be uploaded to the GH
attestations API and associated with the repository from which the workflow was attestations API and associated with the repository from which the workflow was
initiated. initiated.
See [Using artifact attestations to establish provenance for builds](https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)
for more information on artifact attestations.
## Usage ## Usage
### `attest` ### `attest`
-13
View File
@@ -1,23 +1,10 @@
# @actions/attest Releases # @actions/attest Releases
### 1.3.0
- Dynamic construction of Sigstore API URLs
- Switch to new GH provenance build type
- Fetch existing Rekor entry on 409 conflict error
- Bump @sigstore/bundle from 2.3.0 to 2.3.2
- Bump @sigstore/sign from 2.3.0 to 2.3.2
### 1.2.1
- Retry request on attestation persistence failure
### 1.2.0 ### 1.2.0
- Generate attestations using the v0.3 Sigstore bundle format. - Generate attestations using the v0.3 Sigstore bundle format.
- Bump @sigstore/bundle from 2.2.0 to 2.3.0. - Bump @sigstore/bundle from 2.2.0 to 2.3.0.
- Bump @sigstore/sign from 2.2.3 to 2.3.0. - Bump @sigstore/sign from 2.2.3 to 2.3.0.
- Remove dependency on make-fetch-happen
### 1.1.0 ### 1.1.0
@@ -4,7 +4,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
{ {
"params": { "params": {
"buildDefinition": { "buildDefinition": {
"buildType": "https://actions.github.io/buildtypes/workflow/v1", "buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
"externalParameters": { "externalParameters": {
"workflow": { "workflow": {
"path": ".github/workflows/main.yml", "path": ".github/workflows/main.yml",
@@ -17,7 +17,6 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
"event_name": "push", "event_name": "push",
"repository_id": "repo-id", "repository_id": "repo-id",
"repository_owner_id": "owner-id", "repository_owner_id": "owner-id",
"runner_environment": "github-hosted",
}, },
}, },
"resolvedDependencies": [ "resolvedDependencies": [
@@ -31,7 +30,7 @@ exports[`provenance functions buildSLSAProvenancePredicate returns a provenance
}, },
"runDetails": { "runDetails": {
"builder": { "builder": {
"id": "https://github.com/owner/workflows/.github/workflows/publish.yml@main", "id": "https://github.com/actions/runner/github-hosted",
}, },
"metadata": { "metadata": {
"invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt", "invocationId": "https://github.com/owner/repo/actions/runs/run-id/attempts/run-attempt",
@@ -1,41 +0,0 @@
import {signingEndpoints} from '../src/endpoints'
describe('signingEndpoints', () => {
const originalEnv = process.env
afterEach(() => {
process.env = originalEnv
})
describe('when using github.com', () => {
beforeEach(async () => {
process.env = {
...originalEnv,
GITHUB_SERVER_URL: 'https://github.com'
}
})
it('returns expected endpoints', async () => {
const endpoints = signingEndpoints('github')
expect(endpoints.fulcioURL).toEqual('https://fulcio.githubapp.com')
expect(endpoints.tsaServerURL).toEqual('https://timestamp.githubapp.com')
})
})
describe('when using custom domain', () => {
beforeEach(async () => {
process.env = {
...originalEnv,
GITHUB_SERVER_URL: 'https://foo.bar.com'
}
})
it('returns a expected endpoints', async () => {
const endpoints = signingEndpoints('github')
expect(endpoints.fulcioURL).toEqual('https://fulcio.foo.bar.com')
expect(endpoints.tsaServerURL).toEqual('https://timestamp.foo.bar.com')
})
})
})
+1 -2
View File
@@ -45,8 +45,7 @@ describe('getIDTokenClaims', () => {
sha: 'sha', sha: 'sha',
repository: 'repo', repository: 'repo',
event_name: 'push', event_name: 'push',
job_workflow_ref: 'job_workflow_ref', workflow_ref: 'main',
workflow_ref: 'workflow',
repository_id: '1', repository_id: '1',
repository_owner_id: '1', repository_owner_id: '1',
runner_environment: 'github-hosted', runner_environment: 'github-hosted',
+7 -20
View File
@@ -2,8 +2,7 @@ import * as github from '@actions/github'
import {mockFulcio, mockRekor, mockTSA} from '@sigstore/mock' import {mockFulcio, mockRekor, mockTSA} from '@sigstore/mock'
import * as jose from 'jose' import * as jose from 'jose'
import nock from 'nock' import nock from 'nock'
import {MockAgent, setGlobalDispatcher} from 'undici' import {SIGSTORE_GITHUB, SIGSTORE_PUBLIC_GOOD} from '../src/endpoints'
import {SIGSTORE_PUBLIC_GOOD, signingEndpoints} from '../src/endpoints'
import {attestProvenance, buildSLSAProvenancePredicate} from '../src/provenance' import {attestProvenance, buildSLSAProvenancePredicate} from '../src/provenance'
describe('provenance functions', () => { describe('provenance functions', () => {
@@ -13,17 +12,12 @@ describe('provenance functions', () => {
const jwksPath = '/.well-known/jwks.json' const jwksPath = '/.well-known/jwks.json'
const tokenPath = '/token' const tokenPath = '/token'
// MockAgent for mocking @actions/github
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
const claims = { const claims = {
iss: issuer, iss: issuer,
aud: 'nobody', aud: 'nobody',
repository: 'owner/repo', repository: 'owner/repo',
ref: 'refs/heads/main', ref: 'refs/heads/main',
sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b', sha: 'babca52ab0c93ae16539e5923cb0d7403b9a093b',
job_workflow_ref: 'owner/workflows/.github/workflows/publish.yml@main',
workflow_ref: 'owner/repo/.github/workflows/main.yml@main', workflow_ref: 'owner/repo/.github/workflows/main.yml@main',
event_name: 'push', event_name: 'push',
repository_id: 'repo-id', repository_id: 'repo-id',
@@ -96,19 +90,16 @@ describe('provenance functions', () => {
}) })
describe('when using the github Sigstore instance', () => { describe('when using the github Sigstore instance', () => {
const {fulcioURL, tsaServerURL} = signingEndpoints('github') const {fulcioURL, tsaServerURL} = SIGSTORE_GITHUB
beforeEach(async () => { beforeEach(async () => {
// Mock Sigstore // Mock Sigstore
await mockFulcio({baseURL: fulcioURL, strict: false}) await mockFulcio({baseURL: fulcioURL, strict: false})
await mockTSA({baseURL: tsaServerURL}) await mockTSA({baseURL: tsaServerURL})
mockAgent // Mock GH attestations API
.get('https://api.github.com') nock('https://api.github.com')
.intercept({ .post(/^\/repos\/.*\/.*\/attestations$/)
path: /^\/repos\/.*\/.*\/attestations$/,
method: 'post'
})
.reply(201, {id: attestationID}) .reply(201, {id: attestationID})
}) })
@@ -168,12 +159,8 @@ describe('provenance functions', () => {
await mockRekor({baseURL: rekorURL}) await mockRekor({baseURL: rekorURL})
// Mock GH attestations API // Mock GH attestations API
mockAgent nock('https://api.github.com')
.get('https://api.github.com') .post(/^\/repos\/.*\/.*\/attestations$/)
.intercept({
path: /^\/repos\/.*\/.*\/attestations$/,
method: 'post'
})
.reply(201, {id: attestationID}) .reply(201, {id: attestationID})
}) })
+8 -53
View File
@@ -1,4 +1,4 @@
import {MockAgent, setGlobalDispatcher} from 'undici' import nock from 'nock'
import {writeAttestation} from '../src/store' import {writeAttestation} from '../src/store'
describe('writeAttestation', () => { describe('writeAttestation', () => {
@@ -6,9 +6,6 @@ describe('writeAttestation', () => {
const attestation = {foo: 'bar '} const attestation = {foo: 'bar '}
const token = 'token' const token = 'token'
const mockAgent = new MockAgent()
setGlobalDispatcher(mockAgent)
beforeEach(() => { beforeEach(() => {
process.env = { process.env = {
...originalEnv, ...originalEnv,
@@ -22,14 +19,9 @@ describe('writeAttestation', () => {
describe('when the api call is successful', () => { describe('when the api call is successful', () => {
beforeEach(() => { beforeEach(() => {
mockAgent nock('https://api.github.com')
.get('https://api.github.com') .matchHeader('authorization', `token ${token}`)
.intercept({ .post('/repos/foo/bar/attestations', {bundle: attestation})
path: '/repos/foo/bar/attestations',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({bundle: attestation})
})
.reply(201, {id: '123'}) .reply(201, {id: '123'})
}) })
@@ -40,51 +32,14 @@ describe('writeAttestation', () => {
describe('when the api call fails', () => { describe('when the api call fails', () => {
beforeEach(() => { beforeEach(() => {
mockAgent nock('https://api.github.com')
.get('https://api.github.com') .matchHeader('authorization', `token ${token}`)
.intercept({ .post('/repos/foo/bar/attestations', {bundle: attestation})
path: '/repos/foo/bar/attestations',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({bundle: attestation})
})
.reply(500, 'oops') .reply(500, 'oops')
}) })
it('throws an error', async () => { it('throws an error', async () => {
await expect( await expect(writeAttestation(attestation, token)).rejects.toThrow(/oops/)
writeAttestation(attestation, token, {retry: 0})
).rejects.toThrow(/oops/)
})
})
describe('when the api call fails but succeeds on retry', () => {
beforeEach(() => {
const pool = mockAgent.get('https://api.github.com')
pool
.intercept({
path: '/repos/foo/bar/attestations',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({bundle: attestation})
})
.reply(500, 'oops')
.times(1)
pool
.intercept({
path: '/repos/foo/bar/attestations',
method: 'POST',
headers: {authorization: `token ${token}`},
body: JSON.stringify({bundle: attestation})
})
.reply(201, {id: '123'})
.times(1)
})
it('persists the attestation', async () => {
await expect(writeAttestation(attestation, token)).resolves.toEqual('123')
}) })
}) })
}) })
+430 -444
View File
File diff suppressed because it is too large Load Diff
+8 -13
View File
@@ -1,6 +1,6 @@
{ {
"name": "@actions/attest", "name": "@actions/attest",
"version": "1.3.0", "version": "1.2.0",
"description": "Actions attestation lib", "description": "Actions attestation lib",
"keywords": [ "keywords": [
"github", "github",
@@ -35,26 +35,21 @@
"url": "https://github.com/actions/toolkit/issues" "url": "https://github.com/actions/toolkit/issues"
}, },
"devDependencies": { "devDependencies": {
"@sigstore/mock": "^0.7.4", "@sigstore/mock": "^0.6.5",
"@sigstore/rekor-types": "^2.0.0", "@sigstore/rekor-types": "^2.0.0",
"@types/jsonwebtoken": "^9.0.6", "@types/jsonwebtoken": "^9.0.6",
"@types/make-fetch-happen": "^10.0.4",
"jose": "^5.2.3", "jose": "^5.2.3",
"nock": "^13.5.1", "nock": "^13.5.1"
"undici": "^5.28.4"
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/github": "^6.0.0", "@actions/github": "^6.0.0",
"@actions/http-client": "^2.2.1", "@actions/http-client": "^2.2.1",
"@octokit/plugin-retry": "^6.0.1", "@sigstore/bundle": "^2.3.0",
"@sigstore/bundle": "^2.3.2", "@sigstore/sign": "^2.3.0",
"@sigstore/sign": "^2.3.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0" "jwks-rsa": "^3.1.0",
}, "make-fetch-happen": "^13.0.0"
"overrides": {
"@octokit/plugin-retry": {
"@octokit/core": "^5.2.0"
}
} }
} }
+9 -14
View File
@@ -6,6 +6,9 @@ const GITHUB_ID = 'github'
const FULCIO_PUBLIC_GOOD_URL = 'https://fulcio.sigstore.dev' const FULCIO_PUBLIC_GOOD_URL = 'https://fulcio.sigstore.dev'
const REKOR_PUBLIC_GOOD_URL = 'https://rekor.sigstore.dev' const REKOR_PUBLIC_GOOD_URL = 'https://rekor.sigstore.dev'
const FULCIO_INTERNAL_URL = 'https://fulcio.githubapp.com'
const TSA_INTERNAL_URL = 'https://timestamp.githubapp.com'
export type SigstoreInstance = typeof PUBLIC_GOOD_ID | typeof GITHUB_ID export type SigstoreInstance = typeof PUBLIC_GOOD_ID | typeof GITHUB_ID
export type Endpoints = { export type Endpoints = {
@@ -19,6 +22,11 @@ export const SIGSTORE_PUBLIC_GOOD: Endpoints = {
rekorURL: REKOR_PUBLIC_GOOD_URL rekorURL: REKOR_PUBLIC_GOOD_URL
} }
export const SIGSTORE_GITHUB: Endpoints = {
fulcioURL: FULCIO_INTERNAL_URL,
tsaServerURL: TSA_INTERNAL_URL
}
export const signingEndpoints = (sigstore?: SigstoreInstance): Endpoints => { export const signingEndpoints = (sigstore?: SigstoreInstance): Endpoints => {
let instance: SigstoreInstance let instance: SigstoreInstance
@@ -37,19 +45,6 @@ export const signingEndpoints = (sigstore?: SigstoreInstance): Endpoints => {
case PUBLIC_GOOD_ID: case PUBLIC_GOOD_ID:
return SIGSTORE_PUBLIC_GOOD return SIGSTORE_PUBLIC_GOOD
case GITHUB_ID: case GITHUB_ID:
return buildGitHubEndpoints() return SIGSTORE_GITHUB
}
}
function buildGitHubEndpoints(): Endpoints {
const serverURL = process.env.GITHUB_SERVER_URL || 'https://github.com'
let host = new URL(serverURL).hostname
if (host === 'github.com') {
host = 'githubapp.com'
}
return {
fulcioURL: `https://fulcio.${host}`,
tsaServerURL: `https://timestamp.${host}`
} }
} }
-1
View File
@@ -11,7 +11,6 @@ const REQUIRED_CLAIMS = [
'sha', 'sha',
'repository', 'repository',
'event_name', 'event_name',
'job_workflow_ref',
'workflow_ref', 'workflow_ref',
'repository_id', 'repository_id',
'repository_owner_id', 'repository_owner_id',
+6 -4
View File
@@ -3,7 +3,10 @@ import {getIDTokenClaims} from './oidc'
import type {Attestation, Predicate} from './shared.types' import type {Attestation, Predicate} from './shared.types'
const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1' const SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
const GITHUB_BUILD_TYPE = 'https://actions.github.io/buildtypes/workflow/v1'
const GITHUB_BUILDER_ID_PREFIX = 'https://github.com/actions/runner'
const GITHUB_BUILD_TYPE =
'https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1'
const DEFAULT_ISSUER = 'https://token.actions.githubusercontent.com' const DEFAULT_ISSUER = 'https://token.actions.githubusercontent.com'
@@ -52,8 +55,7 @@ export const buildSLSAProvenancePredicate = async (
github: { github: {
event_name: claims.event_name, event_name: claims.event_name,
repository_id: claims.repository_id, repository_id: claims.repository_id,
repository_owner_id: claims.repository_owner_id, repository_owner_id: claims.repository_owner_id
runner_environment: claims.runner_environment
} }
}, },
resolvedDependencies: [ resolvedDependencies: [
@@ -67,7 +69,7 @@ export const buildSLSAProvenancePredicate = async (
}, },
runDetails: { runDetails: {
builder: { builder: {
id: `${serverURL}/${claims.job_workflow_ref}` id: `${GITHUB_BUILDER_ID_PREFIX}/${claims.runner_environment}`
}, },
metadata: { metadata: {
invocationId: `${serverURL}/${claims.repository}/actions/runs/${claims.run_id}/attempts/${claims.run_attempt}` invocationId: `${serverURL}/${claims.repository}/actions/runs/${claims.run_id}/attempts/${claims.run_attempt}`
-1
View File
@@ -87,7 +87,6 @@ const initBundleBuilder = (opts: SignOptions): BundleBuilder => {
new RekorWitness({ new RekorWitness({
rekorBaseURL: opts.rekorURL, rekorBaseURL: opts.rekorURL,
entryType: 'dsse', entryType: 'dsse',
fetchOnConflict: true,
timeout, timeout,
retry retry
}) })
+4 -14
View File
@@ -1,12 +1,8 @@
import * as github from '@actions/github' import * as github from '@actions/github'
import {retry} from '@octokit/plugin-retry' import fetch from 'make-fetch-happen'
const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations' const CREATE_ATTESTATION_REQUEST = 'POST /repos/{owner}/{repo}/attestations'
const DEFAULT_RETRY_COUNT = 5
export type WriteOptions = {
retry?: number
}
/** /**
* Writes an attestation to the repository's attestations endpoint. * Writes an attestation to the repository's attestations endpoint.
* @param attestation - The attestation to write. * @param attestation - The attestation to write.
@@ -16,11 +12,9 @@ export type WriteOptions = {
*/ */
export const writeAttestation = async ( export const writeAttestation = async (
attestation: unknown, attestation: unknown,
token: string, token: string
options: WriteOptions = {}
): Promise<string> => { ): Promise<string> => {
const retries = options.retry ?? DEFAULT_RETRY_COUNT const octokit = github.getOctokit(token, {request: {fetch}})
const octokit = github.getOctokit(token, {retry: {retries}}, retry)
try { try {
const response = await octokit.request(CREATE_ATTESTATION_REQUEST, { const response = await octokit.request(CREATE_ATTESTATION_REQUEST, {
@@ -29,11 +23,7 @@ export const writeAttestation = async (
data: {bundle: attestation} data: {bundle: attestation}
}) })
const data = return response.data?.id
typeof response.data == 'string'
? JSON.parse(response.data)
: response.data
return data?.id
} catch (err) { } catch (err) {
const message = err instanceof Error ? err.message : err const message = err instanceof Error ? err.message : err
throw new Error(`Failed to persist attestation: ${message}`) throw new Error(`Failed to persist attestation: ${message}`)