{
const file = await fs.promises.readFile(testFilePath, {encoding: 'utf8'})
@@ -67,11 +68,12 @@ const fixtures = {
}
}
-describe('@actions/core/src/markdown-summary', () => {
+describe('@actions/core/src/summary', () => {
beforeEach(async () => {
process.env[SUMMARY_ENV_VAR] = testFilePath
+ await fs.promises.mkdir(testDirectoryPath, {recursive: true})
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
- markdownSummary.emptyBuffer()
+ summary.emptyBuffer()
})
afterAll(async () => {
@@ -80,39 +82,39 @@ describe('@actions/core/src/markdown-summary', () => {
it('throws if summary env var is undefined', async () => {
process.env[SUMMARY_ENV_VAR] = undefined
- const write = markdownSummary.addRaw(fixtures.text).write()
+ const write = summary.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 = markdownSummary.addRaw(fixtures.text).write()
+ const write = summary.addRaw(fixtures.text).write()
await expect(write).rejects.toThrow()
})
it('appends text to summary file', async () => {
await fs.promises.writeFile(testFilePath, '# ', {encoding: 'utf8'})
- await markdownSummary.addRaw(fixtures.text).write()
+ await summary.addRaw(fixtures.text).write()
await assertSummary(`# ${fixtures.text}`)
})
it('overwrites text to summary file', async () => {
await fs.promises.writeFile(testFilePath, 'overwrite', {encoding: 'utf8'})
- await markdownSummary.addRaw(fixtures.text).write({overwrite: true})
+ await summary.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 markdownSummary.addRaw(fixtures.text, true).write()
+ await summary.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 markdownSummary
+ await summary
.addRaw(fixtures.text)
.addRaw(fixtures.text)
.addRaw(fixtures.text)
@@ -122,33 +124,33 @@ describe('@actions/core/src/markdown-summary', () => {
it('empties buffer after write', async () => {
await fs.promises.writeFile(testFilePath, '', {encoding: 'utf8'})
- await markdownSummary.addRaw(fixtures.text).write()
+ await summary.addRaw(fixtures.text).write()
await assertSummary(fixtures.text)
- expect(markdownSummary.isEmptyBuffer()).toBe(true)
+ expect(summary.isEmptyBuffer()).toBe(true)
})
it('returns summary buffer as string', () => {
- markdownSummary.addRaw(fixtures.text)
- expect(markdownSummary.stringify()).toEqual(fixtures.text)
+ summary.addRaw(fixtures.text)
+ expect(summary.stringify()).toEqual(fixtures.text)
})
it('return correct values for isEmptyBuffer', () => {
- markdownSummary.addRaw(fixtures.text)
- expect(markdownSummary.isEmptyBuffer()).toBe(false)
+ summary.addRaw(fixtures.text)
+ expect(summary.isEmptyBuffer()).toBe(false)
- markdownSummary.emptyBuffer()
- expect(markdownSummary.isEmptyBuffer()).toBe(true)
+ summary.emptyBuffer()
+ expect(summary.isEmptyBuffer()).toBe(true)
})
it('clears a buffer and summary file', async () => {
await fs.promises.writeFile(testFilePath, 'content', {encoding: 'utf8'})
- await markdownSummary.clear()
+ await summary.clear()
await assertSummary('')
- expect(markdownSummary.isEmptyBuffer()).toBe(true)
+ expect(summary.isEmptyBuffer()).toBe(true)
})
it('adds EOL', async () => {
- await markdownSummary
+ await summary
.addRaw(fixtures.text)
.addEOL()
.write()
@@ -156,37 +158,37 @@ describe('@actions/core/src/markdown-summary', () => {
})
it('adds a code block without language', async () => {
- await markdownSummary.addCodeBlock(fixtures.code).write()
+ await summary.addCodeBlock(fixtures.code).write()
const expected = `func fork() {\n for {\n go fork()\n }\n}
${os.EOL}`
await assertSummary(expected)
})
it('adds a code block with a language', async () => {
- await markdownSummary.addCodeBlock(fixtures.code, 'go').write()
+ await summary.addCodeBlock(fixtures.code, 'go').write()
const expected = `func fork() {\n for {\n go fork()\n }\n}
${os.EOL}`
await assertSummary(expected)
})
it('adds an unordered list', async () => {
- await markdownSummary.addList(fixtures.list).write()
+ await summary.addList(fixtures.list).write()
const expected = `${os.EOL}`
await assertSummary(expected)
})
it('adds an ordered list', async () => {
- await markdownSummary.addList(fixtures.list, true).write()
+ await summary.addList(fixtures.list, true).write()
const expected = `- foo
- bar
- baz
- 💣
${os.EOL}`
await assertSummary(expected)
})
it('adds a table', async () => {
- await markdownSummary.addTable(fixtures.table).write()
+ await summary.addTable(fixtures.table).write()
const expected = `foo | bar | baz | tall |
---|
one | two | three |
wide |
${os.EOL}`
await assertSummary(expected)
})
it('adds a details element', async () => {
- await markdownSummary
+ await summary
.addDetails(fixtures.details.label, fixtures.details.content)
.write()
const expected = `open me
🎉 surprise ${os.EOL}`
@@ -194,13 +196,13 @@ describe('@actions/core/src/markdown-summary', () => {
})
it('adds an image with alt text', async () => {
- await markdownSummary.addImage(fixtures.img.src, fixtures.img.alt).write()
+ await summary.addImage(fixtures.img.src, fixtures.img.alt).write()
const expected = `${os.EOL}`
await assertSummary(expected)
})
it('adds an image with custom dimensions', async () => {
- await markdownSummary
+ await summary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write()
const expected = `${os.EOL}`
@@ -208,7 +210,7 @@ describe('@actions/core/src/markdown-summary', () => {
})
it('adds an image with custom dimensions', async () => {
- await markdownSummary
+ await summary
.addImage(fixtures.img.src, fixtures.img.alt, fixtures.img.options)
.write()
const expected = `${os.EOL}`
@@ -217,21 +219,21 @@ describe('@actions/core/src/markdown-summary', () => {
it('adds headings h1...h6', async () => {
for (const i of [1, 2, 3, 4, 5, 6]) {
- markdownSummary.addHeading('heading', i)
+ summary.addHeading('heading', i)
}
- await markdownSummary.write()
+ await summary.write()
const expected = `heading
${os.EOL}heading
${os.EOL}heading
${os.EOL}heading
${os.EOL}heading
${os.EOL}heading
${os.EOL}`
await assertSummary(expected)
})
it('adds h1 if heading level not specified', async () => {
- await markdownSummary.addHeading('heading').write()
+ await summary.addHeading('heading').write()
const expected = `heading
${os.EOL}`
await assertSummary(expected)
})
it('uses h1 if heading level is garbage or out of range', async () => {
- await markdownSummary
+ await summary
.addHeading('heading', 'foobar')
.addHeading('heading', 1337)
.addHeading('heading', -1)
@@ -242,35 +244,31 @@ describe('@actions/core/src/markdown-summary', () => {
})
it('adds a separator', async () => {
- await markdownSummary.addSeparator().write()
+ await summary.addSeparator().write()
const expected = `
${os.EOL}`
await assertSummary(expected)
})
it('adds a break', async () => {
- await markdownSummary.addBreak().write()
+ await summary.addBreak().write()
const expected = `
${os.EOL}`
await assertSummary(expected)
})
it('adds a quote', async () => {
- await markdownSummary.addQuote(fixtures.quote.text).write()
+ await summary.addQuote(fixtures.quote.text).write()
const expected = `Where the world builds software
${os.EOL}`
await assertSummary(expected)
})
it('adds a quote with citation', async () => {
- await markdownSummary
- .addQuote(fixtures.quote.text, fixtures.quote.cite)
- .write()
+ await summary.addQuote(fixtures.quote.text, fixtures.quote.cite).write()
const expected = `Where the world builds software
${os.EOL}`
await assertSummary(expected)
})
it('adds a link with href', async () => {
- await markdownSummary
- .addLink(fixtures.link.text, fixtures.link.href)
- .write()
+ await summary.addLink(fixtures.link.text, fixtures.link.href).write()
const expected = `GitHub${os.EOL}`
await assertSummary(expected)
})
diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json
index 9a638c5a..82ddb5f8 100644
--- a/packages/core/package-lock.json
+++ b/packages/core/package-lock.json
@@ -1,26 +1,26 @@
{
"name": "@actions/core",
- "version": "1.7.0",
+ "version": "1.8.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/core",
- "version": "1.6.0",
+ "version": "1.8.1",
"license": "MIT",
"dependencies": {
- "@actions/http-client": "^1.0.11"
+ "@actions/http-client": "^2.0.1"
},
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"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==",
+ "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"
+ "tunnel": "^0.0.6"
}
},
"node_modules/@types/node": {
@@ -40,11 +40,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==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
+ "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"requires": {
- "tunnel": "0.0.6"
+ "tunnel": "^0.0.6"
}
},
"@types/node": {
diff --git a/packages/core/package.json b/packages/core/package.json
index 56ee8427..7a0129c0 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@actions/core",
- "version": "1.7.0",
+ "version": "1.8.2",
"description": "Actions core lib",
"keywords": [
"github",
@@ -36,7 +36,7 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
- "@actions/http-client": "^1.0.11"
+ "@actions/http-client": "^2.0.1"
},
"devDependencies": {
"@types/node": "^12.0.2"
diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts
index 8a9170ca..f5e7941b 100644
--- a/packages/core/src/core.ts
+++ b/packages/core/src/core.ts
@@ -361,6 +361,11 @@ export async function getIDToken(aud?: string): Promise {
}
/**
- * Markdown summary exports
+ * Summary exports
*/
-export {markdownSummary} from './markdown-summary'
+export {summary} from './summary'
+
+/**
+ * @deprecated use core.summary
+ */
+export {markdownSummary} from './summary'
diff --git a/packages/core/src/oidc-utils.ts b/packages/core/src/oidc-utils.ts
index e33da5f1..d490a3ce 100644
--- a/packages/core/src/oidc-utils.ts
+++ b/packages/core/src/oidc-utils.ts
@@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-extraneous-class */
import * as actions_http_client from '@actions/http-client'
-import {IRequestOptions} from '@actions/http-client/interfaces'
+import {RequestOptions} from '@actions/http-client/lib/interfaces'
import {HttpClient} from '@actions/http-client'
-import {BearerCredentialHandler} from '@actions/http-client/auth'
+import {BearerCredentialHandler} from '@actions/http-client/lib/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: IRequestOptions = {
+ const requestOptions: RequestOptions = {
allowRetries: allowRetry,
maxRetries: maxRetry
}
diff --git a/packages/core/src/markdown-summary.ts b/packages/core/src/summary.ts
similarity index 80%
rename from packages/core/src/markdown-summary.ts
rename to packages/core/src/summary.ts
index 97d2d3ca..015f2eea 100644
--- a/packages/core/src/markdown-summary.ts
+++ b/packages/core/src/summary.ts
@@ -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-markdown-summary'
+ 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'
export type SummaryTableRow = (SummaryTableCell | string)[]
@@ -51,7 +51,7 @@ export interface SummaryWriteOptions {
overwrite?: boolean
}
-class MarkdownSummary {
+class Summary {
private _buffer: string
private _filePath?: string
@@ -73,7 +73,7 @@ class MarkdownSummary {
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 markdown summaries.`
+ `Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`
)
}
@@ -119,9 +119,9 @@ class MarkdownSummary {
*
* @param {SummaryWriteOptions} [options] (optional) options for write operation
*
- * @returns {Promise} markdown summary instance
+ * @returns {Promise} summary instance
*/
- async write(options?: SummaryWriteOptions): Promise {
+ async write(options?: SummaryWriteOptions): Promise {
const overwrite = !!options?.overwrite
const filePath = await this.filePath()
const writeFunc = overwrite ? writeFile : appendFile
@@ -132,9 +132,9 @@ class MarkdownSummary {
/**
* Clears the summary buffer and wipes the summary file
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- async clear(): Promise {
+ async clear(): Promise {
return this.emptyBuffer().write({overwrite: true})
}
@@ -159,9 +159,9 @@ class MarkdownSummary {
/**
* Resets the summary buffer without writing to summary file
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- emptyBuffer(): MarkdownSummary {
+ emptyBuffer(): Summary {
this._buffer = ''
return this
}
@@ -172,9 +172,9 @@ class MarkdownSummary {
* @param {string} text content to add
* @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false)
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addRaw(text: string, addEOL = false): MarkdownSummary {
+ addRaw(text: string, addEOL = false): Summary {
this._buffer += text
return addEOL ? this.addEOL() : this
}
@@ -182,9 +182,9 @@ class MarkdownSummary {
/**
* Adds the operating system-specific end-of-line marker to the buffer
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addEOL(): MarkdownSummary {
+ addEOL(): Summary {
return this.addRaw(EOL)
}
@@ -194,9 +194,9 @@ class MarkdownSummary {
* @param {string} code content to render within fenced code block
* @param {string} lang (optional) language to syntax highlight code
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addCodeBlock(code: string, lang?: string): MarkdownSummary {
+ addCodeBlock(code: string, lang?: string): Summary {
const attrs = {
...(lang && {lang})
}
@@ -210,9 +210,9 @@ class MarkdownSummary {
* @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 {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addList(items: string[], ordered = false): MarkdownSummary {
+ addList(items: string[], ordered = false): Summary {
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 MarkdownSummary {
*
* @param {SummaryTableCell[]} rows table rows
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addTable(rows: SummaryTableRow[]): MarkdownSummary {
+ addTable(rows: SummaryTableRow[]): Summary {
const tableBody = rows
.map(row => {
const cells = row
@@ -260,9 +260,9 @@ class MarkdownSummary {
* @param {string} label text for the closed state
* @param {string} content collapsable content
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addDetails(label: string, content: string): MarkdownSummary {
+ addDetails(label: string, content: string): Summary {
const element = this.wrap('details', this.wrap('summary', label) + content)
return this.addRaw(element).addEOL()
}
@@ -274,13 +274,9 @@ class MarkdownSummary {
* @param {string} alt text description of the image
* @param {SummaryImageOptions} options (optional) addition image attributes
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addImage(
- src: string,
- alt: string,
- options?: SummaryImageOptions
- ): MarkdownSummary {
+ addImage(src: string, alt: string, options?: SummaryImageOptions): Summary {
const {width, height} = options || {}
const attrs = {
...(width && {width}),
@@ -297,9 +293,9 @@ class MarkdownSummary {
* @param {string} text heading text
* @param {number | string} [level=1] (optional) the heading level, default: 1
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addHeading(text: string, level?: number | string): MarkdownSummary {
+ addHeading(text: string, level?: number | string): Summary {
const tag = `h${level}`
const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag)
? tag
@@ -311,9 +307,9 @@ class MarkdownSummary {
/**
* Adds an HTML thematic break (
) to the summary buffer
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addSeparator(): MarkdownSummary {
+ addSeparator(): Summary {
const element = this.wrap('hr', null)
return this.addRaw(element).addEOL()
}
@@ -321,9 +317,9 @@ class MarkdownSummary {
/**
* Adds an HTML line break (
) to the summary buffer
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addBreak(): MarkdownSummary {
+ addBreak(): Summary {
const element = this.wrap('br', null)
return this.addRaw(element).addEOL()
}
@@ -334,9 +330,9 @@ class MarkdownSummary {
* @param {string} text quote text
* @param {string} cite (optional) citation url
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addQuote(text: string, cite?: string): MarkdownSummary {
+ addQuote(text: string, cite?: string): Summary {
const attrs = {
...(cite && {cite})
}
@@ -350,13 +346,18 @@ class MarkdownSummary {
* @param {string} text link text/content
* @param {string} href hyperlink
*
- * @returns {MarkdownSummary} markdown summary instance
+ * @returns {Summary} summary instance
*/
- addLink(text: string, href: string): MarkdownSummary {
+ addLink(text: string, href: string): Summary {
const element = this.wrap('a', text, {href})
return this.addRaw(element).addEOL()
}
}
-// singleton export
-export const markdownSummary = new MarkdownSummary()
+const _summary = new Summary()
+
+/**
+ * @deprecated use `core.summary`
+ */
+export const markdownSummary = _summary
+export const summary = _summary
diff --git a/packages/github/RELEASES.md b/packages/github/RELEASES.md
index 225953b5..fb7ac2c7 100644
--- a/packages/github/RELEASES.md
+++ b/packages/github/RELEASES.md
@@ -1,5 +1,11 @@
# @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
diff --git a/packages/github/package-lock.json b/packages/github/package-lock.json
index 0a497e0d..b66a87f0 100644
--- a/packages/github/package-lock.json
+++ b/packages/github/package-lock.json
@@ -1,15 +1,15 @@
{
"name": "@actions/github",
- "version": "5.0.1",
+ "version": "5.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/github",
- "version": "5.0.1",
+ "version": "5.0.2",
"license": "MIT",
"dependencies": {
- "@actions/http-client": "^1.0.11",
+ "@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
@@ -19,11 +19,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==",
+ "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"
+ "tunnel": "^0.0.6"
}
},
"node_modules/@octokit/auth-token": {
@@ -361,11 +361,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==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
+ "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"requires": {
- "tunnel": "0.0.6"
+ "tunnel": "^0.0.6"
}
},
"@octokit/auth-token": {
diff --git a/packages/github/package.json b/packages/github/package.json
index 208b5a79..30da3279 100644
--- a/packages/github/package.json
+++ b/packages/github/package.json
@@ -1,6 +1,6 @@
{
"name": "@actions/github",
- "version": "5.0.1",
+ "version": "5.0.3",
"description": "Actions github lib",
"keywords": [
"github",
@@ -38,7 +38,7 @@
"url": "https://github.com/actions/toolkit/issues"
},
"dependencies": {
- "@actions/http-client": "^1.0.11",
+ "@actions/http-client": "^2.0.1",
"@octokit/core": "^3.6.0",
"@octokit/plugin-paginate-rest": "^2.17.0",
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
diff --git a/packages/glob/package-lock.json b/packages/glob/package-lock.json
index ee9a1739..0aee7b60 100644
--- a/packages/glob/package-lock.json
+++ b/packages/glob/package-lock.json
@@ -6,7 +6,7 @@
"packages": {
"": {
"name": "@actions/glob",
- "version": "0.2.1",
+ "version": "0.3.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
@@ -14,9 +14,20 @@
}
},
"node_modules/@actions/core": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
- "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
+ "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"
+ }
},
"node_modules/balanced-match": {
"version": "1.0.0",
@@ -47,13 +58,32 @@
"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.2.6",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
- "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
+ "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"
+ }
},
"balanced-match": {
"version": "1.0.0",
@@ -81,6 +111,11 @@
"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=="
}
}
}
diff --git a/packages/http-client/.gitignore b/packages/http-client/.gitignore
new file mode 100644
index 00000000..d481b576
--- /dev/null
+++ b/packages/http-client/.gitignore
@@ -0,0 +1,2 @@
+testoutput.txt
+npm-debug.log
diff --git a/packages/http-client/LICENSE b/packages/http-client/LICENSE
new file mode 100644
index 00000000..5823a51c
--- /dev/null
+++ b/packages/http-client/LICENSE
@@ -0,0 +1,21 @@
+Actions Http Client for Node.js
+
+Copyright (c) GitHub, Inc.
+
+All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/http-client/README.md b/packages/http-client/README.md
new file mode 100644
index 00000000..7e06adeb
--- /dev/null
+++ b/packages/http-client/README.md
@@ -0,0 +1,73 @@
+# `@actions/http-client`
+
+A lightweight HTTP client optimized for building actions.
+
+## Features
+
+ - HTTP client with TypeScript generics and async/await/Promises
+ - Typings included!
+ - [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.
+ - Redirects supported
+
+Features and releases [here](./RELEASES.md)
+
+## Install
+
+```
+npm install @actions/http-client --save
+```
+
+## Samples
+
+See the [tests](./__tests__) for detailed examples.
+
+## Errors
+
+### HTTP
+
+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.
+
+## Debugging
+
+To enable detailed console logging of all HTTP requests and responses, set the NODE_DEBUG environment varible:
+
+```shell
+export NODE_DEBUG=http
+```
+
+## Node support
+
+The http-client is built using the latest LTS version of Node 12. It may work on previous node LTS versions but it's tested and officially supported on Node12+.
+
+## Support and Versioning
+
+We follow semver and will hold compatibility between major versions and increment the minor version with new features and capabilities (while holding compat).
+
+## Contributing
+
+We welcome PRs. Please create an issue and if applicable, a design before proceeding with code.
+
+once:
+
+```
+npm install
+```
+
+To build:
+
+```
+npm run build
+```
+
+To run all tests:
+
+```
+npm test
+```
diff --git a/packages/http-client/RELEASES.md b/packages/http-client/RELEASES.md
new file mode 100644
index 00000000..344555f9
--- /dev/null
+++ b/packages/http-client/RELEASES.md
@@ -0,0 +1,39 @@
+## 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)
+
+## 1.0.9
+Throw HttpClientError instead of a generic Error from the \Json() helper methods when the server responds with a non-successful status code.
+
+## 1.0.8
+Fixed security issue where a redirect (e.g. 302) to another domain would pass headers. The fix was to strip the authorization header if the hostname was different. More [details in PR #27](https://github.com/actions/http-client/pull/27)
+
+## 1.0.7
+Update NPM dependencies and add 429 to the list of HttpCodes
+
+## 1.0.6
+Automatically sends Content-Type and Accept application/json headers for \Json() helper methods if not set in the client or parameters.
+
+## 1.0.5
+Adds \Json() helper methods for json over http scenarios.
+
+## 1.0.4
+Started to add \Json() helper methods. Do not use this release for that. Use >= 1.0.5 since there was an issue with types.
+
+## 1.0.1 to 1.0.3
+Adds proxy support.
diff --git a/packages/http-client/__tests__/auth.test.ts b/packages/http-client/__tests__/auth.test.ts
new file mode 100644
index 00000000..878fafe9
--- /dev/null
+++ b/packages/http-client/__tests__/auth.test.ts
@@ -0,0 +1,73 @@
+import * as httpm from '../lib'
+import * as am from '../lib/auth'
+
+describe('auth', () => {
+ beforeEach(() => {})
+
+ afterEach(() => {})
+
+ it('does basic http get request with basic auth', async () => {
+ const 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'
+ )
+ 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(
+ auth.substring('Basic '.length),
+ 'base64'
+ ).toString()
+ expect(creds).toBe('johndoe:password')
+ 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.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'
+ )
+ 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(
+ auth.substring('Basic '.length),
+ 'base64'
+ ).toString()
+ 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)
+
+ const http: httpm.HttpClient = new httpm.HttpClient('http-client-tests', [
+ ph
+ ])
+ const 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}`)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ })
+})
diff --git a/packages/http-client/__tests__/basics.test.ts b/packages/http-client/__tests__/basics.test.ts
new file mode 100644
index 00000000..7732264a
--- /dev/null
+++ b/packages/http-client/__tests__/basics.test.ts
@@ -0,0 +1,374 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import * as httpm from '..'
+import * as path from 'path'
+import * as fs from 'fs'
+
+const sampleFilePath: string = path.join(__dirname, 'testoutput.txt')
+
+interface HttpBinData {
+ url: string
+ data: any
+ json: any
+ headers: any
+ args?: any
+}
+
+describe('basics', () => {
+ let _http: httpm.HttpClient
+
+ beforeEach(() => {
+ _http = new httpm.HttpClient('http-client-tests')
+ })
+
+ afterEach(() => {})
+
+ it('constructs', () => {
+ const http: httpm.HttpClient = new httpm.HttpClient('thttp-client-tests')
+ expect(http).toBeDefined()
+ })
+
+ // responses from httpbin return something like:
+ // {
+ // "args": {},
+ // "headers": {
+ // "Connection": "close",
+ // "Host": "httpbin.org",
+ // "User-Agent": "typed-test-client-tests"
+ // },
+ // "origin": "173.95.152.44",
+ // "url": "https://httpbin.org/get"
+ // }
+
+ it('does basic http get request', async () => {
+ const 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)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ expect(obj.headers['User-Agent']).toBeTruthy()
+ })
+
+ 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'
+ )
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = JSON.parse(body)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ expect(obj.headers['User-Agent']).toBeFalsy()
+ })
+
+ it('does basic https get request', async () => {
+ const 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)
+ expect(obj.url).toBe('https://httpbin.org/get')
+ })
+
+ 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'
+ }
+ }
+ )
+ const 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)
+ expect(obj.headers.Accept).toBe('application/json')
+ expect(obj.headers['Content-Type']).toBe('application/json')
+ expect(obj.url).toBe('http://httpbin.org/get')
+ })
+
+ 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'
+ }
+ }
+ )
+ const 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)
+ 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')
+ })
+
+ it('pipes a get request', async () => {
+ return new Promise(async resolve => {
+ const file = 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)
+ 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'
+ )}`
+ )
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = JSON.parse(body)
+ expect(obj.url).toBe('https://httpbin.org/get')
+ })
+
+ 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`
+ )
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = JSON.parse(body)
+ expect(obj.url).toBe('https://httpbin.org/get')
+ })
+
+ 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`
+ )
+ expect(res.message.statusCode).toBe(404)
+ await res.readBody()
+ })
+
+ it('does not follow redirects if disabled', async () => {
+ const http: httpm.HttpClient = new httpm.HttpClient(
+ 'typed-test-client-tests',
+ undefined,
+ {allowRedirects: false}
+ )
+ const 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()
+ })
+
+ it('does not pass auth with diff hostname redirects', async () => {
+ const 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'
+ )}`,
+ headers
+ )
+
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = 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')
+ })
+
+ it('does not pass Auth with diff hostname redirects', async () => {
+ const 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'
+ )}`,
+ headers
+ )
+
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = 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')
+ })
+
+ it('does basic head request', async () => {
+ const res: httpm.HttpClientResponse = await _http.head(
+ 'http://httpbin.org/get'
+ )
+ expect(res.message.statusCode).toBe(200)
+ })
+
+ it('does basic http delete request', async () => {
+ const 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)
+ })
+
+ it('does basic http post request', async () => {
+ const b = 'Hello World!'
+ const 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)
+ expect(obj.data).toBe(b)
+ expect(obj.url).toBe('http://httpbin.org/post')
+ })
+
+ it('does basic http patch request', async () => {
+ const b = 'Hello World!'
+ const 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)
+ expect(obj.data).toBe(b)
+ expect(obj.url).toBe('http://httpbin.org/patch')
+ })
+
+ it('does basic http options request', async () => {
+ const res: httpm.HttpClientResponse = await _http.options(
+ 'http://httpbin.org'
+ )
+ expect(res.message.statusCode).toBe(200)
+ await res.readBody()
+ })
+
+ it('returns 404 for not found get request', async () => {
+ const res: httpm.HttpClientResponse = await _http.get(
+ 'http://httpbin.org/status/404'
+ )
+ expect(res.message.statusCode).toBe(404)
+ await res.readBody()
+ })
+
+ it('gets a json object', async () => {
+ const jsonObj = await _http.getJson('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(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('getting a non existent json object returns null', async () => {
+ const jsonObj = await _http.getJson(
+ '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(
+ '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(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.result?.headers['Content-Type']).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('puts a json object', async () => {
+ const res = {name: 'foo'}
+ const restRes = await _http.putJson(
+ '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?.headers['Accept']).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.result?.headers['Content-Type']).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('patch a json object', async () => {
+ const res = {name: 'foo'}
+ const restRes = await _http.patchJson(
+ '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(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.result?.headers['Content-Type']).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ expect(restRes.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+})
diff --git a/packages/http-client/__tests__/headers.test.ts b/packages/http-client/__tests__/headers.test.ts
new file mode 100644
index 00000000..0af9563c
--- /dev/null
+++ b/packages/http-client/__tests__/headers.test.ts
@@ -0,0 +1,116 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import * as httpm from '..'
+
+describe('headers', () => {
+ let _http: httpm.HttpClient
+
+ beforeEach(() => {
+ _http = new httpm.HttpClient('http-client-tests')
+ })
+
+ it('preserves existing headers on getJson', async () => {
+ const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
+ let jsonObj = await _http.getJson(
+ 'https://httpbin.org/get',
+ additionalHeaders
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('foo')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+
+ const httpWithHeaders = new httpm.HttpClient()
+ httpWithHeaders.requestOptions = {
+ headers: {
+ [httpm.Headers.Accept]: 'baz'
+ }
+ }
+ jsonObj = await httpWithHeaders.getJson('https://httpbin.org/get')
+ expect(jsonObj.result.headers['Accept']).toBe('baz')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('preserves existing headers on postJson', async () => {
+ const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
+ let jsonObj = await _http.postJson(
+ 'https://httpbin.org/post',
+ {},
+ additionalHeaders
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('foo')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+
+ const httpWithHeaders = new httpm.HttpClient()
+ httpWithHeaders.requestOptions = {
+ headers: {
+ [httpm.Headers.Accept]: 'baz'
+ }
+ }
+ jsonObj = await httpWithHeaders.postJson(
+ 'https://httpbin.org/post',
+ {}
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('baz')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('preserves existing headers on putJson', async () => {
+ const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
+ let jsonObj = await _http.putJson(
+ 'https://httpbin.org/put',
+ {},
+ additionalHeaders
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('foo')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+
+ const httpWithHeaders = new httpm.HttpClient()
+ httpWithHeaders.requestOptions = {
+ headers: {
+ [httpm.Headers.Accept]: 'baz'
+ }
+ }
+ jsonObj = await httpWithHeaders.putJson('https://httpbin.org/put', {})
+ expect(jsonObj.result.headers['Accept']).toBe('baz')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+
+ it('preserves existing headers on patchJson', async () => {
+ const additionalHeaders = {[httpm.Headers.Accept]: 'foo'}
+ let jsonObj = await _http.patchJson(
+ 'https://httpbin.org/patch',
+ {},
+ additionalHeaders
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('foo')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+
+ const httpWithHeaders = new httpm.HttpClient()
+ httpWithHeaders.requestOptions = {
+ headers: {
+ [httpm.Headers.Accept]: 'baz'
+ }
+ }
+ jsonObj = await httpWithHeaders.patchJson(
+ 'https://httpbin.org/patch',
+ {}
+ )
+ expect(jsonObj.result.headers['Accept']).toBe('baz')
+ expect(jsonObj.headers[httpm.Headers.ContentType]).toBe(
+ httpm.MediaTypes.ApplicationJson
+ )
+ })
+})
diff --git a/packages/http-client/__tests__/keepalive.test.ts b/packages/http-client/__tests__/keepalive.test.ts
new file mode 100644
index 00000000..ed55be20
--- /dev/null
+++ b/packages/http-client/__tests__/keepalive.test.ts
@@ -0,0 +1,73 @@
+import * as httpm from '../lib'
+
+describe('basics', () => {
+ let _http: httpm.HttpClient
+
+ beforeEach(() => {
+ _http = new httpm.HttpClient('http-client-tests', [], {keepAlive: true})
+ })
+
+ afterEach(() => {
+ _http.dispose()
+ })
+
+ it('does basic http get request with keepAlive true', async () => {
+ const 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)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ })
+
+ it('does basic head request with keepAlive true', async () => {
+ const res: httpm.HttpClientResponse = await _http.head(
+ 'http://httpbin.org/get'
+ )
+ expect(res.message.statusCode).toBe(200)
+ })
+
+ it('does basic http delete request with keepAlive true', async () => {
+ const 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)
+ })
+
+ it('does basic http post request with keepAlive true', async () => {
+ const b = 'Hello World!'
+ const 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)
+ expect(obj.data).toBe(b)
+ expect(obj.url).toBe('http://httpbin.org/post')
+ })
+
+ it('does basic http patch request with keepAlive true', async () => {
+ const b = 'Hello World!'
+ const 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)
+ expect(obj.data).toBe(b)
+ expect(obj.url).toBe('http://httpbin.org/patch')
+ })
+
+ it('does basic http options request with keepAlive true', async () => {
+ const res: httpm.HttpClientResponse = await _http.options(
+ 'http://httpbin.org'
+ )
+ expect(res.message.statusCode).toBe(200)
+ await res.readBody()
+ })
+})
diff --git a/packages/http-client/__tests__/proxy.test.ts b/packages/http-client/__tests__/proxy.test.ts
new file mode 100644
index 00000000..62e8e962
--- /dev/null
+++ b/packages/http-client/__tests__/proxy.test.ts
@@ -0,0 +1,232 @@
+/* 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')
+
+let _proxyConnects: string[]
+let _proxyServer: http.Server
+const _proxyUrl = 'http://127.0.0.1:8080'
+
+describe('proxy', () => {
+ beforeAll(async () => {
+ // Start proxy server
+ _proxyServer = proxy()
+ await new Promise(resolve => {
+ const port = Number(_proxyUrl.split(':')[2])
+ _proxyServer.listen(port, () => resolve())
+ })
+ _proxyServer.on('connect', req => {
+ _proxyConnects.push(req.url)
+ })
+ })
+
+ beforeEach(() => {
+ _proxyConnects = []
+ _clearVars()
+ })
+
+ afterEach(() => {})
+
+ afterAll(async () => {
+ _clearVars()
+
+ // Stop proxy server
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ 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'))
+ expect(bypass).toBeFalsy()
+ })
+
+ it('checkBypass returns false if empty no_proxy', () => {
+ process.env['no_proxy'] = ''
+ const 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(
+ 'http://httpbin.org/get'
+ )
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = JSON.parse(body)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ expect(_proxyConnects).toEqual(['httpbin.org:80'])
+ })
+
+ it('HttoClient does basic http get request when bypass proxy', async () => {
+ process.env['http_proxy'] = _proxyUrl
+ process.env['no_proxy'] = 'httpbin.org'
+ const httpClient = new httpm.HttpClient()
+ const 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)
+ expect(obj.url).toBe('http://httpbin.org/get')
+ expect(_proxyConnects).toHaveLength(0)
+ })
+
+ 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(
+ 'https://httpbin.org/get'
+ )
+ expect(res.message.statusCode).toBe(200)
+ const body: string = await res.readBody()
+ const obj = JSON.parse(body)
+ expect(obj.url).toBe('https://httpbin.org/get')
+ expect(_proxyConnects).toEqual(['httpbin.org:443'])
+ })
+
+ it('HttpClient does basic https get request when bypass proxy', async () => {
+ process.env['https_proxy'] = _proxyUrl
+ process.env['no_proxy'] = 'httpbin.org'
+ const httpClient = new httpm.HttpClient()
+ const 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)
+ expect(obj.url).toBe('https://httpbin.org/get')
+ expect(_proxyConnects).toHaveLength(0)
+ })
+
+ 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
+ console.log(agent)
+ expect(agent.proxyOptions.host).toBe('127.0.0.1')
+ expect(agent.proxyOptions.port).toBe('8080')
+ expect(agent.proxyOptions.proxyAuth).toBe(undefined)
+ })
+
+ 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
+ console.log(agent)
+ expect(agent.proxyOptions.host).toBe('127.0.0.1')
+ expect(agent.proxyOptions.port).toBe('8080')
+ expect(agent.proxyOptions.proxyAuth).toBe('user:password')
+ })
+})
+
+function _clearVars(): void {
+ delete process.env.http_proxy
+ delete process.env.HTTP_PROXY
+ delete process.env.https_proxy
+ delete process.env.HTTPS_PROXY
+ delete process.env.no_proxy
+ delete process.env.NO_PROXY
+}
diff --git a/packages/http-client/package-lock.json b/packages/http-client/package-lock.json
new file mode 100644
index 00000000..3c2c4e54
--- /dev/null
+++ b/packages/http-client/package-lock.json
@@ -0,0 +1,332 @@
+{
+ "name": "@actions/http-client",
+ "version": "2.0.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@actions/http-client",
+ "version": "2.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "tunnel": "^0.0.6"
+ },
+ "devDependencies": {
+ "@types/tunnel": "0.0.3",
+ "proxy": "^1.0.1"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "12.12.31",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.31.tgz",
+ "integrity": "sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg==",
+ "dev": true
+ },
+ "node_modules/@types/tunnel": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
+ "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/args": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
+ "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "5.0.0",
+ "chalk": "2.4.2",
+ "leven": "2.1.0",
+ "mri": "1.1.4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/basic-auth-parser": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
+ "integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
+ "dev": true
+ },
+ "node_modules/camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/leven": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
+ "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/proxy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
+ "integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
+ "dev": true,
+ "dependencies": {
+ "args": "5.0.1",
+ "basic-auth-parser": "0.0.2",
+ "debug": "^4.1.1"
+ },
+ "bin": {
+ "proxy": "bin/proxy.js"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/tunnel": {
+ "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": {
+ "@types/node": {
+ "version": "12.12.31",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.31.tgz",
+ "integrity": "sha512-T+wnJno8uh27G9c+1T+a1/WYCHzLeDqtsGJkoEdSp2X8RTh3oOCZQcUnjAx90CS8cmmADX51O0FI/tu9s0yssg==",
+ "dev": true
+ },
+ "@types/tunnel": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz",
+ "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "args": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/args/-/args-5.0.1.tgz",
+ "integrity": "sha512-1kqmFCFsPffavQFGt8OxJdIcETti99kySRUPMpOhaGjL6mRJn8HFU1OxKY5bMqfZKUwTQc1mZkAjmGYaVOHFtQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "5.0.0",
+ "chalk": "2.4.2",
+ "leven": "2.1.0",
+ "mri": "1.1.4"
+ }
+ },
+ "basic-auth-parser": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/basic-auth-parser/-/basic-auth-parser-0.0.2.tgz",
+ "integrity": "sha1-zp5xp38jwSee7NJlmypGJEwVbkE=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "leven": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+ "dev": true
+ },
+ "mri": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/mri/-/mri-1.1.4.tgz",
+ "integrity": "sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "proxy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/proxy/-/proxy-1.0.2.tgz",
+ "integrity": "sha512-KNac2ueWRpjbUh77OAFPZuNdfEqNynm9DD4xHT14CccGpW8wKZwEkN0yjlb7X9G9Z9F55N0Q+1z+WfgAhwYdzQ==",
+ "dev": true,
+ "requires": {
+ "args": "5.0.1",
+ "basic-auth-parser": "0.0.2",
+ "debug": "^4.1.1"
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "tunnel": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+ "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
+ }
+ }
+}
diff --git a/packages/http-client/package.json b/packages/http-client/package.json
new file mode 100644
index 00000000..c1de2213
--- /dev/null
+++ b/packages/http-client/package.json
@@ -0,0 +1,48 @@
+{
+ "name": "@actions/http-client",
+ "version": "2.0.1",
+ "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"
+ },
+ "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"
+ },
+ "bugs": {
+ "url": "https://github.com/actions/toolkit/issues"
+ },
+ "devDependencies": {
+ "@types/tunnel": "0.0.3",
+ "proxy": "^1.0.1"
+ },
+ "dependencies": {
+ "tunnel": "^0.0.6"
+ }
+}
diff --git a/packages/http-client/src/auth.ts b/packages/http-client/src/auth.ts
new file mode 100644
index 00000000..639adbe2
--- /dev/null
+++ b/packages/http-client/src/auth.ts
@@ -0,0 +1,86 @@
+import * as http from 'http'
+import * as ifm from './interfaces'
+import {HttpClientResponse} from './index'
+
+export class BasicCredentialHandler implements ifm.RequestHandler {
+ username: string
+ password: string
+
+ constructor(username: string, password: string) {
+ this.username = username
+ 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')}`
+ }
+
+ // This handler cannot handle 401
+ canHandleAuthentication(): boolean {
+ return false
+ }
+
+ async handleAuthentication(): Promise {
+ throw new Error('not implemented')
+ }
+}
+
+export class BearerCredentialHandler implements ifm.RequestHandler {
+ token: string
+
+ constructor(token: string) {
+ this.token = token
+ }
+
+ // 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}`
+ }
+
+ // This handler cannot handle 401
+ canHandleAuthentication(): boolean {
+ return false
+ }
+
+ async handleAuthentication(): Promise {
+ throw new Error('not implemented')
+ }
+}
+
+export class PersonalAccessTokenCredentialHandler
+ implements ifm.RequestHandler {
+ token: string
+
+ constructor(token: string) {
+ this.token = token
+ }
+
+ // 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')}`
+ }
+
+ // This handler cannot handle 401
+ canHandleAuthentication(): boolean {
+ return false
+ }
+
+ async handleAuthentication(): Promise {
+ throw new Error('not implemented')
+ }
+}
diff --git a/packages/http-client/src/index.ts b/packages/http-client/src/index.ts
new file mode 100644
index 00000000..f02c2754
--- /dev/null
+++ b/packages/http-client/src/index.ts
@@ -0,0 +1,773 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+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'
+
+export enum HttpCodes {
+ OK = 200,
+ MultipleChoices = 300,
+ MovedPermanently = 301,
+ ResourceMoved = 302,
+ SeeOther = 303,
+ NotModified = 304,
+ UseProxy = 305,
+ SwitchProxy = 306,
+ TemporaryRedirect = 307,
+ PermanentRedirect = 308,
+ BadRequest = 400,
+ Unauthorized = 401,
+ PaymentRequired = 402,
+ Forbidden = 403,
+ NotFound = 404,
+ MethodNotAllowed = 405,
+ NotAcceptable = 406,
+ ProxyAuthenticationRequired = 407,
+ RequestTimeout = 408,
+ Conflict = 409,
+ Gone = 410,
+ TooManyRequests = 429,
+ InternalServerError = 500,
+ NotImplemented = 501,
+ BadGateway = 502,
+ ServiceUnavailable = 503,
+ GatewayTimeout = 504
+}
+
+export enum Headers {
+ Accept = 'accept',
+ ContentType = 'content-type'
+}
+
+export enum MediaTypes {
+ ApplicationJson = 'application/json'
+}
+
+/**
+ * Returns the proxy URL, depending upon the supplied url and proxy environment variables.
+ * @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))
+ return proxyUrl ? proxyUrl.href : ''
+}
+
+const HttpRedirectCodes: number[] = [
+ HttpCodes.MovedPermanently,
+ HttpCodes.ResourceMoved,
+ HttpCodes.SeeOther,
+ HttpCodes.TemporaryRedirect,
+ HttpCodes.PermanentRedirect
+]
+const HttpResponseRetryCodes: number[] = [
+ HttpCodes.BadGateway,
+ HttpCodes.ServiceUnavailable,
+ HttpCodes.GatewayTimeout
+]
+const RetryableHttpVerbs: string[] = ['OPTIONS', 'GET', 'DELETE', 'HEAD']
+const ExponentialBackoffCeiling = 10
+const ExponentialBackoffTimeSlice = 5
+
+export class HttpClientError extends Error {
+ constructor(message: string, statusCode: number) {
+ super(message)
+ this.name = 'HttpClientError'
+ this.statusCode = statusCode
+ Object.setPrototypeOf(this, HttpClientError.prototype)
+ }
+
+ statusCode: number
+ result?: any
+}
+
+export class HttpClientResponse {
+ constructor(message: http.IncomingMessage) {
+ this.message = message
+ }
+
+ message: http.IncomingMessage
+ async readBody(): Promise {
+ return new Promise(async resolve => {
+ let output = Buffer.alloc(0)
+
+ this.message.on('data', (chunk: Buffer) => {
+ output = Buffer.concat([output, chunk])
+ })
+
+ this.message.on('end', () => {
+ resolve(output.toString())
+ })
+ })
+ }
+}
+
+export function isHttps(requestUrl: string): boolean {
+ const parsedUrl: URL = new URL(requestUrl)
+ return parsedUrl.protocol === 'https:'
+}
+
+export class HttpClient {
+ userAgent: string | undefined
+ handlers: ifm.RequestHandler[]
+ requestOptions: ifm.RequestOptions | undefined
+
+ 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
+
+ constructor(
+ userAgent?: string,
+ handlers?: ifm.RequestHandler[],
+ requestOptions?: ifm.RequestOptions
+ ) {
+ this.userAgent = userAgent
+ this.handlers = handlers || []
+ this.requestOptions = requestOptions
+ if (requestOptions) {
+ if (requestOptions.ignoreSslError != null) {
+ this._ignoreSslError = requestOptions.ignoreSslError
+ }
+
+ this._socketTimeout = requestOptions.socketTimeout
+
+ if (requestOptions.allowRedirects != null) {
+ this._allowRedirects = requestOptions.allowRedirects
+ }
+
+ if (requestOptions.allowRedirectDowngrade != null) {
+ this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade
+ }
+
+ if (requestOptions.maxRedirects != null) {
+ this._maxRedirects = Math.max(requestOptions.maxRedirects, 0)
+ }
+
+ if (requestOptions.keepAlive != null) {
+ this._keepAlive = requestOptions.keepAlive
+ }
+
+ if (requestOptions.allowRetries != null) {
+ this._allowRetries = requestOptions.allowRetries
+ }
+
+ if (requestOptions.maxRetries != null) {
+ this._maxRetries = requestOptions.maxRetries
+ }
+ }
+ }
+
+ async options(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('OPTIONS', requestUrl, null, additionalHeaders || {})
+ }
+
+ async get(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('GET', requestUrl, null, additionalHeaders || {})
+ }
+
+ async del(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('DELETE', requestUrl, null, additionalHeaders || {})
+ }
+
+ async post(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('POST', requestUrl, data, additionalHeaders || {})
+ }
+
+ async patch(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('PATCH', requestUrl, data, additionalHeaders || {})
+ }
+
+ async put(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('PUT', requestUrl, data, additionalHeaders || {})
+ }
+
+ async head(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request('HEAD', requestUrl, null, additionalHeaders || {})
+ }
+
+ async sendStream(
+ verb: string,
+ requestUrl: string,
+ stream: NodeJS.ReadableStream,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise {
+ return this.request(verb, requestUrl, stream, additionalHeaders)
+ }
+
+ /**
+ * Gets a typed object from an endpoint
+ * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise
+ */
+ async getJson(
+ requestUrl: string,
+ additionalHeaders: http.OutgoingHttpHeaders = {}
+ ): Promise> {
+ additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.Accept,
+ MediaTypes.ApplicationJson
+ )
+ const res: HttpClientResponse = await this.get(
+ requestUrl,
+ additionalHeaders
+ )
+ return this._processResponse(res, this.requestOptions)
+ }
+
+ async postJson(
+ requestUrl: string,
+ obj: any,
+ additionalHeaders: http.OutgoingHttpHeaders = {}
+ ): Promise> {
+ const data: string = JSON.stringify(obj, null, 2)
+ additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.Accept,
+ MediaTypes.ApplicationJson
+ )
+ additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.ContentType,
+ MediaTypes.ApplicationJson
+ )
+ const res: HttpClientResponse = await this.post(
+ requestUrl,
+ data,
+ additionalHeaders
+ )
+ return this._processResponse(res, this.requestOptions)
+ }
+
+ async putJson(
+ requestUrl: string,
+ obj: any,
+ additionalHeaders: http.OutgoingHttpHeaders = {}
+ ): Promise> {
+ const data: string = JSON.stringify(obj, null, 2)
+ additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.Accept,
+ MediaTypes.ApplicationJson
+ )
+ additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.ContentType,
+ MediaTypes.ApplicationJson
+ )
+ const res: HttpClientResponse = await this.put(
+ requestUrl,
+ data,
+ additionalHeaders
+ )
+ return this._processResponse(res, this.requestOptions)
+ }
+
+ async patchJson(
+ requestUrl: string,
+ obj: any,
+ additionalHeaders: http.OutgoingHttpHeaders = {}
+ ): Promise> {
+ const data: string = JSON.stringify(obj, null, 2)
+ additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.Accept,
+ MediaTypes.ApplicationJson
+ )
+ additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(
+ additionalHeaders,
+ Headers.ContentType,
+ MediaTypes.ApplicationJson
+ )
+ const res: HttpClientResponse = await this.patch(
+ requestUrl,
+ data,
+ additionalHeaders
+ )
+ return this._processResponse(res, this.requestOptions)
+ }
+
+ /**
+ * Makes a raw http request.
+ * All other methods such as get, post, patch, and request ultimately call this.
+ * Prefer get, del, post and patch
+ */
+ async request(
+ verb: string,
+ requestUrl: string,
+ data: string | NodeJS.ReadableStream | null,
+ headers?: http.OutgoingHttpHeaders
+ ): Promise {
+ 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)
+
+ // Only perform retries on reads since writes may not be idempotent.
+ const maxTries: number =
+ this._allowRetries && RetryableHttpVerbs.includes(verb)
+ ? this._maxRetries + 1
+ : 1
+ let numTries = 0
+
+ let response: HttpClientResponse | undefined
+ do {
+ response = await this.requestRaw(info, data)
+
+ // Check if it's an authentication challenge
+ if (
+ response &&
+ response.message &&
+ response.message.statusCode === HttpCodes.Unauthorized
+ ) {
+ let authenticationHandler: ifm.RequestHandler | undefined
+
+ for (const handler of this.handlers) {
+ if (handler.canHandleAuthentication(response)) {
+ authenticationHandler = handler
+ break
+ }
+ }
+
+ if (authenticationHandler) {
+ return authenticationHandler.handleAuthentication(this, info, data)
+ } else {
+ // We have received an unauthorized response but have no handlers to handle it.
+ // Let the response return to the caller.
+ return response
+ }
+ }
+
+ let redirectsRemaining: number = this._maxRedirects
+ while (
+ response.message.statusCode &&
+ HttpRedirectCodes.includes(response.message.statusCode) &&
+ this._allowRedirects &&
+ redirectsRemaining > 0
+ ) {
+ const redirectUrl: string | undefined =
+ response.message.headers['location']
+ if (!redirectUrl) {
+ // if there's no location to redirect to, we won't
+ break
+ }
+ const parsedRedirectUrl = new URL(redirectUrl)
+ if (
+ parsedUrl.protocol === 'https:' &&
+ parsedUrl.protocol !== parsedRedirectUrl.protocol &&
+ !this._allowRedirectDowngrade
+ ) {
+ throw new Error(
+ 'Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'
+ )
+ }
+
+ // we need to finish reading the response before reassigning response
+ // which will leak the open socket.
+ await response.readBody()
+
+ // strip authorization header if redirected to a different hostname
+ if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
+ for (const header in headers) {
+ // header names are case insensitive
+ if (header.toLowerCase() === 'authorization') {
+ delete headers[header]
+ }
+ }
+ }
+
+ // let's make the request with the new redirectUrl
+ info = this._prepareRequest(verb, parsedRedirectUrl, headers)
+ response = await this.requestRaw(info, data)
+ redirectsRemaining--
+ }
+
+ if (
+ !response.message.statusCode ||
+ !HttpResponseRetryCodes.includes(response.message.statusCode)
+ ) {
+ // If not a retry code, return immediately instead of retrying
+ return response
+ }
+
+ numTries += 1
+
+ if (numTries < maxTries) {
+ await response.readBody()
+ await this._performExponentialBackoff(numTries)
+ }
+ } while (numTries < maxTries)
+
+ return response
+ }
+
+ /**
+ * Needs to be called if keepAlive is set to true in request options.
+ */
+ dispose(): void {
+ if (this._agent) {
+ this._agent.destroy()
+ }
+
+ this._disposed = true
+ }
+
+ /**
+ * Raw request.
+ * @param info
+ * @param data
+ */
+ async requestRaw(
+ info: ifm.RequestInfo,
+ data: string | NodeJS.ReadableStream | null
+ ): Promise {
+ return new Promise((resolve, reject) => {
+ function callbackForResult(err?: Error, res?: HttpClientResponse): void {
+ 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)
+ }
+ }
+
+ this.requestRawWithCallback(info, data, callbackForResult)
+ })
+ }
+
+ /**
+ * Raw request with callback.
+ * @param info
+ * @param data
+ * @param onResult
+ */
+ requestRawWithCallback(
+ info: ifm.RequestInfo,
+ data: string | NodeJS.ReadableStream | null,
+ onResult: (err?: Error, res?: HttpClientResponse) => void
+ ): void {
+ 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 {
+ if (!callbackCalled) {
+ callbackCalled = true
+ onResult(err, res)
+ }
+ }
+
+ const req: http.ClientRequest = info.httpModule.request(
+ info.options,
+ (msg: http.IncomingMessage) => {
+ const res: HttpClientResponse = new HttpClientResponse(msg)
+ handleResult(undefined, res)
+ }
+ )
+
+ let socket: net.Socket
+ req.on('socket', sock => {
+ socket = sock
+ })
+
+ // If we ever get disconnected, we want the socket to timeout eventually
+ req.setTimeout(this._socketTimeout || 3 * 60000, () => {
+ if (socket) {
+ socket.end()
+ }
+ handleResult(new Error(`Request timeout: ${info.options.path}`))
+ })
+
+ req.on('error', function(err) {
+ // err has statusCode property
+ // res should have headers
+ handleResult(err)
+ })
+
+ if (data && typeof data === 'string') {
+ req.write(data, 'utf8')
+ }
+
+ if (data && typeof data !== 'string') {
+ data.on('close', function() {
+ req.end()
+ })
+
+ data.pipe(req)
+ } else {
+ req.end()
+ }
+ }
+
+ /**
+ * Gets an http agent. This function is useful when you need an http agent that handles
+ * 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)
+ return this._getAgent(parsedUrl)
+ }
+
+ private _prepareRequest(
+ method: string,
+ requestUrl: URL,
+ headers?: http.OutgoingHttpHeaders
+ ): ifm.RequestInfo {
+ const info: ifm.RequestInfo = {}
+
+ info.parsedUrl = requestUrl
+ const usingSsl: boolean = info.parsedUrl.protocol === 'https:'
+ info.httpModule = usingSsl ? https : http
+ const defaultPort: number = usingSsl ? 443 : 80
+
+ info.options = {}
+ info.options.host = info.parsedUrl.hostname
+ info.options.port = info.parsedUrl.port
+ ? parseInt(info.parsedUrl.port)
+ : defaultPort
+ info.options.path =
+ (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '')
+ info.options.method = method
+ info.options.headers = this._mergeHeaders(headers)
+ if (this.userAgent != null) {
+ info.options.headers['user-agent'] = this.userAgent
+ }
+
+ info.options.agent = this._getAgent(info.parsedUrl)
+
+ // gives handlers an opportunity to participate
+ if (this.handlers) {
+ for (const handler of this.handlers) {
+ handler.prepareRequest(info.options)
+ }
+ }
+
+ return info
+ }
+
+ private _mergeHeaders(
+ headers?: http.OutgoingHttpHeaders
+ ): http.OutgoingHttpHeaders {
+ if (this.requestOptions && this.requestOptions.headers) {
+ return Object.assign(
+ {},
+ lowercaseKeys(this.requestOptions.headers),
+ lowercaseKeys(headers || {})
+ )
+ }
+
+ return lowercaseKeys(headers || {})
+ }
+
+ private _getExistingOrDefaultHeader(
+ additionalHeaders: http.OutgoingHttpHeaders,
+ header: string,
+ _default: string
+ ): string | number | string[] {
+ let clientHeader: string | undefined
+ if (this.requestOptions && this.requestOptions.headers) {
+ clientHeader = lowercaseKeys(this.requestOptions.headers)[header]
+ }
+ return additionalHeaders[header] || clientHeader || _default
+ }
+
+ private _getAgent(parsedUrl: URL): http.Agent {
+ let agent
+ const proxyUrl = pm.getProxyUrl(parsedUrl)
+ const useProxy = proxyUrl && proxyUrl.hostname
+
+ if (this._keepAlive && useProxy) {
+ agent = this._proxyAgent
+ }
+
+ if (this._keepAlive && !useProxy) {
+ agent = this._agent
+ }
+
+ // if agent is already assigned use that agent.
+ if (agent) {
+ return agent
+ }
+
+ const usingSsl = parsedUrl.protocol === 'https:'
+ let maxSockets = 100
+ 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) {
+ const agentOptions = {
+ maxSockets,
+ keepAlive: this._keepAlive,
+ proxy: {
+ ...((proxyUrl.username || proxyUrl.password) && {
+ proxyAuth: `${proxyUrl.username}:${proxyUrl.password}`
+ }),
+ host: proxyUrl.hostname,
+ port: proxyUrl.port
+ }
+ }
+
+ let tunnelAgent: Function
+ const overHttps = proxyUrl.protocol === 'https:'
+ if (usingSsl) {
+ tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp
+ } else {
+ tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp
+ }
+
+ agent = tunnelAgent(agentOptions)
+ this._proxyAgent = agent
+ }
+
+ // 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}
+ agent = usingSsl ? new https.Agent(options) : new http.Agent(options)
+ this._agent = agent
+ }
+
+ // if not using private agent and tunnel agent isn't setup then use global agent
+ if (!agent) {
+ agent = usingSsl ? https.globalAgent : http.globalAgent
+ }
+
+ if (usingSsl && this._ignoreSslError) {
+ // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
+ // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
+ // we have to cast it to any and change it directly
+ agent.options = Object.assign(agent.options || {}, {
+ rejectUnauthorized: false
+ })
+ }
+
+ return agent
+ }
+
+ private async _performExponentialBackoff(retryNumber: number): Promise {
+ retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber)
+ const ms: number = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber)
+ return new Promise(resolve => setTimeout(() => resolve(), ms))
+ }
+
+ private async _processResponse(
+ res: HttpClientResponse,
+ options?: ifm.RequestOptions
+ ): Promise> {
+ return new Promise>(async (resolve, reject) => {
+ const statusCode = res.message.statusCode || 0
+
+ const response: ifm.TypedResponse = {
+ statusCode,
+ result: null,
+ headers: {}
+ }
+
+ // not found leads to null obj returned
+ 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
+
+ try {
+ contents = await res.readBody()
+ if (contents && contents.length > 0) {
+ if (options && options.deserializeDates) {
+ obj = JSON.parse(contents, dateTimeDeserializer)
+ } else {
+ obj = JSON.parse(contents)
+ }
+
+ response.result = obj
+ }
+
+ response.headers = res.message.headers
+ } catch (err) {
+ // Invalid resource (contents not json); leaving result obj null
+ }
+
+ // note that 3xx redirects are handled by the http layer.
+ if (statusCode > 299) {
+ let msg: string
+
+ // if exception/error in body, attempt to get better error
+ if (obj && obj.message) {
+ msg = obj.message
+ } else if (contents && contents.length > 0) {
+ // it may be the case that the exception is in the body message as string
+ msg = contents
+ } else {
+ msg = `Failed request: (${statusCode})`
+ }
+
+ const err = new HttpClientError(msg, statusCode)
+ err.result = response.result
+
+ reject(err)
+ } else {
+ resolve(response)
+ }
+ })
+ }
+}
+
+const lowercaseKeys = (obj: {[index: string]: any}): any =>
+ Object.keys(obj).reduce((c: any, k) => ((c[k.toLowerCase()] = obj[k]), c), {})
diff --git a/packages/http-client/src/interfaces.ts b/packages/http-client/src/interfaces.ts
new file mode 100644
index 00000000..96b0fec7
--- /dev/null
+++ b/packages/http-client/src/interfaces.ts
@@ -0,0 +1,91 @@
+import * as http from 'http'
+import * as https from 'https'
+import {HttpClientResponse} from './index'
+
+export interface HttpClient {
+ options(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ get(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ del(
+ requestUrl: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ post(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ patch(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ put(
+ requestUrl: string,
+ data: string,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ sendStream(
+ verb: string,
+ requestUrl: string,
+ stream: NodeJS.ReadableStream,
+ additionalHeaders?: http.OutgoingHttpHeaders
+ ): Promise
+ request(
+ verb: string,
+ requestUrl: string,
+ data: string | NodeJS.ReadableStream,
+ headers: http.OutgoingHttpHeaders
+ ): Promise
+ requestRaw(
+ info: RequestInfo,
+ data: string | NodeJS.ReadableStream
+ ): Promise
+ requestRawWithCallback(
+ info: RequestInfo,
+ data: string | NodeJS.ReadableStream,
+ onResult: (err?: Error, res?: HttpClientResponse) => void
+ ): void
+}
+
+export interface RequestHandler {
+ prepareRequest(options: http.RequestOptions): void
+ canHandleAuthentication(response: HttpClientResponse): boolean
+ handleAuthentication(
+ httpClient: HttpClient,
+ requestInfo: RequestInfo,
+ data: string | NodeJS.ReadableStream | null
+ ): Promise
+}
+
+export interface RequestInfo {
+ options: http.RequestOptions
+ parsedUrl: URL
+ httpModule: typeof http | typeof https
+}
+
+export interface RequestOptions {
+ headers?: http.OutgoingHttpHeaders
+ socketTimeout?: number
+ ignoreSslError?: boolean
+ allowRedirects?: boolean
+ allowRedirectDowngrade?: boolean
+ maxRedirects?: number
+ maxSockets?: number
+ keepAlive?: boolean
+ deserializeDates?: boolean
+ // Allows retries only on Read operations (since writes may not be idempotent)
+ allowRetries?: boolean
+ maxRetries?: number
+}
+
+export interface TypedResponse {
+ statusCode: number
+ result: T | null
+ headers: http.IncomingHttpHeaders
+}
diff --git a/packages/http-client/src/proxy.ts b/packages/http-client/src/proxy.ts
new file mode 100644
index 00000000..f13409b5
--- /dev/null
+++ b/packages/http-client/src/proxy.ts
@@ -0,0 +1,60 @@
+export function getProxyUrl(reqUrl: URL): URL | undefined {
+ const usingSsl = reqUrl.protocol === 'https:'
+
+ if (checkBypass(reqUrl)) {
+ return undefined
+ }
+
+ const proxyVar = (() => {
+ if (usingSsl) {
+ return process.env['https_proxy'] || process.env['HTTPS_PROXY']
+ } else {
+ return process.env['http_proxy'] || process.env['HTTP_PROXY']
+ }
+ })()
+
+ if (proxyVar) {
+ return new URL(proxyVar)
+ } else {
+ return undefined
+ }
+}
+
+export function checkBypass(reqUrl: URL): boolean {
+ if (!reqUrl.hostname) {
+ return false
+ }
+
+ const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
+ if (!noProxy) {
+ return false
+ }
+
+ // Determine the request port
+ let reqPort: number | undefined
+ if (reqUrl.port) {
+ reqPort = Number(reqUrl.port)
+ } else if (reqUrl.protocol === 'http:') {
+ reqPort = 80
+ } else if (reqUrl.protocol === 'https:') {
+ reqPort = 443
+ }
+
+ // Format the request hostname and hostname with port
+ const upperReqHosts = [reqUrl.hostname.toUpperCase()]
+ if (typeof reqPort === 'number') {
+ upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`)
+ }
+
+ // Compare request host against noproxy
+ for (const upperNoProxyItem of noProxy
+ .split(',')
+ .map(x => x.trim().toUpperCase())
+ .filter(x => x)) {
+ if (upperReqHosts.some(x => x === upperNoProxyItem)) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/packages/http-client/tsconfig.json b/packages/http-client/tsconfig.json
new file mode 100644
index 00000000..4abc2b1c
--- /dev/null
+++ b/packages/http-client/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./lib",
+ "rootDir": "./src",
+ "moduleResolution": "node"
+ },
+ "include": [
+ "./src"
+ ]
+}
\ No newline at end of file
diff --git a/packages/tool-cache/RELEASES.md b/packages/tool-cache/RELEASES.md
index c613f183..9fdd4898 100644
--- a/packages/tool-cache/RELEASES.md
+++ b/packages/tool-cache/RELEASES.md
@@ -1,5 +1,13 @@
# @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)
diff --git a/packages/tool-cache/package-lock.json b/packages/tool-cache/package-lock.json
index 524d1345..1c7dfe7f 100644
--- a/packages/tool-cache/package-lock.json
+++ b/packages/tool-cache/package-lock.json
@@ -1,17 +1,17 @@
{
"name": "@actions/tool-cache",
- "version": "1.7.2",
+ "version": "2.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@actions/tool-cache",
- "version": "1.7.2",
+ "version": "2.0.0",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0",
- "@actions/http-client": "^1.0.8",
+ "@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"semver": "^6.1.0",
"uuid": "^3.3.2"
@@ -24,30 +24,41 @@
}
},
"node_modules/@actions/core": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
- "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
+ "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"
+ }
},
"node_modules/@actions/exec": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz",
- "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
+ "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/http-client": {
- "version": "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==",
+ "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"
+ "tunnel": "^0.0.6"
}
},
"node_modules/@actions/io": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.1.tgz",
- "integrity": "sha512-Qi4JoKXjmE0O67wAOH6y0n26QXhMKMFo7GD/4IXNVcrtLjUlGjGuVys6pQgwF3ArfGTQu0XpqaNr0YhED2RaRA=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
+ "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
},
"node_modules/@types/nock": {
"version": "10.0.3",
@@ -280,30 +291,43 @@
},
"dependencies": {
"@actions/core": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz",
- "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA=="
+ "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"
+ }
+ }
+ }
},
"@actions/exec": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.0.3.tgz",
- "integrity": "sha512-TogJGnueOmM7ntCi0ASTUj4LapRRtDfj57Ja4IhPmg2fls28uVOPbAn8N+JifaOumN2UG3oEO/Ixek2A4NcYSA==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
+ "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/http-client": {
- "version": "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==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
+ "integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
"requires": {
- "tunnel": "0.0.6"
+ "tunnel": "^0.0.6"
}
},
"@actions/io": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.1.tgz",
- "integrity": "sha512-Qi4JoKXjmE0O67wAOH6y0n26QXhMKMFo7GD/4IXNVcrtLjUlGjGuVys6pQgwF3ArfGTQu0XpqaNr0YhED2RaRA=="
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
+ "integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
},
"@types/nock": {
"version": "10.0.3",
diff --git a/packages/tool-cache/package.json b/packages/tool-cache/package.json
index da75109d..c7744d58 100644
--- a/packages/tool-cache/package.json
+++ b/packages/tool-cache/package.json
@@ -1,6 +1,6 @@
{
"name": "@actions/tool-cache",
- "version": "1.7.2",
+ "version": "2.0.1",
"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": "^1.0.8",
+ "@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"semver": "^6.1.0",
"uuid": "^3.3.2"
diff --git a/packages/tool-cache/src/tool-cache.ts b/packages/tool-cache/src/tool-cache.ts
index 92fd519a..694d1252 100644
--- a/packages/tool-cache/src/tool-cache.ts
+++ b/packages/tool-cache/src/tool-cache.ts
@@ -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?: IHeaders
+ headers?: OutgoingHttpHeaders
): Promise {
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?: IHeaders
+ headers?: OutgoingHttpHeaders
): Promise {
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: IHeaders = {}
+ const headers: OutgoingHttpHeaders = {}
if (auth) {
core.debug('set auth')
headers.authorization = auth
diff --git a/scripts/create-package b/scripts/create-package
index ed38d73a..29d07feb 100755
--- a/scripts/create-package
+++ b/scripts/create-package
@@ -9,5 +9,5 @@ if [[ -z "$name" ]]; then
exit 1
fi
-lerna create @actions/$name
-cp packages/toolkit/tsconfig.json packages/$name/tsconfig.json
\ No newline at end of file
+npx lerna create @actions/$name
+cp packages/core/tsconfig.json packages/$name/tsconfig.json
\ No newline at end of file