From 73ad88882e3ab56a584f3c407dcb807c962b3ac1 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 11:26:33 -0700 Subject: [PATCH 01/10] utilize client, fetch IDs --- packages/artifact/package-lock.json | 6 + packages/artifact/package.json | 3 +- packages/artifact/src/internal/shared/util.ts | 73 ++++++++++++ .../src/internal/upload/upload-artifact.ts | 106 +++++++++++++++++- 4 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 packages/artifact/src/internal/shared/util.ts diff --git a/packages/artifact/package-lock.json b/packages/artifact/package-lock.json index 4aa381e2..0933a20c 100644 --- a/packages/artifact/package-lock.json +++ b/packages/artifact/package-lock.json @@ -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", diff --git a/packages/artifact/package.json b/packages/artifact/package.json index d74eddc7..9cd1594b 100644 --- a/packages/artifact/package.json +++ b/packages/artifact/package.json @@ -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" } -} \ No newline at end of file +} diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts new file mode 100644 index 00000000..4f378145 --- /dev/null +++ b/packages/artifact/src/internal/shared/util.ts @@ -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(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) +} diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 07835c99..335936d2 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -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 { - 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 +): Promise { + try { + return await operation() + } catch (error) { + core.warning(`Received error trying to create artifact: ${error}`) + return + } +} + +async function finalizeArtifact( + operation: () => Promise +): Promise { + 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: '' + } + } +} From e8fb71c4bbf7c70f05ebf382db0c990d5136045b Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 11:34:18 -0700 Subject: [PATCH 02/10] lint --- packages/artifact/src/internal/shared/util.ts | 2 +- packages/artifact/src/internal/upload/upload-artifact.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts index 4f378145..1b7ca9e1 100644 --- a/packages/artifact/src/internal/shared/util.ts +++ b/packages/artifact/src/internal/shared/util.ts @@ -1,4 +1,4 @@ -import {getRetentionDays, getRuntimeToken} from './config' +import {getRuntimeToken} from './config' import jwt_decode from 'jwt-decode' import {Timestamp} from 'src/generated' diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 335936d2..a793ce17 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -60,7 +60,7 @@ export async function uploadArtifact( const createArtifactReq: CreateArtifactRequest = { workflowRunBackendId: backendIds.workflowRunBackendId, workflowJobRunBackendId: backendIds.workflowJobRunBackendId, - name: name, + name, version: 4 } @@ -69,7 +69,7 @@ export async function uploadArtifact( if (expiresAt) { createArtifactReq.expiresAt = expiresAt } - const createArtifactResp = await createArtifact(() => + const createArtifactResp = await createArtifact(async () => artifactClient.CreateArtifact(createArtifactReq) ) if (!createArtifactResp || !createArtifactResp.ok) { @@ -82,11 +82,11 @@ export async function uploadArtifact( // TODO - Implement upload functionality // finalize the artifact - const finalizeArtifactResp = await finalizeArtifact(() => + const finalizeArtifactResp = await finalizeArtifact(async () => artifactClient.FinalizeArtifact({ workflowRunBackendId: backendIds.workflowRunBackendId, workflowJobRunBackendId: backendIds.workflowJobRunBackendId, - name: name, + name, size: '0' // TODO - Add size }) ) From b851b70474a8e76bef7a43d3e5f10c73bf933605 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 12:08:43 -0700 Subject: [PATCH 03/10] catch errors at the root, remove unneccessary disabled rule --- packages/artifact/src/internal/client.ts | 10 ++- .../internal/shared/artifact-twirp-client.ts | 4 +- packages/artifact/src/internal/shared/util.ts | 8 +- .../src/internal/upload/upload-artifact.ts | 75 ++++--------------- 4 files changed, 29 insertions(+), 68 deletions(-) diff --git a/packages/artifact/src/internal/client.ts b/packages/artifact/src/internal/client.ts index bf33120f..26546430 100644 --- a/packages/artifact/src/internal/client.ts +++ b/packages/artifact/src/internal/client.ts @@ -1,6 +1,7 @@ import {UploadOptions} from './upload/upload-options' import {UploadResponse} from './upload/upload-response' import {uploadArtifact} from './upload/upload-artifact' +import {warning} from '@actions/core' export interface ArtifactClient { /** @@ -39,6 +40,13 @@ export class Client implements ArtifactClient { rootDirectory: string, options?: UploadOptions | undefined ): Promise { - return uploadArtifact(name, files, rootDirectory, options) + try { + return uploadArtifact(name, files, rootDirectory, options) + } catch (error) { + warning(`Failed to upload artifact: ${error}`) + return { + success: false + } + } } } diff --git a/packages/artifact/src/internal/shared/artifact-twirp-client.ts b/packages/artifact/src/internal/shared/artifact-twirp-client.ts index d2450183..a77d02a4 100644 --- a/packages/artifact/src/internal/shared/artifact-twirp-client.ts +++ b/packages/artifact/src/internal/shared/artifact-twirp-client.ts @@ -56,8 +56,6 @@ class ArtifactHttpClient implements Rpc { const headers = { 'Content-Type': contentType } - info(`Making request to ${url} with data: ${JSON.stringify(data)}`) - try { const response = await this.retryableRequest(async () => this.httpClient.post(url, JSON.stringify(data), headers) @@ -65,7 +63,7 @@ class ArtifactHttpClient implements Rpc { const body = await response.readBody() return JSON.parse(body) } catch (error) { - throw new Error(error.message) + throw new Error(`Failed to ${method}: ${error.message}`) } } diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts index 1b7ca9e1..62849eb5 100644 --- a/packages/artifact/src/internal/shared/util.ts +++ b/packages/artifact/src/internal/shared/util.ts @@ -11,13 +11,15 @@ interface ActionsToken { scp: string } +const InvalidJwtError = new Error('Failed to get backend IDs: The provided JWT token is invalid') + // 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(token) if (!decoded.scp) { - throw new Error('No scp claim in JWT token') + throw InvalidJwtError } /* @@ -29,7 +31,7 @@ export function getBackendIdsFromToken(): BackendIds { const scpParts = decoded.scp.split(' ') if (scpParts.length === 0) { - throw new Error('No scp parts in JWT token') + throw InvalidJwtError } /* * example scpParts: @@ -58,7 +60,7 @@ export function getBackendIdsFromToken(): BackendIds { } } - throw new Error('No valid Actions.Results scope in JWT token') + throw InvalidJwtError } export function getExpiration(retentionDays?: number): Timestamp | undefined { diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index a793ce17..2d3d3333 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -8,30 +8,17 @@ import { getUploadZipSpecification, validateRootDirectory } from './upload-zip-specification' -import {BackendIds, getBackendIdsFromToken, getExpiration} from '../shared/util' -import { - CreateArtifactRequest, - CreateArtifactResponse, - FinalizeArtifactResponse -} from 'src/generated' +import {getBackendIdsFromToken, getExpiration} from '../shared/util' +import {CreateArtifactRequest} from 'src/generated' export async function uploadArtifact( name: string, files: string[], rootDirectory: string, - options?: UploadOptions | undefined // eslint-disable-line @typescript-eslint/no-unused-vars + options?: UploadOptions | undefined ): Promise { - try { - validateArtifactName(name) - validateRootDirectory(rootDirectory) - } catch (error) { - core.warning( - `Received error trying to validate artifact name or root directory: ${error}` - ) - return { - success: false - } - } + validateArtifactName(name) + validateRootDirectory(rootDirectory) const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification( files, @@ -45,7 +32,7 @@ export async function uploadArtifact( } // get the IDs needed for the artifact creation - const backendIds = getBackendIds() + const backendIds = getBackendIdsFromToken() if (!backendIds.workflowRunBackendId || !backendIds.workflowJobRunBackendId) { core.warning(`Failed to get backend ids`) return { @@ -69,10 +56,9 @@ export async function uploadArtifact( if (expiresAt) { createArtifactReq.expiresAt = expiresAt } - const createArtifactResp = await createArtifact(async () => - artifactClient.CreateArtifact(createArtifactReq) - ) - if (!createArtifactResp || !createArtifactResp.ok) { + + const createArtifactResp = await artifactClient.CreateArtifact(createArtifactReq) + if (!createArtifactResp.ok) { core.warning(`Failed to create artifact`) return { success: false @@ -82,20 +68,21 @@ export async function uploadArtifact( // TODO - Implement upload functionality // finalize the artifact - const finalizeArtifactResp = await finalizeArtifact(async () => - artifactClient.FinalizeArtifact({ + const finalizeArtifactResp = await artifactClient.FinalizeArtifact( + { workflowRunBackendId: backendIds.workflowRunBackendId, workflowJobRunBackendId: backendIds.workflowJobRunBackendId, name, size: '0' // TODO - Add size - }) + } ) - if (!finalizeArtifactResp || !finalizeArtifactResp.ok) { + if (!finalizeArtifactResp.ok) { core.warning(`Failed to finalize artifact`) return { success: false } } + const uploadResponse: UploadResponse = { success: true, size: 0, @@ -104,37 +91,3 @@ export async function uploadArtifact( return uploadResponse } - -async function createArtifact( - operation: () => Promise -): Promise { - try { - return await operation() - } catch (error) { - core.warning(`Received error trying to create artifact: ${error}`) - return - } -} - -async function finalizeArtifact( - operation: () => Promise -): Promise { - 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: '' - } - } -} From 08d6314f7c6f9a5d7f452780a88d73214e728ad6 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 12:09:17 -0700 Subject: [PATCH 04/10] prettier --- packages/artifact/src/internal/shared/util.ts | 4 +++- .../src/internal/upload/upload-artifact.ts | 18 +++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts index 62849eb5..0b9dfd4b 100644 --- a/packages/artifact/src/internal/shared/util.ts +++ b/packages/artifact/src/internal/shared/util.ts @@ -11,7 +11,9 @@ interface ActionsToken { scp: string } -const InvalidJwtError = new Error('Failed to get backend IDs: The provided JWT token is invalid') +const InvalidJwtError = new Error( + 'Failed to get backend IDs: The provided JWT token is invalid' +) // uses the JWT token claims to get the // workflow run and workflow job run backend ids diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 2d3d3333..8ad52390 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -57,7 +57,9 @@ export async function uploadArtifact( createArtifactReq.expiresAt = expiresAt } - const createArtifactResp = await artifactClient.CreateArtifact(createArtifactReq) + const createArtifactResp = await artifactClient.CreateArtifact( + createArtifactReq + ) if (!createArtifactResp.ok) { core.warning(`Failed to create artifact`) return { @@ -68,14 +70,12 @@ export async function uploadArtifact( // TODO - Implement upload functionality // finalize the artifact - const finalizeArtifactResp = await artifactClient.FinalizeArtifact( - { - workflowRunBackendId: backendIds.workflowRunBackendId, - workflowJobRunBackendId: backendIds.workflowJobRunBackendId, - name, - size: '0' // TODO - Add size - } - ) + const finalizeArtifactResp = await artifactClient.FinalizeArtifact({ + workflowRunBackendId: backendIds.workflowRunBackendId, + workflowJobRunBackendId: backendIds.workflowJobRunBackendId, + name, + size: '0' // TODO - Add size + }) if (!finalizeArtifactResp.ok) { core.warning(`Failed to finalize artifact`) return { From 4b219f79f3bd66607fea0719579b50ecbe81c4ed Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 12:29:43 -0700 Subject: [PATCH 05/10] Add tests for backend id fetch --- packages/artifact/__tests__/util.test.ts | 50 +++++++++++++++++++ packages/artifact/src/internal/shared/util.ts | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 packages/artifact/__tests__/util.test.ts diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts new file mode 100644 index 00000000..cd43232c --- /dev/null +++ b/packages/artifact/__tests__/util.test.ts @@ -0,0 +1,50 @@ +import * as config from '../src/internal/shared/config' +import * as util from '../src/internal/shared/util' + +describe('get-backend-ids-from-token', () => { + it('should return backend ids when the token is valid', () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjRTenlCTkRYcHNTT3owbERZMHYzS1JWVFJndyJ9.eyJuYW1laWQiOiJkZGRkZGRkZC1kZGRkLWRkZGQtZGRkZC1kZGRkZGRkZGRkZGQiLCJzY3AiOiJBY3Rpb25zLk1hbmFnZU9yZ3M6IEFjdGlvbnMuUmVzdWx0czpjZTdmNTRjNy02MWM3LTRhYWUtODg3Zi0zMGRhNDc1ZjVmMWE6Y2EzOTUwODUtMDQwYS01MjZiLTJjZTgtYmRjODVmNjkyNzc0IiwiSWRlbnRpdHlUeXBlQ2xhaW0iOiJTeXN0ZW06U2VydmljZUlkZW50aXR5IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc2lkIjoiREREREREREQtRERERC1ERERELUREREQtREREREREREREREREIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9wcmltYXJ5c2lkIjoiZGRkZGRkZGQtZGRkZC1kZGRkLWRkZGQtZGRkZGRkZGRkZGRkIiwiYXVpIjoiZDJiZDAxNzItMzYyOC00ODFhLTg2MGUtMjM4NTA3YjQwYmY1Iiwic2lkIjoiOWM0ZmVmZTUtMWYyNC00NzZmLWJiNjctZjg3YTk3ZDVkNGVmIiwiaXNzIjoidnN0b2tlbi5jb2RlZGV2LmxvY2FsaG9zdCIsImF1ZCI6InZzdG9rZW4uY29kZWRldi5sb2NhbGhvc3QiLCJuYmYiOjE2OTE1OTU2ODQsImV4cCI6MTY5MTY4MjY4NH0.Px_VE9iSaQ2dNMxr_sKItRkjo2_OIaCe6LLGaFBRVeac3e3GYGx8FchqNbdP6YKugOq0FO2jQapg4Pqhorw-gPCZbyNyLAlFldZKicTNR4s-nNmESX3CsI9_gEfEaBRXWDVc8bmg121joHS0BxaSBKVhG78Q5rFTyzMUUu6x09Kotf1TW8r2v0BUBnuOzWASV9fl4rAxv3H2KZujT5e0-cWMG7pUkmaHlpoCVHuI4f4tyHZGlCjRp8wYhUh5sIbUyrxYcIHNH5uk1b55Rv8qy498jLvlL1oOArgB361JUmXknZBHitvnU6VoS_k_LRA21AIJ_csw7XoYB1DTexIKTCQCzCBclcfYWCeYkDzQ2B7TBdQMoc_QZyB2S1ulSt2_9YcpE-RLaYitM-JA6MFvGKHcXJdsBtrlW_7vmQDlTuHjGuhpV5gpPZS8d_u72wR2A3n9AcBZsmT0dSg5GQiYQRZkwXsfBCFg4v6sh_CticT6zpFEH__jZ5GcYpeDheTgcCOKJGsBmkH8FWKGBc_NvApvNxXYGYMycWoLZp6G9fXw-EQJ-XVZzG9zmsETkCUhZZBaGvdY0NQBbVpwB87o493ooKeX1Q3pBulWt_obcsKnl1M1yvoy2m1L34sHrkdn6xaiXjaKAOQKz_RAoikYMzbH1i2d2rgjWdmmOvIMmns' + ) + + const backendIds = util.getBackendIdsFromToken() + expect(backendIds.workflowRunBackendId).toBe( + 'ce7f54c7-61c7-4aae-887f-30da475f5f1a' + ) + expect(backendIds.workflowJobRunBackendId).toBe( + 'ca395085-040a-526b-2ce8-bdc85f692774' + ) + }) + + it("should throw an error when the token doesn't have the right scope", () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjRTenlCTkRYcHNTT3owbERZMHYzS1JWVFJndyJ9.eyJuYW1laWQiOiJkZGRkZGRkZC1kZGRkLWRkZGQtZGRkZC1kZGRkZGRkZGRkZGQiLCJzY3AiOiJBY3Rpb25zLk1hbmFnZU9yZ3M6IiwiSWRlbnRpdHlUeXBlQ2xhaW0iOiJTeXN0ZW06U2VydmljZUlkZW50aXR5IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc2lkIjoiREREREREREQtRERERC1ERERELUREREQtREREREREREREREREIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9wcmltYXJ5c2lkIjoiZGRkZGRkZGQtZGRkZC1kZGRkLWRkZGQtZGRkZGRkZGRkZGRkIiwiYXVpIjoiZDk1OTgzODAtNjg1OC00YjRmLWFhMTQtZmFiMDI1YmEyNTIxIiwic2lkIjoiZjM0YWU4OWMtZWI2NC00Y2Y3LTlkMDQtOGVjN2Q3Njk2YjQ5IiwiaXNzIjoidnN0b2tlbi5jb2RlZGV2LmxvY2FsaG9zdCIsImF1ZCI6InZzdG9rZW4uY29kZWRldi5sb2NhbGhvc3QiLCJuYmYiOjE2OTE2MDgyMTAsImV4cCI6MTY5MTY5NTIxMH0.HGmu6b3uWBB74SNCiiClTuEm_fRA8TWcmlDopHV6aMda0Naq0IgHT0_cPPkxYImRRCheCzrw5Z6LmFhuB3KDfBt-9DBYE9aSc9faomFGyiJN2uQlUYL5vlZhxoo80YMEZqgGQCeHebHF4xIdfYkipala1ttIbAHt_I3D0kebOrIQboQI_1M2ZMgoECY5YIA3-7xJVmlHMeQ4lu_ys2kjPxrpysl1t5emUlTitw6AKLBEvAMLhauGn6Etu6h1QGar2SV1bFKtJ-OImQgXrMVdVxcWL5eC-ebqk-DU9R5MLMGywlx_V_aeRBSyhwZRwPxHKixd-_TT-0U4v0siWqzIg944H9-Z-9XiduTVmODIbkF44jKbua_ohSk1kN_CO5uiHTiAkQnnE94Y586eT9QPHUxCKzlUH0KTntc94lD4zPTO-ZTmH3BJY_bbCrEPNnSMuEoBibf3IIgPo9ap67y3NcJrck6-Y8G-MbsVkBivT6Ac41fvxeKD8GCl9P8Zo7KoMoVzUVK0clHPwqWAES5AnzF9gccT4k5-IH25nGdKVz3UJvZVkjjtlRYdQ6ZTWvjU1T6Sd2242yQ-2AzqLZsStWcZC7VRekCMZqDepLkvOrmRti0_vF1DF3D2flrmukMUyjZ2WvfzI4voDAnHituXQ_LlwIEkIqagwBJ-sfydxzw' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) + + it('should throw an error when the token is in an invalid format', () => { + jest.spyOn(config, 'getRuntimeToken').mockReturnValue('token') + + expect(util.getBackendIdsFromToken).toThrowError('Invalid token specified') + }) + + it("should throw an error when the token doesn't have the right field", () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) +}) diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts index 0b9dfd4b..50844a40 100644 --- a/packages/artifact/src/internal/shared/util.ts +++ b/packages/artifact/src/internal/shared/util.ts @@ -1,6 +1,6 @@ import {getRuntimeToken} from './config' import jwt_decode from 'jwt-decode' -import {Timestamp} from 'src/generated' +import {Timestamp} from '../../generated' export interface BackendIds { workflowRunBackendId: string From 4dda3ab8a06d451a09ada283e074a0601095d7e2 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 13:12:30 -0700 Subject: [PATCH 06/10] move getExpiration to upload-artifact --- packages/artifact/src/internal/shared/util.ts | 12 ------------ .../src/internal/upload/upload-artifact.ts | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/artifact/src/internal/shared/util.ts b/packages/artifact/src/internal/shared/util.ts index 50844a40..bc98abde 100644 --- a/packages/artifact/src/internal/shared/util.ts +++ b/packages/artifact/src/internal/shared/util.ts @@ -1,6 +1,5 @@ import {getRuntimeToken} from './config' import jwt_decode from 'jwt-decode' -import {Timestamp} from '../../generated' export interface BackendIds { workflowRunBackendId: string @@ -64,14 +63,3 @@ export function getBackendIdsFromToken(): BackendIds { throw InvalidJwtError } - -export function getExpiration(retentionDays?: number): Timestamp | undefined { - if (!retentionDays) { - return undefined - } - - const expirationDate = new Date() - expirationDate.setDate(expirationDate.getDate() + retentionDays) - - return Timestamp.fromDate(expirationDate) -} diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 8ad52390..087493a3 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -8,8 +8,8 @@ import { getUploadZipSpecification, validateRootDirectory } from './upload-zip-specification' -import {getBackendIdsFromToken, getExpiration} from '../shared/util' -import {CreateArtifactRequest} from 'src/generated' +import {getBackendIdsFromToken} from '../shared/util' +import {CreateArtifactRequest, Timestamp} from 'src/generated' export async function uploadArtifact( name: string, @@ -91,3 +91,14 @@ export async function uploadArtifact( return uploadResponse } + +function getExpiration(retentionDays?: number): Timestamp | undefined { + if (!retentionDays) { + return undefined + } + + const expirationDate = new Date() + expirationDate.setDate(expirationDate.getDate() + retentionDays) + + return Timestamp.fromDate(expirationDate) +} From 2f42c127c73bbe3b18a407e0e327a759423e7190 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 13:20:06 -0700 Subject: [PATCH 07/10] update tests --- packages/artifact/__tests__/util.test.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/artifact/__tests__/util.test.ts b/packages/artifact/__tests__/util.test.ts index cd43232c..76f760fa 100644 --- a/packages/artifact/__tests__/util.test.ts +++ b/packages/artifact/__tests__/util.test.ts @@ -6,7 +6,7 @@ describe('get-backend-ids-from-token', () => { jest .spyOn(config, 'getRuntimeToken') .mockReturnValue( - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjRTenlCTkRYcHNTT3owbERZMHYzS1JWVFJndyJ9.eyJuYW1laWQiOiJkZGRkZGRkZC1kZGRkLWRkZGQtZGRkZC1kZGRkZGRkZGRkZGQiLCJzY3AiOiJBY3Rpb25zLk1hbmFnZU9yZ3M6IEFjdGlvbnMuUmVzdWx0czpjZTdmNTRjNy02MWM3LTRhYWUtODg3Zi0zMGRhNDc1ZjVmMWE6Y2EzOTUwODUtMDQwYS01MjZiLTJjZTgtYmRjODVmNjkyNzc0IiwiSWRlbnRpdHlUeXBlQ2xhaW0iOiJTeXN0ZW06U2VydmljZUlkZW50aXR5IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc2lkIjoiREREREREREQtRERERC1ERERELUREREQtREREREREREREREREIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9wcmltYXJ5c2lkIjoiZGRkZGRkZGQtZGRkZC1kZGRkLWRkZGQtZGRkZGRkZGRkZGRkIiwiYXVpIjoiZDJiZDAxNzItMzYyOC00ODFhLTg2MGUtMjM4NTA3YjQwYmY1Iiwic2lkIjoiOWM0ZmVmZTUtMWYyNC00NzZmLWJiNjctZjg3YTk3ZDVkNGVmIiwiaXNzIjoidnN0b2tlbi5jb2RlZGV2LmxvY2FsaG9zdCIsImF1ZCI6InZzdG9rZW4uY29kZWRldi5sb2NhbGhvc3QiLCJuYmYiOjE2OTE1OTU2ODQsImV4cCI6MTY5MTY4MjY4NH0.Px_VE9iSaQ2dNMxr_sKItRkjo2_OIaCe6LLGaFBRVeac3e3GYGx8FchqNbdP6YKugOq0FO2jQapg4Pqhorw-gPCZbyNyLAlFldZKicTNR4s-nNmESX3CsI9_gEfEaBRXWDVc8bmg121joHS0BxaSBKVhG78Q5rFTyzMUUu6x09Kotf1TW8r2v0BUBnuOzWASV9fl4rAxv3H2KZujT5e0-cWMG7pUkmaHlpoCVHuI4f4tyHZGlCjRp8wYhUh5sIbUyrxYcIHNH5uk1b55Rv8qy498jLvlL1oOArgB361JUmXknZBHitvnU6VoS_k_LRA21AIJ_csw7XoYB1DTexIKTCQCzCBclcfYWCeYkDzQ2B7TBdQMoc_QZyB2S1ulSt2_9YcpE-RLaYitM-JA6MFvGKHcXJdsBtrlW_7vmQDlTuHjGuhpV5gpPZS8d_u72wR2A3n9AcBZsmT0dSg5GQiYQRZkwXsfBCFg4v6sh_CticT6zpFEH__jZ5GcYpeDheTgcCOKJGsBmkH8FWKGBc_NvApvNxXYGYMycWoLZp6G9fXw-EQJ-XVZzG9zmsETkCUhZZBaGvdY0NQBbVpwB87o493ooKeX1Q3pBulWt_obcsKnl1M1yvoy2m1L34sHrkdn6xaiXjaKAOQKz_RAoikYMzbH1i2d2rgjWdmmOvIMmns' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhOmNhMzk1MDg1LTA0MGEtNTI2Yi0yY2U4LWJkYzg1ZjY5Mjc3NCIsImlhdCI6MTUxNjIzOTAyMn0.XYnI_wHPBlUi1mqYveJnnkJhp4dlFjqxzRmISPsqfw8' ) const backendIds = util.getBackendIdsFromToken() @@ -22,7 +22,19 @@ describe('get-backend-ids-from-token', () => { jest .spyOn(config, 'getRuntimeToken') .mockReturnValue( - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjRTenlCTkRYcHNTT3owbERZMHYzS1JWVFJndyJ9.eyJuYW1laWQiOiJkZGRkZGRkZC1kZGRkLWRkZGQtZGRkZC1kZGRkZGRkZGRkZGQiLCJzY3AiOiJBY3Rpb25zLk1hbmFnZU9yZ3M6IiwiSWRlbnRpdHlUeXBlQ2xhaW0iOiJTeXN0ZW06U2VydmljZUlkZW50aXR5IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvc2lkIjoiREREREREREQtRERERC1ERERELUREREQtREREREREREREREREIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9wcmltYXJ5c2lkIjoiZGRkZGRkZGQtZGRkZC1kZGRkLWRkZGQtZGRkZGRkZGRkZGRkIiwiYXVpIjoiZDk1OTgzODAtNjg1OC00YjRmLWFhMTQtZmFiMDI1YmEyNTIxIiwic2lkIjoiZjM0YWU4OWMtZWI2NC00Y2Y3LTlkMDQtOGVjN2Q3Njk2YjQ5IiwiaXNzIjoidnN0b2tlbi5jb2RlZGV2LmxvY2FsaG9zdCIsImF1ZCI6InZzdG9rZW4uY29kZWRldi5sb2NhbGhvc3QiLCJuYmYiOjE2OTE2MDgyMTAsImV4cCI6MTY5MTY5NTIxMH0.HGmu6b3uWBB74SNCiiClTuEm_fRA8TWcmlDopHV6aMda0Naq0IgHT0_cPPkxYImRRCheCzrw5Z6LmFhuB3KDfBt-9DBYE9aSc9faomFGyiJN2uQlUYL5vlZhxoo80YMEZqgGQCeHebHF4xIdfYkipala1ttIbAHt_I3D0kebOrIQboQI_1M2ZMgoECY5YIA3-7xJVmlHMeQ4lu_ys2kjPxrpysl1t5emUlTitw6AKLBEvAMLhauGn6Etu6h1QGar2SV1bFKtJ-OImQgXrMVdVxcWL5eC-ebqk-DU9R5MLMGywlx_V_aeRBSyhwZRwPxHKixd-_TT-0U4v0siWqzIg944H9-Z-9XiduTVmODIbkF44jKbua_ohSk1kN_CO5uiHTiAkQnnE94Y586eT9QPHUxCKzlUH0KTntc94lD4zPTO-ZTmH3BJY_bbCrEPNnSMuEoBibf3IIgPo9ap67y3NcJrck6-Y8G-MbsVkBivT6Ac41fvxeKD8GCl9P8Zo7KoMoVzUVK0clHPwqWAES5AnzF9gccT4k5-IH25nGdKVz3UJvZVkjjtlRYdQ6ZTWvjU1T6Sd2242yQ-2AzqLZsStWcZC7VRekCMZqDepLkvOrmRti0_vF1DF3D2flrmukMUyjZ2WvfzI4voDAnHituXQ_LlwIEkIqagwBJ-sfydxzw' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCIsImlhdCI6MTUxNjIzOTAyMn0.K0IEoULZteGevF38G94xiaA8zcZ5UlKWfGfqE6q3dhw' + ) + + expect(util.getBackendIdsFromToken).toThrowError( + 'Failed to get backend IDs: The provided JWT token is invalid' + ) + }) + + it('should throw an error when the token has a malformed scope', () => { + jest + .spyOn(config, 'getRuntimeToken') + .mockReturnValue( + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwic2NwIjoiQWN0aW9ucy5FeGFtcGxlIEFjdGlvbnMuQW5vdGhlckV4YW1wbGU6dGVzdCBBY3Rpb25zLlJlc3VsdHM6Y2U3ZjU0YzctNjFjNy00YWFlLTg4N2YtMzBkYTQ3NWY1ZjFhIiwiaWF0IjoxNTE2MjM5MDIyfQ.7D0_LRfRFRZFImHQ7GxH2S6ZyFjjZ5U0ujjGCfle1XE' ) expect(util.getBackendIdsFromToken).toThrowError( From 188abfc20bfe377096ef142c0f57d4d9cfdff38c Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 17:42:14 -0700 Subject: [PATCH 08/10] implement feedback --- packages/artifact/__tests__/retention.test.ts | 59 +++++++++++++++++++ packages/artifact/src/internal/client.ts | 18 +++++- .../artifact/src/internal/shared/config.ts | 7 +++ .../artifact/src/internal/upload/retention.ts | 34 +++++++++++ .../src/internal/upload/upload-artifact.ts | 18 ++---- 5 files changed, 123 insertions(+), 13 deletions(-) create mode 100644 packages/artifact/__tests__/retention.test.ts create mode 100644 packages/artifact/src/internal/upload/retention.ts diff --git a/packages/artifact/__tests__/retention.test.ts b/packages/artifact/__tests__/retention.test.ts new file mode 100644 index 00000000..898c52a0 --- /dev/null +++ b/packages/artifact/__tests__/retention.test.ts @@ -0,0 +1,59 @@ +import {Timestamp} from '../src/generated' +import * as retention from '../src/internal/upload/retention' + +describe('retention', () => { + beforeEach(() => { + delete process.env['GITHUB_RETENTION_DAYS'] + }) + it('should return the inputted retention days if it is less than the max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + process.env['GITHUB_RETENTION_DAYS'] = '90' + + const exp = retention.getExpiration(30) + + expect(exp).toBeDefined() + const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 30) + + expect(expDate).toEqual(expected) + }) + + it('should return the max retention days if the inputted retention days is greater than the max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + process.env['GITHUB_RETENTION_DAYS'] = '90' + + const exp = retention.getExpiration(120) + + expect(exp).toBeDefined() + const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 90) + + expect(expDate).toEqual(expected) + }) + + it('should return undefined if the inputted retention days is undefined', () => { + const exp = retention.getExpiration() + expect(exp).toBeUndefined() + }) + + it('should return the inputted retention days if there is no max retention days', () => { + // setup + const mockDate = new Date('2020-01-01') + jest.useFakeTimers().setSystemTime(mockDate) + + const exp = retention.getExpiration(30) + + expect(exp).toBeDefined() + const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 30) + + expect(expDate).toEqual(expected) + }) +}) diff --git a/packages/artifact/src/internal/client.ts b/packages/artifact/src/internal/client.ts index 26546430..f827d882 100644 --- a/packages/artifact/src/internal/client.ts +++ b/packages/artifact/src/internal/client.ts @@ -2,6 +2,7 @@ import {UploadOptions} from './upload/upload-options' import {UploadResponse} from './upload/upload-response' import {uploadArtifact} from './upload/upload-artifact' import {warning} from '@actions/core' +import {isGhes} from './shared/config' export interface ArtifactClient { /** @@ -40,10 +41,25 @@ export class Client implements ArtifactClient { rootDirectory: string, options?: UploadOptions | undefined ): Promise { + if (isGhes()) { + warning( + `@actions/artifact v2 and upload-artifact v4 are not currently supported on GHES.` + ) + return { + success: false + } + } + try { return uploadArtifact(name, files, rootDirectory, options) } catch (error) { - warning(`Failed to upload artifact: ${error}`) + warning( + `Artifact upload failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug enabled for more information. + +If the error persists, please check whether Actions is running normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) return { success: false } diff --git a/packages/artifact/src/internal/shared/config.ts b/packages/artifact/src/internal/shared/config.ts index 23949f20..d1946e96 100644 --- a/packages/artifact/src/internal/shared/config.ts +++ b/packages/artifact/src/internal/shared/config.ts @@ -13,3 +13,10 @@ export function getResultsServiceUrl(): string { } return resultsUrl } + +export function isGhes(): boolean { + const ghUrl = new URL( + process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ) + return ghUrl.hostname.toUpperCase() !== 'GITHUB.COM' +} diff --git a/packages/artifact/src/internal/upload/retention.ts b/packages/artifact/src/internal/upload/retention.ts new file mode 100644 index 00000000..aa16ba1b --- /dev/null +++ b/packages/artifact/src/internal/upload/retention.ts @@ -0,0 +1,34 @@ +import {Timestamp} from '../../generated' +import * as core from '@actions/core' + +export function getExpiration(retentionDays?: number): Timestamp | undefined { + if (!retentionDays) { + return undefined + } + + const maxRetentionDays = getRetentionDays() + if (maxRetentionDays && maxRetentionDays < retentionDays) { + core.warning( + `Retention days cannot be greater than the maximum allowed retention set within the repository. Using ${maxRetentionDays} instead.` + ) + retentionDays = maxRetentionDays + } + + const expirationDate = new Date() + expirationDate.setDate(expirationDate.getDate() + retentionDays) + + return Timestamp.fromDate(expirationDate) +} + +function getRetentionDays(): number | undefined { + const retentionDays = process.env['GITHUB_RETENTION_DAYS'] + if (!retentionDays) { + return undefined + } + const days = parseInt(retentionDays) + if (isNaN(days)) { + return undefined + } + + return days +} diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 087493a3..84a2e90e 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -1,6 +1,7 @@ import * as core from '@actions/core' import {UploadOptions} from './upload-options' import {UploadResponse} from './upload-response' +import {getExpiration} from './util' import {validateArtifactName} from './path-and-artifact-name-validation' import {createArtifactTwirpClient} from '../shared/artifact-twirp-client' import { @@ -9,7 +10,7 @@ import { validateRootDirectory } from './upload-zip-specification' import {getBackendIdsFromToken} from '../shared/util' -import {CreateArtifactRequest, Timestamp} from 'src/generated' +import {CreateArtifactRequest} from 'src/generated' export async function uploadArtifact( name: string, @@ -39,6 +40,10 @@ export async function uploadArtifact( success: false } } + core.debug(`Workflow Run Backend ID: ${backendIds.workflowRunBackendId}`) + core.debug( + `Workflow Job Run Backend ID: ${backendIds.workflowJobRunBackendId}` + ) // create the artifact client const artifactClient = createArtifactTwirpClient('upload') @@ -91,14 +96,3 @@ export async function uploadArtifact( return uploadResponse } - -function getExpiration(retentionDays?: number): Timestamp | undefined { - if (!retentionDays) { - return undefined - } - - const expirationDate = new Date() - expirationDate.setDate(expirationDate.getDate() + retentionDays) - - return Timestamp.fromDate(expirationDate) -} From 58858b5078932ce9bf8c3efa4e9c90a6d19673c7 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 17:48:53 -0700 Subject: [PATCH 09/10] don't use non-null assertions --- packages/artifact/__tests__/retention.test.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/artifact/__tests__/retention.test.ts b/packages/artifact/__tests__/retention.test.ts index 898c52a0..83de5342 100644 --- a/packages/artifact/__tests__/retention.test.ts +++ b/packages/artifact/__tests__/retention.test.ts @@ -14,11 +14,13 @@ describe('retention', () => { const exp = retention.getExpiration(30) expect(exp).toBeDefined() - const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above - const expected = new Date() - expected.setDate(expected.getDate() + 30) + if (exp) { + const expDate = Timestamp.toDate(exp) + const expected = new Date() + expected.setDate(expected.getDate() + 30) - expect(expDate).toEqual(expected) + expect(expDate).toEqual(expected) + } }) it('should return the max retention days if the inputted retention days is greater than the max retention days', () => { @@ -30,11 +32,13 @@ describe('retention', () => { const exp = retention.getExpiration(120) expect(exp).toBeDefined() - const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above - const expected = new Date() - expected.setDate(expected.getDate() + 90) + if (exp) { + const expDate = Timestamp.toDate(exp) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 90) - expect(expDate).toEqual(expected) + expect(expDate).toEqual(expected) + } }) it('should return undefined if the inputted retention days is undefined', () => { @@ -50,10 +54,12 @@ describe('retention', () => { const exp = retention.getExpiration(30) expect(exp).toBeDefined() - const expDate = Timestamp.toDate(exp!) // we check whether exp is defined above - const expected = new Date() - expected.setDate(expected.getDate() + 30) + if (exp) { + const expDate = Timestamp.toDate(exp) // we check whether exp is defined above + const expected = new Date() + expected.setDate(expected.getDate() + 30) - expect(expDate).toEqual(expected) + expect(expDate).toEqual(expected) + } }) }) From f03b6d639f1a0dd25060d742e2fe9a8f149beaf1 Mon Sep 17 00:00:00 2001 From: Bethany Date: Wed, 9 Aug 2023 17:50:46 -0700 Subject: [PATCH 10/10] update import --- packages/artifact/src/internal/upload/upload-artifact.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/artifact/src/internal/upload/upload-artifact.ts b/packages/artifact/src/internal/upload/upload-artifact.ts index 84a2e90e..bdef8f84 100644 --- a/packages/artifact/src/internal/upload/upload-artifact.ts +++ b/packages/artifact/src/internal/upload/upload-artifact.ts @@ -1,7 +1,7 @@ import * as core from '@actions/core' import {UploadOptions} from './upload-options' import {UploadResponse} from './upload-response' -import {getExpiration} from './util' +import {getExpiration} from './retention' import {validateArtifactName} from './path-and-artifact-name-validation' import {createArtifactTwirpClient} from '../shared/artifact-twirp-client' import {