1
0
Fork 0

Merge branch 'main' into kotewar/updating-blobstorage-client-to-12.13.0

pull/1363/head
Sankalp Kotewar 2023-03-09 00:33:52 +05:30 committed by GitHub
commit fe92749762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 125 additions and 10 deletions

View File

@ -153,6 +153,10 @@
- Fix zstd not being used due to `zstd --version` output change in zstd 1.5.4 release. See [#1353](https://github.com/actions/toolkit/pull/1353). - Fix zstd not being used due to `zstd --version` output change in zstd 1.5.4 release. See [#1353](https://github.com/actions/toolkit/pull/1353).
### 3.1.5 ### 3.2.0
- Add `lookupOnly` to cache restore `DownloadOptions`.
### 3.2.1
- Updated @azure/storage-blob to `v12.13.0` - Updated @azure/storage-blob to `v12.13.0`

View File

@ -9,6 +9,7 @@ const useAzureSdk = true
const downloadConcurrency = 8 const downloadConcurrency = 8
const timeoutInMs = 30000 const timeoutInMs = 30000
const segmentTimeoutInMs = 600000 const segmentTimeoutInMs = 600000
const lookupOnly = false
const uploadConcurrency = 4 const uploadConcurrency = 4
const uploadChunkSize = 32 * 1024 * 1024 const uploadChunkSize = 32 * 1024 * 1024
@ -19,7 +20,8 @@ test('getDownloadOptions sets defaults', async () => {
useAzureSdk, useAzureSdk,
downloadConcurrency, downloadConcurrency,
timeoutInMs, timeoutInMs,
segmentTimeoutInMs segmentTimeoutInMs,
lookupOnly
}) })
}) })
@ -28,7 +30,8 @@ test('getDownloadOptions overrides all settings', async () => {
useAzureSdk: false, useAzureSdk: false,
downloadConcurrency: 14, downloadConcurrency: 14,
timeoutInMs: 20000, timeoutInMs: 20000,
segmentTimeoutInMs: 3600000 segmentTimeoutInMs: 3600000,
lookupOnly: true
} }
const actualOptions = getDownloadOptions(expectedOptions) const actualOptions = getDownloadOptions(expectedOptions)
@ -61,7 +64,8 @@ test('getDownloadOptions overrides download timeout minutes', async () => {
useAzureSdk: false, useAzureSdk: false,
downloadConcurrency: 14, downloadConcurrency: 14,
timeoutInMs: 20000, timeoutInMs: 20000,
segmentTimeoutInMs: 3600000 segmentTimeoutInMs: 3600000,
lookupOnly: true
} }
process.env.SEGMENT_DOWNLOAD_TIMEOUT_MINS = '10' process.env.SEGMENT_DOWNLOAD_TIMEOUT_MINS = '10'
const actualOptions = getDownloadOptions(expectedOptions) const actualOptions = getDownloadOptions(expectedOptions)
@ -72,4 +76,5 @@ test('getDownloadOptions overrides download timeout minutes', async () => {
) )
expect(actualOptions.timeoutInMs).toEqual(expectedOptions.timeoutInMs) expect(actualOptions.timeoutInMs).toEqual(expectedOptions.timeoutInMs)
expect(actualOptions.segmentTimeoutInMs).toEqual(600000) expect(actualOptions.segmentTimeoutInMs).toEqual(600000)
expect(actualOptions.lookupOnly).toEqual(expectedOptions.lookupOnly)
}) })

View File

@ -276,3 +276,39 @@ test('restore with cache found for restore key', async () => {
expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression) expect(extractTarMock).toHaveBeenCalledWith(archivePath, compression)
expect(getCompressionMock).toHaveBeenCalledTimes(1) expect(getCompressionMock).toHaveBeenCalledTimes(1)
}) })
test('restore with dry run', async () => {
const paths = ['node_modules']
const key = 'node-test'
const options = {lookupOnly: true}
const cacheEntry: ArtifactCacheEntry = {
cacheKey: key,
scope: 'refs/heads/main',
archiveLocation: 'www.actionscache.test/download'
}
const getCacheMock = jest.spyOn(cacheHttpClient, 'getCacheEntry')
getCacheMock.mockImplementation(async () => {
return Promise.resolve(cacheEntry)
})
const createTempDirectoryMock = jest.spyOn(cacheUtils, 'createTempDirectory')
const downloadCacheMock = jest.spyOn(cacheHttpClient, 'downloadCache')
const compression = CompressionMethod.Gzip
const getCompressionMock = jest
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValue(Promise.resolve(compression))
const cacheKey = await restoreCache(paths, key, undefined, options)
expect(cacheKey).toBe(key)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
expect(getCacheMock).toHaveBeenCalledWith([key], paths, {
compressionMethod: compression,
enableCrossOsArchive: false
})
// creating a tempDir and downloading the cache are skipped
expect(createTempDirectoryMock).toHaveBeenCalledTimes(0)
expect(downloadCacheMock).toHaveBeenCalledTimes(0)
})

4
packages/cache/package-lock.json generated vendored
View File

@ -1,12 +1,12 @@
{ {
"name": "@actions/cache", "name": "@actions/cache",
"version": "3.1.5", "version": "3.2.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@actions/cache", "name": "@actions/cache",
"version": "3.1.5", "version": "3.2.1",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.10.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@actions/cache", "name": "@actions/cache",
"version": "3.1.5", "version": "3.2.1",
"preview": true, "preview": true,
"description": "Actions cache lib", "description": "Actions cache lib",
"keywords": [ "keywords": [

View File

@ -100,6 +100,11 @@ export async function restoreCache(
return undefined return undefined
} }
if (options?.lookupOnly) {
core.info('Lookup only - skipping download')
return cacheEntry.cacheKey
}
archivePath = path.join( archivePath = path.join(
await utils.createTempDirectory(), await utils.createTempDirectory(),
utils.getCacheFileName(compressionMethod) utils.getCacheFileName(compressionMethod)

View File

@ -53,6 +53,15 @@ export interface DownloadOptions {
* @default 3600000 * @default 3600000
*/ */
segmentTimeoutInMs?: number segmentTimeoutInMs?: number
/**
* Weather to skip downloading the cache entry.
* If lookupOnly is set to true, the restore function will only check if
* a matching cache entry exists and return the cache key if it does.
*
* @default false
*/
lookupOnly?: boolean
} }
/** /**
@ -93,6 +102,7 @@ export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions {
downloadConcurrency: 8, downloadConcurrency: 8,
timeoutInMs: 30000, timeoutInMs: 30000,
segmentTimeoutInMs: 600000 segmentTimeoutInMs: 600000
lookupOnly: false
} }
if (copy) { if (copy) {
@ -111,6 +121,10 @@ export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions {
if (typeof copy.segmentTimeoutInMs === 'number') { if (typeof copy.segmentTimeoutInMs === 'number') {
result.segmentTimeoutInMs = copy.segmentTimeoutInMs result.segmentTimeoutInMs = copy.segmentTimeoutInMs
} }
if (typeof copy.lookupOnly === 'boolean') {
result.lookupOnly = copy.lookupOnly
}
} }
const segmentDownloadTimeoutMins = const segmentDownloadTimeoutMins =
process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS'] process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']
@ -129,6 +143,7 @@ export function getDownloadOptions(copy?: DownloadOptions): DownloadOptions {
`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}` `Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`
) )
core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`) core.debug(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`)
core.debug(`Lookup only: ${result.lookupOnly}`)
return result return result
} }

View File

@ -1,5 +1,9 @@
## Releases ## Releases
## 2.1.0
- Add support for `*` and subdomains in `no_proxy` [#1355](https://github.com/actions/toolkit/pull/1355) [#1223](https://github.com/actions/toolkit/pull/1223)
- Bypass proxy for loopback IP adresses [#1360](https://github.com/actions/toolkit/pull/1360))
## 2.0.1 ## 2.0.1
- Fix an issue with missing `tunnel` dependency [#1085](https://github.com/actions/toolkit/pull/1085) - Fix an issue with missing `tunnel` dependency [#1085](https://github.com/actions/toolkit/pull/1085)

View File

@ -176,11 +176,16 @@ describe('proxy', () => {
expect(bypass).toBeTruthy() expect(bypass).toBeTruthy()
}) })
// Do not match wildcard ("*") as per https://github.com/actions/runner/blob/97195bad5870e2ad0915ebfef1616083aacf5818/docs/adrs/0263-proxy-support.md
it('checkBypass returns true if no_proxy is "*"', () => { it('checkBypass returns true if no_proxy is "*"', () => {
process.env['no_proxy'] = '*' process.env['no_proxy'] = '*'
const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com')) const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com'))
expect(bypass).toBeFalsy() expect(bypass).toBeTruthy()
})
it('checkBypass returns true if no_proxy contains comma separated "*"', () => {
process.env['no_proxy'] = 'domain.com,* , example.com'
const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com'))
expect(bypass).toBeTruthy()
}) })
it('HttpClient does basic http get request through proxy', async () => { it('HttpClient does basic http get request through proxy', async () => {
@ -237,6 +242,31 @@ describe('proxy', () => {
expect(_proxyConnects).toHaveLength(0) expect(_proxyConnects).toHaveLength(0)
}) })
it('HttpClient bypasses proxy for loopback addresses (localhost, ::1, 127.*)', async () => {
// setup a server listening on localhost:8091
const server = http.createServer((request, response) => {
response.writeHead(200)
request.pipe(response)
})
server.listen(8091)
try {
process.env['http_proxy'] = _proxyUrl
const httpClient = new httpm.HttpClient()
let res = await httpClient.get('http://localhost:8091')
expect(res.message.statusCode).toBe(200)
res = await httpClient.get('http://127.0.0.1:8091')
expect(res.message.statusCode).toBe(200)
// no support for ipv6 for now
expect(httpClient.get('http://[::1]:8091')).rejects.toThrow()
// proxy at _proxyUrl was ignored
expect(_proxyConnects).toEqual([])
} finally {
server.close()
}
})
it('proxyAuth not set in tunnel agent when authentication is not provided', async () => { it('proxyAuth not set in tunnel agent when authentication is not provided', async () => {
process.env['https_proxy'] = 'http://127.0.0.1:8080' process.env['https_proxy'] = 'http://127.0.0.1:8080'
const httpClient = new httpm.HttpClient() const httpClient = new httpm.HttpClient()

View File

@ -1,6 +1,6 @@
{ {
"name": "@actions/http-client", "name": "@actions/http-client",
"version": "2.0.1", "version": "2.1.0",
"description": "Actions Http Client", "description": "Actions Http Client",
"keywords": [ "keywords": [
"github", "github",

View File

@ -25,6 +25,11 @@ export function checkBypass(reqUrl: URL): boolean {
return false return false
} }
const reqHost = reqUrl.hostname
if (isLoopbackAddress(reqHost)) {
return true
}
const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '' const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''
if (!noProxy) { if (!noProxy) {
return false return false
@ -52,6 +57,7 @@ export function checkBypass(reqUrl: URL): boolean {
.map(x => x.trim().toUpperCase()) .map(x => x.trim().toUpperCase())
.filter(x => x)) { .filter(x => x)) {
if ( if (
upperNoProxyItem === '*' ||
upperReqHosts.some( upperReqHosts.some(
x => x =>
x === upperNoProxyItem || x === upperNoProxyItem ||
@ -66,3 +72,13 @@ export function checkBypass(reqUrl: URL): boolean {
return false return false
} }
function isLoopbackAddress(host: string): boolean {
const hostLower = host.toLowerCase()
return (
hostLower === 'localhost' ||
hostLower.startsWith('127.') ||
hostLower.startsWith('[::1]') ||
hostLower.startsWith('[0:0:0:0:0:0:0:1]')
)
}