Compare commits

..

2 Commits

Author SHA1 Message Date
Luke Tomlinson 73d8e12de2 Fix package-lock.json 2022-03-31 11:10:17 -04:00
Luke Tomlinson 31a98126a0 Update @actions/github to 5.0.1 2022-03-31 10:44:15 -04:00
17 changed files with 3186 additions and 16638 deletions
+3107 -16397
View File
File diff suppressed because it is too large Load Diff
+1 -33
View File
@@ -1,6 +1,5 @@
import {retry, retryTypedResponse} from '../src/internal/requestUtils'
import {retry} from '../src/internal/requestUtils'
import {HttpClientError} from '@actions/http-client'
import * as requestUtils from '../src/internal/requestUtils'
interface ITestResponse {
statusCode: number
@@ -146,34 +145,3 @@ test('retry converts errors to response object', async () => {
null
)
})
test('retryTypedResponse gives an error with error message', async () => {
const httpClientError = new HttpClientError(
'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes',
400
)
jest.spyOn(requestUtils, 'retry').mockReturnValue(
new Promise(resolve => {
resolve(httpClientError)
})
)
try {
await retryTypedResponse<string>(
'reserveCache',
async () =>
new Promise(resolve => {
resolve({
statusCode: 400,
result: '',
headers: {},
error: httpClientError
})
})
)
} catch (error) {
expect(error).toHaveProperty(
'message',
'The cache filesize must be between 0 and 10 * 1024 * 1024 bytes'
)
}
})
+13 -117
View File
@@ -5,12 +5,6 @@ import * as cacheHttpClient from '../src/internal/cacheHttpClient'
import * as cacheUtils from '../src/internal/cacheUtils'
import {CacheFilename, CompressionMethod} from '../src/internal/constants'
import * as tar from '../src/internal/tar'
import {ITypedResponse} from '@actions/http-client/interfaces'
import {
ReserveCacheResponse,
ITypedResponseWithError
} from '../src/internal/contracts'
import {HttpClientError} from '@actions/http-client'
jest.mock('../src/internal/cacheHttpClient')
jest.mock('../src/internal/cacheUtils')
@@ -22,13 +16,16 @@ beforeAll(() => {
jest.spyOn(core, 'info').mockImplementation(() => {})
jest.spyOn(core, 'warning').mockImplementation(() => {})
jest.spyOn(core, 'error').mockImplementation(() => {})
jest.spyOn(cacheUtils, 'getCacheFileName').mockImplementation(cm => {
const actualUtils = jest.requireActual('../src/internal/cacheUtils')
return actualUtils.getCacheFileName(cm)
})
jest.spyOn(cacheUtils, 'resolvePaths').mockImplementation(async filePaths => {
return filePaths.map(x => path.resolve(x))
})
jest.spyOn(cacheUtils, 'createTempDirectory').mockImplementation(async () => {
return Promise.resolve('/foo/bar')
})
@@ -73,98 +70,6 @@ test('save with large cache outputs should fail', async () => {
expect(getCompressionMock).toHaveBeenCalledTimes(1)
})
test('save with large cache outputs should fail in GHES with error message', async () => {
const filePath = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const cachePaths = [path.resolve(filePath)]
const createTarMock = jest.spyOn(tar, 'createTar')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
.mockReturnValueOnce(cacheSize)
const compression = CompressionMethod.Gzip
const getCompressionMock = jest
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValueOnce(Promise.resolve(compression))
jest.spyOn(cacheUtils, 'isGhes').mockReturnValueOnce(true)
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: ITypedResponseWithError<ReserveCacheResponse> = {
statusCode: 400,
result: null,
headers: {},
error: new HttpClientError(
'The cache filesize must be between 0 and 1073741824 bytes',
400
)
}
return response
})
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'The cache filesize must be between 0 and 1073741824 bytes'
)
const archiveFolder = '/foo/bar'
expect(reserveCacheMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledWith(
archiveFolder,
cachePaths,
compression
)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
})
test('save with large cache outputs should fail in GHES without error message', async () => {
const filePath = 'node_modules'
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
const cachePaths = [path.resolve(filePath)]
const createTarMock = jest.spyOn(tar, 'createTar')
const cacheSize = 11 * 1024 * 1024 * 1024 //~11GB, over the 10GB limit
jest
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
.mockReturnValueOnce(cacheSize)
const compression = CompressionMethod.Gzip
const getCompressionMock = jest
.spyOn(cacheUtils, 'getCompressionMethod')
.mockReturnValueOnce(Promise.resolve(compression))
jest.spyOn(cacheUtils, 'isGhes').mockReturnValueOnce(true)
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: ITypedResponseWithError<ReserveCacheResponse> = {
statusCode: 400,
result: null,
headers: {}
}
return response
})
await expect(saveCache([filePath], primaryKey)).rejects.toThrowError(
'Cache size of ~11264 MB (11811160064 B) is over the data cap limit, not saving cache.'
)
const archiveFolder = '/foo/bar'
expect(reserveCacheMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledWith(
archiveFolder,
cachePaths,
compression
)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
})
test('save with reserve cache failure should fail', async () => {
const paths = ['node_modules']
const primaryKey = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
@@ -172,12 +77,7 @@ test('save with reserve cache failure should fail', async () => {
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: null,
headers: {}
}
return response
return -1
})
const createTarMock = jest.spyOn(tar, 'createTar')
@@ -194,7 +94,7 @@ test('save with reserve cache failure should fail', async () => {
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, paths, {
compressionMethod: compression
})
expect(createTarMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledTimes(0)
expect(saveCacheMock).toHaveBeenCalledTimes(0)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
})
@@ -208,12 +108,7 @@ test('save with server error should fail', async () => {
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: {cacheId},
headers: {}
}
return response
return cacheId
})
const createTarMock = jest.spyOn(tar, 'createTar')
@@ -235,14 +130,17 @@ test('save with server error should fail', async () => {
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], {
compressionMethod: compression
})
const archiveFolder = '/foo/bar'
const archiveFile = path.join(archiveFolder, CacheFilename.Zstd)
expect(createTarMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledWith(
archiveFolder,
cachePaths,
compression
)
expect(saveCacheMock).toHaveBeenCalledTimes(1)
expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
@@ -257,12 +155,7 @@ test('save with valid inputs uploads a cache', async () => {
const reserveCacheMock = jest
.spyOn(cacheHttpClient, 'reserveCache')
.mockImplementation(async () => {
const response: ITypedResponse<ReserveCacheResponse> = {
statusCode: 500,
result: {cacheId},
headers: {}
}
return response
return cacheId
})
const createTarMock = jest.spyOn(tar, 'createTar')
@@ -278,14 +171,17 @@ test('save with valid inputs uploads a cache', async () => {
expect(reserveCacheMock).toHaveBeenCalledWith(primaryKey, [filePath], {
compressionMethod: compression
})
const archiveFolder = '/foo/bar'
const archiveFile = path.join(archiveFolder, CacheFilename.Zstd)
expect(createTarMock).toHaveBeenCalledTimes(1)
expect(createTarMock).toHaveBeenCalledWith(
archiveFolder,
cachePaths,
compression
)
expect(saveCacheMock).toHaveBeenCalledTimes(1)
expect(saveCacheMock).toHaveBeenCalledWith(cacheId, archiveFile, undefined)
expect(getCompressionMock).toHaveBeenCalledTimes(1)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "2.0.2",
"version": "2.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/cache",
"version": "2.0.2",
"version": "2.0.0",
"preview": true,
"description": "Actions cache lib",
"keywords": [
+13 -29
View File
@@ -152,7 +152,17 @@ export async function saveCache(
checkKey(key)
const compressionMethod = await utils.getCompressionMethod()
let cacheId = null
core.debug('Reserving Cache')
const cacheId = await cacheHttpClient.reserveCache(key, paths, {
compressionMethod
})
if (cacheId === -1) {
throw new ReserveCacheError(
`Unable to reserve cache with key ${key}, another job may be creating this cache.`
)
}
core.debug(`Cache ID: ${cacheId}`)
const cachePaths = await utils.resolvePaths(paths)
core.debug('Cache Paths:')
@@ -171,12 +181,11 @@ export async function saveCache(
if (core.isDebug()) {
await listTar(archivePath, compressionMethod)
}
const fileSizeLimit = 10 * 1024 * 1024 * 1024 // 10GB per repo limit
const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath)
core.debug(`File Size: ${archiveFileSize}`)
// For GHES, this check will take place in ReserveCache API with enterprise file size limit
if (archiveFileSize > fileSizeLimit && !utils.isGhes()) {
if (archiveFileSize > fileSizeLimit) {
throw new Error(
`Cache size of ~${Math.round(
archiveFileSize / (1024 * 1024)
@@ -184,31 +193,6 @@ export async function saveCache(
)
}
core.debug('Reserving Cache')
const reserveCacheResponse = await cacheHttpClient.reserveCache(
key,
paths,
{
compressionMethod,
cacheSize: archiveFileSize
}
)
if (reserveCacheResponse?.result?.cacheId) {
cacheId = reserveCacheResponse?.result?.cacheId
} else if (reserveCacheResponse?.statusCode === 400) {
throw new Error(
reserveCacheResponse?.error?.message ??
`Cache size of ~${Math.round(
archiveFileSize / (1024 * 1024)
)} MB (${archiveFileSize} B) is over the data cap limit, not saving cache.`
)
} else {
throw new ReserveCacheError(
`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${reserveCacheResponse?.error?.message}`
)
}
core.debug(`Saving Cache (ID: ${cacheId})`)
await cacheHttpClient.saveCache(cacheId, archivePath, options)
} finally {
+4 -6
View File
@@ -13,8 +13,7 @@ import {
InternalCacheOptions,
CommitCacheRequest,
ReserveCacheRequest,
ReserveCacheResponse,
ITypedResponseWithError
ReserveCacheResponse
} from './contracts'
import {downloadCacheHttpClient, downloadCacheStorageSDK} from './downloadUtils'
import {
@@ -144,14 +143,13 @@ export async function reserveCache(
key: string,
paths: string[],
options?: InternalCacheOptions
): Promise<ITypedResponseWithError<ReserveCacheResponse>> {
): Promise<number> {
const httpClient = createHttpClient()
const version = getCacheVersion(paths, options?.compressionMethod)
const reserveCacheRequest: ReserveCacheRequest = {
key,
version,
cacheSize: options?.cacheSize
version
}
const response = await retryTypedResponse('reserveCache', async () =>
httpClient.postJson<ReserveCacheResponse>(
@@ -159,7 +157,7 @@ export async function reserveCache(
reserveCacheRequest
)
)
return response
return response?.result?.cacheId ?? -1
}
function getContentRange(start: number, end: number): string {
-7
View File
@@ -123,10 +123,3 @@ export function assertDefined<T>(name: string, value?: T): T {
return value
}
export function isGhes(): boolean {
const ghUrl = new URL(
process.env['GITHUB_SERVER_URL'] || 'https://github.com'
)
return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM'
}
-8
View File
@@ -1,10 +1,4 @@
import {CompressionMethod} from './constants'
import {ITypedResponse} from '@actions/http-client/interfaces'
import {HttpClientError} from '@actions/http-client'
export interface ITypedResponseWithError<T> extends ITypedResponse<T> {
error?: HttpClientError
}
export interface ArtifactCacheEntry {
cacheKey?: string
@@ -20,7 +14,6 @@ export interface CommitCacheRequest {
export interface ReserveCacheRequest {
key: string
version?: string
cacheSize?: number
}
export interface ReserveCacheResponse {
@@ -29,5 +22,4 @@ export interface ReserveCacheResponse {
export interface InternalCacheOptions {
compressionMethod?: CompressionMethod
cacheSize?: number
}
+8 -7
View File
@@ -1,8 +1,10 @@
import * as core from '@actions/core'
import {HttpCodes, HttpClientError} from '@actions/http-client'
import {IHttpClientResponse} from '@actions/http-client/interfaces'
import {
IHttpClientResponse,
ITypedResponse
} from '@actions/http-client/interfaces'
import {DefaultRetryDelay, DefaultRetryAttempts} from './constants'
import {ITypedResponseWithError} from './contracts'
export function isSuccessStatusCode(statusCode?: number): boolean {
if (!statusCode) {
@@ -92,14 +94,14 @@ export async function retry<T>(
export async function retryTypedResponse<T>(
name: string,
method: () => Promise<ITypedResponseWithError<T>>,
method: () => Promise<ITypedResponse<T>>,
maxAttempts = DefaultRetryAttempts,
delay = DefaultRetryDelay
): Promise<ITypedResponseWithError<T>> {
): Promise<ITypedResponse<T>> {
return await retry(
name,
method,
(response: ITypedResponseWithError<T>) => response.statusCode,
(response: ITypedResponse<T>) => response.statusCode,
maxAttempts,
delay,
// If the error object contains the statusCode property, extract it and return
@@ -109,8 +111,7 @@ export async function retryTypedResponse<T>(
return {
statusCode: error.statusCode,
result: null,
headers: {},
error
headers: {}
}
} else {
return undefined
-3
View File
@@ -1,8 +1,5 @@
# @actions/glob Releases
### 0.3.0
- Added a `verbose` option to HashFiles [#1052](https://github.com/actions/toolkit/pull/1052/files)
### 0.2.1
- Update `lockfileVersion` to `v2` in `package-lock.json [#1023](https://github.com/actions/toolkit/pull/1023)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/glob",
"version": "0.3.0",
"version": "0.2.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@actions/glob",
"version": "0.3.0",
"version": "0.2.1",
"preview": true,
"description": "Actions glob lib",
"keywords": [
+2 -3
View File
@@ -26,13 +26,12 @@ export async function create(
*/
export async function hashFiles(
patterns: string,
options?: HashFileOptions,
verbose: Boolean = false
options?: HashFileOptions
): Promise<string> {
let followSymbolicLinks = true
if (options && typeof options.followSymbolicLinks === 'boolean') {
followSymbolicLinks = options.followSymbolicLinks
}
const globber = await create(patterns, {followSymbolicLinks})
return _hashFiles(globber, verbose)
return _hashFiles(globber)
}
+6 -10
View File
@@ -6,23 +6,19 @@ import * as util from 'util'
import * as path from 'path'
import {Globber} from './glob'
export async function hashFiles(
globber: Globber,
verbose: Boolean = false
): Promise<string> {
const writeDelegate = verbose ? core.info : core.debug
export async function hashFiles(globber: Globber): Promise<string> {
let hasMatch = false
const githubWorkspace = process.env['GITHUB_WORKSPACE'] ?? process.cwd()
const result = crypto.createHash('sha256')
let count = 0
for await (const file of globber.globGenerator()) {
writeDelegate(file)
core.debug(file)
if (!file.startsWith(`${githubWorkspace}${path.sep}`)) {
writeDelegate(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`)
core.debug(`Ignore '${file}' since it is not under GITHUB_WORKSPACE.`)
continue
}
if (fs.statSync(file).isDirectory()) {
writeDelegate(`Skip directory '${file}'.`)
core.debug(`Skip directory '${file}'.`)
continue
}
const hash = crypto.createHash('sha256')
@@ -37,10 +33,10 @@ export async function hashFiles(
result.end()
if (hasMatch) {
writeDelegate(`Found ${count} files to hash.`)
core.debug(`Found ${count} files to hash.`)
return result.digest('hex')
} else {
writeDelegate(`No matches found for glob`)
core.debug(`No matches found for glob`)
return ''
}
}
+16 -2
View File
@@ -332,9 +332,23 @@ describe('rmRF', () => {
await assertExists(filePath)
const fd = await fs.open(filePath, 'r')
await io.rmRF(testPath)
await assertNotExists(testPath)
let worked: boolean
try {
await io.rmRF(testPath)
worked = true
} catch (err) {
worked = false
}
if (os.platform() === 'win32') {
expect(worked).toBe(false)
await assertExists(testPath)
} else {
expect(worked).toBe(true)
await assertNotExists(testPath)
}
await fd.close()
await io.rmRF(testPath)
+12 -12
View File
@@ -164,9 +164,9 @@
"dev": true
},
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"node_modules/mkdirp": {
@@ -217,9 +217,9 @@
}
},
"node_modules/pathval": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
"dev": true,
"engines": {
"node": "*"
@@ -404,9 +404,9 @@
"dev": true
},
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
},
"mkdirp": {
@@ -450,9 +450,9 @@
}
},
"pathval": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
"dev": true
},
"propagate": {