1
0
Fork 0

utilize client, fetch IDs

pull/1487/head
Bethany 2023-08-09 11:26:33 -07:00
parent 92695f58da
commit 73ad88882e
4 changed files with 184 additions and 4 deletions

View File

@ -12,6 +12,7 @@
"@actions/core": "^1.10.0",
"@actions/http-client": "^2.1.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1",
"jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0"
},
"devDependencies": {
@ -198,6 +199,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/jwt-decode": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz",
"integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A=="
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",

View File

@ -42,10 +42,11 @@
"@actions/core": "^1.10.0",
"@actions/http-client": "^2.1.0",
"@protobuf-ts/plugin": "^2.2.3-alpha.1",
"jwt-decode": "^3.1.2",
"twirp-ts": "^2.5.0"
},
"devDependencies": {
"@types/tmp": "^0.2.1",
"typescript": "^4.3.0"
}
}
}

View File

@ -0,0 +1,73 @@
import {getRetentionDays, getRuntimeToken} from './config'
import jwt_decode from 'jwt-decode'
import {Timestamp} from 'src/generated'
export interface BackendIds {
workflowRunBackendId: string
workflowJobRunBackendId: string
}
interface ActionsToken {
scp: string
}
// 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 new Error('No scp claim in JWT token')
}
/*
* 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 new Error('No scp parts in JWT token')
}
/*
* 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(':')
/*
* example scopeParts:
* ["Actions.Results", "ce7f54c7-61c7-4aae-887f-30da475f5f1a", "ca395085-040a-526b-2ce8-bdc85f692774"]
*/
if (scopeParts.length !== 3) {
// not the Actions.Results scope
continue
}
if (scopeParts[0] !== 'Actions.Results') {
// not the Actions.Results scope
continue
}
return {
workflowRunBackendId: scopeParts[1],
workflowJobRunBackendId: scopeParts[2]
}
}
throw new Error('No valid Actions.Results scope in JWT token')
}
export function getExpiration(retentionDays?: number): Timestamp | undefined {
if (!retentionDays) {
return undefined
}
const expirationDate = new Date()
expirationDate.setDate(expirationDate.getDate() + retentionDays)
return Timestamp.fromDate(expirationDate)
}

View File

@ -2,11 +2,18 @@ import * as core from '@actions/core'
import {UploadOptions} from './upload-options'
import {UploadResponse} from './upload-response'
import {validateArtifactName} from './path-and-artifact-name-validation'
import {createArtifactTwirpClient} from '../shared/artifact-twirp-client'
import {
UploadZipSpecification,
getUploadZipSpecification,
validateRootDirectory
} from './upload-zip-specification'
import {BackendIds, getBackendIdsFromToken, getExpiration} from '../shared/util'
import {
CreateArtifactRequest,
CreateArtifactResponse,
FinalizeArtifactResponse
} from 'src/generated'
export async function uploadArtifact(
name: string,
@ -14,8 +21,17 @@ export async function uploadArtifact(
rootDirectory: string,
options?: UploadOptions | undefined // eslint-disable-line @typescript-eslint/no-unused-vars
): Promise<UploadResponse> {
validateArtifactName(name)
validateRootDirectory(rootDirectory)
try {
validateArtifactName(name)
validateRootDirectory(rootDirectory)
} catch (error) {
core.warning(
`Received error trying to validate artifact name or root directory: ${error}`
)
return {
success: false
}
}
const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification(
files,
@ -28,13 +44,97 @@ export async function uploadArtifact(
}
}
// get the IDs needed for the artifact creation
const backendIds = getBackendIds()
if (!backendIds.workflowRunBackendId || !backendIds.workflowJobRunBackendId) {
core.warning(`Failed to get backend ids`)
return {
success: false
}
}
// create the artifact client
const artifactClient = createArtifactTwirpClient('upload')
// create the artifact
const createArtifactReq: CreateArtifactRequest = {
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name: name,
version: 4
}
// if there is a retention period, add it to the request
const expiresAt = getExpiration(options?.retentionDays)
if (expiresAt) {
createArtifactReq.expiresAt = expiresAt
}
const createArtifactResp = await createArtifact(() =>
artifactClient.CreateArtifact(createArtifactReq)
)
if (!createArtifactResp || !createArtifactResp.ok) {
core.warning(`Failed to create artifact`)
return {
success: false
}
}
// TODO - Implement upload functionality
// finalize the artifact
const finalizeArtifactResp = await finalizeArtifact(() =>
artifactClient.FinalizeArtifact({
workflowRunBackendId: backendIds.workflowRunBackendId,
workflowJobRunBackendId: backendIds.workflowJobRunBackendId,
name: name,
size: '0' // TODO - Add size
})
)
if (!finalizeArtifactResp || !finalizeArtifactResp.ok) {
core.warning(`Failed to finalize artifact`)
return {
success: false
}
}
const uploadResponse: UploadResponse = {
success: true,
size: 0,
id: 0
id: parseInt(finalizeArtifactResp.artifactId) // TODO - will this be a problem due to the id being a bigint?
}
return uploadResponse
}
async function createArtifact(
operation: () => Promise<CreateArtifactResponse>
): Promise<CreateArtifactResponse | undefined> {
try {
return await operation()
} catch (error) {
core.warning(`Received error trying to create artifact: ${error}`)
return
}
}
async function finalizeArtifact(
operation: () => Promise<FinalizeArtifactResponse>
): Promise<FinalizeArtifactResponse | undefined> {
try {
return await operation()
} catch (error) {
core.warning(`Received error trying to create artifact: ${error}`)
return
}
}
function getBackendIds(): BackendIds {
try {
return getBackendIdsFromToken()
} catch (error) {
core.warning(`Received error trying to get backend ids: ${error}`)
return {
workflowRunBackendId: '',
workflowJobRunBackendId: ''
}
}
}