Decoupled cache from GitHub API

Signed-off-by: Guillermo Mazzola <guillermo.mazzola@glovoapp.com>
This commit is contained in:
Guillermo Mazzola 2023-06-26 15:42:06 +02:00
parent 1b2daf5833
commit 4ff2ffb7bf
No known key found for this signature in database
GPG key ID: 6A17887FBC885E08
6 changed files with 63 additions and 24 deletions

View file

@ -28,6 +28,12 @@ inputs:
required: false
default: ${{ github.event.repository != null && github.ref_name != github.event.repository.default_branch }}
cache-provider:
description: |
The cache provider to use for caching files. Currently only supports `github`.
required: false
default: github
cache-write-only:
description: |
When 'true', entries will not be restored from the cache but will be saved at the end of the Job.

View file

@ -0,0 +1,24 @@
import * as cache from '@actions/cache'
import {CacheEntry, CacheProvider} from './cache-provider'
const SEGMENT_DOWNLOAD_TIMEOUT_VAR = 'SEGMENT_DOWNLOAD_TIMEOUT_MINS'
const SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT = 10 * 60 * 1000 // 10 minutes
class GitHubCache implements CacheProvider {
// Only override the read timeout if the SEGMENT_DOWNLOAD_TIMEOUT_MINS env var has NOT been set
private cacheRestoreOptions = !process.env[SEGMENT_DOWNLOAD_TIMEOUT_VAR]
? {segmentTimeoutInMs: SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT}
: {}
async saveCache(paths: string[], key: string): Promise<CacheEntry> {
return cache.saveCache(paths, key)
}
async restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[]): Promise<CacheEntry | undefined> {
return cache.restoreCache(paths, primaryKey, restoreKeys, this.cacheRestoreOptions)
}
}
export default function createGitHubCache(): CacheProvider | undefined {
return cache.isFeatureAvailable() ? new GitHubCache() : undefined
}

10
src/cache-provider.ts Normal file
View file

@ -0,0 +1,10 @@
export interface CacheProvider {
saveCache(paths: string[], key: string): Promise<CacheEntry>
restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[]): Promise<CacheEntry | undefined>
}
export interface CacheEntry {
key: string
size?: number
}

View file

@ -1,5 +1,5 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import {cache} from './cache-utils'
/**
* Collects information on what entries were saved and restored during the action.
@ -16,7 +16,7 @@ export class CacheListener {
}
get cacheStatus(): string {
if (!cache.isFeatureAvailable()) return 'not available'
if (!cache) return 'not available'
if (this.cacheDisabled) return 'disabled'
if (this.cacheWriteOnly) return 'write-only'
if (this.cacheReadOnly) return 'read-only'

View file

@ -1,5 +1,4 @@
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as github from '@actions/github'
import * as exec from '@actions/exec'
@ -10,6 +9,9 @@ import * as fs from 'fs'
import * as params from './input-params'
import {CacheEntryListener} from './cache-reporting'
import {CacheEntry, CacheProvider} from './cache-provider'
import {ReserveCacheError, ValidationError} from '@actions/cache'
import createGitHubCache from './cache-provider-github'
const CACHE_PROTOCOL_VERSION = 'v8-'
@ -19,14 +21,14 @@ const CACHE_KEY_JOB_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB'
const CACHE_KEY_JOB_INSTANCE_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB_INSTANCE'
const CACHE_KEY_JOB_EXECUTION_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION'
const SEGMENT_DOWNLOAD_TIMEOUT_VAR = 'SEGMENT_DOWNLOAD_TIMEOUT_MINS'
const SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT = 10 * 60 * 1000 // 10 minutes
export const cache = provisionCache()
function provisionCache(): CacheProvider | undefined {
return createGitHubCache()
}
export function isCacheDisabled(): boolean {
if (!cache.isFeatureAvailable()) {
return true
}
return params.isCacheDisabled()
return !cache || params.isCacheDisabled()
}
export function isCacheReadOnly(): boolean {
@ -146,14 +148,10 @@ export async function restoreCache(
cacheKey: string,
cacheRestoreKeys: string[],
listener: CacheEntryListener
): Promise<cache.CacheEntry | undefined> {
): Promise<CacheEntry | undefined> {
listener.markRequested(cacheKey, cacheRestoreKeys)
try {
// Only override the read timeout if the SEGMENT_DOWNLOAD_TIMEOUT_MINS env var has NOT been set
const cacheRestoreOptions = process.env[SEGMENT_DOWNLOAD_TIMEOUT_VAR]
? {}
: {segmentTimeoutInMs: SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT}
const restoredEntry = await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys, cacheRestoreOptions)
const restoredEntry = await cache?.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
if (restoredEntry !== undefined) {
listener.markRestored(restoredEntry.key, restoredEntry.size)
}
@ -167,10 +165,12 @@ export async function restoreCache(
export async function saveCache(cachePath: string[], cacheKey: string, listener: CacheEntryListener): Promise<void> {
try {
const savedEntry = await cache.saveCache(cachePath, cacheKey)
listener.markSaved(savedEntry.key, savedEntry.size)
const savedEntry = await cache?.saveCache(cachePath, cacheKey)
if (savedEntry) {
listener.markSaved(savedEntry.key, savedEntry.size)
}
} catch (error) {
if (error instanceof cache.ReserveCacheError) {
if (error instanceof ReserveCacheError) {
listener.markAlreadyExists(cacheKey)
} else {
listener.markNotSaved((error as Error).message)
@ -188,11 +188,11 @@ export function cacheDebug(message: string): void {
}
export function handleCacheFailure(error: unknown, message: string): void {
if (error instanceof cache.ValidationError) {
if (error instanceof ValidationError) {
// Fail on cache validation errors
throw error
}
if (error instanceof cache.ReserveCacheError) {
if (error instanceof ReserveCacheError) {
// Reserve cache errors are expected if the artifact has been previously cached
core.info(`${message}: ${error}`)
} else {

View file

@ -3,13 +3,12 @@ import * as os from 'os'
import * as path from 'path'
import * as httpm from '@actions/http-client'
import * as core from '@actions/core'
import * as cache from '@actions/cache'
import * as toolCache from '@actions/tool-cache'
import * as gradlew from './gradlew'
import * as params from './input-params'
import * as layout from './repository-layout'
import {handleCacheFailure, isCacheDisabled, isCacheReadOnly} from './cache-utils'
import {cache, handleCacheFailure, isCacheDisabled, isCacheReadOnly} from './cache-utils'
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
@ -133,7 +132,7 @@ async function downloadAndCacheGradleDistribution(versionInfo: GradleVersionInfo
const cacheKey = `gradle-${versionInfo.version}`
try {
const restoreKey = await cache.restoreCache([downloadPath], cacheKey)
const restoreKey = await cache?.restoreCache([downloadPath], cacheKey)
if (restoreKey) {
core.info(`Restored Gradle distribution ${cacheKey} from cache to ${downloadPath}`)
return downloadPath
@ -145,7 +144,7 @@ async function downloadAndCacheGradleDistribution(versionInfo: GradleVersionInfo
core.info(`Gradle distribution ${versionInfo.version} not found in cache. Will download.`)
await downloadGradleDistribution(versionInfo, downloadPath)
if (!isCacheReadOnly()) {
if (!isCacheReadOnly() && cache) {
try {
await cache.saveCache([downloadPath], cacheKey)
} catch (error) {