2022-08-03 13:36:23 +01:00
"use strict" ;
2023-07-11 20:48:06 +01:00
var _ _createBinding = ( this && this . _ _createBinding ) || ( Object . create ? ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
var desc = Object . getOwnPropertyDescriptor ( m , k ) ;
if ( ! desc || ( "get" in desc ? ! m . _ _esModule : desc . writable || desc . configurable ) ) {
desc = { enumerable : true , get : function ( ) { return m [ k ] ; } } ;
}
Object . defineProperty ( o , k2 , desc ) ;
} ) : ( function ( o , m , k , k2 ) {
if ( k2 === undefined ) k2 = k ;
o [ k2 ] = m [ k ] ;
} ) ) ;
var _ _setModuleDefault = ( this && this . _ _setModuleDefault ) || ( Object . create ? ( function ( o , v ) {
Object . defineProperty ( o , "default" , { enumerable : true , value : v } ) ;
} ) : function ( o , v ) {
o [ "default" ] = v ;
} ) ;
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
if ( mod != null ) for ( var k in mod ) if ( k !== "default" && Object . prototype . hasOwnProperty . call ( mod , k ) ) _ _createBinding ( result , mod , k ) ;
_ _setModuleDefault ( result , mod ) ;
return result ;
} ;
2022-08-03 13:36:23 +01:00
var _ _awaiter = ( this && this . _ _awaiter ) || function ( thisArg , _arguments , P , generator ) {
function adopt ( value ) { return value instanceof P ? value : new P ( function ( resolve ) { resolve ( value ) ; } ) ; }
return new ( P || ( P = Promise ) ) ( function ( resolve , reject ) {
function fulfilled ( value ) { try { step ( generator . next ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function rejected ( value ) { try { step ( generator [ "throw" ] ( value ) ) ; } catch ( e ) { reject ( e ) ; } }
function step ( result ) { result . done ? resolve ( result . value ) : adopt ( result . value ) . then ( fulfilled , rejected ) ; }
step ( ( generator = generator . apply ( thisArg , _arguments || [ ] ) ) . next ( ) ) ;
} ) ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2023-07-11 20:48:06 +01:00
exports . saveCache = exports . restoreCache = exports . isFeatureAvailable = exports . ReserveCacheError = exports . ValidationError = void 0 ;
2022-08-03 13:36:23 +01:00
const core = _ _importStar ( require ( "@actions/core" ) ) ;
const path = _ _importStar ( require ( "path" ) ) ;
const utils = _ _importStar ( require ( "./internal/cacheUtils" ) ) ;
const cacheHttpClient = _ _importStar ( require ( "./internal/cacheHttpClient" ) ) ;
2024-12-09 17:47:54 +00:00
const cacheTwirpClient = _ _importStar ( require ( "./internal/shared/cacheTwirpClient" ) ) ;
const config _1 = require ( "./internal/config" ) ;
2022-08-03 13:36:23 +01:00
const tar _1 = require ( "./internal/tar" ) ;
2024-12-09 17:47:54 +00:00
const constants _1 = require ( "./internal/constants" ) ;
2022-08-03 13:36:23 +01:00
class ValidationError extends Error {
constructor ( message ) {
super ( message ) ;
this . name = 'ValidationError' ;
Object . setPrototypeOf ( this , ValidationError . prototype ) ;
}
}
exports . ValidationError = ValidationError ;
class ReserveCacheError extends Error {
constructor ( message ) {
super ( message ) ;
this . name = 'ReserveCacheError' ;
Object . setPrototypeOf ( this , ReserveCacheError . prototype ) ;
}
}
exports . ReserveCacheError = ReserveCacheError ;
function checkPaths ( paths ) {
if ( ! paths || paths . length === 0 ) {
throw new ValidationError ( ` Path Validation Error: At least one directory or file path is required ` ) ;
}
}
function checkKey ( key ) {
if ( key . length > 512 ) {
throw new ValidationError ( ` Key Validation Error: ${ key } cannot be larger than 512 characters. ` ) ;
}
const regex = /^[^,]*$/ ;
if ( ! regex . test ( key ) ) {
throw new ValidationError ( ` Key Validation Error: ${ key } cannot contain commas. ` ) ;
}
}
/**
* isFeatureAvailable to check the presence of Actions cache service
*
* @returns boolean return true if Actions cache service feature is available, otherwise false
*/
function isFeatureAvailable ( ) {
return ! ! process . env [ 'ACTIONS_CACHE_URL' ] ;
}
exports . isFeatureAvailable = isFeatureAvailable ;
/**
* Restores cache from keys
*
* @param paths a list of file paths to restore from the cache
2024-12-09 17:47:54 +00:00
* @param primaryKey an explicit key for restoring the cache. Lookup is done with prefix matching.
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for primaryKey
2022-08-03 13:36:23 +01:00
* @param downloadOptions cache download options
2023-07-11 20:48:06 +01:00
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
2022-08-03 13:36:23 +01:00
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
2023-07-11 20:48:06 +01:00
function restoreCache ( paths , primaryKey , restoreKeys , options , enableCrossOsArchive = false ) {
2022-08-03 13:36:23 +01:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2024-12-09 17:47:54 +00:00
const cacheServiceVersion = ( 0 , config _1 . getCacheServiceVersion ) ( ) ;
core . debug ( ` Cache service version: ${ cacheServiceVersion } ` ) ;
2022-08-03 13:36:23 +01:00
checkPaths ( paths ) ;
2024-12-09 17:47:54 +00:00
switch ( cacheServiceVersion ) {
case 'v2' :
return yield restoreCacheV2 ( paths , primaryKey , restoreKeys , options , enableCrossOsArchive ) ;
case 'v1' :
default :
return yield restoreCacheV1 ( paths , primaryKey , restoreKeys , options , enableCrossOsArchive ) ;
}
} ) ;
}
exports . restoreCache = restoreCache ;
/**
* Restores cache using the legacy Cache Service
*
* @param paths a list of file paths to restore from the cache
* @param primaryKey an explicit key for restoring the cache. Lookup is done with prefix matching.
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for primaryKey
* @param options cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on Windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCacheV1 ( paths , primaryKey , restoreKeys , options , enableCrossOsArchive = false ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2022-08-03 13:36:23 +01:00
restoreKeys = restoreKeys || [ ] ;
const keys = [ primaryKey , ... restoreKeys ] ;
core . debug ( 'Resolved Keys:' ) ;
core . debug ( JSON . stringify ( keys ) ) ;
if ( keys . length > 10 ) {
throw new ValidationError ( ` Key Validation Error: Keys are limited to a maximum of 10. ` ) ;
}
for ( const key of keys ) {
checkKey ( key ) ;
}
const compressionMethod = yield utils . getCompressionMethod ( ) ;
let archivePath = '' ;
try {
// path are needed to compute version
const cacheEntry = yield cacheHttpClient . getCacheEntry ( keys , paths , {
2023-07-11 20:48:06 +01:00
compressionMethod ,
enableCrossOsArchive
2022-08-03 13:36:23 +01:00
} ) ;
if ( ! ( cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry . archiveLocation ) ) {
// Cache not found
return undefined ;
}
2023-07-11 20:48:06 +01:00
if ( options === null || options === void 0 ? void 0 : options . lookupOnly ) {
core . info ( 'Lookup only - skipping download' ) ;
return cacheEntry . cacheKey ;
}
2022-08-03 13:36:23 +01:00
archivePath = path . join ( yield utils . createTempDirectory ( ) , utils . getCacheFileName ( compressionMethod ) ) ;
core . debug ( ` Archive Path: ${ archivePath } ` ) ;
// Download the cache from the cache entry
yield cacheHttpClient . downloadCache ( cacheEntry . archiveLocation , archivePath , options ) ;
if ( core . isDebug ( ) ) {
2023-07-11 20:48:06 +01:00
yield ( 0 , tar _1 . listTar ) ( archivePath , compressionMethod ) ;
2022-08-03 13:36:23 +01:00
}
const archiveFileSize = utils . getArchiveFileSizeInBytes ( archivePath ) ;
core . info ( ` Cache Size: ~ ${ Math . round ( archiveFileSize / ( 1024 * 1024 ) ) } MB ( ${ archiveFileSize } B) ` ) ;
2023-07-11 20:48:06 +01:00
yield ( 0 , tar _1 . extractTar ) ( archivePath , compressionMethod ) ;
2022-08-03 13:36:23 +01:00
core . info ( 'Cache restored successfully' ) ;
return cacheEntry . cacheKey ;
}
catch ( error ) {
const typedError = error ;
if ( typedError . name === ValidationError . name ) {
throw error ;
}
else {
// Supress all non-validation cache related errors because caching should be optional
core . warning ( ` Failed to restore: ${ error . message } ` ) ;
}
}
finally {
// Try to delete the archive to save space
try {
yield utils . unlinkFile ( archivePath ) ;
}
catch ( error ) {
core . debug ( ` Failed to delete archive: ${ error } ` ) ;
}
}
return undefined ;
} ) ;
}
2024-12-09 17:47:54 +00:00
/**
* Restores cache using Cache Service v2
*
* @param paths a list of file paths to restore from the cache
* @param primaryKey an explicit key for restoring the cache. Lookup is done with prefix matching
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for primaryKey
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
function restoreCacheV2 ( paths , primaryKey , restoreKeys , options , enableCrossOsArchive = false ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// Override UploadOptions to force the use of Azure
options = Object . assign ( Object . assign ( { } , options ) , { useAzureSdk : true } ) ;
restoreKeys = restoreKeys || [ ] ;
const keys = [ primaryKey , ... restoreKeys ] ;
core . debug ( 'Resolved Keys:' ) ;
core . debug ( JSON . stringify ( keys ) ) ;
if ( keys . length > 10 ) {
throw new ValidationError ( ` Key Validation Error: Keys are limited to a maximum of 10. ` ) ;
}
for ( const key of keys ) {
checkKey ( key ) ;
}
let archivePath = '' ;
try {
const twirpClient = cacheTwirpClient . internalCacheTwirpClient ( ) ;
const compressionMethod = yield utils . getCompressionMethod ( ) ;
const request = {
key : primaryKey ,
restoreKeys ,
version : utils . getCacheVersion ( paths , compressionMethod , enableCrossOsArchive )
} ;
const response = yield twirpClient . GetCacheEntryDownloadURL ( request ) ;
if ( ! response . ok ) {
2025-02-17 18:21:02 +00:00
core . debug ( ` Cache not found for keys: ${ keys . join ( ', ' ) } ` ) ;
2024-12-09 17:47:54 +00:00
return undefined ;
}
core . info ( ` Cache hit for: ${ request . key } ` ) ;
if ( options === null || options === void 0 ? void 0 : options . lookupOnly ) {
core . info ( 'Lookup only - skipping download' ) ;
return response . matchedKey ;
}
archivePath = path . join ( yield utils . createTempDirectory ( ) , utils . getCacheFileName ( compressionMethod ) ) ;
core . debug ( ` Archive path: ${ archivePath } ` ) ;
core . debug ( ` Starting download of archive to: ${ archivePath } ` ) ;
yield cacheHttpClient . downloadCache ( response . signedDownloadUrl , archivePath , options ) ;
const archiveFileSize = utils . getArchiveFileSizeInBytes ( archivePath ) ;
core . info ( ` Cache Size: ~ ${ Math . round ( archiveFileSize / ( 1024 * 1024 ) ) } MB ( ${ archiveFileSize } B) ` ) ;
if ( core . isDebug ( ) ) {
yield ( 0 , tar _1 . listTar ) ( archivePath , compressionMethod ) ;
}
yield ( 0 , tar _1 . extractTar ) ( archivePath , compressionMethod ) ;
core . info ( 'Cache restored successfully' ) ;
return response . matchedKey ;
}
catch ( error ) {
const typedError = error ;
if ( typedError . name === ValidationError . name ) {
throw error ;
}
else {
// Supress all non-validation cache related errors because caching should be optional
core . warning ( ` Failed to restore: ${ error . message } ` ) ;
}
}
finally {
try {
if ( archivePath ) {
yield utils . unlinkFile ( archivePath ) ;
}
}
catch ( error ) {
core . debug ( ` Failed to delete archive: ${ error } ` ) ;
}
}
return undefined ;
} ) ;
}
2022-08-03 13:36:23 +01:00
/**
* Saves a list of files with the specified key
*
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
2023-07-11 20:48:06 +01:00
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
2022-08-03 13:36:23 +01:00
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
*/
2023-07-11 20:48:06 +01:00
function saveCache ( paths , key , options , enableCrossOsArchive = false ) {
2022-08-03 13:36:23 +01:00
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2024-12-09 17:47:54 +00:00
const cacheServiceVersion = ( 0 , config _1 . getCacheServiceVersion ) ( ) ;
core . debug ( ` Cache service version: ${ cacheServiceVersion } ` ) ;
2022-08-03 13:36:23 +01:00
checkPaths ( paths ) ;
checkKey ( key ) ;
2024-12-09 17:47:54 +00:00
switch ( cacheServiceVersion ) {
case 'v2' :
return yield saveCacheV2 ( paths , key , options , enableCrossOsArchive ) ;
case 'v1' :
default :
return yield saveCacheV1 ( paths , key , options , enableCrossOsArchive ) ;
}
} ) ;
}
exports . saveCache = saveCache ;
/**
* Save cache using the legacy Cache Service
*
* @param paths
* @param key
* @param options
* @param enableCrossOsArchive
* @returns
*/
function saveCacheV1 ( paths , key , options , enableCrossOsArchive = false ) {
var _a , _b , _c , _d , _e ;
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
2022-08-03 13:36:23 +01:00
const compressionMethod = yield utils . getCompressionMethod ( ) ;
let cacheId = - 1 ;
const cachePaths = yield utils . resolvePaths ( paths ) ;
core . debug ( 'Cache Paths:' ) ;
core . debug ( ` ${ JSON . stringify ( cachePaths ) } ` ) ;
if ( cachePaths . length === 0 ) {
throw new Error ( ` Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved. ` ) ;
}
const archiveFolder = yield utils . createTempDirectory ( ) ;
const archivePath = path . join ( archiveFolder , utils . getCacheFileName ( compressionMethod ) ) ;
core . debug ( ` Archive Path: ${ archivePath } ` ) ;
try {
2023-07-11 20:48:06 +01:00
yield ( 0 , tar _1 . createTar ) ( archiveFolder , cachePaths , compressionMethod ) ;
2022-08-03 13:36:23 +01:00
if ( core . isDebug ( ) ) {
2023-07-11 20:48:06 +01:00
yield ( 0 , tar _1 . listTar ) ( archivePath , compressionMethod ) ;
2022-08-03 13:36:23 +01:00
}
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
2024-12-09 17:47:54 +00:00
if ( archiveFileSize > fileSizeLimit && ! ( 0 , config _1 . isGhes ) ( ) ) {
2022-08-03 13:36:23 +01:00
throw new Error ( ` Cache size of ~ ${ Math . round ( archiveFileSize / ( 1024 * 1024 ) ) } MB ( ${ archiveFileSize } B) is over the 10GB limit, not saving cache. ` ) ;
}
core . debug ( 'Reserving Cache' ) ;
const reserveCacheResponse = yield cacheHttpClient . reserveCache ( key , paths , {
compressionMethod ,
2023-07-11 20:48:06 +01:00
enableCrossOsArchive ,
2022-08-03 13:36:23 +01:00
cacheSize : archiveFileSize
} ) ;
if ( ( _a = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse . result ) === null || _a === void 0 ? void 0 : _a . cacheId ) {
cacheId = ( _b = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse . result ) === null || _b === void 0 ? void 0 : _b . cacheId ;
}
else if ( ( reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse . statusCode ) === 400 ) {
throw new Error ( ( _d = ( _c = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse . error ) === null || _c === void 0 ? void 0 : _c . message ) !== null && _d !== void 0 ? _d : ` 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: ${ ( _e = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse . error ) === null || _e === void 0 ? void 0 : _e . message } ` ) ;
}
core . debug ( ` Saving Cache (ID: ${ cacheId } ) ` ) ;
2024-12-09 17:47:54 +00:00
yield cacheHttpClient . saveCache ( cacheId , archivePath , '' , options ) ;
}
catch ( error ) {
const typedError = error ;
if ( typedError . name === ValidationError . name ) {
throw error ;
}
else if ( typedError . name === ReserveCacheError . name ) {
core . info ( ` Failed to save: ${ typedError . message } ` ) ;
}
else {
core . warning ( ` Failed to save: ${ typedError . message } ` ) ;
}
}
finally {
// Try to delete the archive to save space
try {
yield utils . unlinkFile ( archivePath ) ;
}
catch ( error ) {
core . debug ( ` Failed to delete archive: ${ error } ` ) ;
}
}
return cacheId ;
} ) ;
}
/**
* Save cache using Cache Service v2
*
* @param paths a list of file paths to restore from the cache
* @param key an explicit key for restoring the cache
* @param options cache upload options
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @returns
*/
function saveCacheV2 ( paths , key , options , enableCrossOsArchive = false ) {
return _ _awaiter ( this , void 0 , void 0 , function * ( ) {
// Override UploadOptions to force the use of Azure
// ...options goes first because we want to override the default values
// set in UploadOptions with these specific figures
options = Object . assign ( Object . assign ( { } , options ) , { uploadChunkSize : 64 * 1024 * 1024 , uploadConcurrency : 8 , useAzureSdk : true } ) ;
const compressionMethod = yield utils . getCompressionMethod ( ) ;
const twirpClient = cacheTwirpClient . internalCacheTwirpClient ( ) ;
let cacheId = - 1 ;
const cachePaths = yield utils . resolvePaths ( paths ) ;
core . debug ( 'Cache Paths:' ) ;
core . debug ( ` ${ JSON . stringify ( cachePaths ) } ` ) ;
if ( cachePaths . length === 0 ) {
throw new Error ( ` Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved. ` ) ;
}
const archiveFolder = yield utils . createTempDirectory ( ) ;
const archivePath = path . join ( archiveFolder , utils . getCacheFileName ( compressionMethod ) ) ;
core . debug ( ` Archive Path: ${ archivePath } ` ) ;
try {
yield ( 0 , tar _1 . createTar ) ( archiveFolder , cachePaths , compressionMethod ) ;
if ( core . isDebug ( ) ) {
yield ( 0 , tar _1 . listTar ) ( archivePath , compressionMethod ) ;
}
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 > constants _1 . CacheFileSizeLimit && ! ( 0 , config _1 . isGhes ) ( ) ) {
throw new Error ( ` Cache size of ~ ${ Math . round ( archiveFileSize / ( 1024 * 1024 ) ) } MB ( ${ archiveFileSize } B) is over the 10GB limit, not saving cache. ` ) ;
}
// Set the archive size in the options, will be used to display the upload progress
options . archiveSizeBytes = archiveFileSize ;
core . debug ( 'Reserving Cache' ) ;
const version = utils . getCacheVersion ( paths , compressionMethod , enableCrossOsArchive ) ;
const request = {
key ,
version
} ;
const response = yield twirpClient . CreateCacheEntry ( request ) ;
if ( ! response . ok ) {
throw new ReserveCacheError ( ` Unable to reserve cache with key ${ key } , another job may be creating this cache. ` ) ;
}
core . debug ( ` Attempting to upload cache located at: ${ archivePath } ` ) ;
yield cacheHttpClient . saveCache ( cacheId , archivePath , response . signedUploadUrl , options ) ;
const finalizeRequest = {
key ,
version ,
sizeBytes : ` ${ archiveFileSize } `
} ;
const finalizeResponse = yield twirpClient . FinalizeCacheEntryUpload ( finalizeRequest ) ;
core . debug ( ` FinalizeCacheEntryUploadResponse: ${ finalizeResponse . ok } ` ) ;
if ( ! finalizeResponse . ok ) {
throw new Error ( ` Unable to finalize cache with key ${ key } , another job may be finalizing this cache. ` ) ;
}
cacheId = parseInt ( finalizeResponse . entryId ) ;
2022-08-03 13:36:23 +01:00
}
catch ( error ) {
const typedError = error ;
if ( typedError . name === ValidationError . name ) {
throw error ;
}
else if ( typedError . name === ReserveCacheError . name ) {
core . info ( ` Failed to save: ${ typedError . message } ` ) ;
}
else {
core . warning ( ` Failed to save: ${ typedError . message } ` ) ;
}
}
finally {
// Try to delete the archive to save space
try {
yield utils . unlinkFile ( archivePath ) ;
}
catch ( error ) {
core . debug ( ` Failed to delete archive: ${ error } ` ) ;
}
}
return cacheId ;
} ) ;
}
//# sourceMappingURL=cache.js.map