Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d096588f08 | ||
|
|
662b9d91f5 | ||
|
|
a62f530b6f | ||
|
|
2995cdf0a1 | ||
|
|
f10f9c8217 | ||
|
|
c26e6f3aba | ||
|
|
2b08dc18f2 | ||
|
|
8fcec1fb58 | ||
|
|
aad39a371f |
@@ -1,5 +1,9 @@
|
|||||||
# @actions/artifact Releases
|
# @actions/artifact Releases
|
||||||
|
|
||||||
|
### 2.2.2
|
||||||
|
|
||||||
|
- Default concurrency to 5 for uploading artifacts [#1962](https://github.com/actions/toolkit/pull/1962
|
||||||
|
|
||||||
### 2.2.1
|
### 2.2.1
|
||||||
|
|
||||||
- Add `ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY` and `ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS` environment variables [#1928](https://github.com/actions/toolkit/pull/1928)
|
- Add `ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY` and `ACTIONS_ARTIFACT_UPLOAD_TIMEOUT_MS` environment variables [#1928](https://github.com/actions/toolkit/pull/1928)
|
||||||
|
|||||||
@@ -56,22 +56,30 @@ describe('uploadChunkTimeoutEnv', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('uploadConcurrencyEnv', () => {
|
describe('uploadConcurrencyEnv', () => {
|
||||||
it('should return default 32 when cpu num is <= 4', () => {
|
it('Concurrency default to 5', () => {
|
||||||
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
||||||
expect(config.getConcurrency()).toBe(32)
|
expect(config.getConcurrency()).toBe(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return 16 * num of cpu when cpu num is > 4', () => {
|
it('Concurrency max out at 300 on systems with many CPUs', () => {
|
||||||
;(os.cpus as jest.Mock).mockReturnValue(new Array(6))
|
|
||||||
expect(config.getConcurrency()).toBe(96)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return up to 300 max value', () => {
|
|
||||||
;(os.cpus as jest.Mock).mockReturnValue(new Array(32))
|
;(os.cpus as jest.Mock).mockReturnValue(new Array(32))
|
||||||
|
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '301'
|
||||||
expect(config.getConcurrency()).toBe(300)
|
expect(config.getConcurrency()).toBe(300)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return override value when ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY is set', () => {
|
it('Concurrency can be set to 32 when cpu num is <= 4', () => {
|
||||||
|
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
||||||
|
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '32'
|
||||||
|
expect(config.getConcurrency()).toBe(32)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Concurrency can be set 16 * num of cpu when cpu num is > 4', () => {
|
||||||
|
;(os.cpus as jest.Mock).mockReturnValue(new Array(6))
|
||||||
|
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '96'
|
||||||
|
expect(config.getConcurrency()).toBe(96)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Concurrency can be overridden by env var ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY', () => {
|
||||||
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
||||||
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '10'
|
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '10'
|
||||||
expect(config.getConcurrency()).toBe(10)
|
expect(config.getConcurrency()).toBe(10)
|
||||||
@@ -92,10 +100,4 @@ describe('uploadConcurrencyEnv', () => {
|
|||||||
config.getConcurrency()
|
config.getConcurrency()
|
||||||
}).toThrow()
|
}).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('cannot go over currency cap when override value is greater', () => {
|
|
||||||
;(os.cpus as jest.Mock).mockReturnValue(new Array(4))
|
|
||||||
process.env.ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY = '40'
|
|
||||||
expect(config.getConcurrency()).toBe(32)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.0",
|
"@actions/core": "^1.10.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@actions/artifact",
|
"name": "@actions/artifact",
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"description": "Actions artifact lib",
|
"description": "Actions artifact lib",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -45,10 +45,8 @@ export function getGitHubWorkspaceDir(): string {
|
|||||||
return ghWorkspaceDir
|
return ghWorkspaceDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mimics behavior of azcopy: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-optimize
|
// The maximum value of concurrency is 300.
|
||||||
// If your machine has fewer than 5 CPUs, then the value of this variable is set to 32.
|
// This value can be changed with ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY variable.
|
||||||
// Otherwise, the default value is equal to 16 multiplied by the number of CPUs. The maximum value of this variable is 300.
|
|
||||||
// This value can be lowered with ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY variable.
|
|
||||||
export function getConcurrency(): number {
|
export function getConcurrency(): number {
|
||||||
const numCPUs = os.cpus().length
|
const numCPUs = os.cpus().length
|
||||||
let concurrencyCap = 32
|
let concurrencyCap = 32
|
||||||
@@ -68,15 +66,20 @@ export function getConcurrency(): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (concurrency < concurrencyCap) {
|
if (concurrency < concurrencyCap) {
|
||||||
|
info(
|
||||||
|
`Set concurrency based on the value set in ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY.`
|
||||||
|
)
|
||||||
return concurrency
|
return concurrency
|
||||||
}
|
}
|
||||||
|
|
||||||
info(
|
info(
|
||||||
`ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY is higher than the cap of ${concurrencyCap} based on the number of cpus. Lowering it to the cap.`
|
`ACTIONS_ARTIFACT_UPLOAD_CONCURRENCY is higher than the cap of ${concurrencyCap} based on the number of cpus. Set it to the maximum value allowed.`
|
||||||
)
|
)
|
||||||
|
return concurrencyCap
|
||||||
}
|
}
|
||||||
|
|
||||||
return concurrencyCap
|
// default concurrency to 5
|
||||||
|
return 5
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUploadChunkTimeout(): number {
|
export function getUploadChunkTimeout(): number {
|
||||||
|
|||||||
Vendored
+5
@@ -1,5 +1,10 @@
|
|||||||
# @actions/cache Releases
|
# @actions/cache Releases
|
||||||
|
|
||||||
|
### 4.0.1
|
||||||
|
|
||||||
|
- Remove runtime dependency on `twirp-ts` [#1947](https://github.com/actions/toolkit/pull/1947)
|
||||||
|
- Cache miss as debug, not warning annotation [#1954](https://github.com/actions/toolkit/pull/1954)
|
||||||
|
|
||||||
### 4.0.0
|
### 4.0.0
|
||||||
|
|
||||||
#### Important changes
|
#### Important changes
|
||||||
|
|||||||
+36
-3
@@ -92,14 +92,14 @@ test('save with large cache outputs should fail using', async () => {
|
|||||||
expect(getCompressionMock).toHaveBeenCalledTimes(1)
|
expect(getCompressionMock).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('create cache entry failure', async () => {
|
test('create cache entry failure on non-ok response', async () => {
|
||||||
const paths = ['node_modules']
|
const paths = ['node_modules']
|
||||||
const key = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
|
const key = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
|
||||||
const infoLogMock = jest.spyOn(core, 'info')
|
const infoLogMock = jest.spyOn(core, 'info')
|
||||||
|
|
||||||
const createCacheEntryMock = jest
|
const createCacheEntryMock = jest
|
||||||
.spyOn(CacheServiceClientJSON.prototype, 'CreateCacheEntry')
|
.spyOn(CacheServiceClientJSON.prototype, 'CreateCacheEntry')
|
||||||
.mockReturnValue(Promise.resolve({ok: false, signedUploadUrl: ''}))
|
.mockResolvedValue({ok: false, signedUploadUrl: ''})
|
||||||
|
|
||||||
const createTarMock = jest.spyOn(tar, 'createTar')
|
const createTarMock = jest.spyOn(tar, 'createTar')
|
||||||
const finalizeCacheEntryMock = jest.spyOn(
|
const finalizeCacheEntryMock = jest.spyOn(
|
||||||
@@ -109,7 +109,7 @@ test('create cache entry failure', async () => {
|
|||||||
const compression = CompressionMethod.Zstd
|
const compression = CompressionMethod.Zstd
|
||||||
const getCompressionMock = jest
|
const getCompressionMock = jest
|
||||||
.spyOn(cacheUtils, 'getCompressionMethod')
|
.spyOn(cacheUtils, 'getCompressionMethod')
|
||||||
.mockReturnValueOnce(Promise.resolve(compression))
|
.mockResolvedValueOnce(compression)
|
||||||
const archiveFileSize = 1024
|
const archiveFileSize = 1024
|
||||||
jest
|
jest
|
||||||
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||||
@@ -133,6 +133,39 @@ test('create cache entry failure', async () => {
|
|||||||
expect(saveCacheMock).toHaveBeenCalledTimes(0)
|
expect(saveCacheMock).toHaveBeenCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('create cache entry fails on rejected promise', async () => {
|
||||||
|
const paths = ['node_modules']
|
||||||
|
const key = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
|
||||||
|
const infoLogMock = jest.spyOn(core, 'info')
|
||||||
|
|
||||||
|
const createCacheEntryMock = jest
|
||||||
|
.spyOn(CacheServiceClientJSON.prototype, 'CreateCacheEntry')
|
||||||
|
.mockRejectedValue(new Error('Failed to create cache entry'))
|
||||||
|
|
||||||
|
const createTarMock = jest.spyOn(tar, 'createTar')
|
||||||
|
const compression = CompressionMethod.Zstd
|
||||||
|
const getCompressionMock = jest
|
||||||
|
.spyOn(cacheUtils, 'getCompressionMethod')
|
||||||
|
.mockResolvedValueOnce(compression)
|
||||||
|
const archiveFileSize = 1024
|
||||||
|
jest
|
||||||
|
.spyOn(cacheUtils, 'getArchiveFileSizeInBytes')
|
||||||
|
.mockReturnValueOnce(archiveFileSize)
|
||||||
|
|
||||||
|
const cacheId = await saveCache(paths, key)
|
||||||
|
expect(cacheId).toBe(-1)
|
||||||
|
expect(infoLogMock).toHaveBeenCalledWith(
|
||||||
|
`Failed to save: Unable to reserve cache with key ${key}, another job may be creating this cache.`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(createCacheEntryMock).toHaveBeenCalledWith({
|
||||||
|
key,
|
||||||
|
version: cacheUtils.getCacheVersion(paths, compression)
|
||||||
|
})
|
||||||
|
expect(createTarMock).toHaveBeenCalledTimes(1)
|
||||||
|
expect(getCompressionMock).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
test('save cache fails if a signedUploadURL was not passed', async () => {
|
test('save cache fails if a signedUploadURL was not passed', async () => {
|
||||||
const paths = 'node_modules'
|
const paths = 'node_modules'
|
||||||
const key = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
|
const key = 'Linux-node-bb828da54c148048dd17899ba9fda624811cfb43'
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@actions/cache",
|
"name": "@actions/cache",
|
||||||
"version": "4.0.0",
|
"version": "4.0.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@actions/cache",
|
"name": "@actions/cache",
|
||||||
"version": "4.0.0",
|
"version": "4.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.11.1",
|
"@actions/core": "^1.11.1",
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@actions/cache",
|
"name": "@actions/cache",
|
||||||
"version": "4.0.0",
|
"version": "4.0.1",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"description": "Actions cache lib",
|
"description": "Actions cache lib",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
Vendored
+11
-3
@@ -525,8 +525,16 @@ async function saveCacheV2(
|
|||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await twirpClient.CreateCacheEntry(request)
|
let signedUploadUrl
|
||||||
if (!response.ok) {
|
|
||||||
|
try {
|
||||||
|
const response = await twirpClient.CreateCacheEntry(request)
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Response was not ok')
|
||||||
|
}
|
||||||
|
signedUploadUrl = response.signedUploadUrl
|
||||||
|
} catch (error) {
|
||||||
|
core.debug(`Failed to reserve cache: ${error}`)
|
||||||
throw new ReserveCacheError(
|
throw new ReserveCacheError(
|
||||||
`Unable to reserve cache with key ${key}, another job may be creating this cache.`
|
`Unable to reserve cache with key ${key}, another job may be creating this cache.`
|
||||||
)
|
)
|
||||||
@@ -536,7 +544,7 @@ async function saveCacheV2(
|
|||||||
await cacheHttpClient.saveCache(
|
await cacheHttpClient.saveCache(
|
||||||
cacheId,
|
cacheId,
|
||||||
archivePath,
|
archivePath,
|
||||||
response.signedUploadUrl,
|
signedUploadUrl,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user