Compare commits

..

1 Commits

Author SHA1 Message Date
Brian Cristante bcb0e62b16 Copy over http-client files 2022-04-28 09:33:03 -04:00
60 changed files with 11193 additions and 1915 deletions
+15 -15
View File
@@ -5,8 +5,8 @@ on:
inputs:
package:
required: true
description: 'core, artifact, cache, exec, github, glob, http-client, io, tool-cache'
description: 'core, artifact, cache, exec, github, glob, io, tool-cache'
jobs:
test:
runs-on: macos-latest
@@ -17,40 +17,40 @@ jobs:
- name: verify package exists
run: ls packages/${{ github.event.inputs.package }}
- name: Set Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: npm install
run: npm install
- name: bootstrap
run: npm run bootstrap
- name: build
run: npm run build
- name: test
run: npm run test
- name: pack
run: npm pack
working-directory: packages/${{ github.event.inputs.package }}
- name: upload artifact
uses: actions/upload-artifact@v2
with:
name: ${{ github.event.inputs.package }}
path: packages/${{ github.event.inputs.package }}/*.tgz
publish:
runs-on: macos-latest
needs: test
environment: npm-publish
steps:
- name: download artifact
uses: actions/download-artifact@v2
with:
@@ -58,7 +58,7 @@ jobs:
- name: setup authentication
run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
env:
env:
NPM_TOKEN: ${{ secrets.TOKEN }}
- name: publish
@@ -68,13 +68,13 @@ jobs:
if: failure()
run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":pb__failed: Failed to publish a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
env:
env:
SLACK_WEBHOOK: ${{ secrets.SLACK }}
- name: notify slack on success
if: success()
run: |
curl -X POST -H 'Content-type: application/json' --data '{"text":":dance: Successfully published a new version of ${{ github.event.inputs.package }}"}' $SLACK_WEBHOOK
env:
env:
SLACK_WEBHOOK: ${{ secrets.SLACK }}
-9
View File
@@ -46,15 +46,6 @@ $ npm install @actions/glob
```
<br/>
:phone: [@actions/http-client](packages/http-client)
A lightweight HTTP client optimized for building actions. Read more [here](packages/http-client)
```bash
$ npm install @actions/http-client
```
<br/>
:pencil2: [@actions/io](packages/io)
Provides disk i/o functions like cp, mv, rmRF, which etc. Read more [here](packages/io)
+1 -13
View File
@@ -77,16 +77,4 @@
### 1.0.0
- Update `lockfileVersion` to `v2` in `package-lock.json` [#1009](https://github.com/actions/toolkit/pull/1009)
### 1.0.1
- Update to v2.0.0 of `@actions/http-client`
### 1.0.2
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 1.1.0
- Add `x-actions-results-crc64` and `x-actions-results-md5` checksum headers on upload [#1063](https://github.com/actions/toolkit/pull/1063)
- Update `lockfileVersion` to `v2` in `package-lock.json [#1009](https://github.com/actions/toolkit/pull/1009)
-57
View File
@@ -1,57 +0,0 @@
import CRC64, {CRC64DigestEncoding} from '../src/internal/crc64'
const fixtures = {
data:
'🚀 👉😎👉 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\nUt enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\nDuis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\nExcepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n',
expected: {
hex: '846CE4ADAD6223ED',
base64: '7SNira3kbIQ=',
buffer: Buffer.from([0xed, 0x23, 0x62, 0xad, 0xad, 0xe4, 0x6c, 0x84])
}
}
function assertEncodings(crc: CRC64): void {
const encodings = Object.keys(fixtures.expected) as CRC64DigestEncoding[]
for (const encoding of encodings) {
expect(crc.digest(encoding)).toEqual(fixtures.expected[encoding])
}
}
describe('@actions/artifact/src/internal/crc64', () => {
it('CRC64 from string', async () => {
const crc = new CRC64()
crc.update(fixtures.data)
assertEncodings(crc)
})
it('CRC64 from buffer', async () => {
const crc = new CRC64()
const buf = Buffer.from(fixtures.data)
crc.update(buf)
assertEncodings(crc)
})
it('CRC64 from split data', async () => {
const crc = new CRC64()
const splits = fixtures.data.split('\n').slice(0, -1)
for (const split of splits) {
crc.update(`${split}\n`)
}
assertEncodings(crc)
})
it('flips 64 bits', async () => {
const tests = [
[BigInt(0), BigInt('0xffffffffffffffff')],
[BigInt('0xffffffffffffffff'), BigInt(0)],
[BigInt('0xdeadbeef'), BigInt('0xffffffff21524110')]
]
for (const [input, expected] of tests) {
expect(CRC64.flip64Bits(input)).toEqual(expected)
}
})
})
+3 -2
View File
@@ -3,6 +3,7 @@ import * as net from 'net'
import * as core from '@actions/core'
import * as configVariables from '../src/internal/config-variables'
import {retry} from '../src/internal/requestUtils'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {HttpClientResponse} from '@actions/http-client'
jest.mock('../src/internal/config-variables')
@@ -41,7 +42,7 @@ async function testRetry(
async function handleResponse(
testResponseCode: number | undefined
): Promise<HttpClientResponse> {
): Promise<IHttpClientResponse> {
if (!testResponseCode) {
throw new Error(
'Test incorrectly set up. reverse.pop() was called too many times so not enough test response codes were supplied'
@@ -71,7 +72,7 @@ async function emptyMockReadBody(): Promise<string> {
async function setupSingleMockResponse(
statusCode: number
): Promise<HttpClientResponse> {
): Promise<IHttpClientResponse> {
const mockMessage = new http.IncomingMessage(new net.Socket())
const mockReadBody = emptyMockReadBody
mockMessage.statusCode = statusCode
+2 -19
View File
@@ -10,7 +10,6 @@ import {
getInitialRetryIntervalInMilliseconds,
getRetryMultiplier
} from '../src/internal/config-variables'
import {Readable} from 'stream'
jest.mock('../src/internal/config-variables')
@@ -75,20 +74,15 @@ describe('Utils', () => {
const size = 24
const uncompressedLength = 100
const range = 'bytes 0-199/200'
const digest = {
crc64: 'bSzITYnW/P8=',
md5: 'Xiv1fT9AxLbfadrxk2y3ZvgyN0tPwCWafL/wbi9w8mk='
}
const headers = utils.getUploadHeaders(
contentType,
true,
true,
uncompressedLength,
size,
range,
digest
range
)
expect(Object.keys(headers).length).toEqual(10)
expect(Object.keys(headers).length).toEqual(8)
expect(headers['Accept']).toEqual(
`application/json;api-version=${utils.getApiVersion()}`
)
@@ -99,8 +93,6 @@ describe('Utils', () => {
expect(headers['x-tfs-filelength']).toEqual(uncompressedLength)
expect(headers['Content-Length']).toEqual(size)
expect(headers['Content-Range']).toEqual(range)
expect(headers['x-actions-results-crc64']).toEqual(digest.crc64)
expect(headers['x-actions-results-md5']).toEqual(digest.md5)
})
it('Test constructing upload headers with only required parameter', () => {
@@ -227,13 +219,4 @@ describe('Utils', () => {
const size2 = (await fs.promises.stat(emptyFile2)).size
expect(size2).toEqual(0)
})
it('Creates a digest from a readable stream', async () => {
const data = 'lorem ipsum'
const stream = Readable.from(data)
const digest = await utils.digestForStream(stream)
expect(digest.crc64).toBe('bSzITYnW/P8=')
expect(digest.md5).toBe('gKdR/eV3AoZAxBkADjPrpg==')
})
})
+14 -32
View File
@@ -1,16 +1,16 @@
{
"name": "@actions/artifact",
"version": "1.1.0",
"version": "0.6.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/artifact",
"version": "1.0.1",
"version": "0.6.1",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.11",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2"
},
@@ -20,14 +20,14 @@
}
},
"node_modules/@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
"integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
"dependencies": {
"@actions/http-client": "^1.0.11"
}
},
"node_modules/@actions/core/node_modules/@actions/http-client": {
"node_modules/@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
@@ -35,14 +35,6 @@
"tunnel": "0.0.6"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"dependencies": {
"tunnel": "^0.0.6"
}
},
"node_modules/@types/tmp": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.3.tgz",
@@ -195,29 +187,19 @@
},
"dependencies": {
"@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
"integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
"requires": {
"@actions/http-client": "^1.0.11"
},
"dependencies": {
"@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "0.0.6"
}
}
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"@types/tmp": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/artifact",
"version": "1.1.0",
"version": "1.0.0",
"preview": true,
"description": "Actions artifact lib",
"keywords": [
@@ -38,7 +38,7 @@
},
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.11",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2"
},
-317
View File
@@ -1,317 +0,0 @@
/**
* CRC64: cyclic redundancy check, 64-bits
*
* In order to validate that artifacts are not being corrupted over the wire, this redundancy check allows us to
* validate that there was no corruption during transmission. The implementation here is based on Go's hash/crc64 pkg,
* but without the slicing-by-8 optimization: https://cs.opensource.google/go/go/+/master:src/hash/crc64/crc64.go
*
* This implementation uses a pregenerated table based on 0x9A6C9329AC4BC9B5 as the polynomial, the same polynomial that
* is used for Azure Storage: https://github.com/Azure/azure-storage-net/blob/cbe605f9faa01bfc3003d75fc5a16b2eaccfe102/Lib/Common/Core/Util/Crc64.cs#L27
*/
// when transpile target is >= ES2020 (after dropping node 12) these can be changed to bigint literals - ts(2737)
const PREGEN_POLY_TABLE = [
BigInt('0x0000000000000000'),
BigInt('0x7F6EF0C830358979'),
BigInt('0xFEDDE190606B12F2'),
BigInt('0x81B31158505E9B8B'),
BigInt('0xC962E5739841B68F'),
BigInt('0xB60C15BBA8743FF6'),
BigInt('0x37BF04E3F82AA47D'),
BigInt('0x48D1F42BC81F2D04'),
BigInt('0xA61CECB46814FE75'),
BigInt('0xD9721C7C5821770C'),
BigInt('0x58C10D24087FEC87'),
BigInt('0x27AFFDEC384A65FE'),
BigInt('0x6F7E09C7F05548FA'),
BigInt('0x1010F90FC060C183'),
BigInt('0x91A3E857903E5A08'),
BigInt('0xEECD189FA00BD371'),
BigInt('0x78E0FF3B88BE6F81'),
BigInt('0x078E0FF3B88BE6F8'),
BigInt('0x863D1EABE8D57D73'),
BigInt('0xF953EE63D8E0F40A'),
BigInt('0xB1821A4810FFD90E'),
BigInt('0xCEECEA8020CA5077'),
BigInt('0x4F5FFBD87094CBFC'),
BigInt('0x30310B1040A14285'),
BigInt('0xDEFC138FE0AA91F4'),
BigInt('0xA192E347D09F188D'),
BigInt('0x2021F21F80C18306'),
BigInt('0x5F4F02D7B0F40A7F'),
BigInt('0x179EF6FC78EB277B'),
BigInt('0x68F0063448DEAE02'),
BigInt('0xE943176C18803589'),
BigInt('0x962DE7A428B5BCF0'),
BigInt('0xF1C1FE77117CDF02'),
BigInt('0x8EAF0EBF2149567B'),
BigInt('0x0F1C1FE77117CDF0'),
BigInt('0x7072EF2F41224489'),
BigInt('0x38A31B04893D698D'),
BigInt('0x47CDEBCCB908E0F4'),
BigInt('0xC67EFA94E9567B7F'),
BigInt('0xB9100A5CD963F206'),
BigInt('0x57DD12C379682177'),
BigInt('0x28B3E20B495DA80E'),
BigInt('0xA900F35319033385'),
BigInt('0xD66E039B2936BAFC'),
BigInt('0x9EBFF7B0E12997F8'),
BigInt('0xE1D10778D11C1E81'),
BigInt('0x606216208142850A'),
BigInt('0x1F0CE6E8B1770C73'),
BigInt('0x8921014C99C2B083'),
BigInt('0xF64FF184A9F739FA'),
BigInt('0x77FCE0DCF9A9A271'),
BigInt('0x08921014C99C2B08'),
BigInt('0x4043E43F0183060C'),
BigInt('0x3F2D14F731B68F75'),
BigInt('0xBE9E05AF61E814FE'),
BigInt('0xC1F0F56751DD9D87'),
BigInt('0x2F3DEDF8F1D64EF6'),
BigInt('0x50531D30C1E3C78F'),
BigInt('0xD1E00C6891BD5C04'),
BigInt('0xAE8EFCA0A188D57D'),
BigInt('0xE65F088B6997F879'),
BigInt('0x9931F84359A27100'),
BigInt('0x1882E91B09FCEA8B'),
BigInt('0x67EC19D339C963F2'),
BigInt('0xD75ADABD7A6E2D6F'),
BigInt('0xA8342A754A5BA416'),
BigInt('0x29873B2D1A053F9D'),
BigInt('0x56E9CBE52A30B6E4'),
BigInt('0x1E383FCEE22F9BE0'),
BigInt('0x6156CF06D21A1299'),
BigInt('0xE0E5DE5E82448912'),
BigInt('0x9F8B2E96B271006B'),
BigInt('0x71463609127AD31A'),
BigInt('0x0E28C6C1224F5A63'),
BigInt('0x8F9BD7997211C1E8'),
BigInt('0xF0F5275142244891'),
BigInt('0xB824D37A8A3B6595'),
BigInt('0xC74A23B2BA0EECEC'),
BigInt('0x46F932EAEA507767'),
BigInt('0x3997C222DA65FE1E'),
BigInt('0xAFBA2586F2D042EE'),
BigInt('0xD0D4D54EC2E5CB97'),
BigInt('0x5167C41692BB501C'),
BigInt('0x2E0934DEA28ED965'),
BigInt('0x66D8C0F56A91F461'),
BigInt('0x19B6303D5AA47D18'),
BigInt('0x980521650AFAE693'),
BigInt('0xE76BD1AD3ACF6FEA'),
BigInt('0x09A6C9329AC4BC9B'),
BigInt('0x76C839FAAAF135E2'),
BigInt('0xF77B28A2FAAFAE69'),
BigInt('0x8815D86ACA9A2710'),
BigInt('0xC0C42C4102850A14'),
BigInt('0xBFAADC8932B0836D'),
BigInt('0x3E19CDD162EE18E6'),
BigInt('0x41773D1952DB919F'),
BigInt('0x269B24CA6B12F26D'),
BigInt('0x59F5D4025B277B14'),
BigInt('0xD846C55A0B79E09F'),
BigInt('0xA72835923B4C69E6'),
BigInt('0xEFF9C1B9F35344E2'),
BigInt('0x90973171C366CD9B'),
BigInt('0x1124202993385610'),
BigInt('0x6E4AD0E1A30DDF69'),
BigInt('0x8087C87E03060C18'),
BigInt('0xFFE938B633338561'),
BigInt('0x7E5A29EE636D1EEA'),
BigInt('0x0134D92653589793'),
BigInt('0x49E52D0D9B47BA97'),
BigInt('0x368BDDC5AB7233EE'),
BigInt('0xB738CC9DFB2CA865'),
BigInt('0xC8563C55CB19211C'),
BigInt('0x5E7BDBF1E3AC9DEC'),
BigInt('0x21152B39D3991495'),
BigInt('0xA0A63A6183C78F1E'),
BigInt('0xDFC8CAA9B3F20667'),
BigInt('0x97193E827BED2B63'),
BigInt('0xE877CE4A4BD8A21A'),
BigInt('0x69C4DF121B863991'),
BigInt('0x16AA2FDA2BB3B0E8'),
BigInt('0xF86737458BB86399'),
BigInt('0x8709C78DBB8DEAE0'),
BigInt('0x06BAD6D5EBD3716B'),
BigInt('0x79D4261DDBE6F812'),
BigInt('0x3105D23613F9D516'),
BigInt('0x4E6B22FE23CC5C6F'),
BigInt('0xCFD833A67392C7E4'),
BigInt('0xB0B6C36E43A74E9D'),
BigInt('0x9A6C9329AC4BC9B5'),
BigInt('0xE50263E19C7E40CC'),
BigInt('0x64B172B9CC20DB47'),
BigInt('0x1BDF8271FC15523E'),
BigInt('0x530E765A340A7F3A'),
BigInt('0x2C608692043FF643'),
BigInt('0xADD397CA54616DC8'),
BigInt('0xD2BD67026454E4B1'),
BigInt('0x3C707F9DC45F37C0'),
BigInt('0x431E8F55F46ABEB9'),
BigInt('0xC2AD9E0DA4342532'),
BigInt('0xBDC36EC59401AC4B'),
BigInt('0xF5129AEE5C1E814F'),
BigInt('0x8A7C6A266C2B0836'),
BigInt('0x0BCF7B7E3C7593BD'),
BigInt('0x74A18BB60C401AC4'),
BigInt('0xE28C6C1224F5A634'),
BigInt('0x9DE29CDA14C02F4D'),
BigInt('0x1C518D82449EB4C6'),
BigInt('0x633F7D4A74AB3DBF'),
BigInt('0x2BEE8961BCB410BB'),
BigInt('0x548079A98C8199C2'),
BigInt('0xD53368F1DCDF0249'),
BigInt('0xAA5D9839ECEA8B30'),
BigInt('0x449080A64CE15841'),
BigInt('0x3BFE706E7CD4D138'),
BigInt('0xBA4D61362C8A4AB3'),
BigInt('0xC52391FE1CBFC3CA'),
BigInt('0x8DF265D5D4A0EECE'),
BigInt('0xF29C951DE49567B7'),
BigInt('0x732F8445B4CBFC3C'),
BigInt('0x0C41748D84FE7545'),
BigInt('0x6BAD6D5EBD3716B7'),
BigInt('0x14C39D968D029FCE'),
BigInt('0x95708CCEDD5C0445'),
BigInt('0xEA1E7C06ED698D3C'),
BigInt('0xA2CF882D2576A038'),
BigInt('0xDDA178E515432941'),
BigInt('0x5C1269BD451DB2CA'),
BigInt('0x237C997575283BB3'),
BigInt('0xCDB181EAD523E8C2'),
BigInt('0xB2DF7122E51661BB'),
BigInt('0x336C607AB548FA30'),
BigInt('0x4C0290B2857D7349'),
BigInt('0x04D364994D625E4D'),
BigInt('0x7BBD94517D57D734'),
BigInt('0xFA0E85092D094CBF'),
BigInt('0x856075C11D3CC5C6'),
BigInt('0x134D926535897936'),
BigInt('0x6C2362AD05BCF04F'),
BigInt('0xED9073F555E26BC4'),
BigInt('0x92FE833D65D7E2BD'),
BigInt('0xDA2F7716ADC8CFB9'),
BigInt('0xA54187DE9DFD46C0'),
BigInt('0x24F29686CDA3DD4B'),
BigInt('0x5B9C664EFD965432'),
BigInt('0xB5517ED15D9D8743'),
BigInt('0xCA3F8E196DA80E3A'),
BigInt('0x4B8C9F413DF695B1'),
BigInt('0x34E26F890DC31CC8'),
BigInt('0x7C339BA2C5DC31CC'),
BigInt('0x035D6B6AF5E9B8B5'),
BigInt('0x82EE7A32A5B7233E'),
BigInt('0xFD808AFA9582AA47'),
BigInt('0x4D364994D625E4DA'),
BigInt('0x3258B95CE6106DA3'),
BigInt('0xB3EBA804B64EF628'),
BigInt('0xCC8558CC867B7F51'),
BigInt('0x8454ACE74E645255'),
BigInt('0xFB3A5C2F7E51DB2C'),
BigInt('0x7A894D772E0F40A7'),
BigInt('0x05E7BDBF1E3AC9DE'),
BigInt('0xEB2AA520BE311AAF'),
BigInt('0x944455E88E0493D6'),
BigInt('0x15F744B0DE5A085D'),
BigInt('0x6A99B478EE6F8124'),
BigInt('0x224840532670AC20'),
BigInt('0x5D26B09B16452559'),
BigInt('0xDC95A1C3461BBED2'),
BigInt('0xA3FB510B762E37AB'),
BigInt('0x35D6B6AF5E9B8B5B'),
BigInt('0x4AB846676EAE0222'),
BigInt('0xCB0B573F3EF099A9'),
BigInt('0xB465A7F70EC510D0'),
BigInt('0xFCB453DCC6DA3DD4'),
BigInt('0x83DAA314F6EFB4AD'),
BigInt('0x0269B24CA6B12F26'),
BigInt('0x7D0742849684A65F'),
BigInt('0x93CA5A1B368F752E'),
BigInt('0xECA4AAD306BAFC57'),
BigInt('0x6D17BB8B56E467DC'),
BigInt('0x12794B4366D1EEA5'),
BigInt('0x5AA8BF68AECEC3A1'),
BigInt('0x25C64FA09EFB4AD8'),
BigInt('0xA4755EF8CEA5D153'),
BigInt('0xDB1BAE30FE90582A'),
BigInt('0xBCF7B7E3C7593BD8'),
BigInt('0xC399472BF76CB2A1'),
BigInt('0x422A5673A732292A'),
BigInt('0x3D44A6BB9707A053'),
BigInt('0x759552905F188D57'),
BigInt('0x0AFBA2586F2D042E'),
BigInt('0x8B48B3003F739FA5'),
BigInt('0xF42643C80F4616DC'),
BigInt('0x1AEB5B57AF4DC5AD'),
BigInt('0x6585AB9F9F784CD4'),
BigInt('0xE436BAC7CF26D75F'),
BigInt('0x9B584A0FFF135E26'),
BigInt('0xD389BE24370C7322'),
BigInt('0xACE74EEC0739FA5B'),
BigInt('0x2D545FB4576761D0'),
BigInt('0x523AAF7C6752E8A9'),
BigInt('0xC41748D84FE75459'),
BigInt('0xBB79B8107FD2DD20'),
BigInt('0x3ACAA9482F8C46AB'),
BigInt('0x45A459801FB9CFD2'),
BigInt('0x0D75ADABD7A6E2D6'),
BigInt('0x721B5D63E7936BAF'),
BigInt('0xF3A84C3BB7CDF024'),
BigInt('0x8CC6BCF387F8795D'),
BigInt('0x620BA46C27F3AA2C'),
BigInt('0x1D6554A417C62355'),
BigInt('0x9CD645FC4798B8DE'),
BigInt('0xE3B8B53477AD31A7'),
BigInt('0xAB69411FBFB21CA3'),
BigInt('0xD407B1D78F8795DA'),
BigInt('0x55B4A08FDFD90E51'),
BigInt('0x2ADA5047EFEC8728')
]
export type CRC64DigestEncoding = 'hex' | 'base64' | 'buffer'
class CRC64 {
private _crc: bigint
constructor() {
this._crc = BigInt(0)
}
update(data: Buffer | string): void {
const buffer = typeof data === 'string' ? Buffer.from(data) : data
let crc = CRC64.flip64Bits(this._crc)
for (const dataByte of buffer) {
const crcByte = Number(crc & BigInt(0xff))
crc = PREGEN_POLY_TABLE[crcByte ^ dataByte] ^ (crc >> BigInt(8))
}
this._crc = CRC64.flip64Bits(crc)
}
digest(encoding?: CRC64DigestEncoding): string | Buffer {
switch (encoding) {
case 'hex':
return this._crc.toString(16).toUpperCase()
case 'base64':
return this.toBuffer().toString('base64')
default:
return this.toBuffer()
}
}
private toBuffer(): Buffer {
return Buffer.from(
[0, 8, 16, 24, 32, 40, 48, 56].map(s =>
Number((this._crc >> BigInt(s)) & BigInt(0xff))
)
)
}
static flip64Bits(n: bigint): bigint {
return (BigInt(1) << BigInt(64)) - BigInt(1) - n
}
}
export default CRC64
@@ -18,7 +18,7 @@ import {URL} from 'url'
import {StatusReporter} from './status-reporter'
import {performance} from 'perf_hooks'
import {ListArtifactsResponse, QueryArtifactResponse} from './contracts'
import {HttpClientResponse} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {HttpManager} from './http-manager'
import {DownloadItem} from './download-specification'
import {getDownloadFileConcurrency, getRetryLimit} from './config-variables'
@@ -152,7 +152,7 @@ export class DownloadHttpClient {
const headers = getDownloadHeaders('application/json', true, true)
// a single GET request is used to download a file
const makeDownloadRequest = async (): Promise<HttpClientResponse> => {
const makeDownloadRequest = async (): Promise<IHttpClientResponse> => {
const client = this.downloadHttpManager.getClient(httpClientIndex)
return await client.get(artifactLocation, headers)
}
@@ -225,7 +225,7 @@ export class DownloadHttpClient {
// keep trying to download a file until a retry limit has been reached
while (retryCount <= retryLimit) {
let response: HttpClientResponse
let response: IHttpClientResponse
try {
response = await makeDownloadRequest()
} catch (error) {
@@ -295,7 +295,7 @@ export class DownloadHttpClient {
* @param isGzip a boolean denoting if the content is compressed using gzip and if we need to decode it
*/
async pipeResponseToFile(
response: HttpClientResponse,
response: IHttpClientResponse,
destinationStream: fs.WriteStream,
isGzip: boolean
): Promise<void> {
@@ -1,4 +1,4 @@
import {HttpClient} from '@actions/http-client'
import {HttpClient} from '@actions/http-client/index'
import {createHttpClient} from './utils'
/**
@@ -1,4 +1,4 @@
import {HttpClientResponse} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {
isRetryableStatusCode,
isSuccessStatusCode,
@@ -11,11 +11,11 @@ import {getRetryLimit} from './config-variables'
export async function retry(
name: string,
operation: () => Promise<HttpClientResponse>,
operation: () => Promise<IHttpClientResponse>,
customErrorMessages: Map<number, string>,
maxAttempts: number
): Promise<HttpClientResponse> {
let response: HttpClientResponse | undefined = undefined
): Promise<IHttpClientResponse> {
let response: IHttpClientResponse | undefined = undefined
let statusCode: number | undefined = undefined
let isRetryable = false
let errorMessage = ''
@@ -71,9 +71,9 @@ export async function retry(
export async function retryHttpClientRequest(
name: string,
method: () => Promise<HttpClientResponse>,
method: () => Promise<IHttpClientResponse>,
customErrorMessages: Map<number, string> = new Map(),
maxAttempts = getRetryLimit()
): Promise<HttpClientResponse> {
): Promise<IHttpClientResponse> {
return await retry(name, method, customErrorMessages, maxAttempts)
}
@@ -9,7 +9,6 @@ import {
UploadResults
} from './contracts'
import {
digestForStream,
getArtifactUrl,
getContentRange,
getUploadHeaders,
@@ -32,7 +31,8 @@ import {promisify} from 'util'
import {URL} from 'url'
import {performance} from 'perf_hooks'
import {StatusReporter} from './status-reporter'
import {HttpCodes, HttpClientResponse} from '@actions/http-client'
import {HttpCodes} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {HttpManager} from './http-manager'
import {UploadSpecification} from './upload-specification'
import {UploadOptions} from './upload-options'
@@ -406,9 +406,6 @@ export class UploadHttpClient {
isGzip: boolean,
totalFileSize: number
): Promise<boolean> {
// open a new stream and read it to compute the digest
const digest = await digestForStream(openStream())
// prepare all the necessary headers before making any http call
const headers = getUploadHeaders(
'application/octet-stream',
@@ -416,11 +413,10 @@ export class UploadHttpClient {
isGzip,
totalFileSize,
end - start + 1,
getContentRange(start, end, uploadFileSize),
digest
getContentRange(start, end, uploadFileSize)
)
const uploadChunkRequest = async (): Promise<HttpClientResponse> => {
const uploadChunkRequest = async (): Promise<IHttpClientResponse> => {
const client = this.uploadHttpManager.getClient(httpClientIndex)
return await client.sendStream('PUT', resourceUrl, openStream(), headers)
}
@@ -431,7 +427,7 @@ export class UploadHttpClient {
// Increments the current retry count and then checks if the retry limit has been reached
// If there have been too many retries, fail so the download stops
const incrementAndCheckRetryLimit = (
response?: HttpClientResponse
response?: IHttpClientResponse
): boolean => {
retryCount++
if (retryCount > retryLimit) {
@@ -468,7 +464,7 @@ export class UploadHttpClient {
// allow for failed chunks to be retried multiple times
while (retryCount <= retryLimit) {
let response: HttpClientResponse
let response: IHttpClientResponse
try {
response = await uploadChunkRequest()
+11 -43
View File
@@ -1,9 +1,9 @@
import crypto from 'crypto'
import {promises as fs} from 'fs'
import {IncomingHttpHeaders, OutgoingHttpHeaders} from 'http'
import {debug, info, warning} from '@actions/core'
import {HttpCodes, HttpClient, HttpClientResponse} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {promises as fs} from 'fs'
import {HttpCodes, HttpClient} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/auth'
import {IHeaders, IHttpClientResponse} from '@actions/http-client/interfaces'
import {IncomingHttpHeaders} from 'http'
import {
getRuntimeToken,
getRuntimeUrl,
@@ -11,7 +11,6 @@ import {
getRetryMultiplier,
getInitialRetryIntervalInMilliseconds
} from './config-variables'
import CRC64 from './crc64'
/**
* Returns a retry time in milliseconds that exponentially gets larger
@@ -140,8 +139,8 @@ export function getDownloadHeaders(
contentType: string,
isKeepAlive?: boolean,
acceptGzip?: boolean
): OutgoingHttpHeaders {
const requestOptions: OutgoingHttpHeaders = {}
): IHeaders {
const requestOptions: IHeaders = {}
if (contentType) {
requestOptions['Content-Type'] = contentType
@@ -181,10 +180,9 @@ export function getUploadHeaders(
isGzip?: boolean,
uncompressedLength?: number,
contentLength?: number,
contentRange?: string,
digest?: StreamDigest
): OutgoingHttpHeaders {
const requestOptions: OutgoingHttpHeaders = {}
contentRange?: string
): IHeaders {
const requestOptions: IHeaders = {}
requestOptions['Accept'] = `application/json;api-version=${getApiVersion()}`
if (contentType) {
requestOptions['Content-Type'] = contentType
@@ -204,10 +202,6 @@ export function getUploadHeaders(
if (contentRange) {
requestOptions['Content-Range'] = contentRange
}
if (digest) {
requestOptions['x-actions-results-crc64'] = digest.crc64
requestOptions['x-actions-results-md5'] = digest.md5
}
return requestOptions
}
@@ -233,7 +227,7 @@ export function getArtifactUrl(): string {
* Certain information such as the TLSSocket and the Readable state are not really useful for diagnostic purposes so they can be avoided.
* Other information such as the headers, the response code and message might be useful, so this is displayed.
*/
export function displayHttpDiagnostics(response: HttpClientResponse): void {
export function displayHttpDiagnostics(response: IHttpClientResponse): void {
info(
`##### Begin Diagnostic HTTP information #####
Status Code: ${response.message.statusCode}
@@ -297,29 +291,3 @@ export function getProperRetention(
export async function sleep(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
export interface StreamDigest {
crc64: string
md5: string
}
export async function digestForStream(
stream: NodeJS.ReadableStream
): Promise<StreamDigest> {
return new Promise((resolve, reject) => {
const crc64 = new CRC64()
const md5 = crypto.createHash('md5')
stream
.on('data', data => {
crc64.update(data)
md5.update(data)
})
.on('end', () =>
resolve({
crc64: crc64.digest('base64') as string,
md5: md5.digest('base64')
})
)
.on('error', reject)
})
}
-15
View File
@@ -56,18 +56,3 @@
### 2.0.0
- Added support to check if Actions cache service feature is available or not [#1028](https://github.com/actions/toolkit/pull/1028)
### 2.0.3
- Update to v2.0.0 of `@actions/http-client`
### 2.0.4
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 2.0.5
- Fix to avoid saving empty cache when no files are available for caching. ([issue](https://github.com/actions/cache/issues/624))
### 2.0.6
- Fix `Tar failed with error: The process '/usr/bin/tar' failed with exit code 1` issue when temp directory where tar is getting created is actually the subdirectory of the path mentioned by the user for caching. ([issue](https://github.com/actions/cache/issues/689))
### 3.0.0
- Updated actions/cache to suppress Actions cache server error and log warning for those error [#1122](https://github.com/actions/toolkit/pull/1122)
+2 -6
View File
@@ -73,17 +73,13 @@ test('restore with no cache found', async () => {
test('restore with server error should fail', async () => {
const paths = ['node_modules']
const key = 'node-test'
const logWarningMock = jest.spyOn(core, 'warning')
jest.spyOn(cacheHttpClient, 'getCacheEntry').mockImplementation(() => {
throw new Error('HTTP Error Occurred')
})
const cacheKey = await restoreCache(paths, key)
expect(cacheKey).toBe(undefined)
expect(logWarningMock).toHaveBeenCalledTimes(1)
expect(logWarningMock).toHaveBeenCalledWith(
'Failed to restore: HTTP Error Occurred'
await expect(restoreCache(paths, key)).rejects.toThrowError(
'HTTP Error Occurred'
)
})
+15 -46
View File
@@ -5,7 +5,7 @@ import * as cacheHttpClient from '../src/internal/cacheHttpClient'
import * as cacheUtils from '../src/internal/cacheUtils'
import {CacheFilename, CompressionMethod} from '../src/internal/constants'
import * as tar from '../src/internal/tar'
import {TypedResponse} from '@actions/http-client/lib/interfaces'
import {ITypedResponse} from '@actions/http-client/interfaces'
import {
ReserveCacheResponse,
ITypedResponseWithError
@@ -48,7 +48,6 @@ test('save with large cache outputs should fail', async () => {
const cachePaths = [path.resolve(filePath)]
const createTarMock = jest.spyOn(tar, 'createTar')
const logWarningMock = jest.spyOn(core, 'warning')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
@@ -59,11 +58,8 @@ test('save with large cache outputs should fail', async () => {
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValueOnce(Promise.resolve(compression))
const cacheId = await saveCache([filePath], primaryKey)
expect(cacheId).toBe(-1)
expect(logWarningMock).toHaveBeenCalledTimes(1)
expect(logWarningMock).toHaveBeenCalledWith(
'Failed to save: Cache size of ~11264 MB (11811160064 B) is over the 10GB limit, not saving cache.'
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'Cache size of ~11264 MB (11811160064 B) is over the 10GB limit, not saving cache.'
)
const archiveFolder = '/foo/bar'
@@ -83,7 +79,6 @@ test('save with large cache outputs should fail in GHES with error message', asy
const cachePaths = [path.resolve(filePath)]
const createTarMock = jest.spyOn(tar, 'createTar')
const logWarningMock = jest.spyOn(core, 'warning')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
@@ -111,11 +106,8 @@ test('save with large cache outputs should fail in GHES with error message', asy
return response
})
const cacheId = await saveCache([filePath], primaryKey)
expect(cacheId).toBe(-1)
expect(logWarningMock).toHaveBeenCalledTimes(1)
expect(logWarningMock).toHaveBeenCalledWith(
'Failed to save: The cache filesize must be between 0 and 1073741824 bytes'
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'The cache filesize must be between 0 and 1073741824 bytes'
)
const archiveFolder = '/foo/bar'
@@ -135,7 +127,6 @@ test('save with large cache outputs should fail in GHES without error message',
const cachePaths = [path.resolve(filePath)]
const createTarMock = jest.spyOn(tar, 'createTar')
const logWarningMock = jest.spyOn(core, 'warning')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
@@ -159,11 +150,8 @@ test('save with large cache outputs should fail in GHES without error message',
return response
})
const cacheId = await saveCache([filePath], primaryKey)
expect(cacheId).toBe(-1)
expect(logWarningMock).toHaveBeenCalledTimes(1)
expect(logWarningMock).toHaveBeenCalledWith(
'Failed to save: Cache size of ~11264 MB (11811160064 B) is over the data cap limit, not saving cache.'
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'Cache size of ~11264 MB (11811160064 B) is over the data cap limit, not saving cache.'
)
const archiveFolder = '/foo/bar'
@@ -180,12 +168,11 @@ test('save with large cache outputs should fail in GHES without error message',
test('save with reserve cache failure should fail', async () => {
const paths = ['node_modules']
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const logInfoMock = jest.spyOn(core, 'info')
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: TypedResponse<ReserveCacheResponse> = {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: null,
headers: {}
@@ -200,13 +187,9 @@ test('save with reserve cache failure should fail', async () => {
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValueOnce(Promise.resolve(compression))
const cacheId = await saveCache(paths, primaryKey)
expect(cacheId).toBe(-1)
expect(logInfoMock).toHaveBeenCalledTimes(1)
expect(logInfoMock).toHaveBeenCalledWith(
`Failed to save: Unable to reserve cache with key ${primaryKey}, another job may be creating this cache. More details: undefined`
await expect(saveCache(paths, primaryKey)).rejects.toThrowError(
`Unable to reserve cache with key ${primaryKey}, another job may be creating this cache.`
)
expect(reserveCacheMock).toHaveBeenCalledTimes(1)
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, paths, {
compressionMethod: compression
@@ -220,12 +203,12 @@ test('save with server error should fail', async () => {
const filePath = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const cachePaths = [path.resolve(filePath)]
const logWarningMock = jest.spyOn(core, 'warning')
const cacheId = 4
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: TypedResponse<ReserveCacheResponse> = {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: {cacheId},
headers: {}
@@ -245,12 +228,9 @@ test('save with server error should fail', async () => {
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValueOnce(Promise.resolve(compression))
await saveCache([filePath], primaryKey)
expect(logWarningMock).toHaveBeenCalledTimes(1)
expect(logWarningMock).toHaveBeenCalledWith(
'Failed to save: HTTP Error Occurred'
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'HTTP Error Occurred'
)
expect(reserveCacheMock).toHaveBeenCalledTimes(1)
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], {
compressionMethod: compression
@@ -277,7 +257,7 @@ test('save with valid inputs uploads a cache', async () => {
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: TypedResponse<ReserveCacheResponse> = {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: {cacheId},
headers: {}
@@ -310,14 +290,3 @@ test('save with valid inputs uploads a cache', async () => {
expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
})
test('save with non existing path should not save cache', async () => {
const path = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
jest.spyOn(cacheUtils, 'resolvePaths').mockImplementation(async () => {
return []
})
await expect(saveCache([path], primaryKey)).rejects.toThrowError(
`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`
)
})
-4
View File
@@ -143,8 +143,6 @@ test('zstd create tar', async () => {
'zstd -T0 --long=30',
'-cf',
IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd,
'--exclude',
IS_WINDOWS ? CacheFilename.Zstd.replace(/\\/g, '/') : CacheFilename.Zstd,
'-P',
'-C',
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
@@ -182,8 +180,6 @@ test('gzip create tar', async () => {
'-z',
'-cf',
IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip,
'--exclude',
IS_WINDOWS ? CacheFilename.Gzip.replace(/\\/g, '/') : CacheFilename.Gzip,
'-P',
'-C',
IS_WINDOWS ? workspace?.replace(/\\/g, '/') : workspace,
+38 -23
View File
@@ -1,18 +1,18 @@
{
"name": "@actions/cache",
"version": "3.0.0",
"version": "2.0.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/cache",
"version": "3.0.0",
"version": "2.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.1",
"@actions/glob": "^0.1.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.9",
"@actions/io": "^1.0.1",
"@azure/ms-rest-js": "^2.6.0",
"@azure/storage-blob": "^12.8.0",
@@ -31,9 +31,9 @@
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"dependencies": {
"@actions/io": "^1.0.1"
}
@@ -48,17 +48,25 @@
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz",
"integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"node_modules/@actions/http-client/node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
},
"node_modules/@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
},
"node_modules/@azure/abort-controller": {
"version": "1.0.4",
@@ -610,9 +618,9 @@
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.4.tgz",
"integrity": "sha512-4DPChWow9yc9W3WqEbUj8Nr86xkpyE29ZzWjXucHItclLbEW6jr80Zx4nqv18QL6KK65+cifiQZXvnqgTV6oHw==",
"requires": {
"@actions/io": "^1.0.1"
}
@@ -627,17 +635,24 @@
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.9.tgz",
"integrity": "sha512-0O4SsJ7q+MK0ycvXPl2e6bMXV7dxAXOGjrXS1eTF9s2S401Tp6c/P3c3Joz04QefC1J6Gt942Wl2jbm3f4mLcg==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
},
"dependencies": {
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
}
}
},
"@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.0.2.tgz",
"integrity": "sha512-J8KuFqVPr3p6U8W93DOXlXW6zFvrQAJANdS+vw0YhusLIq+bszW8zmK2Fh1C2kDPX8FMvwIl1OUcFgvJoXLbAg=="
},
"@azure/abort-controller": {
"version": "1.0.4",
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "3.0.0",
"version": "2.0.2",
"preview": true,
"description": "Actions cache lib",
"keywords": [
@@ -40,7 +40,7 @@
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.1",
"@actions/glob": "^0.1.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.9",
"@actions/io": "^1.0.1",
"@azure/ms-rest-js": "^2.6.0",
"@azure/storage-blob": "^12.8.0",
@@ -48,8 +48,8 @@
"uuid": "^3.3.3"
},
"devDependencies": {
"typescript": "^3.8.3",
"@types/semver": "^6.0.0",
"@types/uuid": "^3.4.5",
"typescript": "^3.8.3"
"@types/uuid": "^3.4.5"
}
}
+18 -44
View File
@@ -86,24 +86,23 @@ export async function restoreCache(
}
const compressionMethod = await utils.getCompressionMethod()
let archivePath = ''
// path are needed to compute version
const cacheEntry = await cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
})
if (!cacheEntry?.archiveLocation) {
// Cache not found
return undefined
}
const archivePath = path.join(
await utils.createTempDirectory(),
utils.getCacheFileName(compressionMethod)
)
core.debug(`Archive Path: ${archivePath}`)
try {
// path are needed to compute version
const cacheEntry = await cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod
})
if (!cacheEntry?.archiveLocation) {
// Cache not found
return undefined
}
archivePath = path.join(
await utils.createTempDirectory(),
utils.getCacheFileName(compressionMethod)
)
core.debug(`Archive Path: ${archivePath}`)
// Download the cache from the cache entry
await cacheHttpClient.downloadCache(
cacheEntry.archiveLocation,
@@ -124,16 +123,6 @@ export async function restoreCache(
await extractTar(archivePath, compressionMethod)
core.info('Cache restored successfully')
return cacheEntry.cacheKey
} catch (error) {
const typedError = error as Error
if (typedError.name === ValidationError.name) {
throw error
} else {
// Supress all non-validation cache related errors because caching should be optional
core.warning(`Failed to restore: ${(error as Error).message}`)
}
} finally {
// Try to delete the archive to save space
try {
@@ -143,7 +132,7 @@ export async function restoreCache(
}
}
return undefined
return cacheEntry.cacheKey
}
/**
@@ -163,18 +152,12 @@ export async function saveCache(
checkKey(key)
const compressionMethod = await utils.getCompressionMethod()
let cacheId = -1
let cacheId = null
const cachePaths = await utils.resolvePaths(paths)
core.debug('Cache Paths:')
core.debug(`${JSON.stringify(cachePaths)}`)
if (cachePaths.length === 0) {
throw new Error(
`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`
)
}
const archiveFolder = await utils.createTempDirectory()
const archivePath = path.join(
archiveFolder,
@@ -228,15 +211,6 @@ export async function saveCache(
core.debug(`Saving Cache (ID: ${cacheId})`)
await cacheHttpClient.saveCache(cacheId, archivePath, options)
} catch (error) {
const typedError = error as Error
if (typedError.name === ValidationError.name) {
throw error
} else if (typedError.name === ReserveCacheError.name) {
core.info(`Failed to save: ${typedError.message}`)
} else {
core.warning(`Failed to save: ${typedError.message}`)
}
} finally {
// Try to delete the archive to save space
try {
+5 -8
View File
@@ -1,10 +1,7 @@
import * as core from '@actions/core'
import {HttpClient} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {
RequestOptions,
TypedResponse
} from '@actions/http-client/lib/interfaces'
import {BearerCredentialHandler} from '@actions/http-client/auth'
import {IRequestOptions, ITypedResponse} from '@actions/http-client/interfaces'
import * as crypto from 'crypto'
import * as fs from 'fs'
import {URL} from 'url'
@@ -49,8 +46,8 @@ function createAcceptHeader(type: string, apiVersion: string): string {
return `${type};api-version=${apiVersion}`
}
function getRequestOptions(): RequestOptions {
const requestOptions: RequestOptions = {
function getRequestOptions(): IRequestOptions {
const requestOptions: IRequestOptions = {
headers: {
Accept: createAcceptHeader('application/json', '6.0-preview.1')
}
@@ -278,7 +275,7 @@ async function commitCache(
httpClient: HttpClient,
cacheId: number,
filesize: number
): Promise<TypedResponse<null>> {
): Promise<ITypedResponse<null>> {
const commitCacheRequest: CommitCacheRequest = {size: filesize}
return await retryTypedResponse('commitCache', async () =>
httpClient.postJson<null>(
+2 -2
View File
@@ -1,8 +1,8 @@
import {CompressionMethod} from './constants'
import {TypedResponse} from '@actions/http-client/lib/interfaces'
import {ITypedResponse} from '@actions/http-client/interfaces'
import {HttpClientError} from '@actions/http-client'
export interface ITypedResponseWithError<T> extends TypedResponse<T> {
export interface ITypedResponseWithError<T> extends ITypedResponse<T> {
error?: HttpClientError
}
+3 -2
View File
@@ -1,5 +1,6 @@
import * as core from '@actions/core'
import {HttpClient, HttpClientResponse} from '@actions/http-client'
import {HttpClient} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {BlockBlobClient} from '@azure/storage-blob'
import {TransferProgressEvent} from '@azure/ms-rest-js'
import * as buffer from 'buffer'
@@ -19,7 +20,7 @@ import {retryHttpClientResponse} from './requestUtils'
* @param output the writable stream
*/
async function pipeResponseToStream(
response: HttpClientResponse,
response: IHttpClientResponse,
output: NodeJS.WritableStream
): Promise<void> {
const pipeline = util.promisify(stream.pipeline)
+6 -9
View File
@@ -1,9 +1,6 @@
import * as core from '@actions/core'
import {
HttpCodes,
HttpClientError,
HttpClientResponse
} from '@actions/http-client'
import {HttpCodes, HttpClientError} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {DefaultRetryDelay, DefaultRetryAttempts} from './constants'
import {ITypedResponseWithError} from './contracts'
@@ -106,7 +103,7 @@ export async function retryTypedResponse<T>(
maxAttempts,
delay,
// If the error object contains the statusCode property, extract it and return
// an TypedResponse<T> so it can be processed by the retry logic.
// an ITypedResponse<T> so it can be processed by the retry logic.
(error: Error) => {
if (error instanceof HttpClientError) {
return {
@@ -124,14 +121,14 @@ export async function retryTypedResponse<T>(
export async function retryHttpClientResponse(
name: string,
method: () => Promise<HttpClientResponse>,
method: () => Promise<IHttpClientResponse>,
maxAttempts = DefaultRetryAttempts,
delay = DefaultRetryDelay
): Promise<HttpClientResponse> {
): Promise<IHttpClientResponse> {
return await retry(
name,
method,
(response: HttpClientResponse) => response.message.statusCode,
(response: IHttpClientResponse) => response.message.statusCode,
maxAttempts,
delay
)
+9 -10
View File
@@ -61,15 +61,15 @@ export async function extractTar(
// Create directory to extract tar into
const workingDirectory = getWorkingDirectory()
await io.mkdirP(workingDirectory)
// --decompress: Decompress.
// --d: Decompress.
// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
// Using 30 here because we also support 32-bit self-hosted runners.
function getCompressionProgram(): string[] {
switch (compressionMethod) {
case CompressionMethod.Zstd:
return ['--use-compress-program', 'zstd --decompress --long=30']
return ['--use-compress-program', 'zstd -d --long=30']
case CompressionMethod.ZstdWithoutLong:
return ['--use-compress-program', 'zstd --decompress']
return ['--use-compress-program', 'zstd -d']
default:
return ['-z']
}
@@ -99,16 +99,16 @@ export async function createTar(
)
const workingDirectory = getWorkingDirectory()
// --threads=#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
// -T#: Compress using # working thread. If # is 0, attempt to detect and use the number of physical CPU cores.
// --long=#: Enables long distance matching with # bits. Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
// Using 30 here because we also support 32-bit self-hosted runners.
// Long range mode is added to zstd in v1.3.2 release, so we will not use --long in older version of zstd.
function getCompressionProgram(): string[] {
switch (compressionMethod) {
case CompressionMethod.Zstd:
return ['--use-compress-program', 'zstd --threads=0 --long=30']
return ['--use-compress-program', 'zstd -T0 --long=30']
case CompressionMethod.ZstdWithoutLong:
return ['--use-compress-program', 'zstd --threads=0']
return ['--use-compress-program', 'zstd -T0']
default:
return ['-z']
}
@@ -118,8 +118,6 @@ export async function createTar(
...getCompressionProgram(),
'-cf',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'--exclude',
cacheFileName.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
'-P',
'-C',
workingDirectory.replace(new RegExp(`\\${path.sep}`, 'g'), '/'),
@@ -133,15 +131,16 @@ export async function listTar(
archivePath: string,
compressionMethod: CompressionMethod
): Promise<void> {
// --d: Decompress.
// --long=#: Enables long distance matching with # bits.
// Maximum is 30 (1GB) on 32-bit OS and 31 (2GB) on 64-bit.
// Using 30 here because we also support 32-bit self-hosted runners.
function getCompressionProgram(): string[] {
switch (compressionMethod) {
case CompressionMethod.Zstd:
return ['--use-compress-program', 'zstd --long=30']
return ['--use-compress-program', 'zstd -d --long=30']
case CompressionMethod.ZstdWithoutLong:
return ['--use-compress-program', 'zstd']
return ['--use-compress-program', 'zstd -d']
default:
return ['-z']
}
+1 -24
View File
@@ -309,27 +309,4 @@ outputs:
runs:
using: 'node12'
main: 'dist/index.js'
```
#### Filesystem path helpers
You can use these methods to manipulate file paths across operating systems.
The `toPosixPath` function converts input paths to Posix-style (Linux) paths.
The `toWin32Path` function converts input paths to Windows-style paths. These
functions work independently of the underlying runner operating system.
```js
toPosixPath('\\foo\\bar') // => /foo/bar
toWin32Path('/foo/bar') // => \foo\bar
```
The `toPlatformPath` function converts input paths to the expected value on the runner's operating system.
```js
// On a Windows runner.
toPlatformPath('/foo/bar') // => \foo\bar
// On a Linux runner.
toPlatformPath('\\foo\\bar') // => /foo/bar
```
```
-14
View File
@@ -1,19 +1,5 @@
# @actions/core Releases
### 1.9.0
- Added `toPosixPath`, `toWin32Path` and `toPlatformPath` utilities [#1102](https://github.com/actions/toolkit/pull/1102)
### 1.8.2
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 1.8.1
- Update to v2.0.0 of `@actions/http-client`
### 1.8.0
- Deprecate `markdownSummary` extension export in favor of `summary`
- https://github.com/actions/toolkit/pull/1072
- https://github.com/actions/toolkit/pull/1073
### 1.7.0
- [Added `markdownSummary` extension](https://github.com/actions/toolkit/pull/1014)
@@ -1,10 +1,9 @@
import * as fs from 'fs'
import * as os from 'os'
import path from 'path'
import {summary, SUMMARY_ENV_VAR} from '../src/summary'
import {markdownSummary, SUMMARY_ENV_VAR} from '../src/markdown-summary'
const testDirectoryPath = path.join(__dirname, 'test')
const testFilePath = path.join(testDirectoryPath, 'test-summary.md')
const testFilePath = path.join(__dirname, 'test', 'test-summary.md')
async function assertSummary(expected: string): Promise<void> {
const file = await fs.promises.readFile(testFilePath, {encoding: 'utf8'})
@@ -68,12 +67,11 @@ const fixtures = {
}
}
describe('@actions/core/src/summary', () => {
describe('@actions/core/src/markdown-summary', () => {
beforeEach(async () => {
process.env[SUMMARY_ENV_VAR] = testFilePath
await fs.promises.mkdir(testDirectoryPath, {recursive: true})
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
summary.emptyBuffer()
markdownSummary.emptyBuffer()
})
afterAll(async () => {
@@ -82,39 +80,39 @@ describe('@actions/core/src/summary', () => {
it('throws if summary env var is undefined', async () => {
process.env[SUMMARY_ENV_VAR] = undefined
const write = summary.addRaw(fixtures.text).write()
const write = markdownSummary.addRaw(fixtures.text).write()
await expect(write).rejects.toThrow()
})
it('throws if summary file does not exist', async () => {
await fs.promises.unlink(testFilePath)
const write = summary.addRaw(fixtures.text).write()
const write = markdownSummary.addRaw(fixtures.text).write()
await expect(write).rejects.toThrow()
})
it('appends text to summary file', async () => {
await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write()
await markdownSummary.addRaw(fixtures.text).write()
await assertSummary(`# ${fixtures.text}`)
})
it('overwrites text to summary file', async () => {
await fs.promises.writeFile(testFilePath, 'overwrite', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write({overwrite: true})
await markdownSummary.addRaw(fixtures.text).write({overwrite: true})
await assertSummary(fixtures.text)
})
it('appends text with EOL to summary file', async () => {
await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'})
await summary.addRaw(fixtures.text, true).write()
await markdownSummary.addRaw(fixtures.text, true).write()
await assertSummary(`# ${fixtures.text}${os.EOL}`)
})
it('chains appends text to summary file', async () => {
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
await summary
await markdownSummary
.addRaw(fixtures.text)
.addRaw(fixtures.text)
.addRaw(fixtures.text)
@@ -124,33 +122,33 @@ describe('@actions/core/src/summary', () => {
it('empties buffer after write', async () => {
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
await summary.addRaw(fixtures.text).write()
await markdownSummary.addRaw(fixtures.text).write()
await assertSummary(fixtures.text)
expect(summary.isEmptyBuffer()).toBe(true)
expect(markdownSummary.isEmptyBuffer()).toBe(true)
})
it('returns summary buffer as string', () => {
summary.addRaw(fixtures.text)
expect(summary.stringify()).toEqual(fixtures.text)
markdownSummary.addRaw(fixtures.text)
expect(markdownSummary.stringify()).toEqual(fixtures.text)
})
it('return correct values for isEmptyBuffer', () => {
summary.addRaw(fixtures.text)
expect(summary.isEmptyBuffer()).toBe(false)
markdownSummary.addRaw(fixtures.text)
expect(markdownSummary.isEmptyBuffer()).toBe(false)
summary.emptyBuffer()
expect(summary.isEmptyBuffer()).toBe(true)
markdownSummary.emptyBuffer()
expect(markdownSummary.isEmptyBuffer()).toBe(true)
})
it('clears a buffer and summary file', async () => {
await fs.promises.writeFile(testFilePath, 'content', {encoding: 'utf8'})
await summary.clear()
await markdownSummary.clear()
await assertSummary('')
expect(summary.isEmptyBuffer()).toBe(true)
expect(markdownSummary.isEmptyBuffer()).toBe(true)
})
it('adds EOL', async () => {
await summary
await markdownSummary
.addRaw(fixtures.text)
.addEOL()
.write()
@@ -158,37 +156,37 @@ describe('@actions/core/src/summary', () => {
})
it('adds a code block without language', async () => {
await summary.addCodeBlock(fixtures.code).write()
await markdownSummary.addCodeBlock(fixtures.code).write()
const expected = `<pre><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}`
await assertSummary(expected)
})
it('adds a code block with a language', async () => {
await summary.addCodeBlock(fixtures.code, 'go').write()
await markdownSummary.addCodeBlock(fixtures.code, 'go').write()
const expected = `<pre lang="go"><code>func fork() {\n for {\n go fork()\n }\n}</code></pre>${os.EOL}`
await assertSummary(expected)
})
it('adds an unordered list', async () => {
await summary.addList(fixtures.list).write()
await markdownSummary.addList(fixtures.list).write()
const expected = `<ul><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ul>${os.EOL}`
await assertSummary(expected)
})
it('adds an ordered list', async () => {
await summary.addList(fixtures.list, true).write()
await markdownSummary.addList(fixtures.list, true).write()
const expected = `<ol><li>foo</li><li>bar</li><li>baz</li><li>💣</li></ol>${os.EOL}`
await assertSummary(expected)
})
it('adds a table', async () => {
await summary.addTable(fixtures.table).write()
await markdownSummary.addTable(fixtures.table).write()
const expected = `<table><tr><th>foo</th><th>bar</th><th>baz</th><td rowspan="3">tall</td></tr><tr><td>one</td><td>two</td><td>three</td></tr><tr><td colspan="3">wide</td></tr></table>${os.EOL}`
await assertSummary(expected)
})
it('adds a details element', async () => {
await summary
await markdownSummary
.addDetails(fixtures.details.label, fixtures.details.content)
.write()
const expected = `<details><summary>open me</summary>🎉 surprise</details>${os.EOL}`
@@ -196,13 +194,13 @@ describe('@actions/core/src/summary', () => {
})
it('adds an image with alt text', async () => {
await summary.addImage(fixtures.img.src, fixtures.img.alt).write()
await markdownSummary.addImage(fixtures.img.src, fixtures.img.alt).write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo">${os.EOL}`
await assertSummary(expected)
})
it('adds an image with custom dimensions', async () => {
await summary
await markdownSummary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}`
@@ -210,7 +208,7 @@ describe('@actions/core/src/summary', () => {
})
it('adds an image with custom dimensions', async () => {
await summary
await markdownSummary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write()
const expected = `<img src="https://github.com/actions.png" alt="actions logo" width="32" height="32">${os.EOL}`
@@ -219,21 +217,21 @@ describe('@actions/core/src/summary', () => {
it('adds headings h1...h6', async () => {
for (const i of [1, 2, 3, 4, 5, 6]) {
summary.addHeading('heading', i)
markdownSummary.addHeading('heading', i)
}
await summary.write()
await markdownSummary.write()
const expected = `<h1>heading</h1>${os.EOL}<h2>heading</h2>${os.EOL}<h3>heading</h3>${os.EOL}<h4>heading</h4>${os.EOL}<h5>heading</h5>${os.EOL}<h6>heading</h6>${os.EOL}`
await assertSummary(expected)
})
it('adds h1 if heading level not specified', async () => {
await summary.addHeading('heading').write()
await markdownSummary.addHeading('heading').write()
const expected = `<h1>heading</h1>${os.EOL}`
await assertSummary(expected)
})
it('uses h1 if heading level is garbage or out of range', async () => {
await summary
await markdownSummary
.addHeading('heading', 'foobar')
.addHeading('heading', 1337)
.addHeading('heading', -1)
@@ -244,31 +242,35 @@ describe('@actions/core/src/summary', () => {
})
it('adds a separator', async () => {
await summary.addSeparator().write()
await markdownSummary.addSeparator().write()
const expected = `<hr>${os.EOL}`
await assertSummary(expected)
})
it('adds a break', async () => {
await summary.addBreak().write()
await markdownSummary.addBreak().write()
const expected = `<br>${os.EOL}`
await assertSummary(expected)
})
it('adds a quote', async () => {
await summary.addQuote(fixtures.quote.text).write()
await markdownSummary.addQuote(fixtures.quote.text).write()
const expected = `<blockquote>Where the world builds software</blockquote>${os.EOL}`
await assertSummary(expected)
})
it('adds a quote with citation', async () => {
await summary.addQuote(fixtures.quote.text, fixtures.quote.cite).write()
await markdownSummary
.addQuote(fixtures.quote.text, fixtures.quote.cite)
.write()
const expected = `<blockquote cite="https://github.com/about">Where the world builds software</blockquote>${os.EOL}`
await assertSummary(expected)
})
it('adds a link with href', async () => {
await summary.addLink(fixtures.link.text, fixtures.link.href).write()
await markdownSummary
.addLink(fixtures.link.text, fixtures.link.href)
.write()
const expected = `<a href="https://github.com/">GitHub</a>${os.EOL}`
await assertSummary(expected)
})
-162
View File
@@ -1,162 +0,0 @@
import * as path from 'path'
import {toPlatformPath, toPosixPath, toWin32Path} from '../src/path-utils'
describe('#toPosixPath', () => {
const cases: {
only?: boolean
name: string
input: string
expected: string
}[] = [
{
name: 'empty string',
input: '',
expected: ''
},
{
name: 'single value',
input: 'foo',
expected: 'foo'
},
{
name: 'with posix relative',
input: 'foo/bar/baz',
expected: 'foo/bar/baz'
},
{
name: 'with posix absolute',
input: '/foo/bar/baz',
expected: '/foo/bar/baz'
},
{
name: 'with win32 relative',
input: 'foo\\bar\\baz',
expected: 'foo/bar/baz'
},
{
name: 'with win32 absolute',
input: '\\foo\\bar\\baz',
expected: '/foo/bar/baz'
},
{
name: 'with a mix',
input: '\\foo/bar/baz',
expected: '/foo/bar/baz'
}
]
for (const tc of cases) {
const fn = tc.only ? it.only : it
fn(tc.name, () => {
const result = toPosixPath(tc.input)
expect(result).toEqual(tc.expected)
})
}
})
describe('#toWin32Path', () => {
const cases: {
only?: boolean
name: string
input: string
expected: string
}[] = [
{
name: 'empty string',
input: '',
expected: ''
},
{
name: 'single value',
input: 'foo',
expected: 'foo'
},
{
name: 'with posix relative',
input: 'foo/bar/baz',
expected: 'foo\\bar\\baz'
},
{
name: 'with posix absolute',
input: '/foo/bar/baz',
expected: '\\foo\\bar\\baz'
},
{
name: 'with win32 relative',
input: 'foo\\bar\\baz',
expected: 'foo\\bar\\baz'
},
{
name: 'with win32 absolute',
input: '\\foo\\bar\\baz',
expected: '\\foo\\bar\\baz'
},
{
name: 'with a mix',
input: '\\foo/bar\\baz',
expected: '\\foo\\bar\\baz'
}
]
for (const tc of cases) {
const fn = tc.only ? it.only : it
fn(tc.name, () => {
const result = toWin32Path(tc.input)
expect(result).toEqual(tc.expected)
})
}
})
describe('#toPlatformPath', () => {
const cases: {
only?: boolean
name: string
input: string
expected: string
}[] = [
{
name: 'empty string',
input: '',
expected: ''
},
{
name: 'single value',
input: 'foo',
expected: 'foo'
},
{
name: 'with posix relative',
input: 'foo/bar/baz',
expected: path.join('foo', 'bar', 'baz')
},
{
name: 'with posix absolute',
input: '/foo/bar/baz',
expected: path.join(path.sep, 'foo', 'bar', 'baz')
},
{
name: 'with win32 relative',
input: 'foo\\bar\\baz',
expected: path.join('foo', 'bar', 'baz')
},
{
name: 'with win32 absolute',
input: '\\foo\\bar\\baz',
expected: path.join(path.sep, 'foo', 'bar', 'baz')
},
{
name: 'with a mix',
input: '\\foo/bar\\baz',
expected: path.join(path.sep, 'foo', 'bar', 'baz')
}
]
for (const tc of cases) {
const fn = tc.only ? it.only : it
fn(tc.name, () => {
const result = toPlatformPath(tc.input)
expect(result).toEqual(tc.expected)
})
}
})
+11 -11
View File
@@ -1,26 +1,26 @@
{
"name": "@actions/core",
"version": "1.9.0",
"version": "1.7.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/core",
"version": "1.8.1",
"version": "1.6.0",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.0.1"
"@actions/http-client": "^1.0.11"
},
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"node_modules/@types/node": {
@@ -40,11 +40,11 @@
},
"dependencies": {
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"@types/node": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
"version": "1.9.0",
"version": "1.7.0",
"description": "Actions core lib",
"keywords": [
"github",
@@ -36,7 +36,7 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/http-client": "^2.0.1"
"@actions/http-client": "^1.0.11"
},
"devDependencies": {
"@types/node": "^12.0.2"
+2 -12
View File
@@ -361,16 +361,6 @@ export async function getIDToken(aud?: string): Promise<string> {
}
/**
* Summary exports
* Markdown summary exports
*/
export {summary} from './summary'
/**
* @deprecated use core.summary
*/
export {markdownSummary} from './summary'
/**
* Path exports
*/
export {toPosixPath, toWin32Path, toPlatformPath} from './path-utils'
export {markdownSummary} from './markdown-summary'
@@ -4,7 +4,7 @@ const {access, appendFile, writeFile} = promises
export const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'
export const SUMMARY_DOCS_URL =
'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'
'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary'
export type SummaryTableRow = (SummaryTableCell | string)[]
@@ -51,7 +51,7 @@ export interface SummaryWriteOptions {
overwrite?: boolean
}
class Summary {
class MarkdownSummary {
private _buffer: string
private _filePath?: string
@@ -73,7 +73,7 @@ class Summary {
const pathFromEnv = process.env[SUMMARY_ENV_VAR]
if (!pathFromEnv) {
throw new Error(
`Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`
`Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports markdown summaries.`
)
}
@@ -119,9 +119,9 @@ class Summary {
*
* @param {SummaryWriteOptions} [options] (optional) options for write operation
*
* @returns {Promise<Summary>} summary instance
* @returns {Promise<MarkdownSummary>} markdown summary instance
*/
async write(options?: SummaryWriteOptions): Promise<Summary> {
async write(options?: SummaryWriteOptions): Promise<MarkdownSummary> {
const overwrite = !!options?.overwrite
const filePath = await this.filePath()
const writeFunc = overwrite ? writeFile : appendFile
@@ -132,9 +132,9 @@ class Summary {
/**
* Clears the summary buffer and wipes the summary file
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
async clear(): Promise<Summary> {
async clear(): Promise<MarkdownSummary> {
return this.emptyBuffer().write({overwrite: true})
}
@@ -159,9 +159,9 @@ class Summary {
/**
* Resets the summary buffer without writing to summary file
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
emptyBuffer(): Summary {
emptyBuffer(): MarkdownSummary {
this._buffer = ''
return this
}
@@ -172,9 +172,9 @@ class Summary {
* @param {string} text content to add
* @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false)
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addRaw(text: string, addEOL = false): Summary {
addRaw(text: string, addEOL = false): MarkdownSummary {
this._buffer += text
return addEOL ? this.addEOL() : this
}
@@ -182,9 +182,9 @@ class Summary {
/**
* Adds the operating system-specific end-of-line marker to the buffer
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addEOL(): Summary {
addEOL(): MarkdownSummary {
return this.addRaw(EOL)
}
@@ -194,9 +194,9 @@ class Summary {
* @param {string} code content to render within fenced code block
* @param {string} lang (optional) language to syntax highlight code
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addCodeBlock(code: string, lang?: string): Summary {
addCodeBlock(code: string, lang?: string): MarkdownSummary {
const attrs = {
...(lang && {lang})
}
@@ -210,9 +210,9 @@ class Summary {
* @param {string[]} items list of items to render
* @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false)
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addList(items: string[], ordered = false): Summary {
addList(items: string[], ordered = false): MarkdownSummary {
const tag = ordered ? 'ol' : 'ul'
const listItems = items.map(item => this.wrap('li', item)).join('')
const element = this.wrap(tag, listItems)
@@ -224,9 +224,9 @@ class Summary {
*
* @param {SummaryTableCell[]} rows table rows
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addTable(rows: SummaryTableRow[]): Summary {
addTable(rows: SummaryTableRow[]): MarkdownSummary {
const tableBody = rows
.map(row => {
const cells = row
@@ -260,9 +260,9 @@ class Summary {
* @param {string} label text for the closed state
* @param {string} content collapsable content
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addDetails(label: string, content: string): Summary {
addDetails(label: string, content: string): MarkdownSummary {
const element = this.wrap('details', this.wrap('summary', label) + content)
return this.addRaw(element).addEOL()
}
@@ -274,9 +274,13 @@ class Summary {
* @param {string} alt text description of the image
* @param {SummaryImageOptions} options (optional) addition image attributes
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addImage(src: string, alt: string, options?: SummaryImageOptions): Summary {
addImage(
src: string,
alt: string,
options?: SummaryImageOptions
): MarkdownSummary {
const {width, height} = options || {}
const attrs = {
...(width && {width}),
@@ -293,9 +297,9 @@ class Summary {
* @param {string} text heading text
* @param {number | string} [level=1] (optional) the heading level, default: 1
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addHeading(text: string, level?: number | string): Summary {
addHeading(text: string, level?: number | string): MarkdownSummary {
const tag = `h${level}`
const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag)
? tag
@@ -307,9 +311,9 @@ class Summary {
/**
* Adds an HTML thematic break (<hr>) to the summary buffer
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addSeparator(): Summary {
addSeparator(): MarkdownSummary {
const element = this.wrap('hr', null)
return this.addRaw(element).addEOL()
}
@@ -317,9 +321,9 @@ class Summary {
/**
* Adds an HTML line break (<br>) to the summary buffer
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addBreak(): Summary {
addBreak(): MarkdownSummary {
const element = this.wrap('br', null)
return this.addRaw(element).addEOL()
}
@@ -330,9 +334,9 @@ class Summary {
* @param {string} text quote text
* @param {string} cite (optional) citation url
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addQuote(text: string, cite?: string): Summary {
addQuote(text: string, cite?: string): MarkdownSummary {
const attrs = {
...(cite && {cite})
}
@@ -346,18 +350,13 @@ class Summary {
* @param {string} text link text/content
* @param {string} href hyperlink
*
* @returns {Summary} summary instance
* @returns {MarkdownSummary} markdown summary instance
*/
addLink(text: string, href: string): Summary {
addLink(text: string, href: string): MarkdownSummary {
const element = this.wrap('a', text, {href})
return this.addRaw(element).addEOL()
}
}
const _summary = new Summary()
/**
* @deprecated use `core.summary`
*/
export const markdownSummary = _summary
export const summary = _summary
// singleton export
export const markdownSummary = new MarkdownSummary()
+3 -3
View File
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-extraneous-class */
import * as actions_http_client from '@actions/http-client'
import {RequestOptions} from '@actions/http-client/lib/interfaces'
import {IRequestOptions} from '@actions/http-client/interfaces'
import {HttpClient} from '@actions/http-client'
import {BearerCredentialHandler} from '@actions/http-client/lib/auth'
import {BearerCredentialHandler} from '@actions/http-client/auth'
import {debug, setSecret} from './core'
interface TokenResponse {
value?: string
@@ -13,7 +13,7 @@ export class OidcClient {
allowRetry = true,
maxRetry = 10
): actions_http_client.HttpClient {
const requestOptions: RequestOptions = {
const requestOptions: IRequestOptions = {
allowRetries: allowRetry,
maxRetries: maxRetry
}
-35
View File
@@ -1,35 +0,0 @@
import * as path from 'path'
/**
* toPosixPath converts the given path to the posix form. On Windows, \\ will be
* replaced with /.
*
* @param pth. Path to transform.
* @return string Posix path.
*/
export function toPosixPath(pth: string): string {
return pth.replace(/[\\]/g, '/')
}
/**
* toWin32Path converts the given path to the win32 form. On Linux, / will be
* replaced with \\.
*
* @param pth. Path to transform.
* @return string Win32 path.
*/
export function toWin32Path(pth: string): string {
return pth.replace(/[/]/g, '\\')
}
/**
* toPlatformPath converts the given path to a platform-specific path. It does
* this by replacing instances of / and \ with the platform-specific path
* separator.
*
* @param pth The path to platformize.
* @return string The platform-specific path.
*/
export function toPlatformPath(pth: string): string {
return pth.replace(/[/\\]/g, path.sep)
}
-6
View File
@@ -1,11 +1,5 @@
# @actions/github Releases
### 5.0.3
- - Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 5.0.2
- Update to v2.0.0 of `@actions/http-client`
### 5.0.1
- [Update Octokit Dependencies](https://github.com/actions/toolkit/pull/1037)
### 5.0.0
+11 -11
View File
@@ -1,15 +1,15 @@
{
"name": "@actions/github",
"version": "5.0.3",
"version": "5.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/github",
"version": "5.0.2",
"version": "5.0.1",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.11",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
@@ -19,11 +19,11 @@
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"node_modules/@octokit/auth-token": {
@@ -361,11 +361,11 @@
},
"dependencies": {
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"@octokit/auth-token": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
"version": "5.0.3",
"version": "5.0.1",
"description": "Actions github lib",
"keywords": [
"github",
@@ -38,7 +38,7 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.11",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
+7 -42
View File
@@ -6,7 +6,7 @@
"packages": {
"": {
"name": "@actions/glob",
"version": "0.3.0",
"version": "0.2.1",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
@@ -14,20 +14,9 @@
}
},
"node_modules/@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"dependencies": {
"@actions/http-client": "^1.0.11"
}
},
"node_modules/@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"dependencies": {
"tunnel": "0.0.6"
}
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"node_modules/balanced-match": {
"version": "1.0.0",
@@ -58,32 +47,13 @@
"engines": {
"node": "*"
}
},
"node_modules/tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
"engines": {
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
}
}
},
"dependencies": {
"@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"requires": {
"@actions/http-client": "^1.0.11"
}
},
"@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "0.0.6"
}
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"balanced-match": {
"version": "1.0.0",
@@ -111,11 +81,6 @@
"requires": {
"brace-expansion": "^1.1.7"
}
},
"tunnel": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
}
}
}
+3
View File
@@ -1,2 +1,5 @@
_out
node_modules
.DS_Store
testoutput.txt
npm-debug.log
+19 -13
View File
@@ -1,11 +1,18 @@
# `@actions/http-client`
A lightweight HTTP client optimized for building actions.
<p align="center">
<img src="actions.png">
</p>
# Actions Http-Client
[![Http Status](https://github.com/actions/http-client/workflows/http-tests/badge.svg)](https://github.com/actions/http-client/actions)
A lightweight HTTP client optimized for use with actions, TypeScript with generics and async await.
## Features
- HTTP client with TypeScript generics and async/await/Promises
- Typings included!
- Typings included so no need to acquire separately (great for intellisense and no versioning drift)
- [Proxy support](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners#using-a-proxy-server-with-self-hosted-runners) just works with actions and the runner
- Targets ES2019 (runner runs actions with node 12+). Only supported on node 12+.
- Basic, Bearer and PAT Support out of the box. Extensible handlers for others.
@@ -21,7 +28,7 @@ npm install @actions/http-client --save
## Samples
See the [tests](./__tests__) for detailed examples.
See the [HTTP](./__tests__) tests for detailed examples.
## Errors
@@ -32,13 +39,13 @@ The HTTP client does not throw unless truly exceptional.
* A request that successfully executes resulting in a 404, 500 etc... will return a response object with a status code and a body.
* Redirects (3xx) will be followed by default.
See the [tests](./__tests__) for detailed examples.
See [HTTP tests](./__tests__) for detailed examples.
## Debugging
To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
```shell
```
export NODE_DEBUG=http
```
@@ -56,18 +63,17 @@ We welcome PRs. Please create an issue and if applicable, a design before proce
once:
```
npm install
```bash
$ npm install
```
To build:
```
npm run build
```bash
$ npm run build
```
To run all tests:
```
npm test
```bash
$ npm test
```
-13
View File
@@ -1,18 +1,5 @@
## Releases
## 2.0.1
- Fix an issue with missing `tunnel` dependency [#1085](https://github.com/actions/toolkit/pull/1085)
## 2.0.0
- The package is now compiled with TypeScript's [`strict` compiler setting](https://www.typescriptlang.org/tsconfig#strict). To comply with stricter rules:
- Some exported types now include `| null` or `| undefined`, matching their actual behavior.
- Types implementing the method `RequestHandler.handleAuthentication()` now throw an `Error` rather than returning `null` if they do not support handling an HTTP 401 response. Callers can still use `canHandleAuthentication()` to determine if this handling is supported or not.
- Types using `any` have been scoped to more specific types.
- Following TypeScript's naming conventions, exported interfaces no longer begin with the prefix `I-`.
- Delete the `IHttpClientResponse` interface in favor of the `HttpClientResponse` class.
- Delete the `IHeaders` interface in favor of `http.OutgoingHttpHeaders`.
- The source code of the package was moved to build with [actions/toolkit](https://github.com/actions/toolkit).
## 1.0.11
Contains a bug fix where proxy is defined without a user and password. see [PR here](https://github.com/actions/http-client/pull/42)
+26 -38
View File
@@ -1,5 +1,5 @@
import * as httpm from '../lib'
import * as am from '../lib/auth'
import * as httpm from '../_out'
import * as am from '../_out/auth'
describe('auth', () => {
beforeEach(() => {})
@@ -7,21 +7,17 @@ describe('auth', () => {
afterEach(() => {})
it('does basic http get request with basic auth', async () => {
const bh: am.BasicCredentialHandler = new am.BasicCredentialHandler(
let bh: am.BasicCredentialHandler = new am.BasicCredentialHandler(
'johndoe',
'password'
)
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [
bh
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [bh])
let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
const auth: string = obj.headers.Authorization
const creds: string = Buffer.from(
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
let auth: string = obj.headers.Authorization
let creds: string = Buffer.from(
auth.substring('Basic '.length),
'base64'
).toString()
@@ -30,44 +26,36 @@ describe('auth', () => {
})
it('does basic http get request with pat token auth', async () => {
const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
const ph: am.PersonalAccessTokenCredentialHandler = new am.PersonalAccessTokenCredentialHandler(
let token: string = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
let ph: am.PersonalAccessTokenCredentialHandler = new am.PersonalAccessTokenCredentialHandler(
token
)
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [
ph
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ph])
let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
const auth: string = obj.headers.Authorization
const creds: string = Buffer.from(
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
let auth: string = obj.headers.Authorization
let creds: string = Buffer.from(
auth.substring('Basic '.length),
'base64'
).toString()
expect(creds).toBe(`PAT:${token}`)
expect(creds).toBe('PAT:' + token)
expect(obj.url).toBe('http://httpbin.org/get')
})
it('does basic http get request with pat token auth', async () => {
const token = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
const ph: am.BearerCredentialHandler = new am.BearerCredentialHandler(token)
let token: string = 'scbfb44vxzku5l4xgc3qfazn3lpk4awflfryc76esaiq7aypcbhs'
let ph: am.BearerCredentialHandler = new am.BearerCredentialHandler(token)
const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [
ph
])
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [ph])
let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
const auth: string = obj.headers.Authorization
expect(auth).toBe(`Bearer ${token}`)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
let auth: string = obj.headers.Authorization
expect(auth).toBe('Bearer ' + token)
expect(obj.url).toBe('http://httpbin.org/get')
})
})
+153 -152
View File
@@ -1,10 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as httpm from '..'
import * as httpm from '../_out'
import * as ifm from '../_out/interfaces'
import * as path from 'path'
import * as fs from 'fs'
const sampleFilePath: string = path.join(__dirname, 'testoutput.txt')
let sampleFilePath: string = path.join(__dirname, 'testoutput.txt')
interface HttpBinData {
url: string
@@ -24,7 +23,7 @@ describe('basics', () => {
afterEach(() => {})
it('constructs', () => {
const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
let http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
expect(http).toBeDefined()
})
@@ -40,259 +39,264 @@ describe('basics', () => {
// "url": "https://httpbin.org/get"
// }
it('does basic http get request', async () => {
const res: httpm.HttpClientResponse = await _http.get(
it('does basic http get request', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get')
expect(obj.headers['User-Agent']).toBeTruthy()
done()
})
it('does basic http get request with no user agent', async () => {
const http: httpm.HttpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
it('does basic http get request with no user agent', async done => {
let http: httpm.HttpClient = new httpm.HttpClient()
let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get')
expect(obj.headers['User-Agent']).toBeFalsy()
done()
})
it('does basic https get request', async () => {
const res: httpm.HttpClientResponse = await _http.get(
it('does basic https get request', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
done()
})
it('does basic http get request with default headers', async () => {
const http: httpm.HttpClient = new httpm.HttpClient(
'http-client-tests',
[],
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
it('does basic http get request with default headers', async done => {
let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [], {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
)
const res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get'
)
})
let res: httpm.HttpClientResponse = await http.get('http://httpbin.org/get')
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.headers.Accept).toBe('application/json')
expect(obj.headers['Content-Type']).toBe('application/json')
expect(obj.url).toBe('http://httpbin.org/get')
done()
})
it('does basic http get request with merged headers', async () => {
const http: httpm.HttpClient = new httpm.HttpClient(
'http-client-tests',
[],
{
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
it('does basic http get request with merged headers', async done => {
let http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [], {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
)
const res: httpm.HttpClientResponse = await http.get(
})
let res: httpm.HttpClientResponse = await http.get(
'http://httpbin.org/get',
{
'content-type': 'application/x-www-form-urlencoded'
}
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.headers.Accept).toBe('application/json')
expect(obj.headers['Content-Type']).toBe(
'application/x-www-form-urlencoded'
)
expect(obj.url).toBe('http://httpbin.org/get')
done()
})
it('pipes a get request', async () => {
return new Promise<void>(async resolve => {
const file = fs.createWriteStream(sampleFilePath)
it('pipes a get request', () => {
return new Promise<string>(async (resolve, reject) => {
let file: NodeJS.WritableStream = fs.createWriteStream(sampleFilePath)
;(await _http.get('https://httpbin.org/get')).message
.pipe(file)
.on('close', () => {
const body: string = fs.readFileSync(sampleFilePath).toString()
const obj = JSON.parse(body)
let body: string = fs.readFileSync(sampleFilePath).toString()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
resolve()
})
})
})
it('does basic get request with redirects', async () => {
const res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://httpbin.org/get'
)}`
it('does basic get request with redirects', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://httpbin.org/get')
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
done()
})
it('does basic get request with redirects (303)', async () => {
const res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://httpbin.org/get'
)}&status_code=303`
it('does basic get request with redirects (303)', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://httpbin.org/get') +
'&status_code=303'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
done()
})
it('returns 404 for not found get request on redirect', async () => {
const res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://httpbin.org/status/404'
)}&status_code=303`
it('returns 404 for not found get request on redirect', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://httpbin.org/status/404') +
'&status_code=303'
)
expect(res.message.statusCode).toBe(404)
await res.readBody()
let body: string = await res.readBody()
done()
})
it('does not follow redirects if disabled', async () => {
const http: httpm.HttpClient = new httpm.HttpClient(
it('does not follow redirects if disabled', async done => {
let http: httpm.HttpClient = new httpm.HttpClient(
'typed-test-client-tests',
undefined,
null,
{allowRedirects: false}
)
const res: httpm.HttpClientResponse = await http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://httpbin.org/get'
)}`
let res: httpm.HttpClientResponse = await http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://httpbin.org/get')
)
expect(res.message.statusCode).toBe(302)
await res.readBody()
let body: string = await res.readBody()
done()
})
it('does not pass auth with diff hostname redirects', async () => {
const headers = {
it('does not pass auth with diff hostname redirects', async done => {
let headers = {
accept: 'application/json',
authorization: 'shhh'
}
const res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://www.httpbin.org/get'
)}`,
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://www.httpbin.org/get'),
headers
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
// httpbin "fixes" the casing
expect(obj.headers['Accept']).toBe('application/json')
expect(obj.headers['Authorization']).toBeUndefined()
expect(obj.headers['authorization']).toBeUndefined()
expect(obj.url).toBe('https://www.httpbin.org/get')
done()
})
it('does not pass Auth with diff hostname redirects', async () => {
const headers = {
it('does not pass Auth with diff hostname redirects', async done => {
let headers = {
Accept: 'application/json',
Authorization: 'shhh'
}
const res: httpm.HttpClientResponse = await _http.get(
`https://httpbin.org/redirect-to?url=${encodeURIComponent(
'https://www.httpbin.org/get'
)}`,
let res: httpm.HttpClientResponse = await _http.get(
'https://httpbin.org/redirect-to?url=' +
encodeURIComponent('https://www.httpbin.org/get'),
headers
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
// httpbin "fixes" the casing
expect(obj.headers['Accept']).toBe('application/json')
expect(obj.headers['Authorization']).toBeUndefined()
expect(obj.headers['authorization']).toBeUndefined()
expect(obj.url).toBe('https://www.httpbin.org/get')
done()
})
it('does basic head request', async () => {
const res: httpm.HttpClientResponse = await _http.head(
it('does basic head request', async done => {
let res: httpm.HttpClientResponse = await _http.head(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
done()
})
it('does basic http delete request', async () => {
const res: httpm.HttpClientResponse = await _http.del(
it('does basic http delete request', async done => {
let res: httpm.HttpClientResponse = await _http.del(
'http://httpbin.org/delete'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
done()
})
it('does basic http post request', async () => {
const b = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.post(
it('does basic http post request', async done => {
let b: string = 'Hello World!'
let res: httpm.HttpClientResponse = await _http.post(
'http://httpbin.org/post',
b
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/post')
done()
})
it('does basic http patch request', async () => {
const b = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.patch(
it('does basic http patch request', async done => {
let b: string = 'Hello World!'
let res: httpm.HttpClientResponse = await _http.patch(
'http://httpbin.org/patch',
b
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/patch')
done()
})
it('does basic http options request', async () => {
const res: httpm.HttpClientResponse = await _http.options(
it('does basic http options request', async done => {
let res: httpm.HttpClientResponse = await _http.options(
'http://httpbin.org'
)
expect(res.message.statusCode).toBe(200)
await res.readBody()
let body: string = await res.readBody()
done()
})
it('returns 404 for not found get request', async () => {
const res: httpm.HttpClientResponse = await _http.get(
it('returns 404 for not found get request', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/status/404'
)
expect(res.message.statusCode).toBe(404)
await res.readBody()
let body: string = await res.readBody()
done()
})
it('gets a json object', async () => {
const jsonObj = await _http.getJson<HttpBinData>('https://httpbin.org/get')
let jsonObj: ifm.ITypedResponse<HttpBinData> = await _http.getJson<
HttpBinData
>('https://httpbin.org/get')
expect(jsonObj.statusCode).toBe(200)
expect(jsonObj.result).toBeDefined()
expect(jsonObj.result?.url).toBe('https://httpbin.org/get')
expect(jsonObj.result?.headers['Accept']).toBe(
expect(jsonObj.result.url).toBe('https://httpbin.org/get')
expect(jsonObj.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
@@ -301,27 +305,26 @@ describe('basics', () => {
})
it('getting a non existent json object returns null', async () => {
const jsonObj = await _http.getJson<HttpBinData>(
'https://httpbin.org/status/404'
)
let jsonObj: ifm.ITypedResponse<HttpBinData> = await _http.getJson<
HttpBinData
>('https://httpbin.org/status/404')
expect(jsonObj.statusCode).toBe(404)
expect(jsonObj.result).toBeNull()
})
it('posts a json object', async () => {
const res = {name: 'foo'}
const restRes = await _http.postJson<HttpBinData>(
'https://httpbin.org/post',
res
)
let res: any = {name: 'foo'}
let restRes: ifm.ITypedResponse<HttpBinData> = await _http.postJson<
HttpBinData
>('https://httpbin.org/post', res)
expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/post')
expect(restRes.result?.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe(
expect(restRes.result.url).toBe('https://httpbin.org/post')
expect(restRes.result.json.name).toBe('foo')
expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.result?.headers['Content-Type']).toBe(
expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.headers[httpm.Headers.ContentType]).toBe(
@@ -330,20 +333,19 @@ describe('basics', () => {
})
it('puts a json object', async () => {
const res = {name: 'foo'}
const restRes = await _http.putJson<HttpBinData>(
'https://httpbin.org/put',
res
)
let res: any = {name: 'foo'}
let restRes: ifm.ITypedResponse<HttpBinData> = await _http.putJson<
HttpBinData
>('https://httpbin.org/put', res)
expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/put')
expect(restRes.result?.json.name).toBe('foo')
expect(restRes.result.url).toBe('https://httpbin.org/put')
expect(restRes.result.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe(
expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.result?.headers['Content-Type']).toBe(
expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.headers[httpm.Headers.ContentType]).toBe(
@@ -352,19 +354,18 @@ describe('basics', () => {
})
it('patch a json object', async () => {
const res = {name: 'foo'}
const restRes = await _http.patchJson<HttpBinData>(
'https://httpbin.org/patch',
res
)
let res: any = {name: 'foo'}
let restRes: ifm.ITypedResponse<HttpBinData> = await _http.patchJson<
HttpBinData
>('https://httpbin.org/patch', res)
expect(restRes.statusCode).toBe(200)
expect(restRes.result).toBeDefined()
expect(restRes.result?.url).toBe('https://httpbin.org/patch')
expect(restRes.result?.json.name).toBe('foo')
expect(restRes.result?.headers['Accept']).toBe(
expect(restRes.result.url).toBe('https://httpbin.org/patch')
expect(restRes.result.json.name).toBe('foo')
expect(restRes.result.headers['Accept']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.result?.headers['Content-Type']).toBe(
expect(restRes.result.headers['Content-Type']).toBe(
httpm.MediaTypes.ApplicationJson
)
expect(restRes.headers[httpm.Headers.ContentType]).toBe(
+14 -15
View File
@@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as httpm from '..'
import * as httpm from '../_out'
import * as ifm from '../_out/interfaces'
describe('headers', () => {
let _http: httpm.HttpClient
@@ -10,8 +9,8 @@ describe('headers', () => {
})
it('preserves existing headers on getJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.getJson<any>(
let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj: ifm.ITypedResponse<any> = await _http.getJson<any>(
'https://httpbin.org/get',
additionalHeaders
)
@@ -20,7 +19,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson
)
const httpWithHeaders = new httpm.HttpClient()
let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = {
headers: {
[httpm.Headers.Accept]: 'baz'
@@ -34,8 +33,8 @@ describe('headers', () => {
})
it('preserves existing headers on postJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.postJson<any>(
let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj: ifm.ITypedResponse<any> = await _http.postJson<any>(
'https://httpbin.org/post',
{},
additionalHeaders
@@ -45,7 +44,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson
)
const httpWithHeaders = new httpm.HttpClient()
let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = {
headers: {
[httpm.Headers.Accept]: 'baz'
@@ -62,8 +61,8 @@ describe('headers', () => {
})
it('preserves existing headers on putJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.putJson<any>(
let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj: ifm.ITypedResponse<any> = await _http.putJson<any>(
'https://httpbin.org/put',
{},
additionalHeaders
@@ -73,7 +72,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson
)
const httpWithHeaders = new httpm.HttpClient()
let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = {
headers: {
[httpm.Headers.Accept]: 'baz'
@@ -87,8 +86,8 @@ describe('headers', () => {
})
it('preserves existing headers on patchJson', async () => {
const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj = await _http.patchJson<any>(
let additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
let jsonObj: ifm.ITypedResponse<any> = await _http.patchJson<any>(
'https://httpbin.org/patch',
{},
additionalHeaders
@@ -98,7 +97,7 @@ describe('headers', () => {
httpm.MediaTypes.ApplicationJson
)
const httpWithHeaders = new httpm.HttpClient()
let httpWithHeaders = new httpm.HttpClient()
httpWithHeaders.requestOptions = {
headers: {
[httpm.Headers.Accept]: 'baz'
@@ -1,4 +1,4 @@
import * as httpm from '../lib'
import * as httpm from '../_out'
describe('basics', () => {
let _http: httpm.HttpClient
@@ -11,63 +11,69 @@ describe('basics', () => {
_http.dispose()
})
it('does basic http get request with keepAlive true', async () => {
const res: httpm.HttpClientResponse = await _http.get(
it('does basic http get request with keepAlive true', async done => {
let res: httpm.HttpClientResponse = await _http.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get')
done()
})
it('does basic head request with keepAlive true', async () => {
const res: httpm.HttpClientResponse = await _http.head(
it('does basic head request with keepAlive true', async done => {
let res: httpm.HttpClientResponse = await _http.head(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
done()
})
it('does basic http delete request with keepAlive true', async () => {
const res: httpm.HttpClientResponse = await _http.del(
it('does basic http delete request with keepAlive true', async done => {
let res: httpm.HttpClientResponse = await _http.del(
'http://httpbin.org/delete'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
done()
})
it('does basic http post request with keepAlive true', async () => {
const b = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.post(
it('does basic http post request with keepAlive true', async done => {
let b: string = 'Hello World!'
let res: httpm.HttpClientResponse = await _http.post(
'http://httpbin.org/post',
b
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/post')
done()
})
it('does basic http patch request with keepAlive true', async () => {
const b = 'Hello World!'
const res: httpm.HttpClientResponse = await _http.patch(
it('does basic http patch request with keepAlive true', async done => {
let b: string = 'Hello World!'
let res: httpm.HttpClientResponse = await _http.patch(
'http://httpbin.org/patch',
b
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.data).toBe(b)
expect(obj.url).toBe('http://httpbin.org/patch')
done()
})
it('does basic http options request with keepAlive true', async () => {
const res: httpm.HttpClientResponse = await _http.options(
it('does basic http options request with keepAlive true', async done => {
let res: httpm.HttpClientResponse = await _http.options(
'http://httpbin.org'
)
expect(res.message.statusCode).toBe(200)
await res.readBody()
let body: string = await res.readBody()
done()
})
})
+39 -43
View File
@@ -1,20 +1,18 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as http from 'http'
import * as httpm from '../lib/'
import * as pm from '../lib/proxy'
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const proxy = require('proxy')
import * as httpm from '../_out'
import * as pm from '../_out/proxy'
import * as proxy from 'proxy'
import * as tunnelm from 'tunnel'
let _proxyConnects: string[]
let _proxyServer: http.Server
const _proxyUrl = 'http://127.0.0.1:8080'
let _proxyUrl = 'http://127.0.0.1:8080'
describe('proxy', () => {
beforeAll(async () => {
// Start proxy server
_proxyServer = proxy()
await new Promise<void>(resolve => {
await new Promise(resolve => {
const port = Number(_proxyUrl.split(':')[2])
_proxyServer.listen(port, () => resolve())
})
@@ -34,126 +32,126 @@ describe('proxy', () => {
_clearVars()
// Stop proxy server
await new Promise<void>(resolve => {
await new Promise(resolve => {
_proxyServer.once('close', () => resolve())
_proxyServer.close()
})
})
it('getProxyUrl does not return proxyUrl if variables not set', () => {
const proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeUndefined()
})
it('getProxyUrl returns proxyUrl if https_proxy set for https url', () => {
process.env['https_proxy'] = 'https://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeDefined()
})
it('getProxyUrl does not return proxyUrl if http_proxy set for https url', () => {
process.env['http_proxy'] = 'https://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeUndefined()
})
it('getProxyUrl returns proxyUrl if http_proxy set for http url', () => {
process.env['http_proxy'] = 'http://myproxysvr'
const proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
expect(proxyUrl).toBeDefined()
})
it('getProxyUrl does not return proxyUrl if https_proxy set and in no_proxy list', () => {
process.env['https_proxy'] = 'https://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('https://myserver'))
let proxyUrl = pm.getProxyUrl(new URL('https://myserver'))
expect(proxyUrl).toBeUndefined()
})
it('getProxyUrl returns proxyUrl if https_proxy set and not in no_proxy list', () => {
process.env['https_proxy'] = 'https://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('https://github.com'))
expect(proxyUrl).toBeDefined()
})
it('getProxyUrl does not return proxyUrl if http_proxy set and in no_proxy list', () => {
process.env['http_proxy'] = 'http://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('http://myserver'))
let proxyUrl = pm.getProxyUrl(new URL('http://myserver'))
expect(proxyUrl).toBeUndefined()
})
it('getProxyUrl returns proxyUrl if http_proxy set and not in no_proxy list', () => {
process.env['http_proxy'] = 'http://myproxysvr'
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
let proxyUrl = pm.getProxyUrl(new URL('http://github.com'))
expect(proxyUrl).toBeDefined()
})
it('checkBypass returns true if host as no_proxy list', () => {
process.env['no_proxy'] = 'myserver'
const bypass = pm.checkBypass(new URL('https://myserver'))
let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host in no_proxy list', () => {
process.env['no_proxy'] = 'otherserver,myserver,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://myserver'))
let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host in no_proxy list with spaces', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://myserver'))
let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host in no_proxy list with port', () => {
process.env['no_proxy'] = 'otherserver, myserver:8080 ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver:8080'))
let bypass = pm.checkBypass(new URL('https://myserver:8080'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host with port in no_proxy list without port', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver:8080'))
let bypass = pm.checkBypass(new URL('https://myserver:8080'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host in no_proxy list with default https port', () => {
process.env['no_proxy'] = 'otherserver, myserver:443 ,anotherserver'
const bypass = pm.checkBypass(new URL('https://myserver'))
let bypass = pm.checkBypass(new URL('https://myserver'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns true if host in no_proxy list with default http port', () => {
process.env['no_proxy'] = 'otherserver, myserver:80 ,anotherserver'
const bypass = pm.checkBypass(new URL('http://myserver'))
let bypass = pm.checkBypass(new URL('http://myserver'))
expect(bypass).toBeTruthy()
})
it('checkBypass returns false if host not in no_proxy list', () => {
process.env['no_proxy'] = 'otherserver, myserver ,anotherserver:8080'
const bypass = pm.checkBypass(new URL('https://github.com'))
let bypass = pm.checkBypass(new URL('https://github.com'))
expect(bypass).toBeFalsy()
})
it('checkBypass returns false if empty no_proxy', () => {
process.env['no_proxy'] = ''
const bypass = pm.checkBypass(new URL('https://github.com'))
let bypass = pm.checkBypass(new URL('https://github.com'))
expect(bypass).toBeFalsy()
})
it('HttpClient does basic http get request through proxy', async () => {
process.env['http_proxy'] = _proxyUrl
const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get(
let res: httpm.HttpClientResponse = await httpClient.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get')
expect(_proxyConnects).toEqual(['httpbin.org:80'])
})
@@ -162,12 +160,12 @@ describe('proxy', () => {
process.env['http_proxy'] = _proxyUrl
process.env['no_proxy'] = 'httpbin.org'
const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get(
let res: httpm.HttpClientResponse = await httpClient.get(
'http://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('http://httpbin.org/get')
expect(_proxyConnects).toHaveLength(0)
})
@@ -175,12 +173,12 @@ describe('proxy', () => {
it('HttpClient does basic https get request through proxy', async () => {
process.env['https_proxy'] = _proxyUrl
const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get(
let res: httpm.HttpClientResponse = await httpClient.get(
'https://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
expect(_proxyConnects).toEqual(['httpbin.org:443'])
})
@@ -189,12 +187,12 @@ describe('proxy', () => {
process.env['https_proxy'] = _proxyUrl
process.env['no_proxy'] = 'httpbin.org'
const httpClient = new httpm.HttpClient()
const res: httpm.HttpClientResponse = await httpClient.get(
let res: httpm.HttpClientResponse = await httpClient.get(
'https://httpbin.org/get'
)
expect(res.message.statusCode).toBe(200)
const body: string = await res.readBody()
const obj = JSON.parse(body)
let body: string = await res.readBody()
let obj: any = JSON.parse(body)
expect(obj.url).toBe('https://httpbin.org/get')
expect(_proxyConnects).toHaveLength(0)
})
@@ -202,8 +200,7 @@ describe('proxy', () => {
it('proxyAuth not set in tunnel agent when authentication is not provided', async () => {
process.env['https_proxy'] = 'http://127.0.0.1:8080'
const httpClient = new httpm.HttpClient()
const agent: any = httpClient.getAgent('https://some-url')
// eslint-disable-next-line no-console
let agent: tunnelm.TunnelingAgent = httpClient.getAgent('https://some-url')
console.log(agent)
expect(agent.proxyOptions.host).toBe('127.0.0.1')
expect(agent.proxyOptions.port).toBe('8080')
@@ -213,8 +210,7 @@ describe('proxy', () => {
it('proxyAuth is set in tunnel agent when authentication is provided', async () => {
process.env['https_proxy'] = 'http://user:password@127.0.0.1:8080'
const httpClient = new httpm.HttpClient()
const agent: any = httpClient.getAgent('https://some-url')
// eslint-disable-next-line no-console
let agent: tunnelm.TunnelingAgent = httpClient.getAgent('https://some-url')
console.log(agent)
expect(agent.proxyOptions.host).toBe('127.0.0.1')
expect(agent.proxyOptions.port).toBe('8080')
@@ -222,7 +218,7 @@ describe('proxy', () => {
})
})
function _clearVars(): void {
function _clearVars() {
delete process.env.http_proxy
delete process.env.HTTP_PROXY
delete process.env.https_proxy
+10287 -125
View File
File diff suppressed because it is too large Load Diff
+25 -34
View File
@@ -1,48 +1,39 @@
{
"name": "@actions/http-client",
"version": "2.0.1",
"version": "1.0.11",
"description": "Actions Http Client",
"keywords": [
"github",
"actions",
"http"
],
"homepage": "https://github.com/actions/toolkit/tree/main/packages/http-client",
"license": "MIT",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"!.DS_Store"
],
"publishConfig": {
"access": "public"
"main": "index.js",
"scripts": {
"build": "rm -Rf ./_out && tsc && cp package*.json ./_out && cp *.md ./_out && cp LICENSE ./_out && cp actions.png ./_out",
"test": "jest",
"format": "prettier --write *.ts && prettier --write **/*.ts",
"format-check": "prettier --check *.ts && prettier --check **/*.ts",
"audit-check": "npm audit --audit-level=moderate"
},
"repository": {
"type": "git",
"url": "git+https://github.com/actions/toolkit.git",
"directory": "packages/http-client"
},
"scripts": {
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
"test": "echo \"Error: run tests from root\" && exit 1",
"build": "tsc",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts",
"tsc": "tsc"
"url": "git+https://github.com/actions/http-client.git"
},
"keywords": [
"Actions",
"Http"
],
"author": "GitHub, Inc.",
"license": "MIT",
"bugs": {
"url": "https://github.com/actions/toolkit/issues"
"url": "https://github.com/actions/http-client/issues"
},
"homepage": "https://github.com/actions/http-client#readme",
"devDependencies": {
"@types/tunnel": "0.0.3",
"proxy": "^1.0.1"
"@types/jest": "^25.1.4",
"@types/node": "^12.12.31",
"jest": "^25.1.0",
"prettier": "^2.0.4",
"proxy": "^1.0.1",
"ts-jest": "^25.2.1",
"typescript": "^3.8.3"
},
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
}
+34 -34
View File
@@ -1,8 +1,6 @@
import * as http from 'http'
import * as ifm from './interfaces'
import {HttpClientResponse} from './index'
import ifm = require('./interfaces')
export class BasicCredentialHandler implements ifm.RequestHandler {
export class BasicCredentialHandler implements ifm.IRequestHandler {
username: string
password: string
@@ -11,26 +9,27 @@ export class BasicCredentialHandler implements ifm.RequestHandler {
this.password = password
}
prepareRequest(options: http.RequestOptions): void {
if (!options.headers) {
throw Error('The request has no headers')
}
options.headers['Authorization'] = `Basic ${Buffer.from(
`${this.username}:${this.password}`
).toString('base64')}`
prepareRequest(options: any): void {
options.headers['Authorization'] =
'Basic ' +
Buffer.from(this.username + ':' + this.password).toString('base64')
}
// This handler cannot handle 401
canHandleAuthentication(): boolean {
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false
}
async handleAuthentication(): Promise<HttpClientResponse> {
throw new Error('not implemented')
handleAuthentication(
httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
}
}
export class BearerCredentialHandler implements ifm.RequestHandler {
export class BearerCredentialHandler implements ifm.IRequestHandler {
token: string
constructor(token: string) {
@@ -39,25 +38,26 @@ export class BearerCredentialHandler implements ifm.RequestHandler {
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options: http.RequestOptions): void {
if (!options.headers) {
throw Error('The request has no headers')
}
options.headers['Authorization'] = `Bearer ${this.token}`
prepareRequest(options: any): void {
options.headers['Authorization'] = 'Bearer ' + this.token
}
// This handler cannot handle 401
canHandleAuthentication(): boolean {
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false
}
async handleAuthentication(): Promise<HttpClientResponse> {
throw new Error('not implemented')
handleAuthentication(
httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
}
}
export class PersonalAccessTokenCredentialHandler
implements ifm.RequestHandler {
implements ifm.IRequestHandler {
token: string
constructor(token: string) {
@@ -66,21 +66,21 @@ export class PersonalAccessTokenCredentialHandler
// currently implements pre-authorization
// TODO: support preAuth = false where it hooks on 401
prepareRequest(options: http.RequestOptions): void {
if (!options.headers) {
throw Error('The request has no headers')
}
options.headers['Authorization'] = `Basic ${Buffer.from(
`PAT:${this.token}`
).toString('base64')}`
prepareRequest(options: any): void {
options.headers['Authorization'] =
'Basic ' + Buffer.from('PAT:' + this.token).toString('base64')
}
// This handler cannot handle 401
canHandleAuthentication(): boolean {
canHandleAuthentication(response: ifm.IHttpClientResponse): boolean {
return false
}
async handleAuthentication(): Promise<HttpClientResponse> {
throw new Error('not implemented')
handleAuthentication(
httpClient: ifm.IHttpClient,
requestInfo: ifm.IRequestInfo,
objs
): Promise<ifm.IHttpClientResponse> {
return null
}
}
+178 -183
View File
@@ -1,11 +1,9 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import http = require('http')
import https = require('https')
import ifm = require('./interfaces')
import pm = require('./proxy')
import * as http from 'http'
import * as https from 'https'
import * as ifm from './interfaces'
import * as net from 'net'
import * as pm from './proxy'
import * as tunnel from 'tunnel'
let tunnel: any
export enum HttpCodes {
OK = 200,
@@ -51,7 +49,7 @@ export enum MediaTypes {
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
export function getProxyUrl(serverUrl: string): string {
const proxyUrl = pm.getProxyUrl(new URL(serverUrl))
let proxyUrl = pm.getProxyUrl(new URL(serverUrl))
return proxyUrl ? proxyUrl.href : ''
}
@@ -79,18 +77,18 @@ export class HttpClientError extends Error {
Object.setPrototypeOf(this, HttpClientError.prototype)
}
statusCode: number
result?: any
public statusCode: number
public result?: any
}
export class HttpClientResponse {
export class HttpClientResponse implements ifm.IHttpClientResponse {
constructor(message: http.IncomingMessage) {
this.message = message
}
message: http.IncomingMessage
async readBody(): Promise<string> {
return new Promise<string>(async resolve => {
public message: http.IncomingMessage
readBody(): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
let output = Buffer.alloc(0)
this.message.on('data', (chunk: Buffer) => {
@@ -104,32 +102,32 @@ export class HttpClientResponse {
}
}
export function isHttps(requestUrl: string): boolean {
const parsedUrl: URL = new URL(requestUrl)
export function isHttps(requestUrl: string) {
let parsedUrl: URL = new URL(requestUrl)
return parsedUrl.protocol === 'https:'
}
export class HttpClient {
userAgent: string | undefined
handlers: ifm.RequestHandler[]
requestOptions: ifm.RequestOptions | undefined
handlers: ifm.IRequestHandler[]
requestOptions: ifm.IRequestOptions
private _ignoreSslError = false
private _socketTimeout: number | undefined
private _allowRedirects = true
private _allowRedirectDowngrade = false
private _maxRedirects = 50
private _allowRetries = false
private _maxRetries = 1
private _agent: any
private _proxyAgent: any
private _keepAlive = false
private _disposed = false
private _ignoreSslError: boolean = false
private _socketTimeout: number
private _allowRedirects: boolean = true
private _allowRedirectDowngrade: boolean = false
private _maxRedirects: number = 50
private _allowRetries: boolean = false
private _maxRetries: number = 1
private _agent
private _proxyAgent
private _keepAlive: boolean = false
private _disposed: boolean = false
constructor(
userAgent?: string,
handlers?: ifm.RequestHandler[],
requestOptions?: ifm.RequestOptions
handlers?: ifm.IRequestHandler[],
requestOptions?: ifm.IRequestOptions
) {
this.userAgent = userAgent
this.handlers = handlers || []
@@ -167,64 +165,64 @@ export class HttpClient {
}
}
async options(
public options(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('OPTIONS', requestUrl, null, additionalHeaders || {})
}
async get(
public get(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('GET', requestUrl, null, additionalHeaders || {})
}
async del(
public del(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('DELETE', requestUrl, null, additionalHeaders || {})
}
async post(
public post(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('POST', requestUrl, data, additionalHeaders || {})
}
async patch(
public patch(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('PATCH', requestUrl, data, additionalHeaders || {})
}
async put(
public put(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('PUT', requestUrl, data, additionalHeaders || {})
}
async head(
public head(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request('HEAD', requestUrl, null, additionalHeaders || {})
}
async sendStream(
public sendStream(
verb: string,
requestUrl: string,
stream: NodeJS.ReadableStream,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
additionalHeaders?: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
return this.request(verb, requestUrl, stream, additionalHeaders)
}
@@ -232,28 +230,28 @@ export class HttpClient {
* Gets a typed object from an endpoint
* Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
*/
async getJson<T>(
public async getJson<T>(
requestUrl: string,
additionalHeaders: http.OutgoingHttpHeaders = {}
): Promise<ifm.TypedResponse<T>> {
additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.ITypedResponse<T>> {
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders,
Headers.Accept,
MediaTypes.ApplicationJson
)
const res: HttpClientResponse = await this.get(
let res: ifm.IHttpClientResponse = await this.get(
requestUrl,
additionalHeaders
)
return this._processResponse<T>(res, this.requestOptions)
}
async postJson<T>(
public async postJson<T>(
requestUrl: string,
obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {}
): Promise<ifm.TypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2)
additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.ITypedResponse<T>> {
let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders,
Headers.Accept,
@@ -264,7 +262,7 @@ export class HttpClient {
Headers.ContentType,
MediaTypes.ApplicationJson
)
const res: HttpClientResponse = await this.post(
let res: ifm.IHttpClientResponse = await this.post(
requestUrl,
data,
additionalHeaders
@@ -272,12 +270,12 @@ export class HttpClient {
return this._processResponse<T>(res, this.requestOptions)
}
async putJson<T>(
public async putJson<T>(
requestUrl: string,
obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {}
): Promise<ifm.TypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2)
additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.ITypedResponse<T>> {
let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders,
Headers.Accept,
@@ -288,7 +286,7 @@ export class HttpClient {
Headers.ContentType,
MediaTypes.ApplicationJson
)
const res: HttpClientResponse = await this.put(
let res: ifm.IHttpClientResponse = await this.put(
requestUrl,
data,
additionalHeaders
@@ -296,12 +294,12 @@ export class HttpClient {
return this._processResponse<T>(res, this.requestOptions)
}
async patchJson<T>(
public async patchJson<T>(
requestUrl: string,
obj: any,
additionalHeaders: http.OutgoingHttpHeaders = {}
): Promise<ifm.TypedResponse<T>> {
const data: string = JSON.stringify(obj, null, 2)
additionalHeaders: ifm.IHeaders = {}
): Promise<ifm.ITypedResponse<T>> {
let data: string = JSON.stringify(obj, null, 2)
additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
additionalHeaders,
Headers.Accept,
@@ -312,7 +310,7 @@ export class HttpClient {
Headers.ContentType,
MediaTypes.ApplicationJson
)
const res: HttpClientResponse = await this.patch(
let res: ifm.IHttpClientResponse = await this.patch(
requestUrl,
data,
additionalHeaders
@@ -325,28 +323,28 @@ export class HttpClient {
* All other methods such as get, post, patch, and request ultimately call this.
* Prefer get, del, post and patch
*/
async request(
public async request(
verb: string,
requestUrl: string,
data: string | NodeJS.ReadableStream | null,
headers?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse> {
data: string | NodeJS.ReadableStream,
headers: ifm.IHeaders
): Promise<ifm.IHttpClientResponse> {
if (this._disposed) {
throw new Error('Client has already been disposed.')
}
const parsedUrl = new URL(requestUrl)
let info: ifm.RequestInfo = this._prepareRequest(verb, parsedUrl, headers)
let parsedUrl = new URL(requestUrl)
let info: ifm.IRequestInfo = this._prepareRequest(verb, parsedUrl, headers)
// Only perform retries on reads since writes may not be idempotent.
const maxTries: number =
this._allowRetries && RetryableHttpVerbs.includes(verb)
let maxTries: number =
this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
? this._maxRetries + 1
: 1
let numTries = 0
let numTries: number = 0
let response: HttpClientResponse | undefined
do {
let response: HttpClientResponse
while (numTries < maxTries) {
response = await this.requestRaw(info, data)
// Check if it's an authentication challenge
@@ -355,11 +353,11 @@ export class HttpClient {
response.message &&
response.message.statusCode === HttpCodes.Unauthorized
) {
let authenticationHandler: ifm.RequestHandler | undefined
let authenticationHandler: ifm.IRequestHandler
for (const handler of this.handlers) {
if (handler.canHandleAuthentication(response)) {
authenticationHandler = handler
for (let i = 0; i < this.handlers.length; i++) {
if (this.handlers[i].canHandleAuthentication(response)) {
authenticationHandler = this.handlers[i]
break
}
}
@@ -375,21 +373,19 @@ export class HttpClient {
let redirectsRemaining: number = this._maxRedirects
while (
response.message.statusCode &&
HttpRedirectCodes.includes(response.message.statusCode) &&
HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
this._allowRedirects &&
redirectsRemaining > 0
) {
const redirectUrl: string | undefined =
response.message.headers['location']
const redirectUrl: string | null = response.message.headers['location']
if (!redirectUrl) {
// if there's no location to redirect to, we won't
break
}
const parsedRedirectUrl = new URL(redirectUrl)
let parsedRedirectUrl = new URL(redirectUrl)
if (
parsedUrl.protocol === 'https:' &&
parsedUrl.protocol !== parsedRedirectUrl.protocol &&
parsedUrl.protocol == 'https:' &&
parsedUrl.protocol != parsedRedirectUrl.protocol &&
!this._allowRedirectDowngrade
) {
throw new Error(
@@ -403,7 +399,7 @@ export class HttpClient {
// strip authorization header if redirected to a different hostname
if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
for (const header in headers) {
for (let header in headers) {
// header names are case insensitive
if (header.toLowerCase() === 'authorization') {
delete headers[header]
@@ -417,10 +413,7 @@ export class HttpClient {
redirectsRemaining--
}
if (
!response.message.statusCode ||
!HttpResponseRetryCodes.includes(response.message.statusCode)
) {
if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
// If not a retry code, return immediately instead of retrying
return response
}
@@ -431,7 +424,7 @@ export class HttpClient {
await response.readBody()
await this._performExponentialBackoff(numTries)
}
} while (numTries < maxTries)
}
return response
}
@@ -439,7 +432,7 @@ export class HttpClient {
/**
* Needs to be called if keepAlive is set to true in request options.
*/
dispose(): void {
public dispose() {
if (this._agent) {
this._agent.destroy()
}
@@ -452,20 +445,20 @@ export class HttpClient {
* @param info
* @param data
*/
async requestRaw(
info: ifm.RequestInfo,
data: string | NodeJS.ReadableStream | null
): Promise<HttpClientResponse> {
return new Promise<HttpClientResponse>((resolve, reject) => {
function callbackForResult(err?: Error, res?: HttpClientResponse): void {
public requestRaw(
info: ifm.IRequestInfo,
data: string | NodeJS.ReadableStream
): Promise<ifm.IHttpClientResponse> {
return new Promise<ifm.IHttpClientResponse>((resolve, reject) => {
let callbackForResult = function (
err: any,
res: ifm.IHttpClientResponse
) {
if (err) {
reject(err)
} else if (!res) {
// If `err` is not passed, then `res` must be passed.
reject(new Error('Unknown error'))
} else {
resolve(res)
}
resolve(res)
}
this.requestRawWithCallback(info, data, callbackForResult)
@@ -478,35 +471,33 @@ export class HttpClient {
* @param data
* @param onResult
*/
requestRawWithCallback(
info: ifm.RequestInfo,
data: string | NodeJS.ReadableStream | null,
onResult: (err?: Error, res?: HttpClientResponse) => void
public requestRawWithCallback(
info: ifm.IRequestInfo,
data: string | NodeJS.ReadableStream,
onResult: (err: any, res: ifm.IHttpClientResponse) => void
): void {
let socket
if (typeof data === 'string') {
if (!info.options.headers) {
info.options.headers = {}
}
info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
}
let callbackCalled = false
function handleResult(err?: Error, res?: HttpClientResponse): void {
let callbackCalled: boolean = false
let handleResult = (err: any, res: HttpClientResponse) => {
if (!callbackCalled) {
callbackCalled = true
onResult(err, res)
}
}
const req: http.ClientRequest = info.httpModule.request(
let req: http.ClientRequest = info.httpModule.request(
info.options,
(msg: http.IncomingMessage) => {
const res: HttpClientResponse = new HttpClientResponse(msg)
handleResult(undefined, res)
let res: HttpClientResponse = new HttpClientResponse(msg)
handleResult(null, res)
}
)
let socket: net.Socket
req.on('socket', sock => {
socket = sock
})
@@ -516,13 +507,13 @@ export class HttpClient {
if (socket) {
socket.end()
}
handleResult(new Error(`Request timeout: ${info.options.path}`))
handleResult(new Error('Request timeout: ' + info.options.path), null)
})
req.on('error', function(err) {
req.on('error', function (err) {
// err has statusCode property
// res should have headers
handleResult(err)
handleResult(err, null)
})
if (data && typeof data === 'string') {
@@ -530,7 +521,7 @@ export class HttpClient {
}
if (data && typeof data !== 'string') {
data.on('close', function() {
data.on('close', function () {
req.end()
})
@@ -545,17 +536,17 @@ export class HttpClient {
* routing through a proxy server - depending upon the url and proxy environment variables.
* @param serverUrl The server URL where the request will be sent. For example, https://api.github.com
*/
getAgent(serverUrl: string): http.Agent {
const parsedUrl = new URL(serverUrl)
public getAgent(serverUrl: string): http.Agent {
let parsedUrl = new URL(serverUrl)
return this._getAgent(parsedUrl)
}
private _prepareRequest(
method: string,
requestUrl: URL,
headers?: http.OutgoingHttpHeaders
): ifm.RequestInfo {
const info: ifm.RequestInfo = <ifm.RequestInfo>{}
headers: ifm.IHeaders
): ifm.IRequestInfo {
const info: ifm.IRequestInfo = <ifm.IRequestInfo>{}
info.parsedUrl = requestUrl
const usingSsl: boolean = info.parsedUrl.protocol === 'https:'
@@ -579,22 +570,23 @@ export class HttpClient {
// gives handlers an opportunity to participate
if (this.handlers) {
for (const handler of this.handlers) {
this.handlers.forEach(handler => {
handler.prepareRequest(info.options)
}
})
}
return info
}
private _mergeHeaders(
headers?: http.OutgoingHttpHeaders
): http.OutgoingHttpHeaders {
private _mergeHeaders(headers: ifm.IHeaders): ifm.IHeaders {
const lowercaseKeys = obj =>
Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
if (this.requestOptions && this.requestOptions.headers) {
return Object.assign(
{},
lowercaseKeys(this.requestOptions.headers),
lowercaseKeys(headers || {})
lowercaseKeys(headers)
)
}
@@ -602,11 +594,14 @@ export class HttpClient {
}
private _getExistingOrDefaultHeader(
additionalHeaders: http.OutgoingHttpHeaders,
additionalHeaders: ifm.IHeaders,
header: string,
_default: string
): string | number | string[] {
let clientHeader: string | undefined
) {
const lowercaseKeys = obj =>
Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
let clientHeader: string
if (this.requestOptions && this.requestOptions.headers) {
clientHeader = lowercaseKeys(this.requestOptions.headers)[header]
}
@@ -615,8 +610,8 @@ export class HttpClient {
private _getAgent(parsedUrl: URL): http.Agent {
let agent
const proxyUrl = pm.getProxyUrl(parsedUrl)
const useProxy = proxyUrl && proxyUrl.hostname
let proxyUrl: URL = pm.getProxyUrl(parsedUrl)
let useProxy = proxyUrl && proxyUrl.hostname
if (this._keepAlive && useProxy) {
agent = this._proxyAgent
@@ -627,20 +622,24 @@ export class HttpClient {
}
// if agent is already assigned use that agent.
if (agent) {
if (!!agent) {
return agent
}
const usingSsl = parsedUrl.protocol === 'https:'
let maxSockets = 100
if (this.requestOptions) {
if (!!this.requestOptions) {
maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets
}
// This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis.
if (proxyUrl && proxyUrl.hostname) {
if (useProxy) {
// If using proxy, need tunnel
if (!tunnel) {
tunnel = require('tunnel')
}
const agentOptions = {
maxSockets,
maxSockets: maxSockets,
keepAlive: this._keepAlive,
proxy: {
...((proxyUrl.username || proxyUrl.password) && {
@@ -665,7 +664,7 @@ export class HttpClient {
// if reusing agent across request and tunneling agent isn't assigned create a new agent
if (this._keepAlive && !agent) {
const options = {keepAlive: this._keepAlive, maxSockets}
const options = {keepAlive: this._keepAlive, maxSockets: maxSockets}
agent = usingSsl ? new https.Agent(options) : new http.Agent(options)
this._agent = agent
}
@@ -687,51 +686,50 @@ export class HttpClient {
return agent
}
private async _performExponentialBackoff(retryNumber: number): Promise<void> {
private _performExponentialBackoff(retryNumber: number): Promise<void> {
retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
return new Promise(resolve => setTimeout(() => resolve(), ms))
}
private async _processResponse<T>(
res: HttpClientResponse,
options?: ifm.RequestOptions
): Promise<ifm.TypedResponse<T>> {
return new Promise<ifm.TypedResponse<T>>(async (resolve, reject) => {
const statusCode = res.message.statusCode || 0
private static dateTimeDeserializer(key: any, value: any): any {
if (typeof value === 'string') {
let a = new Date(value)
if (!isNaN(a.valueOf())) {
return a
}
}
const response: ifm.TypedResponse<T> = {
statusCode,
return value
}
private async _processResponse<T>(
res: ifm.IHttpClientResponse,
options: ifm.IRequestOptions
): Promise<ifm.ITypedResponse<T>> {
return new Promise<ifm.ITypedResponse<T>>(async (resolve, reject) => {
const statusCode: number = res.message.statusCode
const response: ifm.ITypedResponse<T> = {
statusCode: statusCode,
result: null,
headers: {}
}
// not found leads to null obj returned
if (statusCode === HttpCodes.NotFound) {
if (statusCode == HttpCodes.NotFound) {
resolve(response)
}
// get the result from the body
function dateTimeDeserializer(key: any, value: any): any {
if (typeof value === 'string') {
const a = new Date(value)
if (!isNaN(a.valueOf())) {
return a
}
}
return value
}
let obj: any
let contents: string | undefined
let contents: string
// get the result from the body
try {
contents = await res.readBody()
if (contents && contents.length > 0) {
if (options && options.deserializeDates) {
obj = JSON.parse(contents, dateTimeDeserializer)
obj = JSON.parse(contents, HttpClient.dateTimeDeserializer)
} else {
obj = JSON.parse(contents)
}
@@ -755,10 +753,10 @@ export class HttpClient {
// it may be the case that the exception is in the body message as string
msg = contents
} else {
msg = `Failed request: (${statusCode})`
msg = 'Failed request: (' + statusCode + ')'
}
const err = new HttpClientError(msg, statusCode)
let err = new HttpClientError(msg, statusCode)
err.result = response.result
reject(err)
@@ -768,6 +766,3 @@ export class HttpClient {
})
}
}
const lowercaseKeys = (obj: {[index: string]: any}): any =>
Object.keys(obj).reduce((c: any, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
+43 -36
View File
@@ -1,76 +1,83 @@
import * as http from 'http'
import * as https from 'https'
import {HttpClientResponse} from './index'
import http = require('http')
export interface HttpClient {
export interface IHeaders {
[key: string]: any
}
export interface IHttpClient {
options(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
get(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
del(
requestUrl: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
post(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
patch(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
put(
requestUrl: string,
data: string,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
sendStream(
verb: string,
requestUrl: string,
stream: NodeJS.ReadableStream,
additionalHeaders?: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
additionalHeaders?: IHeaders
): Promise<IHttpClientResponse>
request(
verb: string,
requestUrl: string,
data: string | NodeJS.ReadableStream,
headers: http.OutgoingHttpHeaders
): Promise<HttpClientResponse>
headers: IHeaders
): Promise<IHttpClientResponse>
requestRaw(
info: RequestInfo,
info: IRequestInfo,
data: string | NodeJS.ReadableStream
): Promise<HttpClientResponse>
): Promise<IHttpClientResponse>
requestRawWithCallback(
info: RequestInfo,
info: IRequestInfo,
data: string | NodeJS.ReadableStream,
onResult: (err?: Error, res?: HttpClientResponse) => void
onResult: (err: any, res: IHttpClientResponse) => void
): void
}
export interface RequestHandler {
export interface IRequestHandler {
prepareRequest(options: http.RequestOptions): void
canHandleAuthentication(response: HttpClientResponse): boolean
canHandleAuthentication(response: IHttpClientResponse): boolean
handleAuthentication(
httpClient: HttpClient,
requestInfo: RequestInfo,
data: string | NodeJS.ReadableStream | null
): Promise<HttpClientResponse>
httpClient: IHttpClient,
requestInfo: IRequestInfo,
objs
): Promise<IHttpClientResponse>
}
export interface RequestInfo {
export interface IHttpClientResponse {
message: http.IncomingMessage
readBody(): Promise<string>
}
export interface IRequestInfo {
options: http.RequestOptions
parsedUrl: URL
httpModule: typeof http | typeof https
httpModule: any
}
export interface RequestOptions {
headers?: http.OutgoingHttpHeaders
export interface IRequestOptions {
headers?: IHeaders
socketTimeout?: number
ignoreSslError?: boolean
allowRedirects?: boolean
@@ -84,8 +91,8 @@ export interface RequestOptions {
maxRetries?: number
}
export interface TypedResponse<T> {
export interface ITypedResponse<T> {
statusCode: number
result: T | null
headers: http.IncomingHttpHeaders
headers: Object
}
+16 -16
View File
@@ -1,23 +1,23 @@
export function getProxyUrl(reqUrl: URL): URL | undefined {
const usingSsl = reqUrl.protocol === 'https:'
let usingSsl = reqUrl.protocol === 'https:'
let proxyUrl: URL
if (checkBypass(reqUrl)) {
return undefined
return proxyUrl
}
const proxyVar = (() => {
if (usingSsl) {
return process.env['https_proxy'] || process.env['HTTPS_PROXY']
} else {
return process.env['http_proxy'] || process.env['HTTP_PROXY']
}
})()
let proxyVar: string
if (usingSsl) {
proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY']
} else {
proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY']
}
if (proxyVar) {
return new URL(proxyVar)
} else {
return undefined
proxyUrl = new URL(proxyVar)
}
return proxyUrl
}
export function checkBypass(reqUrl: URL): boolean {
@@ -25,13 +25,13 @@ export function checkBypass(reqUrl: URL): boolean {
return false
}
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
if (!noProxy) {
return false
}
// Determine the request port
let reqPort: number | undefined
let reqPort: number
if (reqUrl.port) {
reqPort = Number(reqUrl.port)
} else if (reqUrl.protocol === 'http:') {
@@ -41,13 +41,13 @@ export function checkBypass(reqUrl: URL): boolean {
}
// Format the request hostname and hostname with port
const upperReqHosts = [reqUrl.hostname.toUpperCase()]
let upperReqHosts = [reqUrl.hostname.toUpperCase()]
if (typeof reqPort === 'number') {
upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`)
}
// Compare request host against noproxy
for (const upperNoProxyItem of noProxy
for (let upperNoProxyItem of noProxy
.split(',')
.map(x => x.trim().toUpperCase())
.filter(x => x)) {
-11
View File
@@ -1,11 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"rootDir": "./src",
"moduleResolution": "node"
},
"include": [
"./src"
]
}
-8
View File
@@ -1,13 +1,5 @@
# @actions/tool-cache Releases
### 2.0.1
- Update to v2.0.1 of `@actions/http-client` [#1087](https://github.com/actions/toolkit/pull/1087)
### 2.0.0
- Update to v2.0.0 of `@actions/http-client`
- The type of the `headers` parameter in the exported function `downloadTool` has been narrowed from `{ [header: string]: any }` to `{ [header: string]: number | string | string[] | undefined; }` (that is, `http.OutgoingHttpHeaders`).
This is strictly a compile-time change for TypeScript consumers. Previous attempts to use a header value of a type other than those now accepted would have resulted in an error at run time.
### 1.7.2
- Update `lockfileVersion` to `v2` in `package-lock.json [#1025](https://github.com/actions/toolkit/pull/1025)
+29 -53
View File
@@ -1,17 +1,17 @@
{
"name": "@actions/tool-cache",
"version": "2.0.1",
"version": "1.7.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/tool-cache",
"version": "2.0.0",
"version": "1.7.2",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.8",
"@actions/io": "^1.1.1",
"semver": "^6.1.0",
"uuid": "^3.3.2"
@@ -24,41 +24,30 @@
}
},
"node_modules/@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"dependencies": {
"@actions/http-client": "^1.0.11"
}
},
"node_modules/@actions/core/node_modules/@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"dependencies": {
"tunnel": "0.0.6"
}
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz",
"integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"dependencies": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"node_modules/@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.1.tgz",
"integrity": "sha512-Qi4JoKXjmE0O67wAOH6y0n26QXhMKMFo7GD/4IXNVcrtLjUlGjGuVys6pQgwF3ArfGTQu0XpqaNr0YhED2RaRA=="
},
"node_modules/@types/nock": {
"version": "10.0.3",
@@ -291,43 +280,30 @@
},
"dependencies": {
"@actions/core": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.0.tgz",
"integrity": "sha512-XirM+Zo/PFlA+1h+i4bkfvagujta+LIM2AOSzPbt8JqXbbuxb1HTB+FqIyaKmue9yiCx/JIJY6pXsOl3+T8JGw==",
"requires": {
"@actions/http-client": "^1.0.11"
},
"dependencies": {
"@actions/http-client": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz",
"integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==",
"requires": {
"tunnel": "0.0.6"
}
}
}
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
"integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
},
"@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz",
"integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/http-client": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.8.tgz",
"integrity": "sha512-G4JjJ6f9Hb3Zvejj+ewLLKLf99ZC+9v+yCxoYf9vSyH+WkzPLB2LuUtRMGNkooMqdugGBFStIKXOuvH1W+EctA==",
"requires": {
"tunnel": "^0.0.6"
"tunnel": "0.0.6"
}
},
"@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.1.tgz",
"integrity": "sha512-Qi4JoKXjmE0O67wAOH6y0n26QXhMKMFo7GD/4IXNVcrtLjUlGjGuVys6pQgwF3ArfGTQu0XpqaNr0YhED2RaRA=="
},
"@types/nock": {
"version": "10.0.3",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/tool-cache",
"version": "2.0.1",
"version": "1.7.2",
"description": "Actions tool-cache lib",
"keywords": [
"github",
@@ -38,7 +38,7 @@
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/http-client": "^1.0.8",
"@actions/io": "^1.1.1",
"semver": "^6.1.0",
"uuid": "^3.3.2"
+5 -5
View File
@@ -8,12 +8,12 @@ import * as httpm from '@actions/http-client'
import * as semver from 'semver'
import * as stream from 'stream'
import * as util from 'util'
import {ok} from 'assert'
import {OutgoingHttpHeaders} from 'http'
import uuidV4 from 'uuid/v4'
import {exec} from '@actions/exec/lib/exec'
import {ExecOptions} from '@actions/exec/lib/interfaces'
import {ok} from 'assert'
import {RetryHelper} from './retry-helper'
import {IHeaders} from '@actions/http-client/interfaces'
export class HTTPError extends Error {
constructor(readonly httpStatusCode: number | undefined) {
@@ -39,7 +39,7 @@ export async function downloadTool(
url: string,
dest?: string,
auth?: string,
headers?: OutgoingHttpHeaders
headers?: IHeaders
): Promise<string> {
dest = dest || path.join(_getTempDirectory(), uuidV4())
await io.mkdirP(path.dirname(dest))
@@ -82,7 +82,7 @@ async function downloadToolAttempt(
url: string,
dest: string,
auth?: string,
headers?: OutgoingHttpHeaders
headers?: IHeaders
): Promise<string> {
if (fs.existsSync(dest)) {
throw new Error(`Destination file path ${dest} already exists`)
@@ -596,7 +596,7 @@ export async function getManifestFromRepo(
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`
const http: httpm.HttpClient = new httpm.HttpClient('tool-cache')
const headers: OutgoingHttpHeaders = {}
const headers: IHeaders = {}
if (auth) {
core.debug('set auth')
headers.authorization = auth
+2 -2
View File
@@ -9,5 +9,5 @@ if [[ -z "$name" ]]; then
exit 1
fi
npx lerna create @actions/$name
cp packages/core/tsconfig.json packages/$name/tsconfig.json
lerna create @actions/$name
cp packages/toolkit/tsconfig.json packages/$name/tsconfig.json