1
0
Fork 0

consistent promise behavior for upload artifact

pull/1593/head
Rob Herley 2023-12-05 17:35:46 +00:00 committed by GitHub
parent 8ac8bf1d3d
commit 75a3586061
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 53 additions and 63 deletions

View File

@ -7,6 +7,7 @@ import {Timestamp, ArtifactServiceClientJSON} from '../src/generated'
import * as blobUpload from '../src/internal/upload/blob-upload' import * as blobUpload from '../src/internal/upload/blob-upload'
import {uploadArtifact} from '../src/internal/upload/upload-artifact' import {uploadArtifact} from '../src/internal/upload/upload-artifact'
import {noopLogs} from './common' import {noopLogs} from './common'
import {FilesNotFoundError} from '../src/internal/shared/errors'
describe('upload-artifact', () => { describe('upload-artifact', () => {
beforeEach(() => { beforeEach(() => {
@ -59,7 +60,6 @@ describe('upload-artifact', () => {
) )
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({ Promise.resolve({
isSuccess: true,
uploadSize: 1234, uploadSize: 1234,
sha256Hash: 'test-sha256-hash' sha256Hash: 'test-sha256-hash'
}) })
@ -84,7 +84,7 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload' '/home/user/files/plz-upload'
) )
expect(uploadResp).resolves.toEqual({success: true, size: 1234, id: 1}) expect(uploadResp).resolves.toEqual({size: 1234, id: 1})
}) })
it('should throw an error if the root directory is invalid', () => { it('should throw an error if the root directory is invalid', () => {
@ -107,7 +107,7 @@ describe('upload-artifact', () => {
expect(uploadResp).rejects.toThrow('Invalid root directory') expect(uploadResp).rejects.toThrow('Invalid root directory')
}) })
it('should return false if there are no files to upload', () => { it('should reject if there are no files to upload', () => {
jest jest
.spyOn(uploadZipSpecification, 'validateRootDirectory') .spyOn(uploadZipSpecification, 'validateRootDirectory')
.mockReturnValue() .mockReturnValue()
@ -124,7 +124,7 @@ describe('upload-artifact', () => {
], ],
'/home/user/files/plz-upload' '/home/user/files/plz-upload'
) )
expect(uploadResp).resolves.toEqual({success: false}) expect(uploadResp).rejects.toThrowError(FilesNotFoundError)
}) })
it('should reject if no backend IDs are found', () => { it('should reject if no backend IDs are found', () => {
@ -217,7 +217,7 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload' '/home/user/files/plz-upload'
) )
expect(uploadResp).resolves.toEqual({success: false}) expect(uploadResp).rejects.toThrow()
}) })
it('should return false if blob storage upload is unsuccessful', () => { it('should return false if blob storage upload is unsuccessful', () => {
@ -262,7 +262,7 @@ describe('upload-artifact', () => {
) )
jest jest
.spyOn(blobUpload, 'uploadZipToBlobStorage') .spyOn(blobUpload, 'uploadZipToBlobStorage')
.mockReturnValue(Promise.resolve({isSuccess: false})) .mockReturnValue(Promise.reject(new Error('boom')))
// ArtifactHttpClient mocks // ArtifactHttpClient mocks
jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token') jest.spyOn(config, 'getRuntimeToken').mockReturnValue('test-token')
@ -280,10 +280,10 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload' '/home/user/files/plz-upload'
) )
expect(uploadResp).resolves.toEqual({success: false}) expect(uploadResp).rejects.toThrow()
}) })
it('should return false if finalize artifact fails', () => { it('should reject if finalize artifact fails', () => {
const mockDate = new Date('2020-01-01') const mockDate = new Date('2020-01-01')
jest jest
.spyOn(uploadZipSpecification, 'validateRootDirectory') .spyOn(uploadZipSpecification, 'validateRootDirectory')
@ -325,7 +325,6 @@ describe('upload-artifact', () => {
) )
jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue( jest.spyOn(blobUpload, 'uploadZipToBlobStorage').mockReturnValue(
Promise.resolve({ Promise.resolve({
isSuccess: true,
uploadSize: 1234, uploadSize: 1234,
sha256Hash: 'test-sha256-hash' sha256Hash: 'test-sha256-hash'
}) })
@ -350,6 +349,6 @@ describe('upload-artifact', () => {
'/home/user/files/plz-upload' '/home/user/files/plz-upload'
) )
expect(uploadResp).resolves.toEqual({success: false}) expect(uploadResp).rejects.toThrow()
}) })
}) })

View File

@ -4,6 +4,7 @@ import {ArtifactClient, Client} from './internal/client'
* Exported functionality that we want to expose for any users of @actions/artifact * Exported functionality that we want to expose for any users of @actions/artifact
*/ */
export * from './internal/shared/interfaces' export * from './internal/shared/interfaces'
export * from './internal/shared/errors'
export {ArtifactClient} export {ArtifactClient}
export function create(): ArtifactClient { export function create(): ArtifactClient {

View File

@ -0,0 +1,21 @@
export class FilesNotFoundError extends Error {
files: string[]
constructor(files: string[] = []) {
let message = 'No files were found to upload'
if (files.length > 0) {
message += `: ${files.join(', ')}`
}
super(message)
this.files = files
this.name = 'FilesNotFoundError'
}
}
export class InvalidResponseError extends Error {
constructor(message: string) {
super(message)
this.name = 'InvalidResponseError'
}
}

View File

@ -4,11 +4,6 @@
* * * *
*****************************************************************************/ *****************************************************************************/
export interface UploadArtifactResponse { export interface UploadArtifactResponse {
/**
* Denotes if an artifact was successfully uploaded
*/
success: boolean
/** /**
* Total size of the artifact in bytes. Not provided if no artifact was uploaded * Total size of the artifact in bytes. Not provided if no artifact was uploaded
*/ */

View File

@ -7,11 +7,6 @@ import * as crypto from 'crypto'
import * as stream from 'stream' import * as stream from 'stream'
export interface BlobUploadResponse { export interface BlobUploadResponse {
/**
* If the upload was successful or not
*/
isSuccess: boolean
/** /**
* The total reported upload size in bytes. Empty if the upload failed * The total reported upload size in bytes. Empty if the upload failed
*/ */
@ -55,41 +50,28 @@ export async function uploadZipToBlobStorage(
zipUploadStream.pipe(uploadStream) // This stream is used for the upload zipUploadStream.pipe(uploadStream) // This stream is used for the upload
zipUploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the zip content that gets used. Integrity check zipUploadStream.pipe(hashStream).setEncoding('hex') // This stream is used to compute a hash of the zip content that gets used. Integrity check
try { core.info('Beginning upload of artifact content to blob storage')
core.info('Beginning upload of artifact content to blob storage')
await blockBlobClient.uploadStream( await blockBlobClient.uploadStream(
uploadStream, uploadStream,
bufferSize, bufferSize,
maxConcurrency, maxConcurrency,
options options
) )
core.info('Finished uploading artifact content to blob storage!') core.info('Finished uploading artifact content to blob storage!')
hashStream.end() hashStream.end()
sha256Hash = hashStream.read() as string sha256Hash = hashStream.read() as string
core.info(`SHA256 hash of uploaded artifact zip is ${sha256Hash}`) core.info(`SHA256 hash of uploaded artifact zip is ${sha256Hash}`)
} catch (error) {
core.warning(
`Failed to upload artifact zip to blob storage, error: ${error}`
)
return {
isSuccess: false
}
}
if (uploadByteCount === 0) { if (uploadByteCount === 0) {
core.warning( core.warning(
`No data was uploaded to blob storage. Reported upload byte count is 0` `No data was uploaded to blob storage. Reported upload byte count is 0.`
) )
return {
isSuccess: false
}
} }
return { return {
isSuccess: true,
uploadSize: uploadByteCount, uploadSize: uploadByteCount,
sha256Hash sha256Hash
} }

View File

@ -19,6 +19,7 @@ import {
FinalizeArtifactRequest, FinalizeArtifactRequest,
StringValue StringValue
} from '../../generated' } from '../../generated'
import {FilesNotFoundError, InvalidResponseError} from '../shared/errors'
export async function uploadArtifact( export async function uploadArtifact(
name: string, name: string,
@ -34,10 +35,9 @@ export async function uploadArtifact(
rootDirectory rootDirectory
) )
if (zipSpecification.length === 0) { if (zipSpecification.length === 0) {
core.warning(`No files were found to upload`) throw new FilesNotFoundError(
return { zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : []))
success: false )
}
} }
const zipUploadStream = await createZipUploadStream( const zipUploadStream = await createZipUploadStream(
@ -68,10 +68,9 @@ export async function uploadArtifact(
const createArtifactResp = const createArtifactResp =
await artifactClient.CreateArtifact(createArtifactReq) await artifactClient.CreateArtifact(createArtifactReq)
if (!createArtifactResp.ok) { if (!createArtifactResp.ok) {
core.warning(`Failed to create artifact`) throw new InvalidResponseError(
return { 'CreateArtifact: response from backend was not ok'
success: false )
}
} }
// Upload zip to blob storage // Upload zip to blob storage
@ -79,11 +78,6 @@ export async function uploadArtifact(
createArtifactResp.signedUploadUrl, createArtifactResp.signedUploadUrl,
zipUploadStream zipUploadStream
) )
if (uploadResult.isSuccess === false) {
return {
success: false
}
}
// finalize the artifact // finalize the artifact
const finalizeArtifactReq: FinalizeArtifactRequest = { const finalizeArtifactReq: FinalizeArtifactRequest = {
@ -104,10 +98,9 @@ export async function uploadArtifact(
const finalizeArtifactResp = const finalizeArtifactResp =
await artifactClient.FinalizeArtifact(finalizeArtifactReq) await artifactClient.FinalizeArtifact(finalizeArtifactReq)
if (!finalizeArtifactResp.ok) { if (!finalizeArtifactResp.ok) {
core.warning(`Failed to finalize artifact`) throw new InvalidResponseError(
return { 'FinalizeArtifact: response from backend was not ok'
success: false )
}
} }
const artifactId = BigInt(finalizeArtifactResp.artifactId) const artifactId = BigInt(finalizeArtifactResp.artifactId)
@ -116,7 +109,6 @@ export async function uploadArtifact(
) )
return { return {
success: true,
size: uploadResult.uploadSize, size: uploadResult.uploadSize,
id: Number(artifactId) id: Number(artifactId)
} }