Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0db2ef8ead | |||
| e3764a56e8 |
@@ -7,12 +7,6 @@ import {noopLogs} from './common'
|
||||
|
||||
jest.mock('@actions/http-client')
|
||||
|
||||
const clientOptions = {
|
||||
maxAttempts: 5,
|
||||
retryIntervalMs: 1,
|
||||
retryMultiplier: 1.5
|
||||
}
|
||||
|
||||
describe('artifact-http-client', () => {
|
||||
beforeAll(() => {
|
||||
noopLogs()
|
||||
@@ -100,7 +94,11 @@ describe('artifact-http-client', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const client = internalArtifactTwirpClient(clientOptions)
|
||||
const client = internalArtifactTwirpClient({
|
||||
maxAttempts: 5,
|
||||
retryIntervalMs: 1,
|
||||
retryMultiplier: 1.5
|
||||
})
|
||||
const artifact = await client.CreateArtifact({
|
||||
workflowRunBackendId: '1234',
|
||||
workflowJobRunBackendId: '5678',
|
||||
@@ -135,7 +133,11 @@ describe('artifact-http-client', () => {
|
||||
post: mockPost
|
||||
}
|
||||
})
|
||||
const client = internalArtifactTwirpClient(clientOptions)
|
||||
const client = internalArtifactTwirpClient({
|
||||
maxAttempts: 5,
|
||||
retryIntervalMs: 1,
|
||||
retryMultiplier: 1.5
|
||||
})
|
||||
await expect(async () => {
|
||||
await client.CreateArtifact({
|
||||
workflowRunBackendId: '1234',
|
||||
@@ -170,7 +172,11 @@ describe('artifact-http-client', () => {
|
||||
post: mockPost
|
||||
}
|
||||
})
|
||||
const client = internalArtifactTwirpClient(clientOptions)
|
||||
const client = internalArtifactTwirpClient({
|
||||
maxAttempts: 5,
|
||||
retryIntervalMs: 1,
|
||||
retryMultiplier: 1.5
|
||||
})
|
||||
await expect(async () => {
|
||||
await client.CreateArtifact({
|
||||
workflowRunBackendId: '1234',
|
||||
@@ -184,83 +190,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(
|
||||
'Failed to CreateArtifact: Unable to make request: ENOTFOUND\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'
|
||||
)
|
||||
expect(mockHttpClient).toHaveBeenCalledTimes(1)
|
||||
expect(mockPost).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -104,7 +104,6 @@ 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
|
||||
}
|
||||
@@ -114,7 +113,6 @@ 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
|
||||
}
|
||||
|
||||
@@ -352,46 +352,3 @@ describe('upload-artifact', () => {
|
||||
expect(uploadResp).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getBlobClientOptions', () => {
|
||||
afterEach(() => {
|
||||
delete process.env['HTTPS_PROXY']
|
||||
delete process.env['HTTP_PROXY']
|
||||
delete process.env['NO_PROXY']
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('should not use proxy settings if not specified', () => {
|
||||
const opts = blobUpload.getBlobClientOptions('https://blob-storage.local')
|
||||
expect(opts.proxyOptions).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should use https proxy settings from environment', () => {
|
||||
process.env['HTTPS_PROXY'] = 'https://foo:bar@my-proxy.local'
|
||||
const opts = blobUpload.getBlobClientOptions('https://blob-storage.local')
|
||||
expect(opts.proxyOptions).toEqual({
|
||||
host: 'my-proxy.local',
|
||||
port: 443,
|
||||
username: 'foo',
|
||||
password: 'bar'
|
||||
})
|
||||
})
|
||||
|
||||
it('should use http proxy settings from environment', () => {
|
||||
process.env['HTTP_PROXY'] = 'http://foo:bar@my-proxy.local:1234'
|
||||
const opts = blobUpload.getBlobClientOptions('http://blob-storage.local')
|
||||
expect(opts.proxyOptions).toEqual({
|
||||
host: 'my-proxy.local',
|
||||
port: 1234,
|
||||
username: 'foo',
|
||||
password: 'bar'
|
||||
})
|
||||
})
|
||||
|
||||
it('should respect NO_PROXY', () => {
|
||||
process.env['HTTPS_PROXY'] = 'https://foo:bar@my-proxy.local'
|
||||
process.env['NO_PROXY'] = 'no-proxy-me.local'
|
||||
const opts = blobUpload.getBlobClientOptions('https://no-proxy-me.local')
|
||||
expect(opts.proxyOptions).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
Generated
+138
-13
@@ -18,15 +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,11 +471,10 @@
|
||||
"@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": "*"
|
||||
}
|
||||
@@ -589,6 +588,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 +618,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 +668,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 +801,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 +875,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",
|
||||
@@ -960,6 +1029,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",
|
||||
@@ -1204,6 +1278,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,6 +1313,11 @@
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"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/shiki": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz",
|
||||
@@ -1423,13 +1513,48 @@
|
||||
"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": {
|
||||
|
||||
@@ -49,15 +49,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"
|
||||
|
||||
@@ -2,7 +2,7 @@ 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
|
||||
@@ -47,12 +47,7 @@ async function streamExtract(url: string, directory: string): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
response.message
|
||||
.pipe(unzip.Extract({path: directory}))
|
||||
.on('close', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
return response.message.pipe(unzipper.Extract({path: directory})).promise()
|
||||
}
|
||||
|
||||
export async function downloadArtifactPublic(
|
||||
|
||||
@@ -4,7 +4,6 @@ import {info, debug} from '@actions/core'
|
||||
import {ArtifactServiceClientJSON} from '../../generated'
|
||||
import {getResultsServiceUrl, getRuntimeToken} from './config'
|
||||
import {getUserAgentString} from './user-agent'
|
||||
import {NetworkError} from './errors'
|
||||
|
||||
// The twirp http client must implement this interface
|
||||
interface Rpc {
|
||||
@@ -60,10 +59,10 @@ class ArtifactHttpClient implements Rpc {
|
||||
'Content-Type': contentType
|
||||
}
|
||||
try {
|
||||
const {body} = await this.retryableRequest(async () =>
|
||||
const response = await this.retryableRequest(async () =>
|
||||
this.httpClient.post(url, JSON.stringify(data), headers)
|
||||
)
|
||||
|
||||
const body = await response.readBody()
|
||||
return JSON.parse(body)
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to ${method}: ${error.message}`)
|
||||
@@ -72,7 +71,7 @@ class ArtifactHttpClient implements Rpc {
|
||||
|
||||
async retryableRequest(
|
||||
operation: () => Promise<HttpClientResponse>
|
||||
): Promise<{response: HttpClientResponse; body: string}> {
|
||||
): Promise<HttpClientResponse> {
|
||||
let attempt = 0
|
||||
let errorMessage = ''
|
||||
while (attempt < this.maxAttempts) {
|
||||
@@ -81,25 +80,18 @@ class ArtifactHttpClient implements Rpc {
|
||||
try {
|
||||
const response = await operation()
|
||||
const statusCode = response.message.statusCode
|
||||
const body = await response.readBody()
|
||||
debug(`[Response] - ${response.message.statusCode}`)
|
||||
debug(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`)
|
||||
debug(`Body: ${body}`)
|
||||
debug(`[Response] ${response.message.statusCode}`)
|
||||
debug(JSON.stringify(response.message.headers, null, 2))
|
||||
|
||||
if (this.isSuccessStatusCode(statusCode)) {
|
||||
return {response, body}
|
||||
return response
|
||||
}
|
||||
|
||||
isRetryable = this.isRetryableHttpStatusCode(statusCode)
|
||||
errorMessage = `Failed request: (${statusCode}) ${response.message.statusMessage}`
|
||||
const responseMessage = JSON.parse(body).msg
|
||||
if (responseMessage) {
|
||||
errorMessage = `${errorMessage}: ${responseMessage}`
|
||||
}
|
||||
} catch (error) {
|
||||
isRetryable = true
|
||||
errorMessage = error.message
|
||||
if (NetworkError.isNetworkErrorCode(error?.code)) {
|
||||
throw new NetworkError(error?.code)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRetryable) {
|
||||
@@ -139,7 +131,8 @@ class ArtifactHttpClient implements Rpc {
|
||||
HttpCodes.GatewayTimeout,
|
||||
HttpCodes.InternalServerError,
|
||||
HttpCodes.ServiceUnavailable,
|
||||
HttpCodes.TooManyRequests
|
||||
HttpCodes.TooManyRequests,
|
||||
413 // Payload Too Large
|
||||
]
|
||||
|
||||
return retryableStatusCodes.includes(statusCode)
|
||||
|
||||
@@ -35,25 +35,3 @@ export class GHESNotSupportedError extends Error {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import {
|
||||
AnonymousCredential,
|
||||
BlobClient,
|
||||
BlockBlobUploadStreamOptions,
|
||||
StoragePipelineOptions
|
||||
} from '@azure/storage-blob'
|
||||
import {BlobClient, BlockBlobUploadStreamOptions} from '@azure/storage-blob'
|
||||
import {TransferProgressEvent} from '@azure/core-http'
|
||||
import {ZipUploadStream} from './zip'
|
||||
import {getUploadChunkSize, getConcurrency} from '../shared/config'
|
||||
import {getProxyUrl} from '@actions/http-client'
|
||||
import * as core from '@actions/core'
|
||||
import * as crypto from 'crypto'
|
||||
import * as stream from 'stream'
|
||||
import {NetworkError} from '../shared/errors'
|
||||
import {getUserAgentString} from '../shared/user-agent'
|
||||
|
||||
export interface BlobUploadResponse {
|
||||
/**
|
||||
@@ -34,12 +26,7 @@ export async function uploadZipToBlobStorage(
|
||||
|
||||
const maxConcurrency = getConcurrency()
|
||||
const bufferSize = getUploadChunkSize()
|
||||
|
||||
const blobClient = new BlobClient(
|
||||
authenticatedUploadURL,
|
||||
new AnonymousCredential(),
|
||||
getBlobClientOptions(authenticatedUploadURL)
|
||||
)
|
||||
const blobClient = new BlobClient(authenticatedUploadURL)
|
||||
const blockBlobClient = blobClient.getBlockBlobClient()
|
||||
|
||||
core.debug(
|
||||
@@ -65,20 +52,12 @@ export async function uploadZipToBlobStorage(
|
||||
|
||||
core.info('Beginning upload of artifact content to blob storage')
|
||||
|
||||
try {
|
||||
await blockBlobClient.uploadStream(
|
||||
uploadStream,
|
||||
bufferSize,
|
||||
maxConcurrency,
|
||||
options
|
||||
)
|
||||
} catch (error) {
|
||||
if (NetworkError.isNetworkErrorCode(error?.code)) {
|
||||
throw new NetworkError(error?.code)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
await blockBlobClient.uploadStream(
|
||||
uploadStream,
|
||||
bufferSize,
|
||||
maxConcurrency,
|
||||
options
|
||||
)
|
||||
|
||||
core.info('Finished uploading artifact content to blob storage!')
|
||||
|
||||
@@ -97,37 +76,3 @@ export async function uploadZipToBlobStorage(
|
||||
sha256Hash
|
||||
}
|
||||
}
|
||||
|
||||
export function getBlobClientOptions(sasURL: string): StoragePipelineOptions {
|
||||
const options: StoragePipelineOptions = {
|
||||
userAgentOptions: {
|
||||
userAgentPrefix: getUserAgentString()
|
||||
}
|
||||
}
|
||||
|
||||
const proxyUrl = getProxyUrl(sasURL)
|
||||
if (proxyUrl !== '') {
|
||||
const {
|
||||
port: portString,
|
||||
hostname: host,
|
||||
username,
|
||||
password,
|
||||
protocol
|
||||
} = new URL(proxyUrl)
|
||||
core.debug(`Using proxy server for blob storage upload, host: ${host}`)
|
||||
|
||||
let port = protocol === 'https:' ? 443 : 80
|
||||
if (portString !== '') {
|
||||
port = parseInt(portString)
|
||||
}
|
||||
|
||||
options.proxyOptions = {
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ export async function uploadArtifact(
|
||||
)
|
||||
}
|
||||
|
||||
const zipUploadStream = await createZipUploadStream(
|
||||
zipSpecification,
|
||||
options?.compressionLevel
|
||||
)
|
||||
|
||||
// get the IDs needed for the artifact creation
|
||||
const backendIds = getBackendIdsFromToken()
|
||||
|
||||
@@ -68,11 +73,6 @@ export async function uploadArtifact(
|
||||
)
|
||||
}
|
||||
|
||||
const zipUploadStream = await createZipUploadStream(
|
||||
zipSpecification,
|
||||
options?.compressionLevel
|
||||
)
|
||||
|
||||
// Upload zip to blob storage
|
||||
const uploadResult = await uploadZipToBlobStorage(
|
||||
createArtifactResp.signedUploadUrl,
|
||||
|
||||
Reference in New Issue
Block a user