Skip to content

Commit

Permalink
modifies for warpcache
Browse files Browse the repository at this point in the history
  • Loading branch information
prajjwaldimri committed Apr 15, 2024
1 parent 917853e commit 9a8c6b2
Show file tree
Hide file tree
Showing 33 changed files with 699 additions and 144 deletions.
2 changes: 1 addition & 1 deletion packages/warp-cache/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "github-actions.warp-cache",
"version": "0.3.0",
"version": "1.0.0",
"preview": true,
"description": "Github action to use WarpBuild's in-house cache offering",
"keywords": [
Expand Down
80 changes: 49 additions & 31 deletions packages/warp-cache/src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,40 +66,48 @@ export function isFeatureAvailable(): boolean {
* @param restoreKeys an optional ordered list of keys to use for restoring the cache if no cache hit occurred for key
* @param downloadOptions cache download options
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
* @param enableCrossArchArchive an optional boolean enabled to restore cache created on any arch
* @returns string returns the key for the cache hit, otherwise returns undefined
*/
export async function restoreCache(
paths: string[],
primaryKey: string,
restoreKeys?: string[],
options?: DownloadOptions,
enableCrossOsArchive = false
enableCrossOsArchive = false,
enableCrossArchArchive = false
): Promise<string | undefined> {
checkPaths(paths)
checkKey(primaryKey)

restoreKeys = restoreKeys ?? []
const keys = [primaryKey, ...restoreKeys]

core.debug('Resolved Keys:')
core.debug(JSON.stringify(keys))
core.debug('Resolved Restore Keys:')
core.debug(JSON.stringify(restoreKeys))

if (keys.length > 10) {
if (restoreKeys.length > 9) {
throw new ValidationError(
`Key Validation Error: Keys are limited to a maximum of 10.`
)
}
for (const key of keys) {
for (const key of restoreKeys) {
checkKey(key)
}

const compressionMethod = await utils.getCompressionMethod()
let archivePath = ''
try {
// path are needed to compute version
const cacheEntry = await cacheHttpClient.getCacheEntry(keys, paths, {
compressionMethod,
enableCrossOsArchive
})
const cacheEntry = await cacheHttpClient.getCacheEntry(
primaryKey,
restoreKeys,
paths,
{
compressionMethod,
enableCrossOsArchive,
enableCrossArchArchive
}
)

if (!cacheEntry) {
// Internal Error
Expand Down Expand Up @@ -205,13 +213,14 @@ export async function restoreCache(
* @param paths a list of file paths to be cached
* @param key an explicit key for restoring the cache
* @param enableCrossOsArchive an optional boolean enabled to save cache on windows which could be restored on any platform
* @param options cache upload options
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
* @param enableCrossArchArchive an optional boolean enabled to save cache on any arch which could be restored on any arch
* @returns string returns cacheId if the cache was saved successfully and throws an error if save fails
*/
export async function saveCache(
paths: string[],
key: string,
enableCrossOsArchive = false
enableCrossOsArchive = false,
enableCrossArchArchive = false
): Promise<string> {
checkPaths(paths)
checkKey(key)
Expand Down Expand Up @@ -254,6 +263,13 @@ export async function saveCache(
)
}

const cacheVersion = cacheHttpClient.getCacheVersion(
paths,
compressionMethod,
enableCrossOsArchive,
enableCrossArchArchive
)

core.debug('Reserving Cache')
// Calculate number of chunks required. This is only required if backend is S3 as Google Cloud SDK will do it for us
const uploadOptions = getUploadOptions()
Expand All @@ -262,11 +278,7 @@ export async function saveCache(
const reserveCacheResponse = await cacheHttpClient.reserveCache(
key,
numberOfChunks,
{
compressionMethod,
enableCrossOsArchive,
cacheSize: archiveFileSize
}
cacheVersion
)

if (reserveCacheResponse?.statusCode === 400) {
Expand All @@ -278,12 +290,6 @@ export async function saveCache(
)
}

const cacheVersion = cacheHttpClient.getCacheVersion(
paths,
compressionMethod,
enableCrossOsArchive
)

switch (reserveCacheResponse.result?.provider) {
case 's3':
core.debug(`Saving Cache to S3`)
Expand Down Expand Up @@ -341,18 +347,30 @@ export async function saveCache(

/**
* Deletes an entire cache by cache key.
* @param keys The cache keys
* @param key The cache keys
*/
export async function deleteCache(keys: string[]): Promise<void> {
for (const key of keys) {
checkKey(key)
}
export async function deleteCache(
paths: string[],
key: string,
enableCrossOsArchive = false,
enableCrossArchArchive = false
): Promise<void> {
checkKey(key)

core.debug('Deleting Cache')
core.debug(`Cache Keys: ${keys}`)
core.debug(`Cache Key: ${key}`)

const compressionMethod = await utils.getCompressionMethod()

const cacheVersion = cacheHttpClient.getCacheVersion(
paths,
compressionMethod,
enableCrossOsArchive,
enableCrossArchArchive
)

try {
await cacheHttpClient.deleteCache(keys)
await cacheHttpClient.deleteCache(key, cacheVersion)
} catch (error) {
core.warning(`Failed to delete cache: ${error}`)
}
Expand Down
78 changes: 61 additions & 17 deletions packages/warp-cache/src/internal/cacheHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ import {Storage} from '@google-cloud/storage'
import {
CommonsCommitCacheRequest,
CommonsCommitCacheResponse,
CommonsDeleteCacheResponse,
CommonsGetCacheResponse,
CommonsReserveCacheRequest,
CommonsReserveCacheResponse
} from './warpcache-ts-sdk'
import {multiPartUploadToGCS, uploadFileToS3} from './uploadUtils'
import {CommonsGetCacheRequest} from './warpcache-ts-sdk/models/commons-get-cache-request'
import {CommonsDeleteCacheRequest} from './warpcache-ts-sdk/models/commons-delete-cache-request'

const versionSalt = '1.0'

Expand All @@ -47,6 +50,16 @@ function createAcceptHeader(type: string, apiVersion: string): string {
return `${type};api-version=${apiVersion}`
}

function getVCSRepository(): string {
const vcsRepository = process.env['GITHUB_REPOSITORY'] ?? ''
return vcsRepository
}

function getVCSRef(): string {
const vcsBranch = process.env['GITHUB_REF'] ?? ''
return vcsBranch
}

function getRequestOptions(): RequestOptions {
const requestOptions: RequestOptions = {
headers: {
Expand All @@ -71,7 +84,8 @@ function createHttpClient(): HttpClient {
export function getCacheVersion(
paths: string[],
compressionMethod?: CompressionMethod,
enableCrossOsArchive = false
enableCrossOsArchive = false,
enableCrossArchArchive = false
): string {
const components = paths

Expand All @@ -86,29 +100,44 @@ export function getCacheVersion(
components.push('windows-only')
}

// Add architecture to cache version
if (!enableCrossArchArchive) {
components.push(process.arch)
}

// Add salt to cache version to support breaking changes in cache entry
components.push(versionSalt)

return crypto.createHash('sha256').update(components.join('|')).digest('hex')
}

export async function getCacheEntry(
keys: string[],
key: string,
restoreKeys: string[],
paths: string[],
options?: InternalCacheOptions
): Promise<CommonsGetCacheResponse | null> {
const httpClient = createHttpClient()
const version = getCacheVersion(
paths,
options?.compressionMethod,
options?.enableCrossOsArchive
options?.enableCrossOsArchive,
options?.enableCrossArchArchive
)
const resource = `cache?keys=${encodeURIComponent(
keys.join(',')
)}&version=${version}`

const getCacheRequest: CommonsGetCacheRequest = {
cache_key: key,
restore_keys: restoreKeys,
cache_version: version,
vcs_repository: getVCSRepository(),
vcs_ref: getVCSRef()
}

const response = await retryTypedResponse('getCacheEntry', async () =>
httpClient.getJson<CommonsGetCacheResponse>(getCacheApiUrl(resource))
httpClient.postJson<CommonsGetCacheResponse>(
getCacheApiUrl('cache/get'),
getCacheRequest
)
)

if (response.statusCode === 204) {
Expand Down Expand Up @@ -190,14 +219,17 @@ export function downloadCacheStreaming(
export async function reserveCache(
cacheKey: string,
numberOfChunks: number,
options?: InternalCacheOptions
cacheVersion: string
): Promise<ITypedResponseWithError<CommonsReserveCacheResponse>> {
const httpClient = createHttpClient()

const reserveCacheRequest: CommonsReserveCacheRequest = {
cache_key: cacheKey,
cache_version: cacheVersion,
number_of_chunks: numberOfChunks,
content_type: 'application/zstd'
content_type: 'application/zstd',
vcs_repository: getVCSRepository(),
vcs_ref: getVCSRef()
}
const response = await retryTypedResponse('reserveCache', async () =>
httpClient.postJson<CommonsReserveCacheResponse>(
Expand Down Expand Up @@ -227,8 +259,9 @@ async function commitCache(
upload_key: uploadKey,
upload_id: uploadID,
parts: parts,
os: process.env['RUNNER_OS'] ?? 'Linux',
vcs_type: 'github'
vcs_type: 'github',
vcs_repository: getVCSRepository(),
vcs_ref: getVCSRef()
}
return await retryTypedResponse('commitCache', async () =>
httpClient.postJson<CommonsCommitCacheResponse>(
Expand Down Expand Up @@ -340,13 +373,24 @@ export async function saveCache(
return cacheKeyResponse
}

export async function deleteCache(keys: string[]) {
export async function deleteCache(cacheKey: string, cacheVersion: string) {
const httpClient = createHttpClient()
const resource = `cache?keys=${encodeURIComponent(keys.join(','))}`
const response = await httpClient.del(getCacheApiUrl(resource))
if (!isSuccessStatusCode(response.message.statusCode)) {
throw new Error(
`Cache service responded with ${response.message.statusCode}`

const deleteCacheRequest: CommonsDeleteCacheRequest = {
cache_key: cacheKey,
cache_version: cacheVersion,
vcs_repository: getVCSRepository(),
vcs_ref: getVCSRef()
}

const response = await retryTypedResponse('deleteCacheEntry', async () =>
httpClient.postJson<CommonsDeleteCacheResponse>(
getCacheApiUrl('cache/delete'),
deleteCacheRequest
)
)

if (!isSuccessStatusCode(response.statusCode)) {
throw new Error(`Cache service responded with ${response.statusCode}`)
}
}
1 change: 1 addition & 0 deletions packages/warp-cache/src/internal/contracts.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface ITypedResponseWithError<T> extends TypedResponse<T> {
export interface InternalCacheOptions {
compressionMethod?: CompressionMethod
enableCrossOsArchive?: boolean
enableCrossArchArchive?: boolean
cacheSize?: number
}

Expand Down
2 changes: 1 addition & 1 deletion packages/warp-cache/src/internal/requestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export async function retryTypedResponse<T>(
if (error instanceof HttpClientError) {
return {
statusCode: error.statusCode,
result: null,
result: error.result ?? null,
headers: {},
error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ common.ts
configuration.ts
git_push.sh
index.ts
models/commons-cache-entry.ts
models/commons-commit-cache-request.ts
models/commons-commit-cache-response.ts
models/commons-delete-cache-request.ts
models/commons-delete-cache-response.ts
models/commons-gcscommit-cache-response.ts
models/commons-gcsdelete-cache-response.ts
models/commons-gcsget-cache-reponse.ts
models/commons-gcsreserve-cache-response.ts
models/commons-get-cache-request.ts
models/commons-get-cache-response.ts
models/commons-reserve-cache-request.ts
models/commons-reserve-cache-response.ts
Expand Down
Loading

0 comments on commit 9a8c6b2

Please sign in to comment.