1
0
Fork 0

Port dependencies & remove dependency on toolkit/artifacts

pull/1857/head
Bassem Dghaidi 2024-11-14 03:01:04 -08:00 committed by GitHub
parent d109d9c03e
commit 9dff82c727
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 84 additions and 14 deletions

View File

@ -17,8 +17,6 @@ import {
import { CacheFileSizeLimit } from './internal/constants' import { CacheFileSizeLimit } from './internal/constants'
import { UploadCacheFile } from './internal/blob/upload-cache' import { UploadCacheFile } from './internal/blob/upload-cache'
import { DownloadCacheFile } from './internal/blob/download-cache' import { DownloadCacheFile } from './internal/blob/download-cache'
import { getBackendIdsFromToken, BackendIds } from '@actions/artifact/lib/internal/shared/util'
export class ValidationError extends Error { export class ValidationError extends Error {
constructor(message: string) { constructor(message: string) {
super(message) super(message)
@ -62,7 +60,6 @@ function checkKey(key: string): void {
* *
* @returns boolean return true if Actions cache service feature is available, otherwise false * @returns boolean return true if Actions cache service feature is available, otherwise false
*/ */
export function isFeatureAvailable(): boolean { export function isFeatureAvailable(): boolean {
return !!process.env['ACTIONS_CACHE_URL'] return !!process.env['ACTIONS_CACHE_URL']
} }
@ -215,7 +212,8 @@ async function restoreCachev2(
restoreKeys = restoreKeys || [] restoreKeys = restoreKeys || []
const keys = [primaryKey, ...restoreKeys] const keys = [primaryKey, ...restoreKeys]
core.debug(`Resolved Keys: JSON.stringify(keys)`) core.debug('Resolved Keys:')
core.debug(JSON.stringify(keys))
if (keys.length > 10) { if (keys.length > 10) {
throw new ValidationError( throw new ValidationError(
@ -229,7 +227,7 @@ async function restoreCachev2(
let archivePath = '' let archivePath = ''
try { try {
const twirpClient = cacheTwirpClient.internalCacheTwirpClient() const twirpClient = cacheTwirpClient.internalCacheTwirpClient()
const backendIds: BackendIds = getBackendIdsFromToken() const backendIds: utils.BackendIds = utils.getBackendIdsFromToken()
const compressionMethod = await utils.getCompressionMethod() const compressionMethod = await utils.getCompressionMethod()
const request: GetCacheEntryDownloadURLRequest = { const request: GetCacheEntryDownloadURLRequest = {
@ -289,8 +287,7 @@ async function restoreCachev2(
return request.key return request.key
} catch (error) { } catch (error) {
// TODO: handle all the possible error scenarios throw new Error(`Failed to restore: ${error.message}`)
throw new Error(`Unable to download and extract cache: ${error.message}`)
} finally { } finally {
try { try {
await utils.unlinkFile(archivePath) await utils.unlinkFile(archivePath)
@ -450,7 +447,7 @@ async function saveCachev2(
enableCrossOsArchive = false enableCrossOsArchive = false
): Promise<number> { ): Promise<number> {
// BackendIds are retrieved form the signed JWT // BackendIds are retrieved form the signed JWT
const backendIds: BackendIds = getBackendIdsFromToken() const backendIds: utils.BackendIds = utils.getBackendIdsFromToken()
const compressionMethod = await utils.getCompressionMethod() const compressionMethod = await utils.getCompressionMethod()
const twirpClient = cacheTwirpClient.internalCacheTwirpClient() const twirpClient = cacheTwirpClient.internalCacheTwirpClient()
let cacheId = -1 let cacheId = -1
@ -504,16 +501,13 @@ async function saveCachev2(
version: version version: version
} }
const response: CreateCacheEntryResponse = await twirpClient.CreateCacheEntry(request) const response: CreateCacheEntryResponse = await twirpClient.CreateCacheEntry(request)
core.info(`CreateCacheEntryResponse: ${JSON.stringify(response)}`)
// TODO: handle the error cases here
if (!response.ok) { if (!response.ok) {
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.`
) )
} }
// TODO: mask the signed upload URL core.debug(`Saving Cache to: ${core.setSecret(response.signedUploadUrl)}`)
core.debug(`Saving Cache to: ${response.signedUploadUrl}`)
await UploadCacheFile( await UploadCacheFile(
response.signedUploadUrl, response.signedUploadUrl,
archivePath, archivePath,
@ -536,11 +530,10 @@ async function saveCachev2(
) )
} }
// TODO: this is not great, we should handle the types without parsing
cacheId = parseInt(finalizeResponse.entryId) cacheId = parseInt(finalizeResponse.entryId)
} catch (error) { } catch (error) {
const typedError = error as Error const typedError = error as Error
core.debug(typedError.message) core.warning(`Failed to save: ${typedError.message}`)
} finally { } finally {
// Try to delete the archive to save space // Try to delete the archive to save space
try { try {

View File

@ -7,6 +7,7 @@ import * as fs from 'fs'
import * as path from 'path' import * as path from 'path'
import * as semver from 'semver' import * as semver from 'semver'
import * as util from 'util' import * as util from 'util'
import jwt_decode from 'jwt-decode'
import { import {
CacheFilename, CacheFilename,
CompressionMethod, CompressionMethod,
@ -169,4 +170,80 @@ export function getCacheVersion(
components.push(versionSalt) components.push(versionSalt)
return crypto.createHash('sha256').update(components.join('|')).digest('hex') return crypto.createHash('sha256').update(components.join('|')).digest('hex')
}
export function getRuntimeToken(): string {
const token = process.env['ACTIONS_RUNTIME_TOKEN']
if (!token) {
throw new Error('Unable to get the ACTIONS_RUNTIME_TOKEN env variable')
}
return token
}
export interface BackendIds {
workflowRunBackendId: string
workflowJobRunBackendId: string
}
interface ActionsToken {
scp: string
}
const InvalidJwtError = new Error(
'Failed to get backend IDs: The provided JWT token is invalid and/or missing claims'
)
// uses the JWT token claims to get the
// workflow run and workflow job run backend ids
export function getBackendIdsFromToken(): BackendIds {
const token = getRuntimeToken()
const decoded = jwt_decode<ActionsToken>(token)
if (!decoded.scp) {
throw InvalidJwtError
}
/*
* example decoded:
* {
* scp: "Actions.ExampleScope Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774"
* }
*/
const scpParts = decoded.scp.split(' ')
if (scpParts.length === 0) {
throw InvalidJwtError
}
/*
* example scpParts:
* ["Actions.ExampleScope", "Actions.Results:ce7f54c7-61c7-4aae-887f-30da475f5f1a:ca395085-040a-526b-2ce8-bdc85f692774"]
*/
for (const scopes of scpParts) {
const scopeParts = scopes.split(':')
if (scopeParts?.[0] !== 'Actions.Results') {
// not the Actions.Results scope
continue
}
/*
* example scopeParts:
* ["Actions.Results", "ce7f54c7-61c7-4aae-887f-30da475f5f1a", "ca395085-040a-526b-2ce8-bdc85f692774"]
*/
if (scopeParts.length !== 3) {
// missing expected number of claims
throw InvalidJwtError
}
const ids = {
workflowRunBackendId: scopeParts[1],
workflowJobRunBackendId: scopeParts[2]
}
core.debug(`Workflow Run Backend ID: ${ids.workflowRunBackendId}`)
core.debug(`Workflow Job Run Backend ID: ${ids.workflowJobRunBackendId}`)
return ids
}
throw InvalidJwtError
} }